ccxt
Version:
1,171 lines (1,169 loc) • 142 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import Exchange from './abstract/okcoin.js';
import { ExchangeError, ExchangeNotAvailable, OnMaintenance, ArgumentsRequired, BadRequest, AccountSuspended, InvalidAddress, PermissionDenied, NetworkError, InsufficientFunds, InvalidNonce, CancelPending, InvalidOrder, OrderNotFound, AuthenticationError, RequestTimeout, AccountNotEnabled, BadSymbol, RateLimitExceeded, NotSupported } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TICK_SIZE } from './base/functions/number.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
// ---------------------------------------------------------------------------
/**
* @class okcoin
* @augments Exchange
*/
export default class okcoin extends Exchange {
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': ExchangeError,
'2': ExchangeError,
'50000': BadRequest,
'50001': OnMaintenance,
'50002': BadRequest,
'50004': RequestTimeout,
'50005': ExchangeNotAvailable,
'50006': BadRequest,
'50007': AccountSuspended,
'50008': AuthenticationError,
'50009': AccountSuspended,
'50010': ExchangeError,
'50011': RateLimitExceeded,
'50012': ExchangeError,
'50013': ExchangeNotAvailable,
'50014': BadRequest,
'50015': ExchangeError,
'50016': ExchangeError,
'50017': ExchangeError,
'50018': ExchangeError,
'50019': ExchangeError,
'50020': ExchangeError,
'50021': ExchangeError,
'50022': ExchangeError,
'50023': ExchangeError,
'50024': BadRequest,
'50025': ExchangeError,
'50026': ExchangeNotAvailable,
'50027': PermissionDenied,
'50028': ExchangeError,
'50029': ExchangeError,
'50030': PermissionDenied,
'50032': AccountSuspended,
'50033': AccountSuspended,
'50035': BadRequest,
'50036': BadRequest,
'50037': BadRequest,
'50038': ExchangeError,
'50039': ExchangeError,
'50041': ExchangeError,
'50044': BadRequest,
// API Class
'50100': ExchangeError,
'50101': AuthenticationError,
'50102': InvalidNonce,
'50103': AuthenticationError,
'50104': AuthenticationError,
'50105': AuthenticationError,
'50106': AuthenticationError,
'50107': AuthenticationError,
'50108': ExchangeError,
'50109': ExchangeError,
'50110': PermissionDenied,
'50111': AuthenticationError,
'50112': AuthenticationError,
'50113': AuthenticationError,
'50114': AuthenticationError,
'50115': BadRequest,
// Trade Class
'51000': BadRequest,
'51001': BadSymbol,
'51002': BadSymbol,
'51003': BadRequest,
'51004': InvalidOrder,
'51005': InvalidOrder,
'51006': InvalidOrder,
'51007': InvalidOrder,
'51008': InsufficientFunds,
'51009': AccountSuspended,
'51010': AccountNotEnabled,
'51011': InvalidOrder,
'51012': BadSymbol,
'51014': BadSymbol,
'51015': BadSymbol,
'51016': InvalidOrder,
'51017': ExchangeError,
'51018': ExchangeError,
'51019': ExchangeError,
'51020': InvalidOrder,
'51023': ExchangeError,
'51024': AccountSuspended,
'51025': ExchangeError,
'51026': BadSymbol,
'51030': InvalidOrder,
'51031': InvalidOrder,
'51032': InvalidOrder,
'51033': InvalidOrder,
'51037': InvalidOrder,
'51038': InvalidOrder,
'51044': InvalidOrder,
'51046': InvalidOrder,
'51047': InvalidOrder,
'51048': InvalidOrder,
'51049': InvalidOrder,
'51050': InvalidOrder,
'51051': InvalidOrder,
'51052': InvalidOrder,
'51053': InvalidOrder,
'51054': BadRequest,
'51056': InvalidOrder,
'51058': InvalidOrder,
'51059': InvalidOrder,
'51100': InvalidOrder,
'51102': InvalidOrder,
'51103': InvalidOrder,
'51108': InvalidOrder,
'51109': InvalidOrder,
'51110': InvalidOrder,
'51111': BadRequest,
'51112': InvalidOrder,
'51113': RateLimitExceeded,
'51115': InvalidOrder,
'51116': InvalidOrder,
'51117': InvalidOrder,
'51118': InvalidOrder,
'51119': InsufficientFunds,
'51120': InvalidOrder,
'51121': InvalidOrder,
'51122': InvalidOrder,
'51124': InvalidOrder,
'51125': InvalidOrder,
'51126': InvalidOrder,
'51127': InsufficientFunds,
'51128': InvalidOrder,
'51129': InvalidOrder,
'51130': BadSymbol,
'51131': InsufficientFunds,
'51132': InvalidOrder,
'51133': InvalidOrder,
'51134': InvalidOrder,
'51135': InvalidOrder,
'51136': InvalidOrder,
'51137': InvalidOrder,
'51138': InvalidOrder,
'51139': InvalidOrder,
'51156': BadRequest,
'51159': BadRequest,
'51162': InvalidOrder,
'51163': InvalidOrder,
'51166': InvalidOrder,
'51174': InvalidOrder,
'51201': InvalidOrder,
'51202': InvalidOrder,
'51203': InvalidOrder,
'51204': InvalidOrder,
'51205': InvalidOrder,
'51250': InvalidOrder,
'51251': InvalidOrder,
'51252': InvalidOrder,
'51253': InvalidOrder,
'51254': InvalidOrder,
'51255': InvalidOrder,
'51256': InvalidOrder,
'51257': InvalidOrder,
'51258': InvalidOrder,
'51259': InvalidOrder,
'51260': InvalidOrder,
'51261': InvalidOrder,
'51262': InvalidOrder,
'51263': InvalidOrder,
'51264': InvalidOrder,
'51265': InvalidOrder,
'51267': InvalidOrder,
'51268': InvalidOrder,
'51269': InvalidOrder,
'51270': InvalidOrder,
'51271': InvalidOrder,
'51272': InvalidOrder,
'51273': InvalidOrder,
'51274': InvalidOrder,
'51275': InvalidOrder,
'51276': InvalidOrder,
'51277': InvalidOrder,
'51278': InvalidOrder,
'51279': InvalidOrder,
'51280': InvalidOrder,
'51321': InvalidOrder,
'51322': InvalidOrder,
'51323': BadRequest,
'51324': BadRequest,
'51325': InvalidOrder,
'51327': InvalidOrder,
'51328': InvalidOrder,
'51329': InvalidOrder,
'51330': InvalidOrder,
'51400': OrderNotFound,
'51401': OrderNotFound,
'51402': OrderNotFound,
'51403': InvalidOrder,
'51404': InvalidOrder,
'51405': ExchangeError,
'51406': ExchangeError,
'51407': BadRequest,
'51408': ExchangeError,
'51409': ExchangeError,
'51410': CancelPending,
'51500': ExchangeError,
'51501': ExchangeError,
'51502': InsufficientFunds,
'51503': ExchangeError,
'51506': ExchangeError,
'51508': ExchangeError,
'51509': ExchangeError,
'51510': ExchangeError,
'51511': ExchangeError,
'51600': ExchangeError,
'51601': ExchangeError,
'51602': ExchangeError,
'51603': OrderNotFound,
'51732': AuthenticationError,
'51733': AuthenticationError,
'51734': AuthenticationError,
'51735': ExchangeError,
'51736': InsufficientFunds,
// Data class
'52000': ExchangeError,
// SPOT/MARGIN error codes 54000-54999
'54000': ExchangeError,
'54001': ExchangeError,
// FUNDING error codes 58000-58999
'58000': ExchangeError,
'58001': AuthenticationError,
'58002': PermissionDenied,
'58003': ExchangeError,
'58004': AccountSuspended,
'58005': ExchangeError,
'58006': ExchangeError,
'58007': ExchangeError,
'58100': ExchangeError,
'58101': AccountSuspended,
'58102': RateLimitExceeded,
'58103': ExchangeError,
'58104': ExchangeError,
'58105': ExchangeError,
'58106': ExchangeError,
'58107': ExchangeError,
'58108': ExchangeError,
'58109': ExchangeError,
'58110': ExchangeError,
'58111': ExchangeError,
'58112': ExchangeError,
'58114': ExchangeError,
'58115': ExchangeError,
'58116': ExchangeError,
'58117': ExchangeError,
'58125': BadRequest,
'58126': BadRequest,
'58127': BadRequest,
'58128': BadRequest,
'58200': ExchangeError,
'58201': ExchangeError,
'58202': ExchangeError,
'58203': InvalidAddress,
'58204': AccountSuspended,
'58205': ExchangeError,
'58206': ExchangeError,
'58207': InvalidAddress,
'58208': ExchangeError,
'58209': ExchangeError,
'58210': ExchangeError,
'58211': ExchangeError,
'58212': ExchangeError,
'58213': AuthenticationError,
'58221': BadRequest,
'58222': BadRequest,
'58224': BadRequest,
'58227': BadRequest,
'58228': BadRequest,
'58229': InsufficientFunds,
'58300': ExchangeError,
'58350': InsufficientFunds,
// Account error codes 59000-59999
'59000': ExchangeError,
'59001': ExchangeError,
'59100': ExchangeError,
'59101': ExchangeError,
'59102': ExchangeError,
'59103': InsufficientFunds,
'59104': ExchangeError,
'59105': ExchangeError,
'59106': ExchangeError,
'59107': ExchangeError,
'59108': InsufficientFunds,
'59109': ExchangeError,
'59128': InvalidOrder,
'59200': InsufficientFunds,
'59201': InsufficientFunds,
'59216': BadRequest,
'59300': ExchangeError,
'59301': ExchangeError,
'59313': ExchangeError,
'59401': ExchangeError,
'59500': ExchangeError,
'59501': ExchangeError,
'59502': ExchangeError,
'59503': ExchangeError,
'59504': ExchangeError,
'59505': ExchangeError,
'59506': ExchangeError,
'59507': ExchangeError,
'59508': AccountSuspended,
// WebSocket error Codes from 60000-63999
'60001': AuthenticationError,
'60002': AuthenticationError,
'60003': AuthenticationError,
'60004': AuthenticationError,
'60005': AuthenticationError,
'60006': InvalidNonce,
'60007': AuthenticationError,
'60008': AuthenticationError,
'60009': AuthenticationError,
'60010': AuthenticationError,
'60011': AuthenticationError,
'60012': BadRequest,
'60013': BadRequest,
'60014': RateLimitExceeded,
'60015': NetworkError,
'60016': ExchangeNotAvailable,
'60017': BadRequest,
'60018': BadRequest,
'60019': BadRequest,
'63999': ExchangeError,
'70010': BadRequest,
'70013': BadRequest,
'70016': BadRequest, // Please specify your instrument settings for at least one instType.
},
'broad': {
'Internal Server Error': ExchangeNotAvailable,
'server error': ExchangeNotAvailable, // {"code":500,"data":{},"detailMsg":"","error_code":"500","error_message":"server error 1236805249","msg":"server error 1236805249"}
},
},
'precisionMode': 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.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.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 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.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
//