UNPKG

@jalmonter/ccxt

Version:

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

1,160 lines 114 kB
// --------------------------------------------------------------------------- import Exchange from './abstract/woo.js'; import { AuthenticationError, RateLimitExceeded, BadRequest, ExchangeError, InvalidOrder, ArgumentsRequired, NotSupported } 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': 'woo.org', 'has': { 'CORS': undefined, 'spot': true, 'margin': true, 'swap': true, 'future': false, 'option': false, 'addMargin': false, 'cancelAllOrders': true, 'cancelOrder': true, 'cancelWithdraw': false, 'closeAllPositions': false, 'closePosition': false, 'createDepositAddress': false, 'createMarketBuyOrderWithCost': true, 'createMarketOrder': false, 'createMarketOrderWithCost': false, 'createMarketSellOrderWithCost': false, 'createOrder': true, 'createReduceOnlyOrder': true, 'createStopLimitOrder': false, 'createStopMarketOrder': false, 'createStopOrder': false, 'fetchAccounts': true, 'fetchBalance': true, 'fetchCanceledOrders': false, 'fetchClosedOrder': false, 'fetchClosedOrders': false, 'fetchCurrencies': true, 'fetchDepositAddress': false, 'fetchDeposits': true, 'fetchDepositsWithdrawals': true, 'fetchFundingHistory': true, 'fetchFundingRate': true, 'fetchFundingRateHistory': true, 'fetchFundingRates': true, 'fetchIndexOHLCV': false, 'fetchLedger': true, 'fetchLeverage': true, 'fetchMarginMode': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterestHistory': false, 'fetchOpenOrder': false, 'fetchOpenOrders': false, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchOrderTrades': true, 'fetchPosition': true, 'fetchPositionMode': false, 'fetchPositions': true, 'fetchPremiumIndexOHLCV': false, 'fetchStatus': false, 'fetchTicker': false, 'fetchTickers': false, 'fetchTime': false, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': true, 'fetchTransactions': 'emulated', 'fetchTransfers': true, 'fetchWithdrawals': true, 'reduceMargin': false, 'setLeverage': true, 'setMargin': false, 'transfer': true, 'withdraw': true, // exchange have that endpoint disabled atm, but was once implemented in ccxt per old docs: https://kronosresearch.github.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.woo.org', 'public': 'https://api.{hostname}', 'private': 'https://api.{hostname}', }, 'test': { 'pub': 'https://api-pub.staging.woo.org', 'public': 'https://api.staging.woo.org', 'private': 'https://api.staging.woo.org', }, 'www': 'https://woo.org/', 'doc': [ 'https://docs.woo.org/', ], 'fees': [ 'https://support.woo.org/hc/en-001/articles/4404611795353--Trading-Fees', ], 'referral': { 'url': 'https://x.woo.org/register?ref=YWOWC96B', 'discount': 0.35, }, }, 'api': { 'v1': { 'pub': { 'get': { 'hist/kline': 10, 'hist/trades': 1, }, }, '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, 'asset/deposit': 10, 'asset/history': 60, 'sub_account/all': 60, 'sub_account/assets': 60, '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, }, 'post': { 'order': 5, 'asset/main_sub_transfer': 30, 'asset/withdraw': 30, 'interest/repay': 60, 'client/account_mode': 120, 'client/leverage': 120, }, 'delete': { 'order': 1, 'client/order': 1, 'orders': 1, 'asset/withdraw': 120, // implemented in ccxt, disabled on the exchange side https://kronosresearch.github.io/wootrade-documents/#cancel-withdraw-request }, }, }, 'v2': { 'private': { 'get': { 'client/holding': 1, }, }, }, 'v3': { 'public': { 'get': { 'insuranceFund': 3, }, }, 'private': { 'get': { 'algo/order/{oid}': 1, 'algo/orders': 1, 'balances': 1, 'accountinfo': 60, 'positions': 3.33, 'buypower': 1, 'referrals': 60, 'referral_rewards': 60, 'convert/exchangeInfo': 1, 'convert/assetInfo': 1, 'convert/rfq': 60, 'convert/trade': 1, 'convert/trades': 1, }, 'post': { 'algo/order': 5, 'convert/rft': 60, }, 'put': { 'order/{oid}': 2, 'order/client/{client_order_id}': 2, 'algo/order/{oid}': 2, 'algo/order/client/{client_order_id}': 2, }, 'delete': { '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': { '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', }, // override defaultNetworkCodePriorities for a specific currency 'defaultNetworkCodeForCurrencies': { // 'USDT': 'TRC20', // 'BTC': 'BTC', }, 'transfer': { 'fillResponseFromRequest': true, }, 'brokerId': 'bc830de7-50f3-460b-9ee0-f430f83f9dad', }, 'commonCurrencies': {}, 'exceptions': { 'exact': { '-1000': ExchangeError, '-1001': AuthenticationError, '-1002': AuthenticationError, '-1003': RateLimitExceeded, '-1004': BadRequest, '-1005': BadRequest, '-1006': BadRequest, '-1007': BadRequest, '-1008': InvalidOrder, '-1009': BadRequest, '-1011': ExchangeError, '-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': { '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, }); } async fetchMarkets(params = {}) { /** * @method * @name woo#fetchMarkets * @description retrieves data on all markets for woo * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ const response = await this.v1PublicGetInfo(params); // // { // "rows": [ // { // "symbol": "SPOT_AAVE_USDT", // "quote_min": 0, // "quote_max": 100000, // "quote_tick": 0.01, // "base_min": 0.01, // "base_max": 7284, // "base_tick": 0.0001, // "min_notional": 10, // "price_range": 0.1, // "created_time": "0", // "updated_time": "1639107647.988", // "is_stable": 0 // }, // ... // "success": true // } // const data = this.safeValue(response, 'rows', []); return this.parseMarkets(data); } 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 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; } 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': undefined, 'contract': contract, 'linear': linear, 'inverse': undefined, 'contractSize': contractSize, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.safeNumber(market, 'base_tick'), 'price': this.safeNumber(market, 'quote_tick'), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': this.safeNumber(market, 'base_min'), 'max': this.safeNumber(market, 'base_max'), }, 'price': { 'min': this.safeNumber(market, 'quote_min'), 'max': this.safeNumber(market, 'quote_max'), }, 'cost': { 'min': this.safeNumber(market, 'min_notional'), 'max': undefined, }, }, 'created': this.safeTimestamp(market, 'created_time'), 'info': market, }; } async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { /** * @method * @name woo#fetchTrades * @description get the list of most recent trades for a particular symbol * @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} */ await this.loadMarkets(); const market = this.market(symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['limit'] = limit; } const response = await this.v1PublicGetMarketTrades(this.extend(request, params)); // // { // "success": true, // "rows": [ // { // "symbol": "SPOT_BTC_USDT", // "side": "SELL", // "executed_price": 46222.35, // "executed_quantity": 0.0012, // "executed_timestamp": "1641241162.329" // }, // { // "symbol": "SPOT_BTC_USDT", // "side": "SELL", // "executed_price": 46222.35, // "executed_quantity": 0.0012, // "executed_timestamp": "1641241162.329" // }, // { // "symbol": "SPOT_BTC_USDT", // "side": "BUY", // "executed_price": 46224.32, // "executed_quantity": 0.00039, // "executed_timestamp": "1641241162.287" // }, // ... // ] // } // const resultResponse = this.safeValue(response, 'rows', {}); return this.parseTrades(resultResponse, market, since, limit); } parseTrade(trade, market = undefined) { // // public/market_trades // // { // "symbol": "SPOT_BTC_USDT", // "side": "SELL", // "executed_price": 46222.35, // "executed_quantity": 0.0012, // "executed_timestamp": "1641241162.329" // } // // fetchOrderTrades, fetchOrder // // { // "id": "99119876", // "symbol": "SPOT_WOO_USDT", // "fee": "0.0024", // "side": "BUY", // "executed_timestamp": "1641481113.084", // "order_id": "87001234", // "order_tag": "default", <-- this param only in "fetchOrderTrades" // "executed_price": "1", // "executed_quantity": "12", // "fee_asset": "WOO", // "is_maker": "1" // } // const isFromFetchOrder = ('id' in trade); const timestamp = this.safeTimestamp(trade, 'executed_timestamp'); const marketId = this.safeString(trade, 'symbol'); market = this.safeMarket(marketId, market); const symbol = market['symbol']; const price = this.safeString(trade, 'executed_price'); const amount = this.safeString(trade, 'executed_quantity'); const order_id = this.safeString(trade, 'order_id'); const fee = this.parseTokenAndFeeTemp(trade, 'fee_asset', 'fee'); 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.safeString(trade, 'is_maker') === '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, feeTokenKey, feeAmountKey) { const feeCost = this.safeString(item, feeAmountKey); let fee = undefined; if (feeCost !== undefined) { const feeCurrencyId = this.safeString(item, feeTokenKey); const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } return fee; } async fetchTradingFees(params = {}) { /** * @method * @name woo#fetchTradingFees * @description fetch the trading fees for multiple markets * @see https://docs.woo.org/#get-account-information-new * @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 */ await this.loadMarkets(); const response = await this.v3PrivateGetAccountinfo(params); // // { // "success": true, // "data": { // "applicationId": "dsa", // "account": "dsa", // "alias": "haha", // "accountMode": "MARGIN", // "leverage": 1, // "takerFeeRate": 1, // "makerFeeRate": 1, // "interestRate": 1, // "futuresTakerFeeRate": 1, // "futuresMakerFeeRate": 1, // "otpauth": true, // "marginRatio": 1, // "openMarginRatio": 1, // "initialMarginRatio": 1, // "maintenanceMarginRatio": 1, // "totalCollateral": 1, // "freeCollateral": 1, // "totalAccountValue": 1, // "totalVaultValue": 1, // "totalStakingValue": 1 // }, // "timestamp": 1673323685109 // } // const data = this.safeValue(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; } async fetchCurrencies(params = {}) { /** * @method * @name woo#fetchCurrencies * @description fetches all available currencies on an exchange * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an associative dictionary of currencies */ const result = {}; const tokenResponse = await this.v1PublicGetToken(params); // // { // "rows": [ // { // "token": "ETH_USDT", // "fullname": "Tether", // "decimals": 6, // "balance_token": "USDT", // "created_time": "0", // "updated_time": "0" // }, // { // "token": "BSC_USDT", // "fullname": "Tether", // "decimals": 18, // "balance_token": "USDT", // "created_time": "0", // "updated_time": "0" // }, // { // "token": "ZEC", // "fullname": "ZCash", // "decimals": 8, // "balance_token": "ZEC", // "created_time": "0", // "updated_time": "0" // }, // ... // ], // "success": true // } // // only make one request for currrencies... // const tokenNetworkResponse = await this.v1PublicGetTokenNetwork (params); // // { // "rows": [ // { // "protocol": "ERC20", // "token": "USDT", // "name": "Ethereum", // "minimum_withdrawal": 30, // "withdrawal_fee": 25, // "allow_deposit": 1, // "allow_withdraw": 1 // }, // { // "protocol": "TRC20", // "token": "USDT", // "name": "Tron", // "minimum_withdrawal": 30, // "withdrawal_fee": 1, // "allow_deposit": 1, // "allow_withdraw": 1 // }, // ... // ], // "success": true // } // const tokenRows = this.safeValue(tokenResponse, 'rows', []); const networksByCurrencyId = this.groupBy(tokenRows, 'balance_token'); const currencyIds = Object.keys(networksByCurrencyId); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const networks = networksByCurrencyId[currencyId]; const code = this.safeCurrencyCode(currencyId); let name = undefined; let minPrecision = undefined; const resultingNetworks = {}; for (let j = 0; j < networks.length; j++) { const network = networks[j]; name = this.safeString(network, 'fullname'); const networkId = this.safeString(network, 'token'); const splitted = networkId.split('_'); const unifiedNetwork = splitted[0]; const precision = this.parsePrecision(this.safeString(network, 'decimals')); if (precision !== undefined) { minPrecision = (minPrecision === undefined) ? precision : Precise.stringMin(precision, minPrecision); } resultingNetworks[unifiedNetwork] = { 'id': networkId, 'network': unifiedNetwork, 'limits': { 'withdraw': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': undefined, 'max': undefined, }, }, 'active': undefined, 'deposit': undefined, 'withdraw': undefined, 'fee': undefined, 'precision': this.parseNumber(precision), 'info': network, }; } result[code] = { 'id': currencyId, 'name': name, 'code': code, 'precision': this.parseNumber(minPrecision), 'active': undefined, 'fee': undefined, 'networks': resultingNetworks, 'deposit': undefined, 'withdraw': undefined, 'limits': { 'deposit': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, 'info': networks, }; } return result; } async createMarketBuyOrderWithCost(symbol, cost, params = {}) { /** * @method * @name woo#createMarketBuyOrderWithCost * @description create a market buy order by providing the symbol and cost * @see https://docs.woo.org/#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} */ await this.loadMarkets(); const market = this.market(symbol); if (!market['spot']) { throw new NotSupported(this.id + ' createMarketBuyOrderWithCost() supports spot orders only'); } params['createMarketBuyOrderRequiresPrice'] = false; return await this.createOrder(symbol, 'market', 'buy', cost, undefined, params); } async createOrder(symbol, type, side, amount, price = undefined, params = {}) { /** * @method * @name woo#createOrder * @see https://docs.woo.org/#send-order * @see https://docs.woo.org/#send-algo-order * @description create a trade 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 of currency you want to trade in units of 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 {float} [params.triggerPrice] The price a trigger order is triggered at * @param {object} [params.takeProfit] *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered (perpetual swap markets only) * @param {float} [params.takeProfit.triggerPrice] take profit trigger price * @param {object} [params.stopLoss] *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered (perpetual swap markets only) * @param {float} [params.stopLoss.triggerPrice] stop loss trigger price * @param {float} [params.algoType] 'STOP'or 'TRAILING_STOP' or 'OCO' or 'CLOSE_POSITION' * @param {float} [params.cost] *spot market buy only* the quote quantity that can be used as an alternative for the amount * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ const reduceOnly = this.safeValue2(params, 'reduceOnly', 'reduce_only'); params = this.omit(params, ['reduceOnly', 'reduce_only']); const orderType = type.toUpperCase(); await this.loadMarkets(); const market = this.market(symbol); const orderSide = side.toUpperCase(); const request = { 'symbol': market['id'], 'side': orderSide, }; const stopPrice = this.safeNumber2(params, 'triggerPrice', 'stopPrice'); const stopLoss = this.safeValue(params, 'stopLoss'); const takeProfit = this.safeValue(params, 'takeProfit'); const algoType = this.safeString(params, 'algoType'); const isStop = stopPrice !== undefined || stopLoss !== undefined || takeProfit !== undefined || (this.safeValue(params, 'childOrders') !== undefined); const isMarket = orderType === 'MARKET'; const timeInForce = this.safeStringLower(params, 'timeInForce'); const postOnly = this.isPostOnly(isMarket, undefined, params); const reduceOnlyKey = isStop ? 'reduceOnly' : 'reduce_only'; const clientOrderIdKey = isStop ? 'clientOrderId' : 'client_order_id'; const orderQtyKey = isStop ? 'quantity' : 'order_quantity'; const priceKey = isStop ? 'price' : 'order_price'; const typeKey = isStop ? 'type' : 'order_type'; request[typeKey] = orderType; // LIMIT/MARKET/IOC/FOK/POST_ONLY/ASK/BID if (!isStop) { if (postOnly) { request['order_type'] = 'POST_ONLY'; } else if (timeInForce === 'fok') { request['order_type'] = 'FOK'; } else if (timeInForce === 'ioc') { request['order_type'] = 'IOC'; } } if (reduceOnly) { request[reduceOnlyKey] = reduceOnly; } if (price !== undefined) { request[priceKey] = this.priceToPrecision(symbol, price); } if (isMarket && !isStop) { // for market buy it requires the amount of quote currency to spend if (market['spot'] && orderSide === 'BUY') { let quoteAmount = undefined; let createMarketBuyOrderRequiresPrice = true; [createMarketBuyOrderRequiresPrice, params] = this.handleOptionAndParams(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true); const cost = this.safeNumber2(params, 'cost', 'order_amount'); params = this.omit(params, ['cost', 'order_amount']); if (cost !== undefined) { quoteAmount = this.costToPrecision(symbol, cost); } else if (createMarketBuyOrderRequiresPrice) { if (price === undefined) { throw new InvalidOrder(this.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend (amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to false and pass the cost to spend (quote quantity) in the amount argument'); } else { const amountString = this.numberToString(amount); const priceString = this.numberToString(price); const costRequest = Precise.stringMul(amountString, priceString); quoteAmount = this.costToPrecision(symbol, costRequest); } } else { quoteAmount = this.costToPrecision(symbol, amount); } request['order_amount'] = quoteAmount; } else { request['order_quantity'] = this.amountToPrecision(symbol, amount); } } else if (algoType !== 'POSITIONAL_TP_SL') { request[orderQtyKey] = this.amountToPrecision(symbol, amount); } const clientOrderId = this.safeStringN(params, ['clOrdID', 'clientOrderId', 'client_order_id']); if (clientOrderId !== undefined) { request[clientOrderIdKey] = clientOrderId; } if (stopPrice !== undefined) { if (algoType !== 'TRAILING_STOP') { request['triggerPrice'] = this.priceToPrecision(symbol, stopPrice); request['algoType'] = 'STOP'; } } else if ((stopLoss !== undefined) || (takeProfit !== undefined)) { request['algoType'] = 'BRACKET'; const outterOrder = { 'symbol': market['id'], 'reduceOnly': false, 'algoType': 'POSITIONAL_TP_SL', 'childOrders': [], }; const closeSide = (orderSide === 'BUY') ? 'SELL' : 'BUY'; if (stopLoss !== undefined) { const stopLossPrice = this.safeNumber2(stopLoss, 'triggerPrice', 'price', stopLoss); const stopLossOrder = { 'side': closeSide, 'algoType': 'STOP_LOSS', 'triggerPrice': this.priceToPrecision(symbol, stopLossPrice), 'type': 'CLOSE_POSITION', 'reduceOnly': true, }; outterOrder['childOrders'].push(stopLossOrder); } if (takeProfit !== undefined) { const takeProfitPrice = this.safeNumber2(takeProfit, 'triggerPrice', 'price', takeProfit); const takeProfitOrder = { 'side': closeSide, 'algoType': 'TAKE_PROFIT', 'triggerPrice': this.priceToPrecision(symbol, takeProfitPrice), 'type': 'CLOSE_POSITION', 'reduceOnly': true, }; outterOrder['childOrders'].push(takeProfitOrder); } request['childOrders'] = [outterOrder]; } params = this.omit(params, ['clOrdID', 'clientOrderId', 'client_order_id', 'postOnly', 'timeInForce', 'stopPrice', 'triggerPrice', 'stopLoss', 'takeProfit']); let response = undefined; if (isStop) { response = await this.v3PrivatePostAlgoOrder(this.extend(request, params)); } else { response = await this.v1PrivatePostOrder(this.extend(request, params)); } // { // "success": true, // "timestamp": "1641383206.489", // "order_id": "86980774", // "order_type": "LIMIT", // "order_price": "1", // null for "MARKET" order // "order_quantity": "12", // null for "MARKET" order // "order_amount": null, // NOT-null for "MARKET" order // "client_order_id": "0" // } // stop orders // { // "success": true, // "data": { // "rows": [ // { // "orderId": "1578938", // "clientOrderId": "0", // "algoType": "STOP_LOSS", // "quantity": "0.1" // } // ] // }, // "timestamp": "1686149372216" // } const data = this.safeValue(response, 'data'); if (data !== undefined) { const rows = this.safeValue(data, 'rows', []); return this.parseOrder(rows[0], market); } const order = this.parseOrder(response, market); order['type'] = type; return order; } async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) { /** * @method * @name woo#editOrder * @see https://docs.woo.org/#edit-order * @see https://docs.woo.org/#edit-order-by-client_order_id * @see https://docs.woo.org/#edit-algo-order * @see https://docs.woo.org/#edit-algo-order-by-client_order_id * @description edit a trade order * @param {string} id order id * @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 of currency you want to trade in units of 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 {float} [params.triggerPrice] The price a trigger order is triggered at * @param {float} [params.stopLossPrice] price to trigger stop-loss orders * @param {float} [params.takeProfitPrice] price to trigger take-profit orders * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); const market = this.market(symbol); const request = { // 'quantity': this.amountToPrecision (symbol, amount), // 'price': this.priceToPrecision (symbol, price), }; if (price !== undefined) { request['price'] = this.priceToPrecision(symbol, price); } if (amount !== undefined) { request['quantity'] = this.amountToPrecision(symbol, amount); } const clientOrderIdUnified = this.safeString2(params, 'clOrdID', 'clientOrderId'); const clientOrderIdExchangeSpecific = this.safeString(params, 'client_order_id', clientOrderIdUnified); const isByClientOrder = clientOrderIdExchangeSpecific !== undefined; const stopPrice = this.safeNumberN(params, ['triggerPrice', 'stopPrice', 'takeProfitPrice', 'stopLossPrice']); if (stopPrice !== undefined) { request['triggerPrice'] = this.priceToPrecision(symbol, stopPrice); } params = this.omit(params, ['clOrdID', 'clientOrderId', 'client_order_id', 'stopPrice', 'triggerPrice', 'takeProfitPrice', 'stopLossPrice']); const isStop = (stopPrice !== undefined) || (this.safeValue(params, 'childOrders') !== undefined); let response = undefined; if (isByClientOrder) { request['client_order_id'] = clientOrderIdExchangeSpecific; if (isStop) { response = await this.v3PrivatePutAlgoOrderClientClientOrderId(this.extend(request, params)); } else { response = await this.v3PrivatePutOrderClientClientOrderId(this.extend(request, params)); } } else { request['oid'] = id; if (isStop) { response = await this.v3PrivatePutAlgoOrderOid(this.extend(request, params)); } else { response = await this.v3PrivatePutOrderOid(this.extend(request, params)); } } // // { // "code": 0, // "data": { // "status": "string", // "success": true // }, // "message": "string", // "success": true, // "timestamp": 0 // } // const data = this.safeValue(response, 'data', {}); return this.parseOrder(data, market); } async cancelOrder(id, symbol = undefined, params = {}) { /** * @method * @name woo#cancelOrder * @see https://docs.woo.org/#cancel-algo-order * @see https://docs.woo.org/#cancel-order * @see https://docs.woo.org/#cancel-order-by-client_order_id * @description cancels an open order * @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 * @param {boolean} [params.stop] whether the order is a stop/algo order * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ const stop = this.safeValue(params, 'stop', false); params = this.omit(params, 'stop'); if (!stop && (symbol === undefined)) { throw new ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument'); } await this.loadMarkets(); let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } const request = {}; const clientOrderIdUnified = this.safeString2(params, 'clOrdID', 'clientOrderId'); const clientOrderIdExchangeSpecific = this.safeString(params, 'client_order_id', clientOrderIdUnified); const isByClientOrder = clientOrderIdExchangeSpecific !== undefined; let response = undefined; if (stop) { request['order_id'] = id; response = await this.v3PrivateDeleteAlgoOrderOrderId(this.extend(request, params)); } else { request['symbol'] = market['id']; if (isByClientOrder) { request['client_order_id'] = clientOrderIdExchangeSpecific; params = this.omit(params, ['clOrdID', 'clientOrderId', 'client_order_id']); response = await this.v1PrivateDeleteClientOrder(this.extend(request, params)); } else { request['order_id'] = id; response = await this.v1PrivateDeleteOrder(this.extend(request, params)); } } // // { success: true, status: "CANCEL_SENT" } // const extendParams = { 'symbol': symbol }; if (isByClientOrder) { extendParams['client_order_id'] = clientOrderIdExchangeSpecific; } else { extendParams['id'] = id; } return this.extend(this.parseOrder(response), extendParams); } async cancelAllOrders(symbol = undefined, params = {}) { /** * @method * @name woo#cancelAllOrders * @see https://docs.woo.org/#cancel-all-pending-orders * @see https://docs.woo.org/#cancel-orders * @see https://docs.woo.org/#cancel-all-pending-algo-orders * @description cancel all open orders in a market * @param {string} symbol unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.stop] whether the order is a stop/algo order * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); const stop = this.safeValue(params, 'stop'); params = this.omit(params, 'stop'); if (stop) { return await this.v3PrivateDeleteAlgoOrdersPending(params); } if (symbol === undefined) { throw new ArgumentsRequired(this.id + ' cancelOrders() requires a symbol argument'); } const market = this.market(symbol); const request = { 'symbol': market['id'], }; const response = await this.v1PrivateDeleteOrders(this.extend(request, params)); // // { // "success":true, // "status":"CANCEL_ALL_SENT" // } // return response; } async fetchOrder(id, symbol = undefined, params = {}) { /** * @method * @name woo#fetchOrder * @see https://docs.woo.org/#get-algo-order * @see https://docs.woo.org/#get-order * @description fetches information on an order made by the user * @param {string} symbol unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.stop] whether the order is a stop/algo order * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); const market = (symbol !== undefined) ? this.market(symbol) : undefined; const stop = this.safeValue(params, 'stop'); params = this.omit(params, 'stop'); const request = {}; const clientOrderId = this.safeString2(params, 'clOrdID', 'clientOrderId'); let response = undefined; if (stop) { request['oid'] = id; response = await this.v3PrivateGetAlgoOrderOid(this.extend(request, params)); } else if (clientOrderId) { request['client_order_id'] = clientOrderId; response = await this.v1PrivateGetClientOrderClientOrderId(this.extend(request, params)); } else { request['oid'] = id; response = await this.v1PrivateGetOrderOid(this.extend(request, params)); } // // { // "success": true, // "symbol": "SPOT_WOO_USDT", // "status": "FILLED", // FILLED, NEW // "side": "BUY", // "created_time": "1641480933.000", // "order_id": "87541111", // "order_tag": "default", // "price": "1", // "type": "LIMIT", // "quantity": "12", // "amount": null, // "visible": "12", // "executed": "12", // or any partial amount // "total_fee": "0.0024", // "fee_asset": "WOO", // "client_order_id": null, // "average_executed_price"