astro-perp-ccxt-dev
Version:
3,211 lines • 147 kB
JavaScript
'use strict';
var okcoin$1 = require('./abstract/okcoin.js');
var errors = require('./base/errors.js');
var Precise = require('./base/Precise.js');
var number = require('./base/functions/number.js');
var sha256 = require('./static_dependencies/noble-hashes/sha256.js');
// ----------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/**
* @class okcoin
* @augments Exchange
*/
class okcoin extends okcoin$1 {
describe() {
return this.deepExtend(super.describe(), {
'id': 'okcoin',
'name': 'OKCoin',
'countries': ['CN', 'US'],
'version': 'v5',
// cheapest endpoint is 100 requests per 2 seconds
// 50 requests per second => 1000 / 50 = 20ms
'rateLimit': 20,
'pro': true,
'has': {
'CORS': undefined,
'spot': true,
'margin': false,
'swap': false,
'future': true,
'option': undefined,
'cancelOrder': true,
'createMarketBuyOrderWithCost': true,
'createMarketOrderWithCost': false,
'createMarketSellOrderWithCost': false,
'createOrder': true,
'createPostOnlyOrder': true,
'createReduceOnlyOrder': true,
'createStopLimitOrder': true,
'createStopMarketOrder': true,
'createStopOrder': true,
'createTriggerOrder': true,
'fetchBalance': true,
'fetchBorrowInterest': false,
'fetchBorrowRate': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchBorrowRates': false,
'fetchBorrowRatesPerSymbol': false,
'fetchClosedOrders': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': false,
'fetchDeposits': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchLedger': true,
'fetchMarkets': true,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': undefined,
'fetchOrderTrades': true,
'fetchPosition': false,
'fetchPositions': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTransactions': undefined,
'fetchWithdrawals': true,
'reduceMargin': false,
'repayCrossMargin': false,
'repayIsolatedMargin': false,
'setMargin': false,
'transfer': true,
'withdraw': true,
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1H',
'2h': '2H',
'4h': '4H',
'6h': '6H',
'12h': '12H',
'1d': '1D',
'1w': '1W',
'1M': '1M',
'3M': '3M',
},
'hostname': 'okcoin.com',
'urls': {
'logo': 'https://user-images.githubusercontent.com/51840849/87295551-102fbf00-c50e-11ea-90a9-462eebba5829.jpg',
'api': {
'rest': 'https://www.{hostname}',
},
'www': 'https://www.okcoin.com',
'doc': 'https://www.okcoin.com/docs/en/',
'fees': 'https://www.okcoin.com/coin-fees',
'referral': 'https://www.okcoin.com/account/register?flag=activity&channelId=600001513',
'test': {
'rest': 'https://testnet.okex.com',
},
},
'api': {
'public': {
'get': {
'market/tickers': 1,
'market/ticker': 1,
'market/books': 1 / 2,
'market/candles': 1 / 2,
'market/history-candles': 1 / 2,
'market/trades': 1 / 5,
'market/history-trades': 2,
'market/platform-24-volume': 10,
'market/open-oracle': 50,
'market/exchange-rate': 20,
'public/instruments': 1,
'public/time': 2,
},
},
'private': {
'get': {
// trade
'trade/order': 1 / 3,
'trade/orders-pending': 1 / 3,
'trade/orders-history': 1 / 2,
'trade/orders-history-archive': 1 / 2,
'trade/fills': 1 / 3,
'trade/fills-history': 2.2,
'trade/fills-archive': 2,
'trade/order-algo': 1,
'trade/orders-algo-pending': 1,
'trade/orders-algo-history': 1,
// rfq
'otc/rfq/trade': 4,
'otc/rfq/history': 4,
// account
'account/balance': 2,
'account/bills': 5 / 3,
'account/bills-archive': 5 / 3,
'account/config': 4,
'account/max-size': 4,
'account/max-avail-size': 4,
'account/trade-fee': 4,
'account/max-withdrawal': 4,
// funding or assets
'asset/currencies': 5 / 3,
'asset/balances': 5 / 3,
'asset/asset-valuation': 10,
'asset/transfer-state': 10,
'asset/bills': 5 / 3,
'asset/deposit-lightning': 5,
'asset/deposit-address': 5 / 3,
'asset/deposit-history': 5 / 3,
'asset/withdrawal-history': 5 / 3,
'asset/deposit-withdraw-status': 20,
// fiat
'fiat/deposit-history': 5 / 3,
'fiat-withdraw-history': 5 / 3,
'fiat-channel': 5 / 3,
// sub-account
'users/subaccount/list': 10,
'users/subaccount/apiKey': 10,
'account/subaccount/balances': 10,
'asset/subaccount/balances': 10,
'asset/subaccount/bills': 10,
},
'post': {
// trade
'trade/order': 1 / 3,
'trade/batch-orders': 1 / 15,
'trade/cancel-order': 1 / 3,
'trade/cancel-batch-orders': 1 / 15,
'trade/amend-order': 1 / 3,
'trade/amend-batch-orders': 1 / 150,
'trade/order-algo': 1,
'trade/cancel-algos': 1,
'trade/cancel-advance-algos': 1,
// rfq
'otc/rfq/quote': 4,
'otc/rfq/trade': 4,
// funding
'asset/transfer': 4,
'asset/withdrawal': 4,
'asset/withdrawal-lightning': 4,
'asset/withdrawal-cancel': 4,
// fiat
'fiat/deposit': 5 / 3,
'fiat/cancel-deposit': 5 / 3,
'fiat/withdrawal': 5 / 3,
'fiat/cancel-withdrawal': 5 / 3,
// sub-account
'asset/subaccount/transfer': 10,
},
},
},
'features': {
'spot': {
'sandbox': false,
'createOrder': {
'marginMode': true,
'triggerPrice': true,
'triggerDirection': true,
'triggerPriceType': {
'last': true,
'mark': false,
'index': false,
},
'stopLossPrice': true,
'takeProfitPrice': true,
'attachedStopLossTakeProfit': {
'triggerPriceType': {
'last': true,
'mark': false,
'index': false,
},
'price': true,
},
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'hedged': false,
'trailing': true,
'leverage': false,
'marketBuyByCost': true,
'marketBuyRequiresPrice': true,
'selfTradePrevention': false,
'iceberg': true, // todo
},
'createOrders': undefined,
'fetchMyTrades': {
'marginMode': false,
'limit': 100,
'daysBack': 90,
'untilDays': 90,
'symbolRequired': false,
},
'fetchOrder': {
'marginMode': false,
'trigger': true,
'trailing': true,
'symbolRequired': true,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': 100,
'trigger': true,
'trailing': true,
'symbolRequired': false,
},
'fetchOrders': undefined,
'fetchClosedOrders': {
'marginMode': false,
'limit': 100,
'daysBack': 90,
'daysBackCanceled': 1 / 12,
'untilDays': 90,
'trigger': true,
'trailing': true,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 100, // 300 is only possible for 'recent' 1440 candles, which does not make much sense
},
},
'swap': {
'linear': undefined,
'inverse': undefined,
},
'future': {
'linear': undefined,
'inverse': undefined,
},
},
'fees': {
'trading': {
'taker': 0.002,
'maker': 0.001,
},
'spot': {
'taker': 0.0015,
'maker': 0.0010,
},
},
'requiredCredentials': {
'apiKey': true,
'secret': true,
'password': true,
},
'exceptions': {
'exact': {
// Public error codes from 50000-53999
// General Class
'1': errors.ExchangeError,
'2': errors.ExchangeError,
'50000': errors.BadRequest,
'50001': errors.OnMaintenance,
'50002': errors.BadRequest,
'50004': errors.RequestTimeout,
'50005': errors.ExchangeNotAvailable,
'50006': errors.BadRequest,
'50007': errors.AccountSuspended,
'50008': errors.AuthenticationError,
'50009': errors.AccountSuspended,
'50010': errors.ExchangeError,
'50011': errors.RateLimitExceeded,
'50012': errors.ExchangeError,
'50013': errors.ExchangeNotAvailable,
'50014': errors.BadRequest,
'50015': errors.ExchangeError,
'50016': errors.ExchangeError,
'50017': errors.ExchangeError,
'50018': errors.ExchangeError,
'50019': errors.ExchangeError,
'50020': errors.ExchangeError,
'50021': errors.ExchangeError,
'50022': errors.ExchangeError,
'50023': errors.ExchangeError,
'50024': errors.BadRequest,
'50025': errors.ExchangeError,
'50026': errors.ExchangeNotAvailable,
'50027': errors.PermissionDenied,
'50028': errors.ExchangeError,
'50029': errors.ExchangeError,
'50030': errors.PermissionDenied,
'50032': errors.AccountSuspended,
'50033': errors.AccountSuspended,
'50035': errors.BadRequest,
'50036': errors.BadRequest,
'50037': errors.BadRequest,
'50038': errors.ExchangeError,
'50039': errors.ExchangeError,
'50041': errors.ExchangeError,
'50044': errors.BadRequest,
// API Class
'50100': errors.ExchangeError,
'50101': errors.AuthenticationError,
'50102': errors.InvalidNonce,
'50103': errors.AuthenticationError,
'50104': errors.AuthenticationError,
'50105': errors.AuthenticationError,
'50106': errors.AuthenticationError,
'50107': errors.AuthenticationError,
'50108': errors.ExchangeError,
'50109': errors.ExchangeError,
'50110': errors.PermissionDenied,
'50111': errors.AuthenticationError,
'50112': errors.AuthenticationError,
'50113': errors.AuthenticationError,
'50114': errors.AuthenticationError,
'50115': errors.BadRequest,
// Trade Class
'51000': errors.BadRequest,
'51001': errors.BadSymbol,
'51002': errors.BadSymbol,
'51003': errors.BadRequest,
'51004': errors.InvalidOrder,
'51005': errors.InvalidOrder,
'51006': errors.InvalidOrder,
'51007': errors.InvalidOrder,
'51008': errors.InsufficientFunds,
'51009': errors.AccountSuspended,
'51010': errors.AccountNotEnabled,
'51011': errors.InvalidOrder,
'51012': errors.BadSymbol,
'51014': errors.BadSymbol,
'51015': errors.BadSymbol,
'51016': errors.InvalidOrder,
'51017': errors.ExchangeError,
'51018': errors.ExchangeError,
'51019': errors.ExchangeError,
'51020': errors.InvalidOrder,
'51023': errors.ExchangeError,
'51024': errors.AccountSuspended,
'51025': errors.ExchangeError,
'51026': errors.BadSymbol,
'51030': errors.InvalidOrder,
'51031': errors.InvalidOrder,
'51032': errors.InvalidOrder,
'51033': errors.InvalidOrder,
'51037': errors.InvalidOrder,
'51038': errors.InvalidOrder,
'51044': errors.InvalidOrder,
'51046': errors.InvalidOrder,
'51047': errors.InvalidOrder,
'51048': errors.InvalidOrder,
'51049': errors.InvalidOrder,
'51050': errors.InvalidOrder,
'51051': errors.InvalidOrder,
'51052': errors.InvalidOrder,
'51053': errors.InvalidOrder,
'51054': errors.BadRequest,
'51056': errors.InvalidOrder,
'51058': errors.InvalidOrder,
'51059': errors.InvalidOrder,
'51100': errors.InvalidOrder,
'51102': errors.InvalidOrder,
'51103': errors.InvalidOrder,
'51108': errors.InvalidOrder,
'51109': errors.InvalidOrder,
'51110': errors.InvalidOrder,
'51111': errors.BadRequest,
'51112': errors.InvalidOrder,
'51113': errors.RateLimitExceeded,
'51115': errors.InvalidOrder,
'51116': errors.InvalidOrder,
'51117': errors.InvalidOrder,
'51118': errors.InvalidOrder,
'51119': errors.InsufficientFunds,
'51120': errors.InvalidOrder,
'51121': errors.InvalidOrder,
'51122': errors.InvalidOrder,
'51124': errors.InvalidOrder,
'51125': errors.InvalidOrder,
'51126': errors.InvalidOrder,
'51127': errors.InsufficientFunds,
'51128': errors.InvalidOrder,
'51129': errors.InvalidOrder,
'51130': errors.BadSymbol,
'51131': errors.InsufficientFunds,
'51132': errors.InvalidOrder,
'51133': errors.InvalidOrder,
'51134': errors.InvalidOrder,
'51135': errors.InvalidOrder,
'51136': errors.InvalidOrder,
'51137': errors.InvalidOrder,
'51138': errors.InvalidOrder,
'51139': errors.InvalidOrder,
'51156': errors.BadRequest,
'51159': errors.BadRequest,
'51162': errors.InvalidOrder,
'51163': errors.InvalidOrder,
'51166': errors.InvalidOrder,
'51174': errors.InvalidOrder,
'51201': errors.InvalidOrder,
'51202': errors.InvalidOrder,
'51203': errors.InvalidOrder,
'51204': errors.InvalidOrder,
'51205': errors.InvalidOrder,
'51250': errors.InvalidOrder,
'51251': errors.InvalidOrder,
'51252': errors.InvalidOrder,
'51253': errors.InvalidOrder,
'51254': errors.InvalidOrder,
'51255': errors.InvalidOrder,
'51256': errors.InvalidOrder,
'51257': errors.InvalidOrder,
'51258': errors.InvalidOrder,
'51259': errors.InvalidOrder,
'51260': errors.InvalidOrder,
'51261': errors.InvalidOrder,
'51262': errors.InvalidOrder,
'51263': errors.InvalidOrder,
'51264': errors.InvalidOrder,
'51265': errors.InvalidOrder,
'51267': errors.InvalidOrder,
'51268': errors.InvalidOrder,
'51269': errors.InvalidOrder,
'51270': errors.InvalidOrder,
'51271': errors.InvalidOrder,
'51272': errors.InvalidOrder,
'51273': errors.InvalidOrder,
'51274': errors.InvalidOrder,
'51275': errors.InvalidOrder,
'51276': errors.InvalidOrder,
'51277': errors.InvalidOrder,
'51278': errors.InvalidOrder,
'51279': errors.InvalidOrder,
'51280': errors.InvalidOrder,
'51321': errors.InvalidOrder,
'51322': errors.InvalidOrder,
'51323': errors.BadRequest,
'51324': errors.BadRequest,
'51325': errors.InvalidOrder,
'51327': errors.InvalidOrder,
'51328': errors.InvalidOrder,
'51329': errors.InvalidOrder,
'51330': errors.InvalidOrder,
'51400': errors.OrderNotFound,
'51401': errors.OrderNotFound,
'51402': errors.OrderNotFound,
'51403': errors.InvalidOrder,
'51404': errors.InvalidOrder,
'51405': errors.ExchangeError,
'51406': errors.ExchangeError,
'51407': errors.BadRequest,
'51408': errors.ExchangeError,
'51409': errors.ExchangeError,
'51410': errors.CancelPending,
'51500': errors.ExchangeError,
'51501': errors.ExchangeError,
'51502': errors.InsufficientFunds,
'51503': errors.ExchangeError,
'51506': errors.ExchangeError,
'51508': errors.ExchangeError,
'51509': errors.ExchangeError,
'51510': errors.ExchangeError,
'51511': errors.ExchangeError,
'51600': errors.ExchangeError,
'51601': errors.ExchangeError,
'51602': errors.ExchangeError,
'51603': errors.OrderNotFound,
'51732': errors.AuthenticationError,
'51733': errors.AuthenticationError,
'51734': errors.AuthenticationError,
'51735': errors.ExchangeError,
'51736': errors.InsufficientFunds,
// Data class
'52000': errors.ExchangeError,
// SPOT/MARGIN error codes 54000-54999
'54000': errors.ExchangeError,
'54001': errors.ExchangeError,
// FUNDING error codes 58000-58999
'58000': errors.ExchangeError,
'58001': errors.AuthenticationError,
'58002': errors.PermissionDenied,
'58003': errors.ExchangeError,
'58004': errors.AccountSuspended,
'58005': errors.ExchangeError,
'58006': errors.ExchangeError,
'58007': errors.ExchangeError,
'58100': errors.ExchangeError,
'58101': errors.AccountSuspended,
'58102': errors.RateLimitExceeded,
'58103': errors.ExchangeError,
'58104': errors.ExchangeError,
'58105': errors.ExchangeError,
'58106': errors.ExchangeError,
'58107': errors.ExchangeError,
'58108': errors.ExchangeError,
'58109': errors.ExchangeError,
'58110': errors.ExchangeError,
'58111': errors.ExchangeError,
'58112': errors.ExchangeError,
'58114': errors.ExchangeError,
'58115': errors.ExchangeError,
'58116': errors.ExchangeError,
'58117': errors.ExchangeError,
'58125': errors.BadRequest,
'58126': errors.BadRequest,
'58127': errors.BadRequest,
'58128': errors.BadRequest,
'58200': errors.ExchangeError,
'58201': errors.ExchangeError,
'58202': errors.ExchangeError,
'58203': errors.InvalidAddress,
'58204': errors.AccountSuspended,
'58205': errors.ExchangeError,
'58206': errors.ExchangeError,
'58207': errors.InvalidAddress,
'58208': errors.ExchangeError,
'58209': errors.ExchangeError,
'58210': errors.ExchangeError,
'58211': errors.ExchangeError,
'58212': errors.ExchangeError,
'58213': errors.AuthenticationError,
'58221': errors.BadRequest,
'58222': errors.BadRequest,
'58224': errors.BadRequest,
'58227': errors.BadRequest,
'58228': errors.BadRequest,
'58229': errors.InsufficientFunds,
'58300': errors.ExchangeError,
'58350': errors.InsufficientFunds,
// Account error codes 59000-59999
'59000': errors.ExchangeError,
'59001': errors.ExchangeError,
'59100': errors.ExchangeError,
'59101': errors.ExchangeError,
'59102': errors.ExchangeError,
'59103': errors.InsufficientFunds,
'59104': errors.ExchangeError,
'59105': errors.ExchangeError,
'59106': errors.ExchangeError,
'59107': errors.ExchangeError,
'59108': errors.InsufficientFunds,
'59109': errors.ExchangeError,
'59128': errors.InvalidOrder,
'59200': errors.InsufficientFunds,
'59201': errors.InsufficientFunds,
'59216': errors.BadRequest,
'59300': errors.ExchangeError,
'59301': errors.ExchangeError,
'59313': errors.ExchangeError,
'59401': errors.ExchangeError,
'59500': errors.ExchangeError,
'59501': errors.ExchangeError,
'59502': errors.ExchangeError,
'59503': errors.ExchangeError,
'59504': errors.ExchangeError,
'59505': errors.ExchangeError,
'59506': errors.ExchangeError,
'59507': errors.ExchangeError,
'59508': errors.AccountSuspended,
// WebSocket error Codes from 60000-63999
'60001': errors.AuthenticationError,
'60002': errors.AuthenticationError,
'60003': errors.AuthenticationError,
'60004': errors.AuthenticationError,
'60005': errors.AuthenticationError,
'60006': errors.InvalidNonce,
'60007': errors.AuthenticationError,
'60008': errors.AuthenticationError,
'60009': errors.AuthenticationError,
'60010': errors.AuthenticationError,
'60011': errors.AuthenticationError,
'60012': errors.BadRequest,
'60013': errors.BadRequest,
'60014': errors.RateLimitExceeded,
'60015': errors.NetworkError,
'60016': errors.ExchangeNotAvailable,
'60017': errors.BadRequest,
'60018': errors.BadRequest,
'60019': errors.BadRequest,
'63999': errors.ExchangeError,
'70010': errors.BadRequest,
'70013': errors.BadRequest,
'70016': errors.BadRequest, // Please specify your instrument settings for at least one instType.
},
'broad': {
'Internal Server Error': errors.ExchangeNotAvailable,
'server error': errors.ExchangeNotAvailable, // {"code":500,"data":{},"detailMsg":"","error_code":"500","error_message":"server error 1236805249","msg":"server error 1236805249"}
},
},
'precisionMode': number.TICK_SIZE,
'options': {
'fetchOHLCV': {
'type': 'Candles', // Candles or HistoryCandles
},
'createMarketBuyOrderRequiresPrice': true,
'fetchMarkets': ['spot'],
'defaultType': 'spot',
'accountsByType': {
'classic': '1',
'spot': '1',
'funding': '6',
'main': '6',
'unified': '18',
},
'accountsById': {
'1': 'spot',
'6': 'funding',
'18': 'unified',
},
'auth': {
'time': 'public',
'currencies': 'private',
'instruments': 'public',
'rate': 'public',
'{instrument_id}/constituents': 'public',
},
'warnOnFetchCurrenciesWithoutAuthorization': false,
'defaultNetwork': 'ERC20',
'networks': {
'ERC20': 'Ethereum',
'BTC': 'Bitcoin',
'OMNI': 'Omni',
'TRC20': 'TRON',
},
},
'commonCurrencies': {
// OKEX refers to ERC20 version of Aeternity (AEToken)
'AE': 'AET',
'BOX': 'DefiBox',
'HOT': 'Hydro Protocol',
'HSR': 'HC',
'MAG': 'Maggie',
'SBTC': 'Super Bitcoin',
'TRADE': 'Unitrade',
'YOYO': 'YOYOW',
'WIN': 'WinToken', // https://github.com/ccxt/ccxt/issues/5701
},
});
}
/**
* @method
* @name okcoin#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @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.publicGetPublicTime(params);
//
// {
// "code": "0",
// "data":
// [
// {
// "ts": "1737379360033"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data');
const timestamp = this.safeDict(data, 0);
return this.safeInteger(timestamp, 'ts');
}
/**
* @method
* @name okcoin#fetchMarkets
* @see https://www.okcoin.com/docs-v5/en/#rest-api-public-data-get-instruments
* @description retrieves data on all markets for okcoin
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const request = {
'instType': 'SPOT',
};
const response = await this.publicGetPublicInstruments(this.extend(request, params));
const markets = this.safeValue(response, 'data', []);
return this.parseMarkets(markets);
}
parseMarket(market) {
//
// spot markets
//
// {
// "base_currency": "EOS",
// "instrument_id": "EOS-OKB",
// "min_size": "0.01",
// "quote_currency": "OKB",
// "size_increment": "0.000001",
// "tick_size": "0.0001"
// }
//
const id = this.safeString(market, 'instId');
let type = this.safeStringLower(market, 'instType');
if (type === 'futures') {
type = 'future';
}
const spot = (type === 'spot');
const future = (type === 'future');
const swap = (type === 'swap');
const option = (type === 'option');
const contract = swap || future || option;
const baseId = this.safeString(market, 'baseCcy');
const quoteId = this.safeString(market, 'quoteCcy');
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
const symbol = base + '/' + quote;
const tickSize = this.safeString(market, 'tickSz');
const fees = this.safeValue2(this.fees, type, 'trading', {});
let maxLeverage = this.safeString(market, 'lever', '1');
maxLeverage = Precise["default"].stringMax(maxLeverage, '1');
const maxSpotCost = this.safeNumber(market, 'maxMktSz');
return this.extend(fees, {
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': type,
'spot': spot,
'margin': spot && (Precise["default"].stringGt(maxLeverage, '1')),
'swap': false,
'future': false,
'option': false,
'active': true,
'contract': false,
'linear': undefined,
'inverse': undefined,
'contractSize': contract ? this.safeNumber(market, 'ctVal') : undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'created': this.safeInteger(market, 'listTime'),
'precision': {
'amount': this.safeNumber(market, 'lotSz'),
'price': this.parseNumber(tickSize),
},
'limits': {
'leverage': {
'min': this.parseNumber('1'),
'max': this.parseNumber(maxLeverage),
},
'amount': {
'min': this.safeNumber(market, 'minSz'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': contract ? undefined : maxSpotCost,
},
},
'info': market,
});
}
/**
* @method
* @name okcoin#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
*/
async fetchCurrencies(params = {}) {
if (!this.checkRequiredCredentials(false)) {
if (this.options['warnOnFetchCurrenciesWithoutAuthorization']) {
throw new errors.ExchangeError(this.id + ' fetchCurrencies() is a private API endpoint that requires authentication with API keys. Set the API keys on the exchange instance or exchange.options["warnOnFetchCurrenciesWithoutAuthorization"] = false to suppress this warning message.');
}
return undefined;
}
else {
const response = await this.privateGetAssetCurrencies(params);
const data = this.safeList(response, 'data', []);
const result = {};
const dataByCurrencyId = this.groupBy(data, 'ccy');
const currencyIds = Object.keys(dataByCurrencyId);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode(currencyId);
const chains = dataByCurrencyId[currencyId];
const networks = {};
for (let j = 0; j < chains.length; j++) {
const chain = chains[j];
const networkId = this.safeString(chain, 'chain');
if ((networkId !== undefined) && (networkId.indexOf('-') >= 0)) {
const parts = networkId.split('-');
const chainPart = this.safeString(parts, 1, networkId);
const networkCode = this.networkIdToCode(chainPart);
networks[networkCode] = {
'id': networkId,
'network': networkCode,
'active': undefined,
'deposit': this.safeBool(chain, 'canDep'),
'withdraw': this.safeBool(chain, 'canWd'),
'fee': this.safeNumber(chain, 'minFee'),
'precision': this.parseNumber(this.parsePrecision(this.safeString(chain, 'wdTickSz'))),
'limits': {
'withdraw': {
'min': this.safeNumber(chain, 'minWd'),
'max': this.safeNumber(chain, 'maxWd'),
},
},
'info': chain,
};
}
}
const firstChain = this.safeValue(chains, 0);
result[code] = this.safeCurrencyStructure({
'info': chains,
'code': code,
'id': currencyId,
'name': this.safeString(firstChain, 'name'),
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'fee': undefined,
'precision': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
},
'networks': networks,
});
}
return result;
}
}
/**
* @method
* @name okcoin#fetchOrderBook
* @see https://www.okcoin.com/docs-v5/en/#rest-api-market-data-get-order-book
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
limit = (limit === undefined) ? 20 : limit;
if (limit !== undefined) {
request['sz'] = limit; // max 400
}
const response = await this.publicGetMarketBooks(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "asks": [
// ["0.07228","4.211619","0","2"], // price, amount, liquidated orders, total open orders
// ["0.0723","299.880364","0","2"],
// ["0.07231","3.72832","0","1"],
// ],
// "bids": [
// ["0.07221","18.5","0","1"],
// ["0.0722","18.5","0","1"],
// ["0.07219","0.505407","0","1"],
// ],
// "ts": "1621438475342"
// }
// ]
// }
//
const data = this.safeValue(response, 'data', []);
const first = this.safeValue(data, 0, {});
const timestamp = this.safeInteger(first, 'ts');
return this.parseOrderBook(first, symbol, timestamp);
}
parseTicker(ticker, market = undefined) {
//
// {
// "instType": "SPOT",
// "instId": "ETH-BTC",
// "last": "0.07319",
// "lastSz": "0.044378",
// "askPx": "0.07322",
// "askSz": "4.2",
// "bidPx": "0.0732",
// "bidSz": "6.050058",
// "open24h": "0.07801",
// "high24h": "0.07975",
// "low24h": "0.06019",
// "volCcy24h": "11788.887619",
// "vol24h": "167493.829229",
// "ts": "1621440583784",
// "sodUtc0": "0.07872",
// "sodUtc8": "0.07345"
// }
//
const timestamp = this.safeInteger(ticker, 'ts');
const marketId = this.safeString(ticker, 'instId');
market = this.safeMarket(marketId, market, '-');
const symbol = market['symbol'];
const last = this.safeString(ticker, 'last');
const open = this.safeString(ticker, 'open24h');
const spot = this.safeBool(market, 'spot', false);
const quoteVolume = spot ? this.safeString(ticker, 'volCcy24h') : undefined;
const baseVolume = this.safeString(ticker, 'vol24h');
const high = this.safeString(ticker, 'high24h');
const low = this.safeString(ticker, 'low24h');
return this.safeTicker({
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'high': high,
'low': low,
'bid': this.safeString(ticker, 'bidPx'),
'bidVolume': this.safeString(ticker, 'bidSz'),
'ask': this.safeString(ticker, 'askPx'),
'askVolume': this.safeString(ticker, 'askSz'),
'vwap': undefined,
'open': open,
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': undefined,
'average': undefined,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'info': ticker,
}, market);
}
/**
* @method
* @name okcoin#fetchTicker
* @see https://www.okcoin.com/docs-v5/en/#rest-api-market-data-get-ticker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTicker(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
const response = await this.publicGetMarketTicker(this.extend(request, params));
const data = this.safeValue(response, 'data', []);
const first = this.safeValue(data, 0, {});
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "instType": "SPOT",
// "instId": "ETH-BTC",
// "last": "0.07319",
// "lastSz": "0.044378",
// "askPx": "0.07322",
// "askSz": "4.2",
// "bidPx": "0.0732",
// "bidSz": "6.050058",
// "open24h": "0.07801",
// "high24h": "0.07975",
// "low24h": "0.06019",
// "volCcy24h": "11788.887619",
// "vol24h": "167493.829229",
// "ts": "1621440583784",
// "sodUtc0": "0.07872",
// "sodUtc8": "0.07345"
// }
// ]
// }
//
return this.parseTicker(first, market);
}
/**
* @method
* @name okcoin#fetchTickers
* @see https://www.okcoin.com/docs-v5/en/#rest-api-market-data-get-tickers
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @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 = {}) {
symbols = this.marketSymbols(symbols);
const request = {
'instType': 'SPOT',
};
const response = await this.publicGetMarketTickers(this.extend(request, params));
const data = this.safeList(response, 'data', []);
return this.parseTickers(data, symbols, params);
}
parseTrade(trade, market = undefined) {
//
// public fetchTrades
//
// {
// "instId": "ETH-BTC",
// "side": "sell",
// "sz": "0.119501",
// "px": "0.07065",
// "tradeId": "15826757",
// "ts": "1621446178316"
// }
//
// private fetchMyTrades
//
// {
// "side": "buy",
// "fillSz": "0.007533",
// "fillPx": "2654.98",
// "fee": "-0.000007533",
// "ordId": "317321390244397056",
// "instType": "SPOT",
// "instId": "ETH-USDT",
// "clOrdId": "",
// "posSide": "net",
// "billId": "317321390265368576",
// "tag": "0",
// "execType": "T",
// "tradeId": "107601752",
// "feeCcy": "ETH",
// "ts": "1621927314985"
// }
//
const id = this.safeString(trade, 'tradeId');
const marketId = this.safeString(trade, 'instId');
market = this.safeMarket(marketId, market, '-');
const symbol = market['symbol'];
const timestamp = this.safeInteger(trade, 'ts');
const price = this.safeString2(trade, 'fillPx', 'px');
const amount = this.safeString2(trade, 'fillSz', 'sz');
const side = this.safeString(trade, 'side');
const orderId = this.safeString(trade, 'ordId');
const feeCostString = this.safeString(trade, 'fee');
let fee = undefined;
if (feeCostString !== undefined) {
const feeCostSigned = Precise["default"].stringNeg(feeCostString);
const feeCurrencyId = this.safeString(trade, 'feeCcy');
const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId);
fee = {
'cost': feeCostSigned,
'currency': feeCurrencyCode,
};
}
let takerOrMaker = this.safeString(trade, 'execType');
if (takerOrMaker === 'T') {
takerOrMaker = 'taker';
}
else if (takerOrMaker === 'M') {
takerOrMaker = 'maker';
}
return this.safeTrade({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': symbol,
'id': id,
'order': orderId,
'type': undefined,
'takerOrMaker': takerOrMaker,
'side': side,
'price': price,
'amount': amount,
'cost': undefined,
'fee': fee,
}, market);
}
/**
* @method
* @name okcoin#fetchTrades
* @see https://www.okcoin.com/docs-v5/en/#rest-api-market-data-get-trades
* @see https://www.okcoin.com/docs-v5/en/#rest-api-market-data-get-trades-history
* @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}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if ((limit === undefined) || (limit > 100)) {
limit = 100; // maximum = default = 100
}
const request = {
'instId': market['id'],
};
let method = undefined;
[method, params] = this.handleOptionAndParams(params, 'fetchTrades', 'method', 'publicGetMarketTrades');
let response = undefined;
if (method === 'publicGetMarketTrades') {
response = await this.publicGetMarketTrades(this.extend(request, params));
}
else {
response = await this.publicGetMarketHistoryTrades(this.extend(request, params));
}
const data = this.safeList(response, 'data', []);
return this.parseTrades(data, market, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
//
// [
// "1678928760000", // timestamp
// "24341.4", // open
// "24344", // high
// "24313.2", // low
// "24323", // close
// "628", // contract volume
// "2.5819", // base volume
// "62800", // quote volume
// "0" // candlestick state
// ]
//
return [
this.safeInteger(ohlcv, 0),
this.safeNumber(ohlcv, 1),
this.safeNumber(ohlcv, 2),
this.safeNumber(ohlcv, 3),
this.safeNumber(ohlcv, 4),
this.safeNumber(ohlcv, 5),
];
}
/**
* @method
* @name okcoin#fetchOHLCV
* @see https://www.okcoin.com/docs-v5/en/#rest-api-market-data-get-candlesticks
* @see https://www.okcoin.com/docs-v5/en/#rest-api-market-data-get-candlesticks-history
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @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 duration = this.parseTimeframe(timeframe);
const options = this.safeValue(this.options, 'fetchOHLCV', {});
let bar = this.safeString(this.timeframes, timeframe, timeframe);
const timezone = this.safeString(options, 'timezone', 'UTC');
if ((timezone === 'UTC') && (duration >= 21600)) { // if utc and timeframe >= 6h
bar += timezone.toLowerCase();
}
const request = {
'instId': market['id'],
'bar': bar,
};
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
let method = undefined;
[method, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'method', 'publicGetMarketCandles');
let response = undefined;
if (method === 'publicGetMarketCandles') {
response = await this.publicGetMarketCandles(this.extend(request, params));
}
else {
response = await this.publicGetMarketHistoryCandles(this.extend(request, params));
}
const data = this.safeList(response, 'data', []);
return this.parseOHLCVs(data, market, timeframe, since, limit);
}
parseAccountBalance(response) {
//
// account
//
// [
// {
// "balance": 0,
// "available": 0,
// "currency": "BTC",
// "hold": 0
// },
// {
// "balance": 0,
// "available": 0,
// "currency": "ETH",
// "hold": 0
// }
// ]
//
// spot
//
// [
// {
// "frozen": "0",
// "hold": "0",
// "id": "2149632",
// "currency": "BTC",
// "balance": "0.0000000497717339",
// "available": "0.0000000497717339",
// "holds": "0"
// },
// {
// "frozen": "0",
// "hold": "0",
// "id": "2149632",
// "currency": "ICN",
// "balance": "0.00000000925",
// "available": "0.00000000925",
// "holds": "0"
// }
// ]
//
const result = {
'info': response,
'timestamp': undefined,
'datetime': undefined,
};
for (let i = 0; i < response.length; i++) {
const balance = response[i];
const currencyId = this.safeString(balance, 'currency');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
account['total'] = this.safeString(balance, 'balance');
account['used'] = this.safeString(balance, 'hold');
account['free'] = this.safeString(balance, 'available');
result[code] = account;
}
return this.safeBalance(result);
}
/**
* @method
* @name okcoin#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @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 [marketType, query] = this.handleMarketTypeAndParams('fetchBalance', undefined, params);
const request = {
// 'ccy': 'BTC,ETH', // comma-separated list of currency ids
};
let response = undefined;
if (marketType === 'funding') {
response = await this.privateGetAssetBalances(this.extend(request, query));
}
else {
response = await this.privateGetAccountBalance(this.extend(request, query));
}
//
// {
// "code": "0",
// "data": [
// {
// "category": "1",
// "delivery": "",
// "exercise": "",
// "instType": "SPOT",
// "level": "Lv1",
// "maker": "-0.0008",
// "taker": "-0.001",
// "ts": "1639043138472"
// }
// ],
// "msg": ""
// }
//
if (marketType === 'funding') {
return this.parseFundingBalance(response);
}
else {
return this.parseTradingBalance(response);
}
}
parseTradingBalance(response) {
const result = { 'info': response };
const data = this.safeValue(response, 'data', []);
const first = this.safeValue(data, 0, {});
const timestamp = this.safeInteger(first, 'uTime');
const details = this.safeValue(first, 'details', []);
for (let i = 0; i < details.length; i++) {
const balance = details[i];
const currencyId = this.safeString(balance, 'ccy');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
// it may be incorrect to use total, free and used for swap accounts
const eq = this.safeString(balance, 'eq');
const availEq = this.safeString(balance, 'availEq');
if ((eq === undefined) || (availEq === undefined)) {
account['free'] = this.safeString(balance, 'availBal');
account['used'] = this.safeString(balance, 'frozenBal');
}
else {
account['total'] = eq;
account['free'] = availEq;
}
result[code] = account;
}
result['timestamp'] = timestamp;
result['datetime'] = this.iso8601(timestamp);
return this.safeBalance(result);
}
parseFundingBalance(response) {
const result = { 'info': response };
const data = this.safeValue(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const balance = data[i];
const currencyId = this.safeString(balance, 'ccy');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
// it may be incorrect to use total, free and used for swap accounts
account['total'] = this.safeString(balance, 'bal');
account['free'] = this.safeString(balance, 'availBal');
account['used'] = this.safeString(balance, 'frozenBal');
result[code] = account;
}
return this.safeBalance(result);
}
/**
* @method
* @name okcoin#createMarketBuyOrderWithCost
* @description create a market buy order by providing the symbol and cost
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-place-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 errors.NotSupported(this.id + ' createMarketBuyOrderWithCost() supports spot orders only');
}
params['createMarketBuyOrderRequiresPrice'] = false;
params['tgtCcy'] = 'quote_ccy';
return await this.createOrder(symbol, 'market', 'buy', cost, undefined, params);
}
/**
* @method
* @name okcoin#createOrder
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-place-order
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-place-algo-order
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-place-multiple-orders
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-cancel-advance-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 fulfilled, in units of the quote currency, ignored in market orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {bool} [params.reduceOnly] MARGIN orders only, or swap/future orders in net mode
* @param {bool} [params.postOnly] true to place a post only order
* @param {float} [params.triggerPrice] conditional orders only, the price at which the order is to be triggered
* @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 {float} [params.takeProfit.price] used for take profit limit orders, not used for take profit market price orders
* @param {string} [params.takeProfit.type] 'market' or 'limit' used to specify the take profit price type
* @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.stopLoss.price] used for stop loss limit orders, not used for stop loss market price orders
* @param {string} [params.stopLoss.type] 'market' or 'limit' used to specify the stop loss price type
* @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}
*/
async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let request = this.createOrderRequest(symbol, type, side, amount, price, params);
let method = this.safeString(this.options, 'createOrder', 'privatePostTradeBatchOrders');
const requestOrdType = this.safeString(request, 'ordType');
if ((requestOrdType === 'trigger') || (requestOrdType === 'conditional') || (type === 'oco') || (type === 'move_order_stop') || (type === 'iceberg') || (type === 'twap')) {
method = 'privatePostTradeOrderAlgo';
}
if (method === 'privatePostTradeBatchOrders') {
// keep the request body the same
// submit a single order in an array to the batch order endpoint
// because it has a lower ratelimit
request = [request];
}
let response = undefined;
if (method === 'privatePostTradeOrder') {
response = await this.privatePostTradeOrder(request);
}
else if (method === 'privatePostTradeOrderAlgo') {
response = await this.privatePostTradeOrderAlgo(request);
}
else if (method === 'privatePostTradeBatchOrders') {
response = await this.privatePostTradeBatchOrders(request);
}
else {
throw new errors.ExchangeError(this.id + ' createOrder() this.options["createOrder"] must be either privatePostTradeBatchOrders or privatePostTradeOrder or privatePostTradeOrderAlgo');
}
const data = this.safeValue(response, 'data', []);
const first = this.safeValue(data, 0);
const order = this.parseOrder(first, market);
order['type'] = type;
order['side'] = side;
return order;
}
createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
const market = this.market(symbol);
const request = {
'instId': market['id'],
// 'ccy': currency['id'], // only applicable to cross MARGIN orders in single-currency margin
// 'clOrdId': clientOrderId, // up to 32 characters, must be unique
// 'tag': tag, // up to 8 characters
'side': side,
// 'posSide': 'long', // long, short, // required in the long/short mode, and can only be long or short (only for future or swap)
'ordType': type,
// 'ordType': type, // privatePostTradeOrder: market, limit, post_only, fok, ioc, optimal_limit_ioc
// 'ordType': type, // privatePostTradeOrderAlgo: conditional, oco, trigger, move_order_stop, iceberg, twap
// 'sz': this.amountToPrecision (symbol, amount),
// 'px': this.priceToPrecision (symbol, price), // limit orders only
// 'reduceOnly': false,
//
// 'triggerPx': 10, // stopPrice (trigger orders)
// 'orderPx': 10, // Order price if -1, the order will be executed at the market price. (trigger orders)
// 'triggerPxType': 'last', // Conditional default is last, mark or index (trigger orders)
//
// 'tpTriggerPx': 10, // takeProfitPrice (conditional orders)
// 'tpTriggerPxType': 'last', // Conditional default is last, mark or index (conditional orders)
// 'tpOrdPx': 10, // Order price for Take-Profit orders, if -1 will be executed at market price (conditional orders)
//
// 'slTriggerPx': 10, // stopLossPrice (conditional orders)
// 'slTriggerPxType': 'last', // Conditional default is last, mark or index (conditional orders)
// 'slOrdPx': 10, // Order price for Stop-Loss orders, if -1 will be executed at market price (conditional orders)
};
const triggerPrice = this.safeValueN(params, ['triggerPrice', 'stopPrice', 'triggerPx']);
const timeInForce = this.safeString(params, 'timeInForce', 'GTC');
const takeProfitPrice = this.safeValue2(params, 'takeProfitPrice', 'tpTriggerPx');
const tpOrdPx = this.safeValue(params, 'tpOrdPx', price);
const tpTriggerPxType = this.safeString(params, 'tpTriggerPxType', 'last');
const stopLossPrice = this.safeValue2(params, 'stopLossPrice', 'slTriggerPx');
const slOrdPx = this.safeValue(params, 'slOrdPx', price);
const slTriggerPxType = this.safeString(params, 'slTriggerPxType', 'last');
const clientOrderId = this.safeString2(params, 'clOrdId', 'clientOrderId');
const stopLoss = this.safeValue(params, 'stopLoss');
const stopLossDefined = (stopLoss !== undefined);
const takeProfit = this.safeValue(params, 'takeProfit');
const takeProfitDefined = (takeProfit !== undefined);
const defaultMarginMode = this.safeString2(this.options, 'defaultMarginMode', 'marginMode', 'cross');
let marginMode = this.safeString2(params, 'marginMode', 'tdMode'); // cross or isolated, tdMode not ommited so as to be extended into the request
let margin = false;
if ((marginMode !== undefined) && (marginMode !== 'cash')) {
margin = true;
}
else {
marginMode = defaultMarginMode;
margin = this.safeBool(params, 'margin', false);
}
if (margin) {
const defaultCurrency = (side === 'buy') ? market['quote'] : market['base'];
const currency = this.safeString(params, 'ccy', defaultCurrency);
request['ccy'] = this.safeCurrencyCode(currency);
}
const tradeMode = margin ? marginMode : 'cash';
request['tdMode'] = tradeMode;
const isMarketOrder = type === 'market';
let postOnly = false;
[postOnly, params] = this.handlePostOnly(isMarketOrder, type === 'post_only', params);
params = this.omit(params, ['currency', 'ccy', 'marginMode', 'timeInForce', 'stopPrice', 'triggerPrice', 'clientOrderId', 'stopLossPrice', 'takeProfitPrice', 'slOrdPx', 'tpOrdPx', 'margin', 'stopLoss', 'takeProfit']);
const ioc = (timeInForce === 'IOC') || (type === 'ioc');
const fok = (timeInForce === 'FOK') || (type === 'fok');
const trigger = (triggerPrice !== undefined) || (type === 'trigger');
const conditional = (stopLossPrice !== undefined) || (takeProfitPrice !== undefined) || (type === 'conditional');
const marketIOC = (isMarketOrder && ioc) || (type === 'optimal_limit_ioc');
const defaultTgtCcy = this.safeString(this.options, 'tgtCcy', 'base_ccy');
const tgtCcy = this.safeString(params, 'tgtCcy', defaultTgtCcy);
if ((!margin)) {
request['tgtCcy'] = tgtCcy;
}
if (isMarketOrder || marketIOC) {
request['ordType'] = 'market';
if (side === 'buy') {
// spot market buy: "sz" can refer either to base currency units or to quote currency units
// see documentation: https://www.okx.com/docs-v5/en/#rest-api-trade-place-order
if (tgtCcy === 'quote_ccy') {
// quote_ccy: sz refers to units of quote currency
let quoteAmount = undefined;
let createMarketBuyOrderRequiresPrice = true;
[createMarketBuyOrderRequiresPrice, params] = this.handleOptionAndParams(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true);
const cost = this.safeNumber2(params, 'cost', 'sz');
params = this.omit(params, ['cost', 'sz']);
if (cost !== undefined) {
quoteAmount = this.costToPrecision(symbol, cost);
}
else if (createMarketBuyOrderRequiresPrice) {
if (price === undefined) {
throw new errors.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["default"].stringMul(amountString, priceString);
quoteAmount = this.costToPrecision(symbol, costRequest);
}
}
else {
quoteAmount = this.costToPrecision(symbol, amount);
}
request['sz'] = quoteAmount;
}
else {
request['sz'] = this.amountToPrecision(symbol, amount);
}
}
else {
request['sz'] = this.amountToPrecision(symbol, amount);
}
}
else {
request['sz'] = this.amountToPrecision(symbol, amount);
if ((!trigger) && (!conditional)) {
request['px'] = this.priceToPrecision(symbol, price);
}
}
if (postOnly) {
request['ordType'] = 'post_only';
}
else if (ioc && !marketIOC) {
request['ordType'] = 'ioc';
}
else if (fok) {
request['ordType'] = 'fok';
}
else if (stopLossDefined || takeProfitDefined) {
if (stopLossDefined) {
const stopLossTriggerPrice = this.safeValueN(stopLoss, ['triggerPrice', 'stopPrice', 'slTriggerPx']);
if (stopLossTriggerPrice === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a trigger price in params["stopLoss"]["triggerPrice"] for a stop loss order');
}
request['slTriggerPx'] = this.priceToPrecision(symbol, stopLossTriggerPrice);
const stopLossLimitPrice = this.safeValueN(stopLoss, ['price', 'stopLossPrice', 'slOrdPx']);
const stopLossOrderType = this.safeString(stopLoss, 'type');
if (stopLossOrderType !== undefined) {
const stopLossLimitOrderType = (stopLossOrderType === 'limit');
const stopLossMarketOrderType = (stopLossOrderType === 'market');
if ((!stopLossLimitOrderType) && (!stopLossMarketOrderType)) {
throw new errors.InvalidOrder(this.id + ' createOrder() params["stopLoss"]["type"] must be either "limit" or "market"');
}
else if (stopLossLimitOrderType) {
if (stopLossLimitPrice === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a limit price in params["stopLoss"]["price"] for a stop loss limit order');
}
else {
request['slOrdPx'] = this.priceToPrecision(symbol, stopLossLimitPrice);
}
}
else if (stopLossOrderType === 'market') {
request['slOrdPx'] = '-1';
}
}
else if (stopLossLimitPrice !== undefined) {
request['slOrdPx'] = this.priceToPrecision(symbol, stopLossLimitPrice); // limit sl order
}
else {
request['slOrdPx'] = '-1'; // market sl order
}
const stopLossTriggerPriceType = this.safeString2(stopLoss, 'triggerPriceType', 'slTriggerPxType', 'last');
if (stopLossTriggerPriceType !== undefined) {
if ((stopLossTriggerPriceType !== 'last') && (stopLossTriggerPriceType !== 'index') && (stopLossTriggerPriceType !== 'mark')) {
throw new errors.InvalidOrder(this.id + ' createOrder() stop loss trigger price type must be one of "last", "index" or "mark"');
}
request['slTriggerPxType'] = stopLossTriggerPriceType;
}
}
if (takeProfitDefined) {
const takeProfitTriggerPrice = this.safeValueN(takeProfit, ['triggerPrice', 'stopPrice', 'tpTriggerPx']);
if (takeProfitTriggerPrice === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a trigger price in params["takeProfit"]["triggerPrice"], or params["takeProfit"]["stopPrice"], or params["takeProfit"]["tpTriggerPx"] for a take profit order');
}
request['tpTriggerPx'] = this.priceToPrecision(symbol, takeProfitTriggerPrice);
const takeProfitLimitPrice = this.safeValueN(takeProfit, ['price', 'takeProfitPrice', 'tpOrdPx']);
const takeProfitOrderType = this.safeString(takeProfit, 'type');
if (takeProfitOrderType !== undefined) {
const takeProfitLimitOrderType = (takeProfitOrderType === 'limit');
const takeProfitMarketOrderType = (takeProfitOrderType === 'market');
if ((!takeProfitLimitOrderType) && (!takeProfitMarketOrderType)) {
throw new errors.InvalidOrder(this.id + ' createOrder() params["takeProfit"]["type"] must be either "limit" or "market"');
}
else if (takeProfitLimitOrderType) {
if (takeProfitLimitPrice === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a limit price in params["takeProfit"]["price"] or params["takeProfit"]["tpOrdPx"] for a take profit limit order');
}
else {
request['tpOrdPx'] = this.priceToPrecision(symbol, takeProfitLimitPrice);
}
}
else if (takeProfitOrderType === 'market') {
request['tpOrdPx'] = '-1';
}
}
else if (takeProfitLimitPrice !== undefined) {
request['tpOrdPx'] = this.priceToPrecision(symbol, takeProfitLimitPrice); // limit tp order
}
else {
request['tpOrdPx'] = '-1'; // market tp order
}
const takeProfitTriggerPriceType = this.safeString2(takeProfit, 'triggerPriceType', 'tpTriggerPxType', 'last');
if (takeProfitTriggerPriceType !== undefined) {
if ((takeProfitTriggerPriceType !== 'last') && (takeProfitTriggerPriceType !== 'index') && (takeProfitTriggerPriceType !== 'mark')) {
throw new errors.InvalidOrder(this.id + ' createOrder() take profit trigger price type must be one of "last", "index" or "mark"');
}
request['tpTriggerPxType'] = takeProfitTriggerPriceType;
}
}
}
else if (trigger) {
request['ordType'] = 'trigger';
request['triggerPx'] = this.priceToPrecision(symbol, triggerPrice);
request['orderPx'] = isMarketOrder ? '-1' : this.priceToPrecision(symbol, price);
}
else if (conditional) {
request['ordType'] = 'conditional';
const twoWayCondition = ((takeProfitPrice !== undefined) && (stopLossPrice !== undefined));
// if TP and SL are sent together
// as ordType 'conditional' only stop-loss order will be applied
if (twoWayCondition) {
request['ordType'] = 'oco';
}
if (takeProfitPrice !== undefined) {
request['tpTriggerPx'] = this.priceToPrecision(symbol, takeProfitPrice);
request['tpOrdPx'] = (tpOrdPx === undefined) ? '-1' : this.priceToPrecision(symbol, tpOrdPx);
request['tpTriggerPxType'] = tpTriggerPxType;
}
if (stopLossPrice !== undefined) {
request['slTriggerPx'] = this.priceToPrecision(symbol, stopLossPrice);
request['slOrdPx'] = (slOrdPx === undefined) ? '-1' : this.priceToPrecision(symbol, slOrdPx);
request['slTriggerPxType'] = slTriggerPxType;
}
}
if (clientOrderId === undefined) {
const brokerId = this.safeString(this.options, 'brokerId');
if (brokerId !== undefined) {
request['clOrdId'] = brokerId + this.uuid16();
request['tag'] = brokerId;
}
}
else {
request['clOrdId'] = clientOrderId;
params = this.omit(params, ['clOrdId', 'clientOrderId']);
}
return this.extend(request, params);
}
/**
* @method
* @name okcoin#cancelOrder
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-cancel-order
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-cancel-algo-order
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-cancel-advance-algo-order
* @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 {bool} [params.trigger] True if cancel trigger or conditional orders
* @param {bool} [params.advanced] True if canceling advanced orders only
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrder(id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets();
const trigger = this.safeValue2(params, 'stop', 'trigger');
const advanced = this.safeValue(params, 'advanced');
if (trigger || advanced) {
const orderInner = await this.cancelOrders([id], symbol, params);
return this.safeValue(orderInner, 0);
}
const market = this.market(symbol);
const request = {
'instId': market['id'],
// 'ordId': id, // either ordId or clOrdId is required
// 'clOrdId': clientOrderId,
};
const clientOrderId = this.safeString2(params, 'clOrdId', 'clientOrderId');
if (clientOrderId !== undefined) {
request['clOrdId'] = clientOrderId;
}
else {
request['ordId'] = id.toString();
}
const query = this.omit(params, ['clOrdId', 'clientOrderId']);
const response = await this.privatePostTradeCancelOrder(this.extend(request, query));
// {"code":"0","data":[{"clOrdId":"","ordId":"317251910906576896","sCode":"0","sMsg":""}],"msg":""}
const data = this.safeValue(response, 'data', []);
const order = this.safeDict(data, 0);
return this.parseOrder(order, market);
}
parseIds(ids) {
/**
* @ignore
* @method
* @name okx#parseIds
* @param {string[]|string} ids order ids
* @returns {string[]} list of order ids
*/
if (typeof ids === 'string') {
return ids.split(',');
}
else {
return ids;
}
}
/**
* @method
* @name okcoin#cancelOrders
* @description cancel multiple orders
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-cancel-multiple-orders
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-cancel-algo-order
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-cancel-advance-algo-order
* @param {string[]} ids order ids
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrders(ids, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' cancelOrders() requires a symbol argument');
}
await this.loadMarkets();
const trigger = this.safeValue2(params, 'stop', 'trigger');
const advanced = this.safeValue(params, 'advanced');
params = this.omit(params, ['stop', 'trigger', 'advanced']);
const market = this.market(symbol);
const request = [];
const clientOrderIds = this.parseIds(this.safeValue2(params, 'clOrdId', 'clientOrderId'));
const algoIds = this.parseIds(this.safeValue(params, 'algoId'));
if (clientOrderIds === undefined) {
ids = this.parseIds(ids);
if (algoIds !== undefined) {
for (let i = 0; i < algoIds.length; i++) {
request.push({
'algoId': algoIds[i],
'instId': market['id'],
});
}
}
for (let i = 0; i < ids.length; i++) {
if (trigger || advanced) {
request.push({
'algoId': ids[i],
'instId': market['id'],
});
}
else {
request.push({
'ordId': ids[i],
'instId': market['id'],
});
}
}
}
else {
for (let i = 0; i < clientOrderIds.length; i++) {
request.push({
'instId': market['id'],
'clOrdId': clientOrderIds[i],
});
}
}
let response = undefined;
if (trigger) {
response = await this.privatePostTradeCancelAlgos(request);
}
else if (advanced) {
response = await this.privatePostTradeCancelAdvanceAlgos(request);
}
else {
response = await this.privatePostTradeCancelBatchOrders(request); // * dont extend with params, otherwise ARRAY will be turned into OBJECT
}
//
// {
// "code": "0",
// "data": [
// {
// "clOrdId": "e123456789ec4dBC1123456ba123b45e",
// "ordId": "405071912345641543",
// "sCode": "0",
// "sMsg": ""
// },
// ...
// ],
// "msg": ""
// }
//
//
const ordersData = this.safeList(response, 'data', []);
return this.parseOrders(ordersData, market, undefined, undefined, params);
}
parseOrderStatus(status) {
const statuses = {
'canceled': 'canceled',
'live': 'open',
'partially_filled': 'open',
'filled': 'closed',
'effective': 'closed',
};
return this.safeString(statuses, status, status);
}
parseOrder(order, market = undefined) {
//
// createOrder
//
// {
// "clOrdId": "oktswap6",
// "ordId": "312269865356374016",
// "tag": "",
// "sCode": "0",
// "sMsg": ""
// }
//
// editOrder
//
// {
// "clOrdId": "e847386590ce4dBCc1a045253497a547",
// "ordId": "559176536793178112",
// "reqId": "",
// "sCode": "0",
// "sMsg": ""
// }
//
// Spot and Swap fetchOrder, fetchOpenOrders
//
// {
// "accFillSz": "0",
// "avgPx": "",
// "cTime": "1621910749815",
// "category": "normal",
// "ccy": "",
// "clOrdId": "",
// "fee": "0",
// "feeCcy": "ETH",
// "fillPx": "",
// "fillSz": "0",
// "fillTime": "",
// "instId": "ETH-USDT",
// "instType": "SPOT",
// "lever": "",
// "ordId": "317251910906576896",
// "ordType": "limit",
// "pnl": "0",
// "posSide": "net",
// "px": "2000",
// "rebate": "0",
// "rebateCcy": "USDT",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "state": "live",
// "sz": "0.001",
// "tag": "",
// "tdMode": "cash",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tradeId": "",
// "uTime": "1621910749815"
// }
//
// Algo Order fetchOpenOrders, fetchCanceledOrders, fetchClosedOrders
//
// {
// "activePx": "",
// "activePxType": "",
// "actualPx": "",
// "actualSide": "buy",
// "actualSz": "0",
// "algoId": "431375349042380800",
// "cTime": "1649119897778",
// "callbackRatio": "",
// "callbackSpread": "",
// "ccy": "",
// "ctVal": "0.01",
// "instId": "BTC-USDT-SWAP",
// "instType": "SWAP",
// "last": "46538.9",
// "lever": "125",
// "moveTriggerPx": "",
// "notionalUsd": "467.059",
// "ordId": "",
// "ordPx": "50000",
// "ordType": "trigger",
// "posSide": "long",
// "pxLimit": "",
// "pxSpread": "",
// "pxVar": "",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "slTriggerPxType": "",
// "state": "live",
// "sz": "1",
// "szLimit": "",
// "tag": "",
// "tdMode": "isolated",
// "tgtCcy": "",
// "timeInterval": "",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tpTriggerPxType": "",
// "triggerPx": "50000",
// "triggerPxType": "last",
// "triggerTime": "",
// "uly": "BTC-USDT"
// }
//
const id = this.safeString2(order, 'algoId', 'ordId');
const timestamp = this.safeInteger(order, 'cTime');
const lastUpdateTimestamp = this.safeInteger(order, 'uTime');
const lastTradeTimestamp = this.safeInteger(order, 'fillTime');
const side = this.safeString(order, 'side');
let type = this.safeString(order, 'ordType');
let postOnly = undefined;
let timeInForce = undefined;
if (type === 'post_only') {
postOnly = true;
type = 'limit';
}
else if (type === 'fok') {
timeInForce = 'FOK';
type = 'limit';
}
else if (type === 'ioc') {
timeInForce = 'IOC';
type = 'limit';
}
const marketId = this.safeString(order, 'instId');
market = this.safeMarket(marketId, market);
const symbol = this.safeSymbol(marketId, market, '-');
const filled = this.safeString(order, 'accFillSz');
const price = this.safeString2(order, 'px', 'ordPx');
const average = this.safeString(order, 'avgPx');
const status = this.parseOrderStatus(this.safeString(order, 'state'));
const feeCostString = this.safeString(order, 'fee');
let amount = undefined;
let cost = undefined;
// spot market buy: "sz" can refer either to base currency units or to quote currency units
// see documentation: https://www.okx.com/docs-v5/en/#rest-api-trade-place-order
const defaultTgtCcy = this.safeString(this.options, 'tgtCcy', 'base_ccy');
const tgtCcy = this.safeString(order, 'tgtCcy', defaultTgtCcy);
if ((side === 'buy') && (type === 'market') && (tgtCcy === 'quote_ccy')) {
// "sz" refers to the cost
cost = this.safeString(order, 'sz');
}
else {
// "sz" refers to the trade currency amount
amount = this.safeString(order, 'sz');
}
let fee = undefined;
if (feeCostString !== undefined) {
const feeCostSigned = Precise["default"].stringNeg(feeCostString);
const feeCurrencyId = this.safeString(order, 'feeCcy');
const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId);
fee = {
'cost': this.parseNumber(feeCostSigned),
'currency': feeCurrencyCode,
};
}
let clientOrderId = this.safeString(order, 'clOrdId');
if ((clientOrderId !== undefined) && (clientOrderId.length < 1)) {
clientOrderId = undefined; // fix empty clientOrderId string
}
const stopLossPrice = this.safeNumber2(order, 'slTriggerPx', 'slOrdPx');
const takeProfitPrice = this.safeNumber2(order, 'tpTriggerPx', 'tpOrdPx');
const reduceOnlyRaw = this.safeString(order, 'reduceOnly');
let reduceOnly = false;
if (reduceOnly !== undefined) {
reduceOnly = (reduceOnlyRaw === 'true');
}
return this.safeOrder({
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
'lastUpdateTimestamp': lastUpdateTimestamp,
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopLossPrice': stopLossPrice,
'takeProfitPrice': takeProfitPrice,
'triggerPrice': this.safeNumberN(order, ['triggerPx', 'moveTriggerPx']),
'average': average,
'cost': cost,
'amount': amount,
'filled': filled,
'remaining': undefined,
'status': status,
'fee': fee,
'trades': undefined,
'reduceOnly': reduceOnly,
}, market);
}
/**
* @method
* @name okcoin#fetchOrder
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-order-details
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-algo-order-list
* @description fetches information on an order made by the user
* @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 fetchOrder(id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
// 'clOrdId': 'abcdef12345', // optional, [a-z0-9]{1,32}
// 'ordId': id,
};
const clientOrderId = this.safeString2(params, 'clOrdId', 'clientOrderId');
const trigger = this.safeValue2(params, 'stop', 'trigger');
if (trigger) {
if (clientOrderId !== undefined) {
request['algoClOrdId'] = clientOrderId;
}
else {
request['algoId'] = id;
}
}
else {
if (clientOrderId !== undefined) {
request['clOrdId'] = clientOrderId;
}
else {
request['ordId'] = id;
}
}
const query = this.omit(params, ['clientOrderId', 'stop', 'trigger']);
let response = undefined;
if (trigger) {
response = await this.privateGetTradeOrderAlgo(this.extend(request, query));
}
else {
response = await this.privateGetTradeOrder(this.extend(request, query));
}
const data = this.safeValue(response, 'data', []);
const order = this.safeDict(data, 0);
return this.parseOrder(order);
}
/**
* @method
* @name okcoin#fetchOpenOrders
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-order-list
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-algo-order-list
* @description fetch all unfilled currently open orders
* @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 orders structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {bool} [params.trigger] True if fetching trigger or conditional orders
* @param {string} [params.ordType] "conditional", "oco", "trigger", "move_order_stop", "iceberg", or "twap"
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const request = {
// 'instId': market['id'],
// 'ordType': 'limit', // market, limit, post_only, fok, ioc, comma-separated, stop orders: conditional, oco, trigger, move_order_stop, iceberg, or twap
// 'state': 'live', // live, partially_filled
// 'after': orderId,
// 'before': orderId,
// 'limit': limit, // default 100, max 100
};
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['instId'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
const ordType = this.safeString(params, 'ordType');
const trigger = this.safeValue(params, 'stop') || (this.safeString(params, 'ordType') !== undefined);
if (trigger && (ordType === undefined)) {
request['ordType'] = 'trigger'; // default to trigger
}
params = this.omit(params, ['stop']);
let response = undefined;
if (trigger) {
response = await this.privateGetTradeOrdersAlgoPending(this.extend(request, params));
}
else {
response = await this.privateGetTradeOrdersPending(this.extend(request, params));
}
const data = this.safeList(response, 'data', []);
return this.parseOrders(data, market, since, limit);
}
/**
* @method
* @name okcoin#fetchClosedOrders
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-algo-order-history
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-order-history-last-3-months
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-order-history-last-7-days
* @description fetches information on multiple closed orders made by the user
* @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 {bool} [params.trigger] True if fetching trigger or conditional orders
* @param {string} [params.ordType] "conditional", "oco", "trigger", "move_order_stop", "iceberg", or "twap"
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const request = {
'instType': 'SPOT',
};
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['instId'] = market['id'];
}
const ordType = this.safeString(params, 'ordType');
const trigger = this.safeValue(params, 'stop') || (this.safeString(params, 'ordType') !== undefined);
if (trigger && (ordType === undefined)) {
request['ordType'] = 'trigger'; // default to trigger
}
params = this.omit(params, ['stop']);
let response = undefined;
if (trigger) {
response = await this.privateGetTradeOrdersAlgoHistory(this.extend(request, params));
}
else {
let method = undefined;
[method, params] = this.handleOptionAndParams(params, 'fetchClosedOrders', 'method', 'privateGetTradeOrdersHistory');
if (method === 'privateGetTradeOrdersHistory') {
response = await this.privateGetTradeOrdersHistory(this.extend(request, params));
}
else {
response = await this.privateGetTradeOrdersHistoryArchive(this.extend(request, params));
}
}
// {
// "code": "0",
// "data": [
// {
// "accFillSz": "0",
// "avgPx": "",
// "cTime": "1621910749815",
// "category": "normal",
// "ccy": "",
// "clOrdId": "",
// "fee": "0",
// "feeCcy": "ETH",
// "fillPx": "",
// "fillSz": "0",
// "fillTime": "",
// "instId": "ETH-USDT",
// "instType": "SPOT",
// "lever": "",
// "ordId": "317251910906576896",
// "ordType": "limit",
// "pnl": "0",
// "posSide": "net",
// "px":"20 00",
// "rebate": "0",
// "rebateCcy": "USDT",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "state": "live",
// "sz":"0. 001",
// "tag": "",
// "tdMode": "cash",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tradeId": "",
// "uTime": "1621910749815"
// }
// ],
// "msg":""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseOrders(data, market, since, limit);
}
parseDepositAddress(depositAddress, currency = undefined) {
//
// {
// "addr": "okbtothemoon",
// "memo": "971668", // may be missing
// "tag":"52055", // may be missing
// "pmtId": "", // may be missing
// "ccy": "BTC",
// "to": "6", // 1 SPOT, 3 FUTURES, 6 FUNDING, 9 SWAP, 12 OPTION, 18 Unified account
// "selected": true
// }
//
// {
// "ccy":"usdt-erc20",
// "to":"6",
// "addr":"0x696abb81974a8793352cbd33aadcf78eda3cfdfa",
// "selected":true
// }
//
// {
// "chain": "ETH-OKExChain",
// "addrEx": { "comment": "6040348" }, // some currencies like TON may have this field,
// "ctAddr": "72315c",
// "ccy": "ETH",
// "to": "6",
// "addr": "0x1c9f2244d1ccaa060bd536827c18925db10db102",
// "selected": true
// }
//
const address = this.safeString(depositAddress, 'addr');
let tag = this.safeStringN(depositAddress, ['tag', 'pmtId', 'memo']);
if (tag === undefined) {
const addrEx = this.safeValue(depositAddress, 'addrEx', {});
tag = this.safeString(addrEx, 'comment');
}
const currencyId = this.safeString(depositAddress, 'ccy');
currency = this.safeCurrency(currencyId, currency);
const code = currency['code'];
const chain = this.safeString(depositAddress, 'chain');
const networkId = chain.replace(currencyId + '-', '');
const network = this.networkIdToCode(networkId);
// inconsistent naming responses from exchange
// with respect to network naming provided in currency info vs address chain-names and ids
//
// response from address endpoint:
// {
// "chain": "USDT-Polygon",
// "ctAddr": "",
// "ccy": "USDT",
// "to":"6" ,
// "addr": "0x1903441e386cc49d937f6302955b5feb4286dcfa",
// "selected": true
// }
// network information from currency['networks'] field:
// Polygon: {
// "info": {
// "canDep": false,
// "canInternal": false,
// "canWd": false,
// "ccy": "USDT",
// "chain": "USDT-Polygon-Bridge",
// "mainNet": false,
// "maxFee": "26.879528",
// "minFee": "13.439764",
// "minWd": "0.001",
// "name": ''
// },
// "id": "USDT-Polygon-Bridge",
// "network": "Polygon",
// "active": false,
// "deposit": false,
// "withdraw": false,
// "fee": 13.439764,
// "precision": undefined,
// "limits": {
// "withdraw": {
// "min": 0.001,
// "max": undefined
// }
// }
// },
//
this.checkAddress(address);
return {
'info': depositAddress,
'currency': code,
'network': network,
'address': address,
'tag': tag,
};
}
/**
* @method
* @name okcoin#fetchDepositAddress
* @description fetch the deposit address for a currency associated with this account
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-deposit-address
* @param {string} code unified currency code
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
*/
async fetchDepositAddress(code, params = {}) {
await this.loadMarkets();
const defaultNetwork = this.safeString(this.options, 'defaultNetwork', 'ERC20');
const networkId = this.safeString(params, 'network', defaultNetwork);
const networkCode = this.networkIdToCode(networkId);
params = this.omit(params, 'network');
const response = await this.fetchDepositAddressesByNetwork(code, params);
const result = this.safeValue(response, networkCode);
if (result === undefined) {
throw new errors.InvalidAddress(this.id + ' fetchDepositAddress() cannot find ' + networkCode + ' deposit address for ' + code);
}
return result;
}
/**
* @method
* @name okcoin#fetchDepositAddressesByNetwork
* @description fetch a dictionary of addresses for a currency, indexed by network
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-deposit-address
* @param {string} code unified currency code of the currency for the deposit address
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [address structures]{@link https://docs.ccxt.com/#/?id=address-structure} indexed by the network
*/
async fetchDepositAddressesByNetwork(code, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'ccy': currency['id'],
};
const response = await this.privateGetAssetDepositAddress(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "addr": "okbtothemoon",
// "memo": "971668", // may be missing
// "tag":"52055", // may be missing
// "pmtId": "", // may be missing
// "ccy": "BTC",
// "to": "6", // 1 SPOT, 3 FUTURES, 6 FUNDING, 9 SWAP, 12 OPTION, 18 Unified account
// "selected": true
// },
// // {"ccy":"usdt-erc20","to":"6","addr":"0x696abb81974a8793352cbd33aadcf78eda3cfdfa","selected":true},
// // {"ccy":"usdt-trc20","to":"6","addr":"TRrd5SiSZrfQVRKm4e9SRSbn2LNTYqCjqx","selected":true},
// // {"ccy":"usdt_okexchain","to":"6","addr":"0x696abb81974a8793352cbd33aadcf78eda3cfdfa","selected":true},
// // {"ccy":"usdt_kip20","to":"6","addr":"0x696abb81974a8793352cbd33aadcf78eda3cfdfa","selected":true},
// ]
// }
//
const data = this.safeValue(response, 'data', []);
const filtered = this.filterBy(data, 'selected', true);
const parsed = this.parseDepositAddresses(filtered, [currency['code']], false);
return this.indexBy(parsed, 'network');
}
/**
* @method
* @name okcoin#transfer
* @see https://www.okcoin.com/docs-v5/en/#rest-api-funding-funds-transfer
* @description transfer currency internally between wallets on the same account
* @param {string} code unified currency code
* @param {float} amount amount to transfer
* @param {string} fromAccount account to transfer from
* @param {string} toAccount account to transfer to
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [transfer structure]{@link https://docs.ccxt.com/#/?id=transfer-structure}
*/
async transfer(code, amount, fromAccount, toAccount, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const accountsByType = this.safeValue(this.options, 'accountsByType', {});
const fromId = this.safeString(accountsByType, fromAccount, fromAccount);
const toId = this.safeString(accountsByType, toAccount, toAccount);
const request = {
'ccy': currency['id'],
'amt': this.currencyToPrecision(code, amount),
'type': '0',
'from': fromId,
'to': toId, // beneficiary account, 6: Funding account, 18: Trading account
// 'subAcct': 'sub-account-name', // optional, only required when type is 1, 2 or 4
// 'loanTrans': false, // Whether or not borrowed coins can be transferred out under Multi-currency margin and Portfolio margin. The default is false
// 'clientId': 'client-supplied id', // A combination of case-sensitive alphanumerics, all numbers, or all letters of up to 32 characters
// 'omitPosRisk': false, // Ignore position risk. Default is false. Applicable to Portfolio margin
};
if (fromId === 'master') {
request['type'] = '1';
request['subAcct'] = toId;
request['from'] = this.safeString(params, 'from', '6');
request['to'] = this.safeString(params, 'to', '6');
}
else if (toId === 'master') {
request['type'] = '2';
request['subAcct'] = fromId;
request['from'] = this.safeString(params, 'from', '6');
request['to'] = this.safeString(params, 'to', '6');
}
const response = await this.privatePostAssetTransfer(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "transId": "754147",
// "ccy": "USDT",
// "from": "6",
// "amt": "0.1",
// "to": "18"
// }
// ]
// }
//
const data = this.safeValue(response, 'data', []);
const rawTransfer = this.safeDict(data, 0, {});
return this.parseTransfer(rawTransfer, currency);
}
parseTransfer(transfer, currency = undefined) {
//
// transfer
//
// {
// "transId": "754147",
// "ccy": "USDT",
// "from": "6",
// "amt": "0.1",
// "to": "18"
// }
//
// fetchTransfer
//
// {
// "amt": "5",
// "ccy": "USDT",
// "from": "18",
// "instId": "",
// "state": "success",
// "subAcct": "",
// "to": "6",
// "toInstId": "",
// "transId": "464424732",
// "type": "0"
// }
//
// fetchTransfers
//
// {
// "bal": "70.6874353780312913",
// "balChg": "-4.0000000000000000", // negative means "to funding", positive meand "from funding"
// "billId": "588900695232225299",
// "ccy": "USDT",
// "execType": "",
// "fee": "",
// "from": "18",
// "instId": "",
// "instType": "",
// "mgnMode": "",
// "notes": "To Funding Account",
// "ordId": "",
// "pnl": "",
// "posBal": "",
// "posBalChg": "",
// "price": "0",
// "subType": "12",
// "sz": "-4",
// "to": "6",
// "ts": "1686676866989",
// "type": "1"
// }
//
const id = this.safeString2(transfer, 'transId', 'billId');
const currencyId = this.safeString(transfer, 'ccy');
const code = this.safeCurrencyCode(currencyId, currency);
let amount = this.safeNumber(transfer, 'amt');
const fromAccountId = this.safeString(transfer, 'from');
const toAccountId = this.safeString(transfer, 'to');
const accountsById = this.safeValue(this.options, 'accountsById', {});
const timestamp = this.safeInteger(transfer, 'ts', this.milliseconds());
const balanceChange = this.safeString(transfer, 'sz');
if (balanceChange !== undefined) {
amount = this.parseNumber(Precise["default"].stringAbs(balanceChange));
}
return {
'info': transfer,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'currency': code,
'amount': amount,
'fromAccount': this.safeString(accountsById, fromAccountId),
'toAccount': this.safeString(accountsById, toAccountId),
'status': this.parseTransferStatus(this.safeString(transfer, 'state')),
};
}
parseTransferStatus(status) {
const statuses = {
'success': 'ok',
};
return this.safeString(statuses, status, status);
}
/**
* @method
* @name okcoin#withdraw
* @see https://www.okcoin.com/docs-v5/en/#rest-api-funding-withdrawal
* @description make a withdrawal
* @param {string} code unified currency code
* @param {float} amount the amount to withdraw
* @param {string} address the address to withdraw to
* @param {string} tag
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async withdraw(code, amount, address, tag = undefined, params = {}) {
[tag, params] = this.handleWithdrawTagAndParams(tag, params);
this.checkAddress(address);
await this.loadMarkets();
const currency = this.currency(code);
if ((tag !== undefined) && (tag.length > 0)) {
address = address + ':' + tag;
}
const request = {
'ccy': currency['id'],
'toAddr': address,
'dest': '4',
'amt': this.numberToString(amount),
};
let network = this.safeString(params, 'network'); // this line allows the user to specify either ERC20 or ETH
if (network !== undefined) {
const networks = this.safeValue(this.options, 'networks', {});
network = this.safeString(networks, network.toUpperCase(), network); // handle ETH>ERC20 alias
request['chain'] = currency['id'] + '-' + network;
params = this.omit(params, 'network');
}
let fee = this.safeString(params, 'fee');
if (fee === undefined) {
const targetNetwork = this.safeValue(currency['networks'], this.networkIdToCode(network), {});
fee = this.safeString(targetNetwork, 'fee');
if (fee === undefined) {
throw new errors.ArgumentsRequired(this.id + ' withdraw() requires a "fee" string parameter, network transaction fee must be ≥ 0. Withdrawals to OKCoin or OKX are fee-free, please set "0". Withdrawing to external digital asset address requires network transaction fee.');
}
}
request['fee'] = this.numberToString(fee); // withdrawals to OKCoin or OKX are fee-free, please set 0
const response = await this.privatePostAssetWithdrawal(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "amt": "0.1",
// "wdId": "67485",
// "ccy": "BTC"
// }
// ]
// }
//
const data = this.safeValue(response, 'data', []);
const transaction = this.safeDict(data, 0);
return this.parseTransaction(transaction, currency);
}
/**
* @method
* @name okcoin#fetchDeposits
* @see https://www.okcoin.com/docs-v5/en/#rest-api-funding-get-deposit-history
* @description fetch all deposits made to an account
* @param {string} code unified currency code
* @param {int} [since] the earliest time in ms to fetch deposits for
* @param {int} [limit] the maximum number of deposits structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let request = {
// 'ccy': currency['id'],
// 'state': 2, // 0 waiting for confirmation, 1 deposit credited, 2 deposit successful
// 'after': since,
// 'before' this.milliseconds (),
// 'limit': limit, // default 100, max 100
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
if (since !== undefined) {
request['before'] = Math.max(since - 1, 0);
}
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
[request, params] = this.handleUntilOption('after', request, params);
const response = await this.privateGetAssetDepositHistory(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "amt": "0.01044408",
// "txId": "1915737_3_0_0_asset",
// "ccy": "BTC",
// "from": "13801825426",
// "to": "",
// "ts": "1597026383085",
// "state": "2",
// "depId": "4703879"
// },
// {
// "amt": "491.6784211",
// "txId": "1744594_3_184_0_asset",
// "ccy": "OKB",
// "from": "",
// "to": "",
// "ts": "1597026383085",
// "state": "2",
// "depId": "4703809"
// },
// {
// "amt": "223.18782496",
// "txId": "6d892c669225b1092c780bf0da0c6f912fc7dc8f6b8cc53b003288624c",
// "ccy": "USDT",
// "from": "",
// "to": "39kK4XvgEuM7rX9frgyHoZkWqx4iKu1spD",
// "ts": "1597026383085",
// "state": "2",
// "depId": "4703779"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTransactions(data, currency, since, limit, params);
}
/**
* @method
* @name okcoin#fetchWithdrawals
* @see https://www.okcoin.com/docs-v5/en/#rest-api-funding-get-withdrawal-history
* @description fetch all withdrawals made from an account
* @param {string} code unified currency code
* @param {int} [since] the earliest time in ms to fetch withdrawals for
* @param {int} [limit] the maximum number of withdrawals structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let request = {
// 'ccy': currency['id'],
// 'state': 2, // -3: pending cancel, -2 canceled, -1 failed, 0, pending, 1 sending, 2 sent, 3 awaiting email verification, 4 awaiting manual verification, 5 awaiting identity verification
// 'after': since,
// 'before': this.milliseconds (),
// 'limit': limit, // default 100, max 100
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
if (since !== undefined) {
request['before'] = Math.max(since - 1, 0);
}
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
[request, params] = this.handleUntilOption('after', request, params);
const response = await this.privateGetAssetWithdrawalHistory(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "amt": "0.094",
// "wdId": "4703879",
// "fee": "0.01000000eth",
// "txId": "0x62477bac6509a04512819bb1455e923a60dea5966c7caeaa0b24eb8fb0432b85",
// "ccy": "ETH",
// "from": "13426335357",
// "to": "0xA41446125D0B5b6785f6898c9D67874D763A1519",
// "ts": "1597026383085",
// "state": "2"
// },
// {
// "amt": "0.01",
// "wdId": "4703879",
// "fee": "0.00000000btc",
// "txId": "",
// "ccy": "BTC",
// "from": "13426335357",
// "to": "13426335357",
// "ts": "1597026383085",
// "state": "2"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTransactions(data, currency, since, limit, params);
}
parseTransactionStatus(status) {
//
// deposit statuses
//
// {
// "0": "waiting for confirmation",
// "1": "confirmation account",
// "2": "recharge success"
// }
//
// withdrawal statues
//
// {
// '-3': "pending cancel",
// "-2": "cancelled",
// "-1": "failed",
// "0": "pending",
// "1": "sending",
// "2": "sent",
// "3": "email confirmation",
// "4": "manual confirmation",
// "5": "awaiting identity confirmation"
// }
//
const statuses = {
'-3': 'pending',
'-2': 'canceled',
'-1': 'failed',
'0': 'pending',
'1': 'pending',
'2': 'ok',
'3': 'pending',
'4': 'pending',
'5': 'pending',
};
return this.safeString(statuses, status, status);
}
parseTransaction(transaction, currency = undefined) {
//
// withdraw
//
// {
// "amt": "0.1",
// "wdId": "67485",
// "ccy": "BTC"
// }
//
// fetchWithdrawals
//
// {
// "amt": "0.094",
// "wdId": "4703879",
// "fee": "0.01000000eth",
// "txId": "0x62477bac6509a04512819bb1455e923a60dea5966c7caeaa0b24eb8fb0432b85",
// "ccy": "ETH",
// "from": "13426335357",
// "to": "0xA41446125D0B5b6785f6898c9D67874D763A1519",
// "tag",
// "pmtId",
// "memo",
// "ts": "1597026383085",
// "state": "2"
// }
//
// fetchDeposits
//
// {
// "amt": "0.01044408",
// "txId": "1915737_3_0_0_asset",
// "ccy": "BTC",
// "from": "13801825426",
// "to": "",
// "ts": "1597026383085",
// "state": "2",
// "depId": "4703879"
// }
//
let type = undefined;
let id = undefined;
const withdrawalId = this.safeString(transaction, 'wdId');
const addressFrom = this.safeString(transaction, 'from');
const addressTo = this.safeString(transaction, 'to');
const address = addressTo;
let tagTo = this.safeString2(transaction, 'tag', 'memo');
tagTo = this.safeString2(transaction, 'pmtId', tagTo);
if (withdrawalId !== undefined) {
type = 'withdrawal';
id = withdrawalId;
}
else {
// the payment_id will appear on new deposits but appears to be removed from the response after 2 months
id = this.safeString(transaction, 'depId');
type = 'deposit';
}
const currencyId = this.safeString(transaction, 'ccy');
const code = this.safeCurrencyCode(currencyId);
const amount = this.safeNumber(transaction, 'amt');
const status = this.parseTransactionStatus(this.safeString(transaction, 'state'));
const txid = this.safeString(transaction, 'txId');
const timestamp = this.safeInteger(transaction, 'ts');
let feeCost = undefined;
if (type === 'deposit') {
feeCost = 0;
}
else {
feeCost = this.safeNumber(transaction, 'fee');
}
// todo parse tags
return {
'info': transaction,
'id': id,
'currency': code,
'amount': amount,
'network': undefined,
'addressFrom': addressFrom,
'addressTo': addressTo,
'address': address,
'tagFrom': undefined,
'tagTo': tagTo,
'tag': tagTo,
'status': status,
'type': type,
'updated': undefined,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'comment': undefined,
'internal': undefined,
'fee': {
'currency': code,
'cost': feeCost,
},
};
}
/**
* @method
* @name okcoin#fetchMyTrades
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-transaction-details-last-3-days
* @see https://www.okcoin.com/docs-v5/en/#rest-api-trade-get-transaction-details-last-3-months
* @description fetch all trades made by the user
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
*/
async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const request = {
'instType': 'SPOT',
};
if ((limit !== undefined) && (limit > 100)) {
limit = 100;
}
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['instId'] = market['id'];
}
let method = undefined;
[method, params] = this.handleOptionAndParams(params, 'fetchMyTrades', 'method', 'privateGetTradeFillsHistory');
let response = undefined;
if (method === 'privateGetTradeFillsHistory') {
response = await this.privateGetTradeFillsHistory(this.extend(request, params));
}
else {
response = await this.privateGetTradeFills(this.extend(request, params));
}
const data = this.safeList(response, 'data', []);
return this.parseTrades(data, market, since, limit);
}
/**
* @method
* @name okcoin#fetchOrderTrades
* @description fetch all the trades made from a single order
* @param {string} id order id
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
*/
async fetchOrderTrades(id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
const request = {
// 'instrument_id': market['id'],
'order_id': id,
// 'after': '1', // return the page after the specified page number
// 'before': '1', // return the page before the specified page number
// 'limit': limit, // optional, number of results per request, default = maximum = 100
};
return await this.fetchMyTrades(symbol, since, limit, this.extend(request, params));
}
/**
* @method
* @name okcoin#fetchLedger
* @description fetch the history of changes, actions done by the user or operations that altered the balance of the user
* @see https://www.okcoin.com/docs-v5/en/#rest-api-funding-asset-bills-details
* @see https://www.okcoin.com/docs-v5/en/#rest-api-account-get-bills-details-last-7-days
* @see https://www.okcoin.com/docs-v5/en/#rest-api-account-get-bills-details-last-3-months
* @param {string} [code] unified currency code, default is undefined
* @param {int} [since] timestamp in ms of the earliest ledger entry, default is undefined
* @param {int} [limit] max number of ledger entries to return, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger}
*/
async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let method = undefined;
[method, params] = this.handleOptionAndParams(params, 'fetchLedger', 'method', 'privateGetAccountBills');
let request = {
// 'instType': undefined, // 'SPOT', 'MARGIN', 'SWAP', 'FUTURES", 'OPTION'
// 'ccy': undefined, // currency['id'],
// 'ctType': undefined, // 'linear', 'inverse', only applicable to FUTURES/SWAP
// 'type': varies depending the 'method' endpoint :
// - https://www.okx.com/docs-v5/en/#rest-api-account-get-bills-details-last-7-days
// - https://www.okx.com/docs-v5/en/#rest-api-funding-asset-bills-details
// - https://www.okx.com/docs-v5/en/#rest-api-account-get-bills-details-last-3-months
// 'after': 'id', // return records earlier than the requested bill id
// 'before': 'id', // return records newer than the requested bill id
// 'limit': 100, // default 100, max 100
};
if (limit !== undefined) {
request['limit'] = limit;
}
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
[request, params] = this.handleUntilOption('end', request, params);
let response = undefined;
if (method === 'privateGetAccountBillsArchive') {
response = await this.privateGetAccountBillsArchive(this.extend(request, params));
}
else if (method === 'privateGetAssetBills') {
response = await this.privateGetAssetBills(this.extend(request, params));
}
else {
response = await this.privateGetAccountBills(this.extend(request, params));
}
//
// privateGetAccountBills, privateGetAccountBillsArchive
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "bal": "0.0000819307998198",
// "balChg": "-664.2679586599999802",
// "billId": "310394313544966151",
// "ccy": "USDT",
// "fee": "0",
// "from": "",
// "instId": "LTC-USDT",
// "instType": "SPOT",
// "mgnMode": "cross",
// "notes": "",
// "ordId": "310394313519800320",
// "pnl": "0",
// "posBal": "0",
// "posBalChg": "0",
// "subType": "2",
// "sz": "664.26795866",
// "to": "",
// "ts": "1620275771196",
// "type": "2"
// }
// ]
// }
//
// privateGetAssetBills
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "billId": "12344",
// "ccy": "BTC",
// "balChg": "2",
// "bal": "12",
// "type": "1",
// "ts": "1597026383085"
// }
// ]
// }
//
const data = this.safeValue(response, 'data', []);
return this.parseLedger(data, currency, since, limit);
}
parseLedgerEntryType(type) {
const types = {
'1': 'transfer',
'2': 'trade',
'3': 'trade',
'4': 'rebate',
'5': 'trade',
'6': 'transfer',
'7': 'trade',
'8': 'fee',
'9': 'trade',
'10': 'trade',
'11': 'trade', // system token conversion
};
return this.safeString(types, type, type);
}
parseLedgerEntry(item, currency = undefined) {
//
// privateGetAccountBills, privateGetAccountBillsArchive
//
// {
// "bal": "0.0000819307998198",
// "balChg": "-664.2679586599999802",
// "billId": "310394313544966151",
// "ccy": "USDT",
// "fee": "0",
// "from": "",
// "instId": "LTC-USDT",
// "instType": "SPOT",
// "mgnMode": "cross",
// "notes": "",
// "ordId": "310394313519800320",
// "pnl": "0",
// "posBal": "0",
// "posBalChg": "0",
// "subType": "2",
// "sz": "664.26795866",
// "to": "",
// "ts": "1620275771196",
// "type": "2"
// }
//
// privateGetAssetBills
//
// {
// "billId": "12344",
// "ccy": "BTC",
// "balChg": "2",
// "bal": "12",
// "type": "1",
// "ts": "1597026383085"
// }
//
const currencyId = this.safeString(item, 'ccy');
const code = this.safeCurrencyCode(currencyId, currency);
currency = this.safeCurrency(currencyId, currency);
const timestamp = this.safeInteger(item, 'ts');
const feeCostString = this.safeString(item, 'fee');
let fee = undefined;
if (feeCostString !== undefined) {
fee = {
'cost': this.parseToNumeric(Precise["default"].stringNeg(feeCostString)),
'currency': code,
};
}
const marketId = this.safeString(item, 'instId');
const symbol = this.safeSymbol(marketId, undefined, '-');
return this.safeLedgerEntry({
'info': item,
'id': this.safeString(item, 'billId'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'account': undefined,
'referenceId': this.safeString(item, 'ordId'),
'referenceAccount': undefined,
'type': this.parseLedgerEntryType(this.safeString(item, 'type')),
'currency': code,
'symbol': symbol,
'amount': this.safeNumber(item, 'balChg'),
'before': undefined,
'after': this.safeNumber(item, 'bal'),
'status': 'ok',
'fee': fee,
}, currency);
}
sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
const isArray = Array.isArray(params);
const request = '/api/' + this.version + '/' + this.implodeParams(path, params);
const query = this.omit(params, this.extractParams(path));
let url = this.implodeHostname(this.urls['api']['rest']) + request;
if (api === 'public') {
if (Object.keys(query).length) {
url += '?' + this.urlencode(query);
}
}
else if (api === 'private') {
this.checkRequiredCredentials();
const timestamp = this.iso8601(this.milliseconds());
headers = {
'OK-ACCESS-KEY': this.apiKey,
'OK-ACCESS-PASSPHRASE': this.password,
'OK-ACCESS-TIMESTAMP': timestamp,
// 'OK-FROM': '',
// 'OK-TO': '',
// 'OK-LIMIT': '',
};
let auth = timestamp + method + request;
if (method === 'GET') {
if (Object.keys(query).length) {
const urlencodedQuery = '?' + this.urlencode(query);
url += urlencodedQuery;
auth += urlencodedQuery;
}
}
else {
if (isArray || Object.keys(query).length) {
body = this.json(query);
auth += body;
}
headers['Content-Type'] = 'application/json';
}
const signature = this.hmac(this.encode(auth), this.encode(this.secret), sha256.sha256, 'base64');
headers['OK-ACCESS-SIGN'] = signature;
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
parseBalanceByType(type, response) {
if (type === 'funding') {
return this.parseFundingBalance(response);
}
else {
return this.parseTradingBalance(response);
}
}
handleErrors(httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (!response) {
return undefined; // fallback to default error handler
}
//
// {
// "code": "1",
// "data": [
// {
// "clOrdId": "",
// "ordId": "",
// "sCode": "51119",
// "sMsg": "Order placement failed due to insufficient balance. ",
// "tag": ""
// }
// ],
// "msg": ""
// },
// {
// "code": "58001",
// "data": [],
// "msg": "Incorrect trade password"
// }
//
const code = this.safeString(response, 'code');
if (code !== '0') {
const feedback = this.id + ' ' + body;
const data = this.safeValue(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const error = data[i];
const errorCode = this.safeString(error, 'sCode');
const message = this.safeString(error, 'sMsg');
this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
}
this.throwExactlyMatchedException(this.exceptions['exact'], code, feedback);
throw new errors.ExchangeError(feedback); // unknown message
}
return undefined;
}
}
module.exports = okcoin;