UNPKG

ccxt

Version:

A cryptocurrency trading API with more than 100 exchanges in JavaScript / TypeScript / Python / C# / PHP / Go

1,171 lines • 86.3 kB
// --------------------------------------------------------------------------- import { Precise } from './base/Precise.js'; import Exchange from './abstract/foxbit.js'; import { AccountSuspended, ArgumentsRequired, AuthenticationError, BadRequest, BadSymbol, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OnMaintenance, PermissionDenied, RateLimitExceeded } from './base/errors.js'; import { DECIMAL_PLACES } from './base/functions/number.js'; import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; // --------------------------------------------------------------------------- /** * @class foxbit * @augments Exchange */ export default class foxbit extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'foxbit', 'name': 'Foxbit', 'countries': ['pt-BR'], // 300 requests per 10 seconds = 30 requests per second // rateLimit = 1000 ms / 30 requests ~= 33.334 'rateLimit': 33.334, 'version': '1', 'comment': 'Foxbit Exchange', 'certified': false, 'pro': false, 'has': { 'CORS': true, 'spot': true, 'margin': undefined, 'swap': undefined, 'future': undefined, 'option': undefined, 'cancelAllOrders': true, 'cancelOrder': true, 'createLimitBuyOrder': true, 'createLimitSellOrder': true, 'createMarketBuyOrder': true, 'createMarketSellOrder': true, 'createOrder': true, 'fecthOrderBook': true, 'fetchBalance': true, 'fetchCanceledOrders': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchL2OrderBook': true, 'fetchLedger': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrders': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'fetchTransactions': true, 'fetchWithdrawals': true, 'loadMarkets': true, 'sandbox': false, 'withdraw': true, 'ws': false, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '12h': '12h', '1d': '1d', '1w': '1w', '2w': '2w', '1M': '1M', }, 'urls': { 'logo': 'https://github.com/user-attachments/assets/1f8faca2-ae2f-4222-b33e-5671e7d873dd', 'api': { 'public': 'https://api.foxbit.com.br', 'private': 'https://api.foxbit.com.br', 'status': 'https://metadata-v2.foxbit.com.br/api', }, 'www': 'https://app.foxbit.com.br', 'doc': [ 'https://docs.foxbit.com.br', ], }, 'precisionMode': DECIMAL_PLACES, 'exceptions': { 'exact': { // https://docs.foxbit.com.br/rest/v3/#tag/API-Codes/Errors '400': BadRequest, '429': RateLimitExceeded, '404': BadRequest, '500': ExchangeError, '2001': AuthenticationError, '2002': AuthenticationError, '2003': AuthenticationError, '2004': BadRequest, '2005': PermissionDenied, '3001': PermissionDenied, '3002': PermissionDenied, '3003': AccountSuspended, '4001': BadRequest, '4002': InsufficientFunds, '4003': InvalidOrder, '4004': BadSymbol, '4005': BadRequest, '4007': ExchangeError, '4008': InvalidOrder, '4009': PermissionDenied, '4011': RateLimitExceeded, '4012': ExchangeError, '5001': ExchangeNotAvailable, '5002': OnMaintenance, '5003': OnMaintenance, '5004': InvalidOrder, '5005': InvalidOrder, '5006': InvalidOrder, // Significant price deviation detected, exceeding acceptable limits. The order price is exceeding acceptable limits from market to complete your request. }, 'broad': { // todo: add details messages that can be usefull here, like when market is not found }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'api': { 'v3': { 'public': { 'get': { 'currencies': 5, 'markets': 5, 'markets/ticker/24hr': 60, 'markets/{market}/orderbook': 6, 'markets/{market}/candlesticks': 12, 'markets/{market}/trades/history': 12, 'markets/{market}/ticker/24hr': 15, // 4 requests per 2 seconds }, }, 'private': { 'get': { 'accounts': 2, 'accounts/{symbol}/transactions': 60, 'orders': 2, 'orders/by-order-id/{id}': 2, 'trades': 6, 'deposits/address': 10, 'deposits': 10, 'withdrawals': 10, 'me/fees/trading': 60, // 1 requests per 2 seconds }, 'post': { 'orders': 2, 'orders/batch': 7.5, 'orders/cancel-replace': 3, 'withdrawals': 10, // 3 requests per second }, 'put': { 'orders/cancel': 2, // 30 requests per 2 seconds }, }, }, 'status': { 'public': { 'get': { 'status': 30, // 1 request per second }, }, }, }, 'fees': { 'trading': { 'feeSide': 'get', 'tierBased': false, 'percentage': true, 'taker': this.parseNumber('0.005'), 'maker': this.parseNumber('0.0025'), }, }, 'options': { 'sandboxMode': false, 'networksById': { 'algorand': 'ALGO', 'arbitrum': 'ARBITRUM', 'avalanchecchain': 'AVAX', 'bitcoin': 'BTC', 'bitcoincash': 'BCH', 'bsc': 'BEP20', 'cardano': 'ADA', 'cosmos': 'ATOM', 'dogecoin': 'DOGE', 'erc20': 'ETH', 'hedera': 'HBAR', 'litecoin': 'LTC', 'near': 'NEAR', 'optimism': 'OPTIMISM', 'polkadot': 'DOT', 'polygon': 'MATIC', 'ripple': 'XRP', 'solana': 'SOL', 'stacks': 'STX', 'stellar': 'XLM', 'tezos': 'XTZ', 'trc20': 'TRC20', }, 'networks': { 'ALGO': 'algorand', 'ARBITRUM': 'arbitrum', 'AVAX': 'avalanchecchain', 'BTC': 'bitcoin', 'BCH': 'bitcoincash', 'BEP20': 'bsc', 'ADA': 'cardano', 'ATOM': 'cosmos', 'DOGE': 'dogecoin', 'ETH': 'erc20', 'HBAR': 'hedera', 'LTC': 'litecoin', 'NEAR': 'near', 'OPTIMISM': 'optimism', 'DOT': 'polkadot', 'MATIC': 'polygon', 'XRP': 'ripple', 'SOL': 'solana', 'STX': 'stacks', 'XLM': 'stellar', 'XTZ': 'tezos', 'TRC20': 'trc20', }, }, 'features': { 'spot': { 'sandbox': false, 'createOrder': { 'marginMode': false, 'triggerPrice': true, 'triggerPriceType': { 'last': true, 'mark': false, 'index': false, }, 'triggerDirection': false, 'stopLossPrice': false, 'takeProfitPrice': false, 'attachedStopLossTakeProfit': undefined, 'timeInForce': { 'GTC': true, 'FOK': true, 'IOC': true, 'PO': true, 'GTD': false, }, 'hedged': false, 'leverage': false, 'marketBuyByCost': false, 'marketBuyRequiresPrice': false, 'selfTradePrevention': { 'expire_maker': true, 'expire_taker': true, 'expire_both': true, 'none': true, // foxbit prevents self trading by default, no params can change this }, 'trailing': false, 'icebergAmount': false, }, 'createOrders': { 'max': 5, }, 'fetchMyTrades': { 'marginMode': false, 'limit': 100, 'daysBack': 90, 'untilDays': 10000, 'symbolRequired': true, }, 'fetchOrder': { 'marginMode': false, 'limit': 1, 'daysBack': 90, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': 100, 'daysBack': 90, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOrders': { 'marginMode': true, 'limit': 100, 'daysBack': 90, 'untilDays': 10000, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchClosedOrders': { 'marginMode': true, 'limit': 100, 'daysBack': 90, 'daysBackCanceled': 90, 'untilDays': 10000, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOHLCV': { 'limit': 500, }, }, }, }); } async fetchCurrencies(params = {}) { const response = await this.v3PublicGetCurrencies(params); // { // "data": [ // { // "symbol": "btc", // "name": "Bitcoin", // "type": "CRYPTO", // "precision": 8, // "deposit_info": { // "min_to_confirm": "1", // "min_amount": "0.0001" // }, // "withdraw_info": { // "enabled": true, // "min_amount": "0.0001", // "fee": "0.0001" // }, // "category": { // "code": "cripto", // "name": "Cripto" // }, // "networks": [ // { // "name": "Bitcoin", // "code": "btc", // "deposit_info": { // status: "ENABLED", // }, // "withdraw_info": { // "status": "ENABLED", // "fee": "0.0001", // }, // "has_destination_tag": false // } // ] // } // ] // } const data = this.safeList(response, 'data', []); const result = {}; for (let i = 0; i < data.length; i++) { const currency = data[i]; const precision = this.safeInteger(currency, 'precision'); const currencyId = this.safeString(currency, 'symbol'); const name = this.safeString(currency, 'name'); const code = this.safeCurrencyCode(currencyId); const depositInfo = this.safeDict(currency, 'deposit_info'); const withdrawInfo = this.safeDict(currency, 'withdraw_info'); const networks = this.safeList(currency, 'networks', []); const type = this.safeStringLower(currency, 'type'); const parsedNetworks = {}; for (let j = 0; j < networks.length; j++) { const network = networks[j]; const networkId = this.safeString(network, 'code'); const networkCode = this.networkIdToCode(networkId, code); const networkWithdrawInfo = this.safeDict(network, 'withdraw_info'); const networkDepositInfo = this.safeDict(network, 'deposit_info'); const isWithdrawEnabled = this.safeString(networkWithdrawInfo, 'status') === 'ENABLED'; const isDepositEnabled = this.safeString(networkDepositInfo, 'status') === 'ENABLED'; parsedNetworks[networkCode] = { 'info': currency, 'id': networkId, 'network': networkCode, 'name': this.safeString(network, 'name'), 'deposit': isDepositEnabled, 'withdraw': isWithdrawEnabled, 'active': true, 'precision': precision, 'fee': this.safeNumber(networkWithdrawInfo, 'fee'), 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': this.safeNumber(depositInfo, 'min_amount'), 'max': undefined, }, 'withdraw': { 'min': this.safeNumber(withdrawInfo, 'min_amount'), 'max': undefined, }, }, }; } if (this.safeDict(result, code) === undefined) { result[code] = this.safeCurrencyStructure({ 'id': currencyId, 'code': code, 'info': currency, 'name': name, 'active': true, 'type': type, 'deposit': this.safeBool(depositInfo, 'enabled', false), 'withdraw': this.safeBool(withdrawInfo, 'enabled', false), 'fee': this.safeNumber(withdrawInfo, 'fee'), 'precision': precision, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': this.safeNumber(depositInfo, 'min_amount'), 'max': undefined, }, 'withdraw': { 'min': this.safeNumber(withdrawInfo, 'min_amount'), 'max': undefined, }, }, 'networks': parsedNetworks, }); } } return result; } /** * @method * @name foxbit#fetchMarkets * @description Retrieves data on all markets for foxbit. * @see https://docs.foxbit.com.br/rest/v3/#tag/Market-Data/operation/MarketsController_index * @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.v3PublicGetMarkets(params); // { // "data": [ // { // "symbol": "btcbrl", // "quantity_min": "0.00000236", // "quantity_increment": "0.00000001", // "quantity_precision": 8, // "price_min": "0.0001", // "price_increment": "0.0001", // "price_precision": 4, // "default_fees": { // "maker": "0.001", // "taker": "0.001" // }, // "base": { // "symbol": "btc", // "name": "Bitcoin", // "type": "CRYPTO", // "precision": 8, // "category": { // "code": "cripto", // "name": "Cripto" // }, // "deposit_info": { // "min_to_confirm": "1", // "min_amount": "0.0001", // "enabled": true // }, // "withdraw_info": { // "enabled": true, // "min_amount": "0.0001", // "fee": "0.0001" // }, // "networks": [ // { // "name": "Bitcoin", // "code": "bitcoin", // "deposit_info": { // "status": "ENABLED" // }, // "withdraw_info": { // "status": "ENABLED", // "fee": "0.0001" // }, // "has_destination_tag": false // } // ], // "default_network_code": "bitcoin" // }, // "quote": { // "symbol": "btc", // "name": "Bitcoin", // "type": "CRYPTO", // "precision": 8, // "category": { // "code": "cripto", // "name": "Cripto" // }, // "deposit_info": { // "min_to_confirm": "1", // "min_amount": "0.0001", // "enabled": true // }, // "withdraw_info": { // "enabled": true, // "min_amount": "0.0001", // "fee": "0.0001" // }, // "networks": [ // { // "name": "Bitcoin", // "code": "bitcoin", // "deposit_info": { // "status": "ENABLED" // }, // "withdraw_info": { // "status": "ENABLED", // "fee": "0.0001" // }, // "has_destination_tag": false // } // ], // "default_network_code": "bitcoin" // }, // "order_type": [ // "LIMIT", // "MARKET", // "INSTANT", // "STOP_LIMIT", // "STOP_MARKET" // ] // } // ] // } const markets = this.safeList(response, 'data', []); return this.parseMarkets(markets); } /** * @method * @name foxbit#fetchTicker * @description Get last 24 hours ticker information, in real-time, for given market. * @see https://docs.foxbit.com.br/rest/v3/#tag/Market-Data/operation/MarketsController_ticker * @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 = { 'market': market['id'], }; const response = await this.v3PublicGetMarketsMarketTicker24hr(this.extend(request, params)); // { // "data": [ // { // "market_symbol": "btcbrl", // "last_trade": { // "price": "358504.69340000", // "volume": "0.00027893", // "date": "2024-01-01T00:00:00.000Z" // }, // "rolling_24h": { // "price_change": "3211.87290000", // "price_change_percent": "0.90400726", // "volume": "20.03206866", // "trades_count": "4376", // "open": "355292.82050000", // "high": "362999.99990000", // "low": "355002.88880000" // }, // "best": { // "ask": { // "price": "358504.69340000", // "volume": "0.00027893" // }, // "bid": { // "price": "358504.69340000", // "volume": "0.00027893" // } // } // } // ] // } const data = this.safeList(response, 'data', []); const result = this.safeDict(data, 0, {}); return this.parseTicker(result, market); } /** * @method * @name foxbit#fetchTickers * @description Retrieve the ticker data of all markets. * @see https://docs.foxbit.com.br/rest/v3/#tag/Market-Data/operation/MarketsController_tickers * @param {string[]|undefined} 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.v3PublicGetMarketsTicker24hr(params); // { // "data": [ // { // "market_symbol": "btcbrl", // "last_trade": { // "price": "358504.69340000", // "volume": "0.00027893", // "date": "2024-01-01T00:00:00.000Z" // }, // "rolling_24h": { // "price_change": "3211.87290000", // "price_change_percent": "0.90400726", // "volume": "20.03206866", // "trades_count": "4376", // "open": "355292.82050000", // "high": "362999.99990000", // "low": "355002.88880000" // }, // } // ] // } const data = this.safeList(response, 'data', []); return this.parseTickers(data, symbols); } /** * @method * @name foxbit#fetchTradingFees * @description fetch the trading fees for multiple markets * @see https://docs.foxbit.com.br/rest/v3/#tag/Member-Info/operation/MembersController_listTradingFees * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols */ async fetchTradingFees(params = {}) { await this.loadMarkets(); const response = await this.v3PrivateGetMeFeesTrading(params); // [ // { // "market_symbol": "btcbrl", // "maker": "0.0025", // "taker": "0.005" // } // ] const data = this.safeList(response, 'data', []); const result = {}; for (let i = 0; i < data.length; i++) { const entry = data[i]; const marketId = this.safeString(entry, 'market_symbol'); const market = this.safeMarket(marketId); const symbol = market['symbol']; result[symbol] = this.parseTradingFee(entry, market); } return result; } /** * @method * @name foxbit#fetchOrderBook * @description Exports a copy of the order book of a specific market. * @see https://docs.foxbit.com.br/rest/v3/#tag/Market-Data/operation/MarketsController_findOrderbook * @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, the maximum is 100 * @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 defaultLimit = 20; const request = { 'market': market['id'], 'depth': (limit === undefined) ? defaultLimit : limit, }; const response = await this.v3PublicGetMarketsMarketOrderbook(this.extend(request, params)); // { // "sequence_id": 1234567890, // "timestamp": 1713187921336, // "bids": [ // [ // "3.00000000", // "300.00000000" // ], // [ // "1.70000000", // "310.00000000" // ] // ], // "asks": [ // [ // "3.00000000", // "300.00000000" // ], // [ // "2.00000000", // "321.00000000" // ] // ] // } const timestamp = this.safeInteger(response, 'timestamp'); return this.parseOrderBook(response, symbol, timestamp); } /** * @method * @name foxbit#fetchTrades * @description Retrieve the trades of a specific market. * @see https://docs.foxbit.com.br/rest/v3/#tag/Market-Data/operation/MarketsController_publicTrades * @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 * @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(); const market = this.market(symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['page_size'] = limit; if (limit > 200) { request['page_size'] = 200; } } // [ // { // "id": 1, // "price": "329248.74700000", // "volume": "0.00100000", // "taker_side": "BUY", // "created_at": "2024-01-01T00:00:00Z" // } // ] const response = await this.v3PublicGetMarketsMarketTradesHistory(this.extend(request, params)); const data = this.safeList(response, 'data', []); return this.parseTrades(data, market, since, limit); } /** * @method * @name foxbit#fetchOHLCV * @description Fetch historical candlestick data containing the open, high, low, and close price, and the volume of a market. * @see https://docs.foxbit.com.br/rest/v3/#tag/Market-Data/operation/MarketsController_findCandlesticks * @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 * @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); const interval = this.safeString(this.timeframes, timeframe, timeframe); const request = { 'market': market['id'], 'interval': interval, }; if (since !== undefined) { request['start_time'] = this.iso8601(since); } if (limit !== undefined) { request['limit'] = limit; if (limit > 500) { request['limit'] = 500; } } const response = await this.v3PublicGetMarketsMarketCandlesticks(this.extend(request, params)); // [ // [ // "1692918000000", // timestamp // "127772.05150000", // open // "128467.99980000", // high // "127750.01000000", // low // "128353.99990000", // close // "1692918060000", // close timestamp // "0.17080431", // base volume // "21866.35948786", // quote volume // 66, // number of trades // "0.12073605", // taker buy base volume // "15466.34096391" // taker buy quote volume // ] // ] return this.parseOHLCVs(response, market, interval, since, limit); } /** * @method * @name foxbit#fetchBalance * @description Query for balance and get the amount of funds available for trading or funds locked in orders. * @see https://docs.foxbit.com.br/rest/v3/#tag/Account/operation/AccountsController_all * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ async fetchBalance(params = {}) { await this.loadMarkets(); const response = await this.v3PrivateGetAccounts(params); // { // "data": [ // { // "currency_symbol": "btc", // "balance": "10000.0", // "balance_available": "9000.0", // "balance_locked": "1000.0" // } // ] // } const accounts = this.safeList(response, 'data', []); const result = { 'info': response, }; for (let i = 0; i < accounts.length; i++) { const account = accounts[i]; const currencyId = this.safeString(account, 'currency_symbol'); const currencyCode = this.safeCurrencyCode(currencyId); const total = this.safeString(account, 'balance'); const used = this.safeString(account, 'balance_locked'); const free = this.safeString(account, 'balance_available'); const balanceObj = { 'free': free, 'used': used, 'total': total, }; result[currencyCode] = balanceObj; } return this.safeBalance(result); } /** * @method * @name foxbit#fetchOpenOrders * @description Fetch all unfilled currently open orders. * @see https://docs.foxbit.com.br/rest/v3/#tag/Trading/operation/OrdersController_listOrders * @param {string} symbol unified market symbol * @param {int} [since] the earliest time in ms to fetch open orders for * @param {int} [limit] the maximum number of open order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus('ACTIVE', symbol, since, limit, params); } /** * @method * @name foxbit#fetchClosedOrders * @description Fetch all currently closed orders. * @see https://docs.foxbit.com.br/rest/v3/#tag/Trading/operation/OrdersController_listOrders * @param {string} symbol unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus('FILLED', symbol, since, limit, params); } async fetchCanceledOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByStatus('CANCELED', symbol, since, limit, params); } async fetchOrdersByStatus(status, symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let market = undefined; const request = { 'state': status, }; if (symbol !== undefined) { market = this.market(symbol); request['market_symbol'] = market['id']; } if (since !== undefined) { request['start_time'] = this.iso8601(since); } if (limit !== undefined) { request['page_size'] = limit; if (limit > 100) { request['page_size'] = 100; } } const response = await this.v3PrivateGetOrders(this.extend(request, params)); const data = this.safeList(response, 'data', []); return this.parseOrders(data); } /** * @method * @name foxbit#createOrder * @description Create an order with the specified characteristics * @see https://docs.foxbit.com.br/rest/v3/#tag/Trading/operation/OrdersController_create * @param {string} symbol unified symbol of the market to create an order in * @param {string} type 'market', 'limit', 'stop_market', 'stop_limit', 'instant' * @param {string} side 'buy' or 'sell' * @param {float} amount how much you want to trade in units of the base currency * @param {float} [price] the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.timeInForce] "GTC", "FOK", "IOC", "PO" * @param {float} [params.triggerPrice] The time in force for the order. One of GTC, FOK, IOC, PO. See .features or foxbit's doc to see more details. * @param {bool} [params.postOnly] true or false whether the order is post-only * @param {string} [params.clientOrderId] a unique identifier for the order * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async createOrder(symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); type = type.toUpperCase(); if (type !== 'LIMIT' && type !== 'MARKET' && type !== 'STOP_MARKET' && type !== 'STOP_LIMIT' && type !== 'INSTANT') { throw new InvalidOrder('Invalid order type: ' + type + '. Must be one of: limit, market, stop_market, stop_limit, instant.'); } const timeInForce = this.safeStringUpper(params, 'timeInForce'); const postOnly = this.safeBool(params, 'postOnly', false); const triggerPrice = this.safeNumber(params, 'triggerPrice'); const request = { 'market_symbol': market['id'], 'side': side.toUpperCase(), 'type': type, }; if (type === 'STOP_MARKET' || type === 'STOP_LIMIT') { if (triggerPrice === undefined) { throw new InvalidOrder('Invalid order type: ' + type + '. Must have triggerPrice.'); } } if (timeInForce !== undefined) { if (timeInForce === 'PO') { request['post_only'] = true; } else { request['time_in_force'] = timeInForce; } } if (postOnly) { request['post_only'] = true; } if (triggerPrice !== undefined) { request['stop_price'] = this.priceToPrecision(symbol, triggerPrice); } if (type === 'INSTANT') { request['amount'] = this.priceToPrecision(symbol, amount); } else { request['quantity'] = this.amountToPrecision(symbol, amount); } if (type === 'LIMIT' || type === 'STOP_LIMIT') { request['price'] = this.priceToPrecision(symbol, price); } const clientOrderId = this.safeString(params, 'clientOrderId'); if (clientOrderId !== undefined) { request['client_order_id'] = clientOrderId; } params = this.omit(params, ['timeInForce', 'postOnly', 'triggerPrice', 'clientOrderId']); const response = await this.v3PrivatePostOrders(this.extend(request, params)); // { // "id": 1234567890, // "sn": "OKMAKSDHRVVREK", // "client_order_id": "451637946501" // } return this.parseOrder(response, market); } /** * @method * @name foxbit#createOrders * @description create a list of trade orders * @see https://docs.foxbit.com.br/rest/v3/#tag/Trading/operation/createBatch * @param {Array} orders list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async createOrders(orders, params = {}) { await this.loadMarkets(); const ordersRequests = []; for (let i = 0; i < orders.length; i++) { const order = this.safeDict(orders, i); const symbol = this.safeString(order, 'symbol'); const market = this.market(symbol); const type = this.safeStringUpper(order, 'type'); const orderParams = this.safeDict(order, 'params', {}); if (type !== 'LIMIT' && type !== 'MARKET' && type !== 'STOP_MARKET' && type !== 'STOP_LIMIT' && type !== 'INSTANT') { throw new InvalidOrder('Invalid order type: ' + type + '. Must be one of: limit, market, stop_market, stop_limit, instant.'); } const timeInForce = this.safeStringUpper(orderParams, 'timeInForce'); const postOnly = this.safeBool(orderParams, 'postOnly', false); const triggerPrice = this.safeNumber(orderParams, 'triggerPrice'); const request = { 'market_symbol': market['id'], 'side': this.safeStringUpper(order, 'side'), 'type': type, }; if (type === 'STOP_MARKET' || type === 'STOP_LIMIT') { if (triggerPrice === undefined) { throw new InvalidOrder('Invalid order type: ' + type + '. Must have triggerPrice.'); } } if (timeInForce !== undefined) { if (timeInForce === 'PO') { request['post_only'] = true; } else { request['time_in_force'] = timeInForce; } delete orderParams['timeInForce']; } if (postOnly) { request['post_only'] = true; delete orderParams['postOnly']; } if (triggerPrice !== undefined) { request['stop_price'] = this.priceToPrecision(symbol, triggerPrice); delete orderParams['triggerPrice']; } if (type === 'INSTANT') { request['amount'] = this.priceToPrecision(symbol, this.safeString(order, 'amount')); } else { request['quantity'] = this.amountToPrecision(symbol, this.safeString(order, 'amount')); } if (type === 'LIMIT' || type === 'STOP_LIMIT') { request['price'] = this.priceToPrecision(symbol, this.safeString(order, 'price')); } ordersRequests.push(this.extend(request, orderParams)); } const createOrdersRequest = { 'data': ordersRequests }; const response = await this.v3PrivatePostOrdersBatch(this.extend(createOrdersRequest, params)); // { // "data": [ // { // "side": "BUY", // "type": "LIMIT", // "market_symbol": "btcbrl", // "client_order_id": "451637946501", // "remark": "A remarkable note for the order.", // "quantity": "0.42", // "price": "250000.0", // "post_only": true, // "time_in_force": "GTC" // } // ] // } const data = this.safeList(response, 'data', []); return this.parseOrders(data); } /** * @method * @name foxbit#cancelOrder * @description Cancel open orders. * @see https://docs.foxbit.com.br/rest/v3/#tag/Trading/operation/OrdersController_cancel * @param {string} id order id * @param {string} symbol unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async cancelOrder(id, symbol = undefined, params = {}) { await this.loadMarkets(); const request = { 'id': this.parseNumber(id), 'type': 'ID', }; const response = await this.v3PrivatePutOrdersCancel(this.extend(request, params)); // { // "data": [ // { // "sn": "OKMAKSDHRVVREK", // "id": 123456789 // } // ] // } const data = this.safeList(response, 'data', []); const result = this.safeDict(data, 0, {}); return this.parseOrder(result); } /** * @method * @name foxbit#cancelAllOrders * @description Cancel all open orders or all open orders for a specific market. * @see https://docs.foxbit.com.br/rest/v3/#tag/Trading/operation/OrdersController_cancel * @param {string} symbol unified market symbol of the market to cancel orders in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async cancelAllOrders(symbol = undefined, params = {}) { await this.loadMarkets(); const request = { 'type': 'ALL', }; if (symbol !== undefined) { const market = this.market(symbol); request['type'] = 'MARKET'; request['market_symbol'] = market['id']; } const response = await this.v3PrivatePutOrdersCancel(this.extend(request, params)); // { // "data": [ // { // "sn": "OKMAKSDHRVVREK", // "id": 123456789 // } // ] // } return [this.safeOrder({ 'info': response, })]; } /** * @method * @name foxbit#fetchOrder * @description Get an order by ID. * @see https://docs.foxbit.com.br/rest/v3/#tag/Trading/operation/OrdersController_findByOrderId * @param id * @param {string} symbol it is not used in the foxbit API * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async fetchOrder(id, symbol = undefined, params = {}) { await this.loadMarkets(); const request = { 'id': id, }; const response = await this.v3PrivateGetOrdersByOrderIdId(this.extend(request, params)); // { // "id": "1234567890", // "sn": "OKMAKSDHRVVREK", // "client_order_id": "451637946501", // "market_symbol": "btcbrl", // "side": "BUY", // "type": "LIMIT", // "state": "ACTIVE", // "price": "290000.0", // "price_avg": "295333.3333", // "quantity": "0.42", // "quantity_executed": "0.41", // "instant_amount": "290.0", // "instant_amount_executed": "290.0", // "created_at": "2021-02-15T22:06:32.999Z", // "trades_count": "2", // "remark": "A remarkable note for the order.", // "funds_received": "290.0" // } return this.parseOrder(response, undefined); } /** * @method * @name foxbit#fetchOrders * @description fetches information on multiple orders made by the user * @see https://docs.foxbit.com.br/rest/v3/#tag/Trading/operation/OrdersController_listOrders * @param {string} symbol unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.state] Enum: ACTIVE, CANCELED, FILLED, PARTIALLY_CANCELED, PARTIALLY_FILLED * @param {string} [params.side] Enum: BUY, SELL * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market(symbol); request['market_symbol'] = market['id']; } if (since !== undefined) { request['start_time'] = this.iso8601(since); } if (limit !== undefined) { request['page_size'] = limit; if (limit > 100) { request['page_siz