ccxt
Version:
1,148 lines (1,146 loc) • 118 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import Exchange from './abstract/blofin.js';
import { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, InvalidOrder, AuthenticationError, RateLimitExceeded, InsufficientFunds } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TICK_SIZE } from './base/functions/number.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
// ---------------------------------------------------------------------------
/**
* @class blofin
* @augments Exchange
*/
export default class blofin extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'blofin',
'name': 'BloFin',
'countries': ['US'],
'version': 'v1',
'rateLimit': 100,
'pro': true,
'has': {
'CORS': undefined,
'spot': false,
'margin': false,
'swap': true,
'future': false,
'option': false,
'addMargin': false,
'borrowMargin': false,
'cancelAllOrders': false,
'cancelOrder': true,
'cancelOrders': true,
'closeAllPositions': false,
'closePosition': true,
'createDepositAddress': false,
'createMarketBuyOrderWithCost': false,
'createMarketSellOrderWithCost': false,
'createOrder': true,
'createOrders': true,
'createOrderWithTakeProfitAndStopLoss': true,
'createPostOnlyOrder': false,
'createReduceOnlyOrder': false,
'createStopLimitOrder': false,
'createStopLossOrder': true,
'createStopMarketOrder': false,
'createStopOrder': false,
'createTakeProfitOrder': true,
'createTriggerOrder': true,
'editOrder': false,
'fetchAccounts': false,
'fetchBalance': true,
'fetchBidsAsks': undefined,
'fetchBorrowInterest': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchCanceledOrders': false,
'fetchClosedOrder': false,
'fetchClosedOrders': true,
'fetchCrossBorrowRate': false,
'fetchCrossBorrowRates': false,
'fetchCurrencies': false,
'fetchDeposit': false,
'fetchDepositAddress': false,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': false,
'fetchDeposits': true,
'fetchDepositsWithdrawals': false,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': false,
'fetchFundingHistory': true,
'fetchFundingRate': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': false,
'fetchGreeks': false,
'fetchIndexOHLCV': false,
'fetchIsolatedBorrowRate': false,
'fetchIsolatedBorrowRates': false,
'fetchL3OrderBook': false,
'fetchLedger': true,
'fetchLedgerEntry': undefined,
'fetchLeverage': true,
'fetchLeverages': true,
'fetchLeverageTiers': false,
'fetchMarginMode': true,
'fetchMarginModes': false,
'fetchMarketLeverageTiers': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMySettlementHistory': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterest': false,
'fetchOpenInterestHistory': false,
'fetchOpenOrder': undefined,
'fetchOpenOrders': true,
'fetchOrder': undefined,
'fetchOrderBook': true,
'fetchOrderBooks': false,
'fetchOrders': false,
'fetchOrderTrades': true,
'fetchPosition': true,
'fetchPositionMode': true,
'fetchPositions': true,
'fetchPositionsForSymbol': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchSettlementHistory': false,
'fetchStatus': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': false,
'fetchTrades': true,
'fetchTradingFee': false,
'fetchTradingFees': false,
'fetchTradingLimits': false,
'fetchTransactionFee': false,
'fetchTransactionFees': false,
'fetchTransactions': false,
'fetchTransfer': false,
'fetchTransfers': false,
'fetchUnderlyingAssets': false,
'fetchVolatilityHistory': false,
'fetchWithdrawal': false,
'fetchWithdrawals': true,
'fetchWithdrawalWhitelist': false,
'reduceMargin': false,
'repayCrossMargin': false,
'setLeverage': true,
'setMargin': false,
'setMarginMode': true,
'setPositionMode': true,
'signIn': false,
'transfer': true,
'withdraw': false,
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1H',
'2h': '2H',
'4h': '4H',
'6h': '6H',
'8h': '8H',
'12h': '12H',
'1d': '1D',
'3d': '3D',
'1w': '1W',
'1M': '1M',
},
'hostname': 'www.blofin.com',
'urls': {
'logo': 'https://github.com/user-attachments/assets/518cdf80-f05d-4821-a3e3-d48ceb41d73b',
'api': {
'rest': 'https://openapi.blofin.com',
},
'test': {
'rest': 'https://demo-trading-openapi.blofin.com',
},
'referral': {
'url': 'https://blofin.com/register?referral_code=f79EsS',
'discount': 0.05,
},
'www': 'https://www.blofin.com',
'doc': 'https://blofin.com/docs',
},
'api': {
'public': {
'get': {
'market/instruments': 1,
'market/tickers': 1,
'market/books': 1,
'market/trades': 1,
'market/candles': 1,
'market/mark-price': 1,
'market/funding-rate': 1,
'market/funding-rate-history': 1,
},
},
'private': {
'get': {
'asset/balances': 1,
'trade/orders-pending': 1,
'trade/fills-history': 1,
'asset/deposit-history': 1,
'asset/withdrawal-history': 1,
'asset/bills': 1,
'account/balance': 1,
'account/positions': 1,
'account/leverage-info': 1,
'account/margin-mode': 1,
'account/position-mode': 1,
'account/batch-leverage-info': 1,
'trade/orders-tpsl-pending': 1,
'trade/orders-algo-pending': 1,
'trade/orders-history': 1,
'trade/orders-tpsl-history': 1,
'trade/orders-algo-history': 1,
'trade/order/price-range': 1,
'user/query-apikey': 1,
'affiliate/basic': 1,
'copytrading/instruments': 1,
'copytrading/account/balance': 1,
'copytrading/account/positions-by-order': 1,
'copytrading/account/positions-details-by-order': 1,
'copytrading/account/positions-by-contract': 1,
'copytrading/account/position-mode': 1,
'copytrading/account/leverage-info': 1,
'copytrading/trade/orders-pending': 1,
'copytrading/trade/pending-tpsl-by-contract': 1,
'copytrading/trade/position-history-by-order': 1,
'copytrading/trade/orders-history': 1,
'copytrading/trade/pending-tpsl-by-order': 1,
},
'post': {
'account/set-margin-mode': 1,
'account/set-position-mode': 1,
'trade/order': 1,
'trade/order-algo': 1,
'trade/cancel-order': 1,
'trade/cancel-algo': 1,
'account/set-leverage': 1,
'trade/batch-orders': 1,
'trade/order-tpsl': 1,
'trade/cancel-batch-orders': 1,
'trade/cancel-tpsl': 1,
'trade/close-position': 1,
'asset/transfer': 1,
'copytrading/account/set-position-mode': 1,
'copytrading/account/set-leverage': 1,
'copytrading/trade/place-order': 1,
'copytrading/trade/cancel-order': 1,
'copytrading/trade/place-tpsl-by-contract': 1,
'copytrading/trade/cancel-tpsl-by-contract': 1,
'copytrading/trade/place-tpsl-by-order': 1,
'copytrading/trade/cancel-tpsl-by-order': 1,
'copytrading/trade/close-position-by-order': 1,
'copytrading/trade/close-position-by-contract': 1,
},
},
},
'fees': {
'swap': {
'taker': this.parseNumber('0.00060'),
'maker': this.parseNumber('0.00020'),
},
},
'requiredCredentials': {
'apiKey': true,
'secret': true,
'password': true,
},
'features': {
'default': {
'sandbox': false,
'createOrder': {
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'leverage': false,
'marketBuyRequiresPrice': false,
'marketBuyByCost': false,
'selfTradePrevention': false,
'trailing': false,
'iceberg': false,
},
'createOrders': {
'max': 10,
},
'fetchMyTrades': {
'marginMode': false,
'limit': 100,
'daysBack': 100000,
'untilDays': 100000,
'symbolRequired': false,
},
'fetchOrder': undefined,
'fetchOpenOrders': {
'marginMode': false,
'limit': 100,
'trigger': true,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': undefined,
'fetchClosedOrders': {
'marginMode': false,
'limit': 1000,
'daysBack': 100000,
'daysBackCanceled': 1,
'untilDays': 100000,
'trigger': true,
'trailing': false,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 1440,
},
},
'spot': {
'extends': 'default',
'createOrder': {
'marginMode': false,
'triggerPrice': false,
'triggerPriceType': undefined,
'triggerDirection': false,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'hedged': false,
},
},
'forDerivatives': {
'extends': 'default',
'createOrder': {
'marginMode': true,
'triggerPrice': false,
'triggerPriceType': undefined,
'triggerDirection': false,
'stopLossPrice': true,
'takeProfitPrice': true,
'attachedStopLossTakeProfit': {
'triggerPriceType': undefined,
'price': true,
},
'hedged': true,
},
},
'swap': {
'linear': {
'extends': 'forDerivatives',
},
'inverse': undefined,
},
'future': {
'linear': undefined,
'inverse': undefined,
},
},
'exceptions': {
'exact': {
'400': BadRequest,
'401': AuthenticationError,
'500': ExchangeError,
'404': BadRequest,
'405': BadRequest,
'406': BadRequest,
'429': RateLimitExceeded,
'152001': BadRequest,
'152002': BadRequest,
'152003': BadRequest,
'152004': BadRequest,
'152005': BadRequest,
'152006': InvalidOrder,
'152007': InvalidOrder,
'152008': InvalidOrder,
'152009': InvalidOrder,
'150003': InvalidOrder,
'150004': InvalidOrder,
'542': InvalidOrder,
'102002': InvalidOrder,
'102005': InvalidOrder,
'102014': InvalidOrder,
'102015': InvalidOrder,
'102022': InvalidOrder,
'102037': InvalidOrder,
'102038': InvalidOrder,
'102039': InvalidOrder,
'102040': InvalidOrder,
'102047': InvalidOrder,
'102048': InvalidOrder,
'102049': InvalidOrder,
'102050': InvalidOrder,
'102051': InvalidOrder,
'102052': InvalidOrder,
'102053': InvalidOrder,
'102054': InvalidOrder,
'102055': InvalidOrder,
'102064': BadRequest,
'102065': BadRequest,
'102068': BadRequest,
'103013': ExchangeError,
'Order failed. Insufficient USDT margin in account': InsufficientFunds, // Insufficient USDT margin in account
},
'broad': {
'Internal Server Error': ExchangeNotAvailable,
'server error': ExchangeNotAvailable, // {"code":500,"data":{},"detailMsg":"","error_code":"500","error_message":"server error 1236805249","msg":"server error 1236805249"}
},
},
'httpExceptions': {
'429': ExchangeNotAvailable, // https://github.com/ccxt/ccxt/issues/9612
},
'precisionMode': TICK_SIZE,
'options': {
'brokerId': 'ec6dd3a7dd982d0b',
'accountsByType': {
'swap': 'futures',
'funding': 'funding',
'future': 'futures',
'copy_trading': 'copy_trading',
'earn': 'earn',
'spot': 'spot',
},
'accountsById': {
'funding': 'funding',
'futures': 'swap',
'copy_trading': 'copy_trading',
'earn': 'earn',
'spot': 'spot',
},
'defaultNetwork': 'ERC20',
'defaultNetworks': {
'ETH': 'ERC20',
'BTC': 'BTC',
'USDT': 'TRC20',
},
'networks': {
'BTC': 'Bitcoin',
'BEP20': 'BSC',
'ERC20': 'ERC20',
'TRC20': 'TRC20',
},
'fetchOpenInterestHistory': {
'timeframes': {
'5m': '5m',
'1h': '1H',
'8h': '8H',
'1d': '1D',
'5M': '5m',
'1H': '1H',
'8H': '8H',
'1D': '1D',
},
},
'fetchOHLCV': {
// 'type': 'Candles', // Candles or HistoryCandles, IndexCandles, MarkPriceCandles
'timezone': 'UTC', // UTC, HK
},
'fetchPositions': {
'method': 'privateGetAccountPositions', // privateGetAccountPositions or privateGetAccountPositionsHistory
},
'createOrder': 'privatePostTradeOrder',
'createMarketBuyOrderRequiresPrice': false,
'fetchMarkets': ['swap'],
'defaultType': 'swap',
'fetchLedger': {
'method': 'privateGetAssetBills',
},
'fetchOpenOrders': {
'method': 'privateGetTradeOrdersPending',
},
'cancelOrders': {
'method': 'privatePostTradeCancelBatchOrders',
},
'fetchCanceledOrders': {
'method': 'privateGetTradeOrdersHistory', // privateGetTradeOrdersTpslHistory
},
'fetchClosedOrders': {
'method': 'privateGetTradeOrdersHistory', // privateGetTradeOrdersTpslHistory
},
'withdraw': {
// a funding password credential is required by the exchange for the
// withdraw call (not to be confused with the api password credential)
'password': undefined,
'pwd': undefined, // password or pwd both work
},
'exchangeType': {
'spot': 'SPOT',
'swap': 'SWAP',
'SPOT': 'SPOT',
'SWAP': 'SWAP',
},
},
});
}
/**
* @method
* @name blofin#fetchMarkets
* @description retrieves data on all markets for blofin
* @see https://blofin.com/docs#get-instruments
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const response = await this.publicGetMarketInstruments(params);
const data = this.safeList(response, 'data', []);
return this.parseMarkets(data);
}
parseMarket(market) {
const id = this.safeString(market, 'instId');
const type = this.safeStringLower(market, 'instType');
const spot = (type === 'spot');
const future = (type === 'future');
const swap = (type === 'swap');
const option = (type === 'option');
const contract = swap || future;
const baseId = this.safeString(market, 'baseCurrency');
const quoteId = this.safeString(market, 'quoteCurrency');
const settleId = this.safeString(market, 'quoteCurrency');
const settle = this.safeCurrencyCode(settleId);
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
let symbol = base + '/' + quote;
if (swap) {
symbol = symbol + ':' + settle;
}
const expiry = undefined;
const strikePrice = undefined;
const optionType = undefined;
const tickSize = this.safeString(market, 'tickSize');
const fees = this.safeDict2(this.fees, type, 'trading', {});
const taker = this.safeNumber(fees, 'taker');
const maker = this.safeNumber(fees, 'maker');
let maxLeverage = this.safeString(market, 'maxLeverage', '100');
maxLeverage = Precise.stringMax(maxLeverage, '1');
const isActive = (this.safeString(market, 'state') === 'live');
return this.safeMarketStructure({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'settle': settle,
'settleId': settleId,
'type': type,
'spot': spot,
'option': option,
'margin': spot && (Precise.stringGt(maxLeverage, '1')),
'swap': swap,
'future': future,
'active': isActive,
'taker': taker,
'maker': maker,
'contract': contract,
'linear': contract ? (quoteId === settleId) : undefined,
'inverse': contract ? (baseId === settleId) : undefined,
'contractSize': contract ? this.safeNumber(market, 'contractValue') : undefined,
'expiry': expiry,
'expiryDatetime': expiry,
'strike': strikePrice,
'optionType': optionType,
'created': this.safeInteger(market, 'listTime'),
'precision': {
'amount': this.safeNumber(market, 'lotSize'),
'price': this.parseNumber(tickSize),
},
'limits': {
'leverage': {
'min': this.parseNumber('1'),
'max': this.parseNumber(maxLeverage),
},
'amount': {
'min': this.safeNumber(market, 'minSize'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': market,
});
}
/**
* @method
* @name blofin#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://blofin.com/docs#get-order-book
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
limit = (limit === undefined) ? 50 : limit;
if (limit !== undefined) {
request['size'] = limit; // max 100
}
const response = await this.publicGetMarketBooks(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "asks": [
// ["0.07228","4.211619","0","2"], // price, amount, liquidated orders, total open orders
// ["0.0723","299.880364","0","2"],
// ["0.07231","3.72832","0","1"],
// ],
// "bids": [
// ["0.07221","18.5","0","1"],
// ["0.0722","18.5","0","1"],
// ["0.07219","0.505407","0","1"],
// ],
// "ts": "1621438475342"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
const timestamp = this.safeInteger(first, 'ts');
return this.parseOrderBook(first, symbol, timestamp);
}
parseTicker(ticker, market = undefined) {
//
// response similar for REST & WS
//
// {
// instId: "ADA-USDT",
// ts: "1707736811486",
// last: "0.5315",
// lastSize: "4",
// askPrice: "0.5318",
// askSize: "248",
// bidPrice: "0.5315",
// bidSize: "63",
// open24h: "0.5555",
// high24h: "0.5563",
// low24h: "0.5315",
// volCurrency24h: "198560100",
// vol24h: "1985601",
// }
//
const timestamp = this.safeInteger(ticker, 'ts');
const marketId = this.safeString(ticker, 'instId');
market = this.safeMarket(marketId, market, '-');
const symbol = market['symbol'];
const last = this.safeString(ticker, 'last');
const open = this.safeString(ticker, 'open24h');
const spot = this.safeBool(market, 'spot', false);
const quoteVolume = spot ? this.safeString(ticker, 'volCurrency24h') : undefined;
const baseVolume = this.safeString(ticker, 'vol24h');
const high = this.safeString(ticker, 'high24h');
const low = this.safeString(ticker, 'low24h');
return this.safeTicker({
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'high': high,
'low': low,
'bid': this.safeString(ticker, 'bidPrice'),
'bidVolume': this.safeString(ticker, 'bidSize'),
'ask': this.safeString(ticker, 'askPrice'),
'askVolume': this.safeString(ticker, 'askSize'),
'vwap': undefined,
'open': open,
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': undefined,
'average': undefined,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'indexPrice': this.safeString(ticker, 'indexPrice'),
'markPrice': this.safeString(ticker, 'markPrice'),
'info': ticker,
}, market);
}
/**
* @method
* @name blofin#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://blofin.com/docs#get-tickers
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTicker(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
const response = await this.publicGetMarketTickers(this.extend(request, params));
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
return this.parseTicker(first, market);
}
/**
* @method
* @name blofin#fetchMarkPrice
* @description fetches mark price for the market
* @see https://docs.blofin.com/index.html#get-mark-price
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.subType] "linear" or "inverse"
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchMarkPrice(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'symbol': market['id'],
};
const response = await this.publicGetMarketMarkPrice(this.extend(request, params));
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
return this.parseTicker(first, market);
}
/**
* @method
* @name blofin#fetchTickers
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @see https://blofin.com/docs#get-tickers
* @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTickers(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols);
const response = await this.publicGetMarketTickers(params);
const tickers = this.safeList(response, 'data', []);
return this.parseTickers(tickers, symbols);
}
parseTrade(trade, market = undefined) {
//
// fetch trades (response similar for REST & WS)
//
// {
// "tradeId": "3263934920",
// "instId": "LTC-USDT",
// "price": "67.87",
// "size": "1",
// "side": "buy",
// "ts": "1707232020854"
// }
//
// my trades
// {
// "instId": "LTC-USDT",
// "tradeId": "1440847",
// "orderId": "2075705202",
// "fillPrice": "67.850000000000000000",
// "fillSize": "1.000000000000000000",
// "fillPnl": "0.000000000000000000",
// "side": "buy",
// "positionSide": "net",
// "fee": "0.040710000000000000",
// "ts": "1707224678878",
// "brokerId": ""
// }
//
const id = this.safeString(trade, 'tradeId');
const marketId = this.safeString(trade, 'instId');
market = this.safeMarket(marketId, market, '-');
const symbol = market['symbol'];
const timestamp = this.safeInteger(trade, 'ts');
const price = this.safeString2(trade, 'price', 'fillPrice');
const amount = this.safeString2(trade, 'size', 'fillSize');
const side = this.safeString(trade, 'side');
const orderId = this.safeString(trade, 'orderId');
const feeCost = this.safeString(trade, 'fee');
let fee = undefined;
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': market['settle'],
};
}
return this.safeTrade({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': symbol,
'id': id,
'order': orderId,
'type': undefined,
'takerOrMaker': undefined,
'side': side,
'price': price,
'amount': amount,
'cost': undefined,
'fee': fee,
}, market);
}
/**
* @method
* @name blofin#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://blofin.com/docs#get-trades
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.paginate] *only applies to publicGetMarketHistoryTrades* default false, when true will automatically paginate by calling this endpoint multiple times
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchTrades', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallCursor('fetchTrades', symbol, since, limit, params, 'tradeId', 'after', undefined, 100);
}
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
let response = undefined;
if (limit !== undefined) {
request['limit'] = limit; // default 100
}
let method = undefined;
[method, params] = this.handleOptionAndParams(params, 'fetchTrades', 'method', 'publicGetMarketTrades');
if (method === 'publicGetMarketTrades') {
response = await this.publicGetMarketTrades(this.extend(request, params));
}
const data = this.safeList(response, 'data', []);
return this.parseTrades(data, market, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
//
// [
// "1678928760000", // timestamp
// "24341.4", // open
// "24344", // high
// "24313.2", // low
// "24323", // close
// "628", // contract volume
// "2.5819", // base volume
// "62800", // quote volume
// "0" // candlestick state
// ]
//
return [
this.safeInteger(ohlcv, 0),
this.safeNumber(ohlcv, 1),
this.safeNumber(ohlcv, 2),
this.safeNumber(ohlcv, 3),
this.safeNumber(ohlcv, 4),
this.safeNumber(ohlcv, 6),
];
}
/**
* @method
* @name blofin#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://blofin.com/docs#get-candlesticks
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest candle to fetch
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 100);
}
if (limit === undefined) {
limit = 100; // default 100, max 100
}
const request = {
'instId': market['id'],
'bar': this.safeString(this.timeframes, timeframe, timeframe),
'limit': limit,
};
const until = this.safeInteger(params, 'until');
if (until !== undefined) {
request['after'] = until;
params = this.omit(params, 'until');
}
let response = undefined;
response = await this.publicGetMarketCandles(this.extend(request, params));
const data = this.safeList(response, 'data', []);
return this.parseOHLCVs(data, market, timeframe, since, limit);
}
/**
* @method
* @name blofin#fetchFundingRateHistory
* @description fetches historical funding rate prices
* @see https://blofin.com/docs#get-funding-rate-history
* @param {string} symbol unified symbol of the market to fetch the funding rate history for
* @param {int} [since] timestamp in ms of the earliest funding rate to fetch
* @param {int} [limit] the maximum amount of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure} to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {object[]} a list of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure}
*/
async fetchFundingRateHistory(symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired(this.id + ' fetchFundingRateHistory() requires a symbol argument');
}
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchFundingRateHistory', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDeterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params);
}
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
if (since !== undefined) {
request['before'] = Math.max(since - 1, 0);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetMarketFundingRateHistory(this.extend(request, params));
const rates = [];
const data = this.safeList(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const rate = data[i];
const timestamp = this.safeInteger(rate, 'fundingTime');
rates.push({
'info': rate,
'symbol': market['symbol'],
'fundingRate': this.safeNumber(rate, 'fundingRate'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
});
}
const sorted = this.sortBy(rates, 'timestamp');
return this.filterBySymbolSinceLimit(sorted, market['symbol'], since, limit);
}
parseFundingRate(contract, market = undefined) {
//
// {
// "fundingRate": "0.00027815",
// "fundingTime": "1634256000000",
// "instId": "BTC-USD-SWAP",
// }
//
const marketId = this.safeString(contract, 'instId');
const symbol = this.safeSymbol(marketId, market);
const fundingTime = this.safeInteger(contract, 'fundingTime');
// > The current interest is 0.
return {
'info': contract,
'symbol': symbol,
'markPrice': undefined,
'indexPrice': undefined,
'interestRate': this.parseNumber('0'),
'estimatedSettlePrice': undefined,
'timestamp': undefined,
'datetime': undefined,
'fundingRate': this.safeNumber(contract, 'fundingRate'),
'fundingTimestamp': fundingTime,
'fundingDatetime': this.iso8601(fundingTime),
'nextFundingRate': undefined,
'nextFundingTimestamp': undefined,
'nextFundingDatetime': undefined,
'previousFundingRate': undefined,
'previousFundingTimestamp': undefined,
'previousFundingDatetime': undefined,
'interval': undefined,
};
}
/**
* @method
* @name blofin#fetchFundingRate
* @description fetch the current funding rate
* @see https://blofin.com/docs#get-funding-rate
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [funding rate structure]{@link https://docs.ccxt.com/#/?id=funding-rate-structure}
*/
async fetchFundingRate(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['swap']) {
throw new ExchangeError(this.id + ' fetchFundingRate() is only valid for swap markets');
}
const request = {
'instId': market['id'],
};
const response = await this.publicGetMarketFundingRate(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "fundingRate": "0.00027815",
// "fundingTime": "1634256000000",
// "instId": "BTC-USD-SWAP",
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const entry = this.safeDict(data, 0, {});
return this.parseFundingRate(entry, market);
}
parseBalanceByType(response) {
const data = this.safeList(response, 'data');
if ((data !== undefined) && Array.isArray(data)) {
return this.parseFundingBalance(response);
}
else {
return this.parseBalance(response);
}
}
parseBalance(response) {
//
// "data" similar for REST & WS
//
// {
// "code": "0",
// "msg": "success",
// "data": {
// "ts": "1697021343571",
// "totalEquity": "10011254.077985990315787910",
// "isolatedEquity": "861.763132108800000000",
// "details": [
// {
// "currency": "USDT",
// "equity": "10014042.988958415234430699548",
// "balance": "10013119.885958415234430699",
// "ts": "1697021343571",
// "isolatedEquity": "862.003200000000000000048",
// "available": "9996399.4708691159703362725",
// "availableEquity": "9996399.4708691159703362725",
// "frozen": "15805.149672632597427761",
// "orderFrozen": "14920.994472632597427761",
// "equityUsd": "10011254.077985990315787910",
// "isolatedUnrealizedPnl": "-22.151999999999999999952",
// "bonus": "0" // present only in REST
// "unrealizedPnl": "0" // present only in WS
// }
// ]
// }
// }
//
const result = { 'info': response };
const data = this.safeDict(response, 'data', {});
const timestamp = this.safeInteger(data, 'ts');
const details = this.safeList(data, 'details', []);
for (let i = 0; i < details.length; i++) {
const balance = details[i];
const currencyId = this.safeString(balance, 'currency');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
// it may be incorrect to use total, free and used for swap accounts
const eq = this.safeString(balance, 'equity');
const availEq = this.safeString(balance, 'available');
if ((eq === undefined) || (availEq === undefined)) {
account['free'] = this.safeString(balance, 'availableEquity');
account['used'] = this.safeString(balance, 'frozen');
}
else {
account['total'] = eq;
account['free'] = availEq;
}
result[code] = account;
}
result['timestamp'] = timestamp;
result['datetime'] = this.iso8601(timestamp);
return this.safeBalance(result);
}
parseFundingBalance(response) {
//
// {
// "code": "0",
// "msg": "success",
// "data": [
// {
// "currency": "USDT",
// "balance": "10012514.919418081548717298",
// "available": "9872132.414278782284622898",
// "frozen": "138556.471805965930761067",
// "bonus": "0"
// }
// ]
// }
//
const result = { 'info': response };
const data = this.safeList(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const balance = data[i];
const currencyId = this.safeString(balance, 'currency');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
// it may be incorrect to use total, free and used for swap accounts
account['total'] = this.safeString(balance, 'balance');
account['free'] = this.safeString(balance, 'available');
account['used'] = this.safeString(balance, 'frozen');
result[code] = account;
}
return this.safeBalance(result);
}
parseTradingFee(fee, market = undefined) {
return {
'info': fee,
'symbol': this.safeSymbol(undefined, market),
// blofin returns the fees as negative values opposed to other exchanges, so the sign needs to be flipped
'maker': this.parseNumber(Precise.stringNeg(this.safeString2(fee, 'maker', 'makerU'))),
'taker': this.parseNumber(Precise.stringNeg(this.safeString2(fee, 'taker', 'takerU'))),
'percentage': undefined,
'tierBased': undefined,
};
}
/**
* @method
* @name blofin#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://blofin.com/docs#get-balance
* @see https://blofin.com/docs#get-futures-account-balance
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.accountType] the type of account to fetch the balance for, either 'funding' or 'futures' or 'copy_trading' or 'earn'
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
*/
async fetchBalance(params = {}) {
await this.loadMarkets();
let accountType = undefined;
[accountType, params] = this.handleOptionAndParams2(params, 'fetchBalance', 'accountType', 'type');
const request = {};
let response = undefined;
if (accountType !== undefined && accountType !== 'swap') {
const options = this.safeDict(this.options, 'accountsByType', {});
const parsedAccountType = this.safeString(options, accountType, accountType);
request['accountType'] = parsedAccountType;
response = await this.privateGetAssetBalances(this.extend(request, params));
}
else {
response = await this.privateGetAccountBalance(this.extend(request, par