UNPKG

ccxt

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

1,148 lines (1,146 loc) • 118 kB
// ---------------------------------------------------------------------------- // 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