ccxt
Version:
1,162 lines • 187 kB
JavaScript
// ---------------------------------------------------------------------------
import Exchange from './abstract/woo.js';
import { AuthenticationError, RateLimitExceeded, BadRequest, OperationFailed, ExchangeError, InvalidOrder, ArgumentsRequired, NotSupported, OnMaintenance } 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 woo
* @augments Exchange
*/
export default class woo extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'woo',
'name': 'WOO X',
'countries': ['KY'],
'rateLimit': 100,
'version': 'v1',
'certified': true,
'pro': true,
'hostname': 'woox.io',
'has': {
'CORS': undefined,
'spot': true,
'margin': true,
'swap': true,
'future': false,
'option': false,
'addMargin': true,
'cancelAllOrders': true,
'cancelAllOrdersAfter': true,
'cancelOrder': true,
'cancelWithdraw': false,
'closeAllPositions': false,
'closePosition': false,
'createConvertTrade': true,
'createDepositAddress': false,
'createMarketBuyOrderWithCost': true,
'createMarketOrder': false,
'createMarketOrderWithCost': false,
'createMarketSellOrderWithCost': true,
'createOrder': true,
'createOrderWithTakeProfitAndStopLoss': true,
'createReduceOnlyOrder': true,
'createStopLimitOrder': false,
'createStopLossOrder': true,
'createStopMarketOrder': false,
'createStopOrder': false,
'createTakeProfitOrder': true,
'createTrailingAmountOrder': true,
'createTrailingPercentOrder': true,
'createTriggerOrder': true,
'fetchAccounts': true,
'fetchBalance': true,
'fetchCanceledOrders': false,
'fetchClosedOrder': false,
'fetchClosedOrders': true,
'fetchConvertCurrencies': true,
'fetchConvertQuote': true,
'fetchConvertTrade': true,
'fetchConvertTradeHistory': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': false,
'fetchDeposits': true,
'fetchDepositsWithdrawals': true,
'fetchFundingHistory': true,
'fetchFundingInterval': true,
'fetchFundingIntervals': false,
'fetchFundingRate': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': true,
'fetchIndexOHLCV': false,
'fetchLedger': true,
'fetchLeverage': true,
'fetchMarginAdjustmentHistory': false,
'fetchMarginMode': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterestHistory': false,
'fetchOpenOrder': false,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchOrderTrades': true,
'fetchPosition': true,
'fetchPositionHistory': false,
'fetchPositionMode': false,
'fetchPositions': true,
'fetchPositionsHistory': false,
'fetchPremiumIndexOHLCV': false,
'fetchStatus': true,
'fetchTicker': false,
'fetchTickers': false,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': true,
'fetchTransactions': 'emulated',
'fetchTransfers': true,
'fetchWithdrawals': true,
'reduceMargin': false,
'sandbox': true,
'setLeverage': true,
'setMargin': false,
'setPositionMode': true,
'transfer': true,
'withdraw': true, // exchange have that endpoint disabled atm, but was once implemented in ccxt per old docs: https://docx.woo.io/wootrade-documents/#token-withdraw
},
'timeframes': {
'1m': '1m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'4h': '4h',
'12h': '12h',
'1d': '1d',
'1w': '1w',
'1M': '1mon',
'1y': '1y',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/150730761-1a00e5e0-d28c-480f-9e65-089ce3e6ef3b.jpg',
'api': {
'pub': 'https://api-pub.woox.io',
'public': 'https://api.{hostname}',
'private': 'https://api.{hostname}',
},
'test': {
'pub': 'https://api-pub.staging.woox.io',
'public': 'https://api.staging.woox.io',
'private': 'https://api.staging.woox.io',
},
'www': 'https://woox.io/',
'doc': [
'https://docs.woox.io/',
],
'fees': [
'https://support.woox.io/hc/en-001/articles/4404611795353--Trading-Fees',
],
'referral': {
'url': 'https://woox.io/register?ref=DIJT0CNL',
'discount': 0.35,
},
},
'api': {
'v1': {
'pub': {
'get': {
'hist/kline': 10,
'hist/trades': 10,
},
},
'public': {
'get': {
'info': 1,
'info/{symbol}': 1,
'system_info': 1,
'market_trades': 1,
'token': 1,
'token_network': 1,
'funding_rates': 1,
'funding_rate/{symbol}': 1,
'funding_rate_history': 1,
'futures': 1,
'futures/{symbol}': 1,
'orderbook/{symbol}': 1,
'kline': 1,
},
},
'private': {
'get': {
'client/token': 1,
'order/{oid}': 1,
'client/order/{client_order_id}': 1,
'orders': 1,
'client/trade/{tid}': 1,
'order/{oid}/trades': 1,
'client/trades': 1,
'client/hist_trades': 1,
'staking/yield_history': 1,
'client/holding': 1,
'asset/deposit': 10,
'asset/history': 60,
'sub_account/all': 60,
'sub_account/assets': 60,
'sub_account/asset_detail': 60,
'sub_account/ip_restriction': 10,
'asset/main_sub_transfer_history': 30,
'token_interest': 60,
'token_interest/{token}': 60,
'interest/history': 60,
'interest/repay': 60,
'funding_fee/history': 30,
'positions': 3.33,
'position/{symbol}': 3.33,
'client/transaction_history': 60,
'client/futures_leverage': 60,
},
'post': {
'order': 1,
'order/cancel_all_after': 1,
'asset/main_sub_transfer': 30,
'asset/ltv': 30,
'asset/withdraw': 30,
'asset/internal_withdraw': 30,
'interest/repay': 60,
'client/account_mode': 120,
'client/position_mode': 5,
'client/leverage': 120,
'client/futures_leverage': 30,
'client/isolated_margin': 30,
},
'delete': {
'order': 1,
'client/order': 1,
'orders': 1,
'asset/withdraw': 120, // implemented in ccxt, disabled on the exchange side https://docx.woo.io/wootrade-documents/#cancel-withdraw-request
},
},
},
'v2': {
'private': {
'get': {
'client/holding': 1,
},
},
},
'v3': {
'public': {
'get': {
'systemInfo': 1,
'instruments': 1,
'token': 1,
'tokenNetwork': 1,
'tokenInfo': 1,
'marketTrades': 1,
'marketTradesHistory': 1,
'orderbook': 1,
'kline': 1,
'klineHistory': 1,
'futures': 1,
'fundingRate': 1,
'fundingRateHistory': 1,
'insuranceFund': 1, // 10/1s
},
},
'private': {
'get': {
'trade/order': 2,
'trade/orders': 1,
'trade/algoOrder': 1,
'trade/algoOrders': 1,
'trade/transaction': 1,
'trade/transactionHistory': 5,
'trade/tradingFee': 5,
'account/info': 60,
'account/tokenConfig': 1,
'account/symbolConfig': 1,
'account/subAccounts/all': 60,
'account/referral/summary': 60,
'account/referral/rewardHistory': 60,
'account/credentials': 60,
'asset/balances': 1,
'asset/token/history': 60,
'asset/transfer/history': 30,
'asset/wallet/history': 60,
'asset/wallet/deposit': 60,
'asset/staking/yieldHistory': 60,
'futures/positions': 3.33,
'futures/leverage': 60,
'futures/defaultMarginMode': 60,
'futures/fundingFee/history': 30,
'spotMargin/interestRate': 60,
'spotMargin/interestHistory': 60,
'spotMargin/maxMargin': 60,
'algo/order/{oid}': 1,
'algo/orders': 1,
'balances': 1,
'positions': 3.33,
'buypower': 1,
'convert/exchangeInfo': 1,
'convert/assetInfo': 1,
'convert/rfq': 60,
'convert/trade': 1,
'convert/trades': 1,
},
'post': {
'trade/order': 2,
'trade/algoOrder': 5,
'trade/cancelAllAfter': 1,
'account/tradingMode': 120,
'account/listenKey': 20,
'asset/transfer': 30,
'asset/wallet/withdraw': 60,
'spotMargin/leverage': 120,
'spotMargin/interestRepay': 60,
'algo/order': 5,
'convert/rft': 60,
},
'put': {
'trade/order': 2,
'trade/algoOrder': 2,
'futures/leverage': 60,
'futures/positionMode': 120,
'order/{oid}': 2,
'order/client/{client_order_id}': 2,
'algo/order/{oid}': 2,
'algo/order/client/{client_order_id}': 2,
},
'delete': {
'trade/order': 1,
'trade/orders': 1,
'trade/algoOrder': 1,
'trade/algoOrders': 1,
'trade/allOrders': 1,
'algo/order/{order_id}': 1,
'algo/orders/pending': 1,
'algo/orders/pending/{symbol}': 1,
'orders/pending': 1,
},
},
},
},
'fees': {
'trading': {
'tierBased': true,
'percentage': true,
'maker': this.parseNumber('0.0002'),
'taker': this.parseNumber('0.0005'),
},
},
'options': {
'timeDifference': 0,
'adjustForTimeDifference': false,
'sandboxMode': false,
'createMarketBuyOrderRequiresPrice': true,
// these network aliases require manual mapping here
'network-aliases-for-tokens': {
'HT': 'ERC20',
'OMG': 'ERC20',
'UATOM': 'ATOM',
'ZRX': 'ZRX',
},
'networks': {
'TRX': 'TRON',
'TRC20': 'TRON',
'ERC20': 'ETH',
'BEP20': 'BSC',
'ARB': 'Arbitrum',
},
'networksById': {
'TRX': 'TRC20',
'TRON': 'TRC20',
},
// override defaultNetworkCodePriorities for a specific currency
'defaultNetworkCodeForCurrencies': {
// 'USDT': 'TRC20',
// 'BTC': 'BTC',
},
'transfer': {
'fillResponseFromRequest': true,
},
'brokerId': 'bc830de7-50f3-460b-9ee0-f430f83f9dad',
},
'features': {
'default': {
'sandbox': true,
'createOrder': {
'marginMode': true,
'triggerPrice': true,
'triggerPriceType': {
'last': true,
'mark': true,
'index': false,
},
'triggerDirection': false,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': true,
},
'hedged': false,
'trailing': true,
'leverage': false,
'marketBuyByCost': true,
'marketBuyRequiresPrice': false,
'selfTradePrevention': false,
'iceberg': true, // todo implement
},
'createOrders': undefined,
'fetchMyTrades': {
'marginMode': false,
'limit': 500,
'daysBack': 90,
'untilDays': 10000,
'symbolRequired': false,
},
'fetchOrder': {
'marginMode': false,
'trigger': true,
'trailing': false,
'symbolRequired': false,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': 500,
'trigger': true,
'trailing': true,
'symbolRequired': false,
},
'fetchOrders': {
'marginMode': false,
'limit': 500,
'daysBack': undefined,
'untilDays': 100000,
'trigger': true,
'trailing': true,
'symbolRequired': false,
},
'fetchClosedOrders': {
'marginMode': false,
'limit': 500,
'daysBack': undefined,
'daysBackCanceled': undefined,
'untilDays': 100000,
'trigger': true,
'trailing': true,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 1000,
},
},
'spot': {
'extends': 'default',
},
'forSwap': {
'extends': 'default',
'createOrder': {
'hedged': true,
},
},
'swap': {
'linear': {
'extends': 'forSwap',
},
'inverse': undefined,
},
'future': {
'linear': undefined,
'inverse': undefined,
},
},
'commonCurrencies': {},
'exceptions': {
'exact': {
'-1000': OperationFailed,
'-1001': AuthenticationError,
'-1002': AuthenticationError,
'-1003': RateLimitExceeded,
'-1004': BadRequest,
'-1005': BadRequest,
'-1006': BadRequest,
'-1007': BadRequest,
'-1008': InvalidOrder,
'-1009': BadRequest,
'-1012': BadRequest,
'-1101': InvalidOrder,
'-1102': InvalidOrder,
'-1103': InvalidOrder,
'-1104': InvalidOrder,
'-1105': InvalidOrder, // { "code": -1105, "message": "Price is X% too high or X% too low from the mid price." }
},
'broad': {
'Can not place': ExchangeError,
'maintenance': OnMaintenance,
'symbol must not be blank': BadRequest,
'The token is not supported': BadRequest,
'Your order and symbol are not valid or already canceled': BadRequest,
'Insufficient WOO. Please enable margin trading for leverage trading': BadRequest, // when selling insufficent token [-1012]
},
},
'precisionMode': TICK_SIZE,
});
}
/**
* @method
* @name woo#fetchStatus
* @description the latest known information on the availability of the exchange API
* @see https://developer.woox.io/api-reference/endpoint/public_data/systemInfo
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure}
*/
async fetchStatus(params = {}) {
const response = await this.v3PublicGetSystemInfo(params);
//
// {
// "success": true,
// "data": {
// "status": 0,
// "msg": "System is functioning properly.",
// "estimatedEndTime": 1749963600362
// },
// "timestamp": 1751442989564
// }
//
const data = this.safeDict(response, 'data', {});
let status = this.safeString(data, 'status');
if (status === undefined) {
status = 'error';
}
else if (status === '0') {
status = 'ok';
}
else {
status = 'maintenance';
}
return {
'status': status,
'updated': undefined,
'eta': undefined,
'url': undefined,
'info': response,
};
}
/**
* @method
* @name woo#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @see https://developer.woox.io/api-reference/endpoint/public_data/systemInfo
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
async fetchTime(params = {}) {
const response = await this.v3PublicGetSystemInfo(params);
//
// {
// "success": true,
// "data": {
// "status": 0,
// "msg": "System is functioning properly.",
// "estimatedEndTime": 1749963600362
// },
// "timestamp": 1751442989564
// }
//
return this.safeInteger(response, 'timestamp');
}
/**
* @method
* @name woo#fetchMarkets
* @description retrieves data on all markets for woo
* @see https://developer.woox.io/api-reference/endpoint/public_data/instruments
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
if (this.options['adjustForTimeDifference']) {
await this.loadTimeDifference();
}
const response = await this.v3PublicGetInstruments(params);
//
// {
// "success": true,
// "data": {
// "rows": [
// {
// "symbol": "SPOT_AAVE_USDT",
// "status": "TRADING",
// "baseAsset": "AAVE",
// "baseAssetMultiplier": 1,
// "quoteAsset": "USDT",
// "quoteMin": "0",
// "quoteMax": "100000",
// "quoteTick": "0.01",
// "baseMin": "0.005",
// "baseMax": "5000",
// "baseTick": "0.0001",
// "minNotional": "1",
// "bidCapRatio": "1.1",
// "bidFloorRatio": null,
// "askCapRatio": null,
// "askFloorRatio": "0.9",
// "orderMode": "NORMAL",
// "impactNotional": null,
// "isAllowedRpi": false,
// "tickGranularity": null
// }
// ]
// },
// "timestamp": 1751512951338
// }
//
const data = this.safeDict(response, 'data', {});
const rows = this.safeList(data, 'rows', []);
return this.parseMarkets(rows);
}
parseMarket(market) {
const marketId = this.safeString(market, 'symbol');
const parts = marketId.split('_');
const first = this.safeString(parts, 0);
let marketType;
let spot = false;
let swap = false;
if (first === 'SPOT') {
spot = true;
marketType = 'spot';
}
else if (first === 'PERP') {
swap = true;
marketType = 'swap';
}
const baseId = this.safeString(parts, 1);
const quoteId = this.safeString(parts, 2);
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
let settleId = undefined;
let settle = undefined;
let symbol = base + '/' + quote;
let contractSize = undefined;
let linear = undefined;
let inverse = undefined;
let margin = true;
const contract = swap;
if (contract) {
margin = false;
settleId = this.safeString(parts, 2);
settle = this.safeCurrencyCode(settleId);
symbol = base + '/' + quote + ':' + settle;
contractSize = this.parseNumber('1');
linear = true;
inverse = false;
}
const active = this.safeString(market, 'status') === 'TRADING';
return {
'id': marketId,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': marketType,
'spot': spot,
'margin': margin,
'swap': swap,
'future': false,
'option': false,
'active': active,
'contract': contract,
'linear': linear,
'inverse': inverse,
'contractSize': contractSize,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.safeNumber(market, 'baseTick'),
'price': this.safeNumber(market, 'quoteTick'),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber(market, 'baseMin'),
'max': this.safeNumber(market, 'baseMax'),
},
'price': {
'min': this.safeNumber(market, 'quoteMin'),
'max': this.safeNumber(market, 'quoteMax'),
},
'cost': {
'min': this.safeNumber(market, 'minNotional'),
'max': undefined,
},
},
'created': undefined,
'info': market,
};
}
/**
* @method
* @name woo#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://developer.woox.io/api-reference/endpoint/public_data/marketTrades
* @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 = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.v3PublicGetMarketTrades(this.extend(request, params));
//
// {
// "success": true,
// "data": {
// "rows": [
// {
// "symbol": "SPOT_BTC_USDT",
// "side": "SELL",
// "source": 0,
// "executedPrice": "108741.01",
// "executedQuantity": "0.02477",
// "executedTimestamp": 1751513940144
// }
// ]
// },
// "timestamp": 1751513988543
// }
//
const data = this.safeDict(response, 'data', {});
const rows = this.safeList(data, 'rows', []);
return this.parseTrades(rows, market, since, limit);
}
parseTrade(trade, market = undefined) {
//
// public/market_trades
//
// {
// "symbol": "SPOT_BTC_USDT",
// "side": "SELL",
// "source": 0,
// "executedPrice": "108741.01",
// "executedQuantity": "0.02477",
// "executedTimestamp": 1751513940144
// }
//
// fetchOrderTrades, fetchOrder
//
// {
// "id": 1734947821,
// "symbol": "SPOT_LTC_USDT",
// "orderId": 60780383217,
// "executedPrice": 87.86,
// "executedQuantity": 0.1,
// "fee": 0.0001,
// "realizedPnl": null,
// "feeAsset": "LTC",
// "orderTag": "default",
// "side": "BUY",
// "executedTimestamp": "1752055173.630",
// "isMaker": 0
// }
//
const isFromFetchOrder = ('id' in trade);
const timestampString = this.safeString2(trade, 'executed_timestamp', 'executedTimestamp');
let timestamp = undefined;
if (timestampString !== undefined) {
if (timestampString.indexOf('.') > -1) {
timestamp = this.safeTimestamp2(trade, 'executed_timestamp', 'executedTimestamp');
}
else {
timestamp = this.safeInteger(trade, 'executedTimestamp');
}
}
const marketId = this.safeString(trade, 'symbol');
market = this.safeMarket(marketId, market);
const symbol = market['symbol'];
const price = this.safeString2(trade, 'executed_price', 'executedPrice');
const amount = this.safeString2(trade, 'executed_quantity', 'executedQuantity');
const order_id = this.safeString2(trade, 'order_id', 'orderId');
const fee = this.parseTokenAndFeeTemp(trade, ['fee_asset', 'feeAsset'], ['fee']);
const feeCost = this.safeString(fee, 'cost');
if (feeCost !== undefined) {
fee['cost'] = feeCost;
}
const cost = Precise.stringMul(price, amount);
const side = this.safeStringLower(trade, 'side');
const id = this.safeString(trade, 'id');
let takerOrMaker = undefined;
if (isFromFetchOrder) {
const isMaker = this.safeString2(trade, 'is_maker', 'isMaker') === '1';
takerOrMaker = isMaker ? 'maker' : 'taker';
}
return this.safeTrade({
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': symbol,
'side': side,
'price': price,
'amount': amount,
'cost': cost,
'order': order_id,
'takerOrMaker': takerOrMaker,
'type': undefined,
'fee': fee,
'info': trade,
}, market);
}
parseTokenAndFeeTemp(item, feeTokenKeys, feeAmountKeys) {
const feeCost = this.safeStringN(item, feeAmountKeys);
let fee = undefined;
if (feeCost !== undefined) {
const feeCurrencyId = this.safeStringN(item, feeTokenKeys);
const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId);
fee = {
'cost': feeCost,
'currency': feeCurrencyCode,
};
}
return fee;
}
parseTradingFee(fee, market = undefined) {
const marketId = this.safeString(fee, 'symbol');
const symbol = this.safeSymbol(marketId, market);
return {
'info': fee,
'symbol': symbol,
'maker': this.parseNumber(Precise.stringDiv(this.safeString(fee, 'makerFee'), '100')),
'taker': this.parseNumber(Precise.stringDiv(this.safeString(fee, 'takerFee'), '100')),
'percentage': undefined,
'tierBased': undefined,
};
}
/**
* @method
* @name woo#fetchTradingFee
* @description fetch the trading fees for a market
* @see https://developer.woox.io/api-reference/endpoint/trading/get_tradingFee
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.portfolioMargin] set to true if you would like to fetch trading fees in a portfolio margin account
* @param {string} [params.subType] "linear" or "inverse"
* @returns {object} a [fee structure]{@link https://docs.ccxt.com/#/?id=fee-structure}
*/
async fetchTradingFee(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'symbol': market['id'],
};
const response = await this.v3PrivateGetTradeTradingFee(this.extend(request, params));
//
// {
// "success": true,
// "data": {
// "symbol": "SPOT_BTC_USDT",
// "takerFee": "10",
// "makerFee": "8"
// },
// "timestamp": 1751858977368
// }
//
const data = this.safeDict(response, 'data', {});
return this.parseTradingFee(data, market);
}
/**
* @method
* @name woo#fetchTradingFees
* @description fetch the trading fees for multiple markets
* @see https://developer.woox.io/api-reference/endpoint/account/get_account_info
* @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.v3PrivateGetAccountInfo(params);
//
// {
// "success": true,
// "data": {
// "applicationId": "251bf5c4-f3c8-4544-bb8b-80001007c3c0",
// "account": "carlos_jose_lima@yahoo.com",
// "alias": "carlos_jose_lima@yahoo.com",
// "otpauth": true,
// "accountMode": "FUTURES",
// "positionMode": "ONE_WAY",
// "leverage": 0,
// "makerFeeRate": 0,
// "takerFeeRate": 0,
// "marginRatio": "10",
// "openMarginRatio": "10",
// "initialMarginRatio": "10",
// "maintenanceMarginRatio": "0.03",
// "totalCollateral": "165.55629469",
// "freeCollateral": "165.55629469",
// "totalAccountValue": "167.32418611",
// "totalTradingValue": "167.32418611",
// "totalVaultValue": "0",
// "totalStakingValue": "0",
// "totalLaunchpadValue": "0",
// "totalEarnValue": "0",
// "referrerID": null,
// "accountType": "Main"
// },
// "timestamp": 1752062807915
// }
//
const data = this.safeDict(response, 'data', {});
const maker = this.safeString(data, 'makerFeeRate');
const taker = this.safeString(data, 'takerFeeRate');
const result = {};
for (let i = 0; i < this.symbols.length; i++) {
const symbol = this.symbols[i];
result[symbol] = {
'info': response,
'symbol': symbol,
'maker': this.parseNumber(Precise.stringDiv(maker, '10000')),
'taker': this.parseNumber(Precise.stringDiv(taker, '10000')),
'percentage': true,
'tierBased': true,
};
}
return result;
}
/**
* @method
* @name woo#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://docs.woox.io/#available-token-public
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an associative dictionary of currencies
*/
async fetchCurrencies(params = {}) {
const result = {};
const tokenResponsePromise = this.v1PublicGetToken(params);
//
// {
// "rows": [
// {
// "token": "ETH_USDT",
// "fullname": "Tether",
// "network": "ETH",
// "decimals": "6",
// "delisted": false,
// "balance_token": "USDT",
// "created_time": "1710123398",
// "updated_time": "1746528481",
// "can_collateral": true,
// "can_short": true
// },
// {
// "token": "BSC_USDT",
// "fullname": "Tether",
// "network": "BSC",
// "decimals": "18",
// "delisted": false,
// "balance_token": "USDT",
// "created_time": "1710123395",
// "updated_time": "1746528601",
// "can_collateral": true,
// "can_short": true
// },
// {
// "token": "ALGO",
// "fullname": "Algorand",
// "network": "ALGO",
// "decimals": "6",
// "delisted": false,
// "balance_token": "ALGO",
// "created_time": "1710123394",
// "updated_time": "1723087518",
// "can_collateral": true,
// "can_short": true
// },
// ...
// ],
// "success": true
// }
//
// only make one request for currrencies...
const tokenNetworkResponsePromise = this.v1PublicGetTokenNetwork(params);
//
// {
// "rows": [
// {
// "protocol": "ERC20",
// "network": "ETH",
// "token": "USDT",
// "name": "Ethereum (ERC20)",
// "minimum_withdrawal": "10.00000000",
// "withdrawal_fee": "2.00000000",
// "allow_deposit": "1",
// "allow_withdraw": "1"
// },
// {
// "protocol": "TRC20",
// "network": "TRX",
// "token": "USDT",
// "name": "Tron (TRC20)",
// "minimum_withdrawal": "10.00000000",
// "withdrawal_fee": "4.50000000",
// "allow_deposit": "1",
// "allow_withdraw": "1"
// },
// ...
// ],
// "success": true
// }
//
const [tokenResponse, tokenNetworkResponse] = await Promise.all([tokenResponsePromise, tokenNetworkResponsePromise]);
const tokenRows = this.safeList(tokenResponse, 'rows', []);
const tokenNetworkRows = this.safeList(tokenNetworkResponse, 'rows', []);
const networksById = this.groupBy(tokenNetworkRows, 'token');
const tokensById = this.groupBy(tokenRows, 'balance_token');
const currencyIds = Object.keys(tokensById);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode(currencyId);
const tokensByNetworkId = this.indexBy(tokensById[currencyId], 'network');
const chainsByNetworkId = this.indexBy(networksById[currencyId], 'network');
const keys = Object.keys(chainsByNetworkId);
const resultingNetworks = {};
for (let j = 0; j < keys.length; j++) {
const networkId = keys[j];
const tokenEntry = this.safeDict(tokensByNetworkId, networkId, {});
const networkEntry = this.safeDict(chainsByNetworkId, networkId, {});
const networkCode = this.networkIdToCode(networkId, code);
const specialNetworkId = this.safeString(tokenEntry, 'token');
resultingNetworks[networkCode] = {
'id': networkId,
'currencyNetworkId': specialNetworkId,
'network': networkCode,
'active': undefined,
'deposit': this.safeString(networkEntry, 'allow_deposit') === '1',
'withdraw': this.safeString(networkEntry, 'allow_withdraw') === '1',
'fee': this.safeNumber(networkEntry, 'withdrawal_fee'),
'precision': this.parseNumber(this.parsePrecision(this.safeString(tokenEntry, 'decimals'))),
'limits': {
'withdraw': {
'min': this.safeNumber(networkEntry, 'minimum_withdrawal'),
'max': undefined,
},
'deposit': {
'min': undefined,
'max': undefined,
},
},
'info': [networkEntry, tokenEntry],
};
}
result[code] = this.safeCurrencyStructure({
'id': currencyId,
'name': undefined,
'code': code,
'precision': undefined,
'active': undefined,
'fee': undefined,
'networks': resultingNetworks,
'deposit': undefined,
'withdraw': undefined,
'type': 'crypto',
'limits': {
'deposit': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': undefined,
'max': undefined,
},
},
'info': [tokensByNetworkId, chainsByNetworkId],
});
}
return result;
}
/**
* @method
* @name woo#createMarketBuyOrderWithCost
* @description create a market buy order by providing the symbol and cost
* @see https://docs.woox.io/#send-order
* @param {string} symbol unified symbol of the market to create an order in
* @param {float} cost how much you want to trade in units of the quote currency
* @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 createMarketBuyOrderWithCost(symbol, cost, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['spot']) {
throw new NotSupported(this.id + ' createMarketBuyOrderWithCost() supports spot orders only');
}
return await this.createOrder(symbol, 'market', 'buy', cost, 1, params);
}
/**
* @method
* @name woo#createMarketSellOrderWithCost
* @description create a market sell order by providing the symbol and cost
* @see https://docs.woox.io/#send-order
* @param {string} symbol unified symbol of the market to create an order in
* @param {float} cost how much you want to trade in units of the quote currency
* @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 createMarketSellOrderWithCost(symbol, cost, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['spot']) {
throw new NotSupported(this.id + ' createMarketSellOrderWithCost() supports spot orders only');
}
return await this.createOrder(symbol, 'market', 'sell', cost, 1, params);
}
/**
* @method
* @name woo#createTrailingAmountOrder
* @description create a trailing order by providing the symbol, type, side, amount, price and trailingAmount
* @see https://docs.woox.io/#send-algo-order
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much you want to trade in units of the base currency, or number of contracts
* @param {float} [price] the price for the order to be filled at, in units of the quote currency, ignored in market orders
* @param {float} trailingAmount the quote amount to trail away from the current market price
* @param {float} trailingTriggerPrice the price to activate a trailing order, default uses the price argument
* @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 createTrailingAmountOrder(symbol, type, side, amount, price = undefined, trailingAmount = undefined, trailingTriggerPrice = undefined, params = {}) {
if (trailingAmount === undefined) {
throw new ArgumentsRequired(this.id + ' createTrailingAmountOrder() requires a trailingAmount argument');
}
if (trailingTriggerPrice === undefined) {
throw new ArgumentsRequired(this.id + ' createTrailingAmountOrder() requires a trailingTriggerPrice argument');
}
params['trailingAmount'] = trailingAmount;
params['trailingTriggerPrice'] = trailingTriggerPrice;
return await this.createOrder(symbol, type, side, amount, price, params);
}
/**
* @method
* @name woo#createTrailingPercentOrder
* @description create a trailing order by providing the symbol, type, side, amount, price and trailingPercent
* @see https://docs.woox.io/#send-algo-order
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much you want to trade in units of the base currency, or number of contracts
* @param {float} [price] the price for the order to be filled at, in units of the quote currency, ignored in market orders
* @param {float} trailingPercent the percent to trail away from the current market price
* @param {float} trailingTriggerPrice the price to activate a trailing order, default uses the price argument
* @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 createTrailingPercentOrder(symbol, type, side, amount, price = undefined, trailingPercent = undefined, trailingTriggerPrice = undefined, params = {}) {
if (trailingPercent === undefined) {
throw new ArgumentsRequired(