ccxt
Version:
1,171 lines • 86.3 kB
JavaScript
// ---------------------------------------------------------------------------
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