UNPKG

ccxt-bybit

Version:

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

926 lines (910 loc) 122 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, AccountSuspended, InvalidAddress, PermissionDenied, DDoSProtection, InsufficientFunds, InvalidNonce, CancelPending, InvalidOrder, OrderNotFound, AuthenticationError, RequestTimeout, NotSupported, BadSymbol } = require ('./base/errors'); // --------------------------------------------------------------------------- module.exports = class okex3 extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'okex3', 'name': 'OKEX', 'countries': [ 'CN', 'US' ], 'version': 'v3', 'rateLimit': 1000, // up to 3000 requests per 5 minutes ≈ 600 requests per minute ≈ 10 requests per second ≈ 100 ms 'has': { 'CORS': false, 'fetchOHLCV': true, 'fetchOrder': true, 'fetchOrders': false, 'fetchOpenOrders': true, 'fetchClosedOrders': true, 'fetchCurrencies': false, // see below 'fetchDeposits': true, 'fetchWithdrawals': true, 'fetchTime': true, 'fetchTransactions': false, 'fetchMyTrades': false, // they don't have it 'fetchDepositAddress': true, 'fetchOrderTrades': true, 'fetchTickers': true, 'fetchLedger': true, 'withdraw': true, 'futures': true, }, 'timeframes': { '1m': '60', '3m': '180', '5m': '300', '15m': '900', '30m': '1800', '1h': '3600', '2h': '7200', '4h': '14400', '6h': '21600', '12h': '43200', '1d': '86400', '1w': '604800', }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/32552768-0d6dd3c6-c4a6-11e7-90f8-c043b64756a7.jpg', 'api': 'https://www.okex.com', 'www': 'https://www.okex.com', 'doc': 'https://www.okex.com/docs/en/', 'fees': 'https://www.okex.com/pages/products/fees.html', }, 'api': { 'general': { 'get': [ 'time', ], }, 'account': { 'get': [ 'currencies', 'wallet', 'wallet/{currency}', 'withdrawal/fee', 'withdrawal/history', 'withdrawal/history/{currency}', 'ledger', 'deposit/address', 'deposit/history', 'deposit/history/{currency}', ], 'post': [ 'transfer', 'withdrawal', ], }, 'spot': { 'get': [ 'accounts', 'accounts/{currency}', 'accounts/{currency}/ledger', 'orders', 'orders_pending', 'orders/{order_id}', 'orders/{client_oid}', 'fills', 'algo', // public 'instruments', 'instruments/{instrument_id}/book', 'instruments/ticker', 'instruments/{instrument_id}/ticker', 'instruments/{instrument_id}/trades', 'instruments/{instrument_id}/candles', ], 'post': [ 'order_algo', 'orders', 'batch_orders', 'cancel_orders/{order_id}', 'cancel_orders/{client_oid}', 'cancel_batch_algos', 'cancel_batch_orders', ], }, 'margin': { 'get': [ 'accounts', 'accounts/{instrument_id}', 'accounts/{instrument_id}/ledger', 'accounts/availability', 'accounts/{instrument_id}/availability', 'accounts/borrowed', 'accounts/{instrument_id}/borrowed', 'orders', 'orders/{order_id}', 'orders/{client_oid}', 'orders_pending', 'fills', ], 'post': [ 'accounts/borrow', 'accounts/repayment', 'orders', 'batch_orders', 'cancel_orders', 'cancel_orders/{order_id}', 'cancel_orders/{client_oid}', 'cancel_batch_orders', ], }, 'futures': { 'get': [ 'position', '{instrument_id}/position', 'accounts', 'accounts/{currency}', 'accounts/{currency}/leverage', 'accounts/{currency}/ledger', 'order_algo/{instrument_id}', 'orders/{instrument_id}', 'orders/{instrument_id}/{order_id}', 'orders/{instrument_id}/{client_oid}', 'fills', // public 'instruments', 'instruments/{instrument_id}/book', 'instruments/ticker', 'instruments/{instrument_id}/ticker', 'instruments/{instrument_id}/trades', 'instruments/{instrument_id}/candles', 'accounts/{instrument_id}/holds', 'instruments/{instrument_id}/index', 'rate', 'instruments/{instrument_id}/estimated_price', 'instruments/{instrument_id}/open_interest', 'instruments/{instrument_id}/price_limit', 'instruments/{instrument_id}/liquidation', 'instruments/{instrument_id}/mark_price', ], 'post': [ 'accounts/{currency}/leverage', 'accounts/margin_mode', 'order', 'orders', 'order_algo', 'cancel_algos', 'cancel_order/{instrument_id}/{order_id}', 'cancel_order/{instrument_id}/{client_oid}', 'cancel_batch_orders/{instrument_id}', 'close_position', 'cancel_all', ], }, 'swap': { 'get': [ 'position', '{instrument_id}/position', 'accounts', '{instrument_id}/accounts', 'accounts/{instrument_id}/settings', 'accounts/{instrument_id}/ledger', 'accounts/{instrument_id}/holds', 'order_algo/{instrument_id}', 'orders/{instrument_id}', 'orders/{instrument_id}/{order_id}', 'orders/{instrument_id}/{client_oid}', 'fills', // public 'instruments', 'instruments/{instrument_id}/depth', 'instruments/ticker', 'instruments/{instrument_id}/ticker', 'instruments/{instrument_id}/trades', 'instruments/{instrument_id}/candles', 'instruments/{instrument_id}/index', 'rate', 'instruments/{instrument_id}/open_interest', 'instruments/{instrument_id}/price_limit', 'instruments/{instrument_id}/liquidation', 'instruments/{instrument_id}/funding_time', 'instruments/{instrument_id}/mark_price', 'instruments/{instrument_id}/historical_funding_rate', ], 'post': [ 'accounts/{instrument_id}/leverage', 'order', 'order_algo', 'orders', 'cancel_algos', 'cancel_order/{instrument_id}/{order_id}', 'cancel_order/{instrument_id}/{client_oid}', 'cancel_batch_orders/{instrument_id}', ], }, // they have removed this part from public 'ett': { 'get': [ 'accounts', 'accounts/{currency}', 'accounts/{currency}/ledger', 'orders', // fetchOrder, fetchOrders // public 'constituents/{ett}', 'define-price/{ett}', ], 'post': [ 'orders', 'orders/{order_id}', ], }, }, 'fees': { 'trading': { 'taker': 0.0015, 'maker': 0.0010, }, 'spot': { 'taker': 0.0015, 'maker': 0.0010, }, 'futures': { 'taker': 0.0005, 'maker': 0.0002, }, 'swap': { 'taker': 0.00075, 'maker': 0.00020, }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, 'password': true, }, 'exceptions': { // http error codes // 400 Bad Request — Invalid request format // 401 Unauthorized — Invalid API Key // 403 Forbidden — You do not have access to the requested resource // 404 Not Found // 500 Internal Server Error — We had a problem with our server 'exact': { '1': ExchangeError, // { "code": 1, "message": "System error" } // undocumented 'failure to get a peer from the ring-balancer': ExchangeNotAvailable, // { "message": "failure to get a peer from the ring-balancer" } '4010': PermissionDenied, // { "code": 4010, "message": "For the security of your funds, withdrawals are not permitted within 24 hours after changing fund password / mobile number / Google Authenticator settings " } // common '30001': AuthenticationError, // { "code": 30001, "message": 'request header "OK_ACCESS_KEY" cannot be blank'} '30002': AuthenticationError, // { "code": 30002, "message": 'request header "OK_ACCESS_SIGN" cannot be blank'} '30003': AuthenticationError, // { "code": 30003, "message": 'request header "OK_ACCESS_TIMESTAMP" cannot be blank'} '30004': AuthenticationError, // { "code": 30004, "message": 'request header "OK_ACCESS_PASSPHRASE" cannot be blank'} '30005': InvalidNonce, // { "code": 30005, "message": "invalid OK_ACCESS_TIMESTAMP" } '30006': AuthenticationError, // { "code": 30006, "message": "invalid OK_ACCESS_KEY" } '30007': BadRequest, // { "code": 30007, "message": 'invalid Content_Type, please use "application/json" format'} '30008': RequestTimeout, // { "code": 30008, "message": "timestamp request expired" } '30009': ExchangeError, // { "code": 30009, "message": "system error" } '30010': AuthenticationError, // { "code": 30010, "message": "API validation failed" } '30011': PermissionDenied, // { "code": 30011, "message": "invalid IP" } '30012': AuthenticationError, // { "code": 30012, "message": "invalid authorization" } '30013': AuthenticationError, // { "code": 30013, "message": "invalid sign" } '30014': DDoSProtection, // { "code": 30014, "message": "request too frequent" } '30015': AuthenticationError, // { "code": 30015, "message": 'request header "OK_ACCESS_PASSPHRASE" incorrect'} '30016': ExchangeError, // { "code": 30015, "message": "you are using v1 apiKey, please use v1 endpoint. If you would like to use v3 endpoint, please subscribe to v3 apiKey" } '30017': ExchangeError, // { "code": 30017, "message": "apikey's broker id does not match" } '30018': ExchangeError, // { "code": 30018, "message": "apikey's domain does not match" } '30019': ExchangeNotAvailable, // { "code": 30019, "message": "Api is offline or unavailable" } '30020': BadRequest, // { "code": 30020, "message": "body cannot be blank" } '30021': BadRequest, // { "code": 30021, "message": "Json data format error" }, { "code": 30021, "message": "json data format error" } '30022': PermissionDenied, // { "code": 30022, "message": "Api has been frozen" } '30023': BadRequest, // { "code": 30023, "message": "{0} parameter cannot be blank" } '30024': BadSymbol, // {"code":30024,"message":"\"instrument_id\" is an invalid parameter"} '30025': BadRequest, // { "code": 30025, "message": "{0} parameter category error" } '30026': DDoSProtection, // { "code": 30026, "message": "requested too frequent" } '30027': AuthenticationError, // { "code": 30027, "message": "login failure" } '30028': PermissionDenied, // { "code": 30028, "message": "unauthorized execution" } '30029': AccountSuspended, // { "code": 30029, "message": "account suspended" } '30030': ExchangeError, // { "code": 30030, "message": "endpoint request failed. Please try again" } '30031': BadRequest, // { "code": 30031, "message": "token does not exist" } '30032': BadSymbol, // { "code": 30032, "message": "pair does not exist" } '30033': BadRequest, // { "code": 30033, "message": "exchange domain does not exist" } '30034': ExchangeError, // { "code": 30034, "message": "exchange ID does not exist" } '30035': ExchangeError, // { "code": 30035, "message": "trading is not supported in this website" } '30036': ExchangeError, // { "code": 30036, "message": "no relevant data" } '30038': AuthenticationError, // { "code": 30038, "message": "user does not exist" } '30037': ExchangeNotAvailable, // { "code": 30037, "message": "endpoint is offline or unavailable" } // futures '32001': AccountSuspended, // { "code": 32001, "message": "futures account suspended" } '32002': PermissionDenied, // { "code": 32002, "message": "futures account does not exist" } '32003': CancelPending, // { "code": 32003, "message": "canceling, please wait" } '32004': ExchangeError, // { "code": 32004, "message": "you have no unfilled orders" } '32005': InvalidOrder, // { "code": 32005, "message": "max order quantity" } '32006': InvalidOrder, // { "code": 32006, "message": "the order price or trigger price exceeds USD 1 million" } '32007': InvalidOrder, // { "code": 32007, "message": "leverage level must be the same for orders on the same side of the contract" } '32008': InvalidOrder, // { "code": 32008, "message": "Max. positions to open (cross margin)" } '32009': InvalidOrder, // { "code": 32009, "message": "Max. positions to open (fixed margin)" } '32010': ExchangeError, // { "code": 32010, "message": "leverage cannot be changed with open positions" } '32011': ExchangeError, // { "code": 32011, "message": "futures status error" } '32012': ExchangeError, // { "code": 32012, "message": "futures order update error" } '32013': ExchangeError, // { "code": 32013, "message": "token type is blank" } '32014': ExchangeError, // { "code": 32014, "message": "your number of contracts closing is larger than the number of contracts available" } '32015': ExchangeError, // { "code": 32015, "message": "margin ratio is lower than 100% before opening positions" } '32016': ExchangeError, // { "code": 32016, "message": "margin ratio is lower than 100% after opening position" } '32017': ExchangeError, // { "code": 32017, "message": "no BBO" } '32018': ExchangeError, // { "code": 32018, "message": "the order quantity is less than 1, please try again" } '32019': ExchangeError, // { "code": 32019, "message": "the order price deviates from the price of the previous minute by more than 3%" } '32020': ExchangeError, // { "code": 32020, "message": "the price is not in the range of the price limit" } '32021': ExchangeError, // { "code": 32021, "message": "leverage error" } '32022': ExchangeError, // { "code": 32022, "message": "this function is not supported in your country or region according to the regulations" } '32023': ExchangeError, // { "code": 32023, "message": "this account has outstanding loan" } '32024': ExchangeError, // { "code": 32024, "message": "order cannot be placed during delivery" } '32025': ExchangeError, // { "code": 32025, "message": "order cannot be placed during settlement" } '32026': ExchangeError, // { "code": 32026, "message": "your account is restricted from opening positions" } '32029': ExchangeError, // { "code": 32029, "message": "order info does not exist" } '32028': ExchangeError, // { "code": 32028, "message": "account is suspended and liquidated" } '32027': ExchangeError, // { "code": 32027, "message": "cancelled over 20 orders" } '32044': ExchangeError, // { "code": 32044, "message": "The margin ratio after submitting this order is lower than the minimum requirement ({0}) for your tier." } // token and margin trading '33001': PermissionDenied, // { "code": 33001, "message": "margin account for this pair is not enabled yet" } '33002': AccountSuspended, // { "code": 33002, "message": "margin account for this pair is suspended" } '33003': InsufficientFunds, // { "code": 33003, "message": "no loan balance" } '33004': ExchangeError, // { "code": 33004, "message": "loan amount cannot be smaller than the minimum limit" } '33005': ExchangeError, // { "code": 33005, "message": "repayment amount must exceed 0" } '33006': ExchangeError, // { "code": 33006, "message": "loan order not found" } '33007': ExchangeError, // { "code": 33007, "message": "status not found" } '33008': ExchangeError, // { "code": 33008, "message": "loan amount cannot exceed the maximum limit" } '33009': ExchangeError, // { "code": 33009, "message": "user ID is blank" } '33010': ExchangeError, // { "code": 33010, "message": "you cannot cancel an order during session 2 of call auction" } '33011': ExchangeError, // { "code": 33011, "message": "no new market data" } '33012': ExchangeError, // { "code": 33012, "message": "order cancellation failed" } '33013': InvalidOrder, // { "code": 33013, "message": "order placement failed" } '33014': OrderNotFound, // { "code": 33014, "message": "order does not exist" } '33015': InvalidOrder, // { "code": 33015, "message": "exceeded maximum limit" } '33016': ExchangeError, // { "code": 33016, "message": "margin trading is not open for this token" } '33017': InsufficientFunds, // { "code": 33017, "message": "insufficient balance" } '33018': ExchangeError, // { "code": 33018, "message": "this parameter must be smaller than 1" } '33020': ExchangeError, // { "code": 33020, "message": "request not supported" } '33021': BadRequest, // { "code": 33021, "message": "token and the pair do not match" } '33022': InvalidOrder, // { "code": 33022, "message": "pair and the order do not match" } '33023': ExchangeError, // { "code": 33023, "message": "you can only place market orders during call auction" } '33024': InvalidOrder, // { "code": 33024, "message": "trading amount too small" } '33025': InvalidOrder, // { "code": 33025, "message": "base token amount is blank" } '33026': ExchangeError, // { "code": 33026, "message": "transaction completed" } '33027': InvalidOrder, // { "code": 33027, "message": "cancelled order or order cancelling" } '33028': InvalidOrder, // { "code": 33028, "message": "the decimal places of the trading price exceeded the limit" } '33029': InvalidOrder, // { "code": 33029, "message": "the decimal places of the trading size exceeded the limit" } '33034': ExchangeError, // { "code": 33034, "message": "You can only place limit order after Call Auction has started" } '33059': BadRequest, // { "code": 33059, "message": "client_oid or order_id is required" } '33060': BadRequest, // { "code": 33060, "message": "Only fill in either parameter client_oid or order_id" } // account '34001': PermissionDenied, // { "code": 34001, "message": "withdrawal suspended" } '34002': InvalidAddress, // { "code": 34002, "message": "please add a withdrawal address" } '34003': ExchangeError, // { "code": 34003, "message": "sorry, this token cannot be withdrawn to xx at the moment" } '34004': ExchangeError, // { "code": 34004, "message": "withdrawal fee is smaller than minimum limit" } '34005': ExchangeError, // { "code": 34005, "message": "withdrawal fee exceeds the maximum limit" } '34006': ExchangeError, // { "code": 34006, "message": "withdrawal amount is lower than the minimum limit" } '34007': ExchangeError, // { "code": 34007, "message": "withdrawal amount exceeds the maximum limit" } '34008': InsufficientFunds, // { "code": 34008, "message": "insufficient balance" } '34009': ExchangeError, // { "code": 34009, "message": "your withdrawal amount exceeds the daily limit" } '34010': ExchangeError, // { "code": 34010, "message": "transfer amount must be larger than 0" } '34011': ExchangeError, // { "code": 34011, "message": "conditions not met" } '34012': ExchangeError, // { "code": 34012, "message": "the minimum withdrawal amount for NEO is 1, and the amount must be an integer" } '34013': ExchangeError, // { "code": 34013, "message": "please transfer" } '34014': ExchangeError, // { "code": 34014, "message": "transfer limited" } '34015': ExchangeError, // { "code": 34015, "message": "subaccount does not exist" } '34016': PermissionDenied, // { "code": 34016, "message": "transfer suspended" } '34017': AccountSuspended, // { "code": 34017, "message": "account suspended" } '34018': AuthenticationError, // { "code": 34018, "message": "incorrect trades password" } '34019': PermissionDenied, // { "code": 34019, "message": "please bind your email before withdrawal" } '34020': PermissionDenied, // { "code": 34020, "message": "please bind your funds password before withdrawal" } '34021': InvalidAddress, // { "code": 34021, "message": "Not verified address" } '34022': ExchangeError, // { "code": 34022, "message": "Withdrawals are not available for sub accounts" } '34023': PermissionDenied, // { "code": 34023, "message": "Please enable futures trading before transferring your funds" } // swap '35001': ExchangeError, // { "code": 35001, "message": "Contract does not exist" } '35002': ExchangeError, // { "code": 35002, "message": "Contract settling" } '35003': ExchangeError, // { "code": 35003, "message": "Contract paused" } '35004': ExchangeError, // { "code": 35004, "message": "Contract pending settlement" } '35005': AuthenticationError, // { "code": 35005, "message": "User does not exist" } '35008': InvalidOrder, // { "code": 35008, "message": "Risk ratio too high" } '35010': InvalidOrder, // { "code": 35010, "message": "Position closing too large" } '35012': InvalidOrder, // { "code": 35012, "message": "Incorrect order size" } '35014': InvalidOrder, // { "code": 35014, "message": "Order price is not within limit" } '35015': InvalidOrder, // { "code": 35015, "message": "Invalid leverage level" } '35017': ExchangeError, // { "code": 35017, "message": "Open orders exist" } '35019': InvalidOrder, // { "code": 35019, "message": "Order size too large" } '35020': InvalidOrder, // { "code": 35020, "message": "Order price too high" } '35021': InvalidOrder, // { "code": 35021, "message": "Order size exceeded current tier limit" } '35022': ExchangeError, // { "code": 35022, "message": "Contract status error" } '35024': ExchangeError, // { "code": 35024, "message": "Contract not initialized" } '35025': InsufficientFunds, // { "code": 35025, "message": "No account balance" } '35026': ExchangeError, // { "code": 35026, "message": "Contract settings not initialized" } '35029': OrderNotFound, // { "code": 35029, "message": "Order does not exist" } '35030': InvalidOrder, // { "code": 35030, "message": "Order size too large" } '35031': InvalidOrder, // { "code": 35031, "message": "Cancel order size too large" } '35032': ExchangeError, // { "code": 35032, "message": "Invalid user status" } '35039': ExchangeError, // { "code": 35039, "message": "Open order quantity exceeds limit" } '35040': InvalidOrder, // {"error_message":"Invalid order type","result":"true","error_code":"35040","order_id":"-1"} '35044': ExchangeError, // { "code": 35044, "message": "Invalid order status" } '35046': InsufficientFunds, // { "code": 35046, "message": "Negative account balance" } '35047': InsufficientFunds, // { "code": 35047, "message": "Insufficient account balance" } '35048': ExchangeError, // { "code": 35048, "message": "User contract is frozen and liquidating" } '35049': InvalidOrder, // { "code": 35049, "message": "Invalid order type" } '35050': InvalidOrder, // { "code": 35050, "message": "Position settings are blank" } '35052': InsufficientFunds, // { "code": 35052, "message": "Insufficient cross margin" } '35053': ExchangeError, // { "code": 35053, "message": "Account risk too high" } '35055': InsufficientFunds, // { "code": 35055, "message": "Insufficient account balance" } '35057': ExchangeError, // { "code": 35057, "message": "No last traded price" } '35058': ExchangeError, // { "code": 35058, "message": "No limit" } '35059': BadRequest, // { "code": 35059, "message": "client_oid or order_id is required" } '35060': BadRequest, // { "code": 35060, "message": "Only fill in either parameter client_oid or order_id" } '35061': BadRequest, // { "code": 35061, "message": "Invalid instrument_id" } '35062': InvalidOrder, // { "code": 35062, "message": "Invalid match_price" } '35063': InvalidOrder, // { "code": 35063, "message": "Invalid order_size" } '35064': InvalidOrder, // { "code": 35064, "message": "Invalid client_oid" } }, 'broad': { }, }, 'options': { 'createMarketBuyOrderRequiresPrice': true, 'fetchMarkets': [ 'spot', 'futures', 'swap' ], 'defaultType': 'spot', // 'account', 'spot', 'margin', 'futures', 'swap' 'auth': { 'time': 'public', 'currencies': 'private', 'instruments': 'public', 'rate': 'public', 'constituents/{ett}': 'public', 'define-price/{ett}': 'public', }, }, 'commonCurrencies': { // OKEX refers to ERC20 version of Aeternity (AEToken) 'AE': 'AET', // https://github.com/ccxt/ccxt/issues/4981 'HOT': 'Hydro Protocol', 'HSR': 'HC', 'MAG': 'Maggie', 'YOYO': 'YOYOW', 'WIN': 'WinToken', // https://github.com/ccxt/ccxt/issues/5701 }, }); } async fetchTime (params = {}) { const response = await this.generalGetTime (params); // // { // "iso": "2015-01-07T23:47:25.201Z", // "epoch": 1420674445.201 // } // return this.parse8601 (this.safeString (response, 'iso')); } async fetchMarkets (params = {}) { const types = this.safeValue (this.options, 'fetchMarkets'); let result = []; for (let i = 0; i < types.length; i++) { const markets = await this.fetchMarketsByType (types[i], params); result = this.arrayConcat (result, markets); } return result; } parseMarkets (markets) { const result = []; for (let i = 0; i < markets.length; i++) { result.push (this.parseMarket (markets[i])); } return result; } parseMarket (market) { // // spot markets // // [ { base_currency: "EOS", // instrument_id: "EOS-OKB", // min_size: "0.01", // product_id: "EOS-OKB", // quote_currency: "OKB", // size_increment: "0.000001", // tick_size: "0.0001" }, // // ..., // the spot endpoint also returns ETT instruments // // { base_currency: "OK06ETT", // base_increment: "0.00000001", // base_min_size: "0.01", // instrument_id: "OK06ETT-USDT", // min_size: "0.01", // product_id: "OK06ETT-USDT", // quote_currency: "USDT", // quote_increment: "0.0001", // size_increment: "0.00000001", // tick_size: "0.0001" } ] // // futures markets // // [ { instrument_id: "BTG-USD-190329", // underlying_index: "BTG", // quote_currency: "USD", // tick_size: "0.01", // contract_val: "10", // listing: "2018-12-14", // delivery: "2019-03-29", // trade_increment: "1" } ] // // swap markets // // [ { instrument_id: "BTC-USD-SWAP", // underlying_index: "BTC", // quote_currency: "USD", // coin: "BTC", // contract_val: "100", // listing: "2018-10-23T20:11:00.443Z", // delivery: "2018-10-24T20:11:00.443Z", // size_increment: "4", // tick_size: "4" } ] // const id = this.safeString (market, 'instrument_id'); let marketType = 'spot'; let spot = true; let future = false; let swap = false; let baseId = this.safeString (market, 'base_currency'); const contractVal = this.safeFloat (market, 'contract_val'); if (contractVal !== undefined) { marketType = 'swap'; spot = false; swap = true; baseId = this.safeString (market, 'coin'); const futuresAlias = this.safeString (market, 'alias'); if (futuresAlias !== undefined) { swap = false; future = true; marketType = 'futures'; baseId = this.safeString (market, 'underlying_index'); } } const quoteId = this.safeString (market, 'quote_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = spot ? (base + '/' + quote) : id; let amountPrecision = this.safeString (market, 'size_increment'); if (amountPrecision !== undefined) { amountPrecision = this.precisionFromString (amountPrecision); } let pricePrecision = this.safeString (market, 'tick_size'); if (pricePrecision !== undefined) { pricePrecision = this.precisionFromString (pricePrecision); } const precision = { 'amount': amountPrecision, 'price': pricePrecision, }; const minAmount = this.safeFloat2 (market, 'min_size', 'base_min_size'); let minPrice = this.safeFloat (market, 'tick_size'); if (precision['price'] !== undefined) { minPrice = Math.pow (10, -precision['price']); } let minCost = undefined; if (minAmount !== undefined && minPrice !== undefined) { minCost = minAmount * minPrice; } const active = true; const fees = this.safeValue2 (this.fees, marketType, 'trading', {}); return this.extend (fees, { 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'info': market, 'type': marketType, 'spot': spot, 'futures': future, 'swap': swap, 'active': active, 'precision': precision, 'limits': { 'amount': { 'min': minAmount, 'max': undefined, }, 'price': { 'min': minPrice, 'max': undefined, }, 'cost': { 'min': minCost, 'max': undefined, }, }, }); } async fetchMarketsByType (type, params = {}) { const method = type + 'GetInstruments'; const response = await this[method] (params); // // spot markets // // [ { base_currency: "EOS", // base_increment: "0.000001", // base_min_size: "0.01", // instrument_id: "EOS-OKB", // min_size: "0.01", // product_id: "EOS-OKB", // quote_currency: "OKB", // quote_increment: "0.0001", // size_increment: "0.000001", // tick_size: "0.0001" } ] // // futures markets // // [ { instrument_id: "BTG-USD-190329", // underlying_index: "BTG", // quote_currency: "USD", // tick_size: "0.01", // contract_val: "10", // listing: "2018-12-14", // delivery: "2019-03-29", // trade_increment: "1" } ] // // swap markets // // [ { instrument_id: "BTC-USD-SWAP", // underlying_index: "BTC", // quote_currency: "USD", // coin: "BTC", // contract_val: "100", // listing: "2018-10-23T20:11:00.443Z", // delivery: "2018-10-24T20:11:00.443Z", // size_increment: "4", // tick_size: "4" } ] // return this.parseMarkets (response); } async fetchCurrencies (params = {}) { // has['fetchCurrencies'] is currently set to false // despite that their docs say these endpoints are public: // https://www.okex.com/api/account/v3/withdrawal/fee // https://www.okex.com/api/account/v3/currencies // it will still reply with { "code":30001, "message": "OK-ACCESS-KEY header is required" } // if you attempt to access it without authentication const response = await this.accountGetCurrencies (params); // // [ // { // name: '', // currency: 'BTC', // can_withdraw: '1', // can_deposit: '1', // min_withdrawal: '0.0100000000000000' // }, // ] // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString (currency, 'currency'); const code = this.safeCurrencyCode (id); const precision = 8; // default precision, todo: fix "magic constants" const name = this.safeString (currency, 'name'); const canDeposit = this.safeInteger (currency, 'can_deposit'); const canWithdraw = this.safeInteger (currency, 'can_withdraw'); const active = canDeposit && canWithdraw; result[code] = { 'id': id, 'code': code, 'info': currency, 'type': undefined, 'name': name, 'active': active, 'fee': undefined, // todo: redesign 'precision': precision, 'limits': { 'amount': { 'min': undefined, 'max': undefined }, 'price': { 'min': undefined, 'max': undefined }, 'cost': { 'min': undefined, 'max': undefined }, 'withdraw': { 'min': this.safeFloat (currency, 'min_withdrawal'), 'max': undefined, }, }, }; } return result; } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); let method = market['type'] + 'GetInstrumentsInstrumentId'; method += (market['type'] === 'swap') ? 'Depth' : 'Book'; const request = { 'instrument_id': market['id'], }; if (limit !== undefined) { request['size'] = limit; // max 200 } const response = await this[method] (this.extend (request, params)); // // { asks: [ ["0.02685268", "0.242571", "1"], // ["0.02685493", "0.164085", "1"], // ... // ["0.02779", "1.039", "1"], // ["0.027813", "0.0876", "1"] ], // bids: [ ["0.02684052", "10.371849", "1"], // ["0.02684051", "3.707", "4"], // ... // ["0.02634963", "0.132934", "1"], // ["0.02634962", "0.264838", "2"] ], // timestamp: "2018-12-17T20:24:16.159Z" } // const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); return this.parseOrderBook (response, timestamp); } parseTicker (ticker, market = undefined) { // // { best_ask: "0.02665472", // best_bid: "0.02665221", // instrument_id: "ETH-BTC", // product_id: "ETH-BTC", // last: "0.02665472", // ask: "0.02665472", // missing in the docs // bid: "0.02665221", // not mentioned in the docs // open_24h: "0.02645482", // high_24h: "0.02714633", // low_24h: "0.02614109", // base_volume_24h: "572298.901923", // timestamp: "2018-12-17T21:20:07.856Z", // quote_volume_24h: "15094.86831261" } // const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); let symbol = undefined; const marketId = this.safeString (ticker, 'instrument_id'); if (marketId in this.markets_by_id) { market = this.markets_by_id[marketId]; } else if (marketId !== undefined) { const parts = marketId.split ('-'); const numParts = parts.length; if (numParts === 2) { const [ baseId, quoteId ] = parts; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); symbol = base + '/' + quote; } else { symbol = marketId; } } if (market !== undefined) { symbol = market['symbol']; } const last = this.safeFloat (ticker, 'last'); const open = this.safeFloat (ticker, 'open_24h'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeFloat (ticker, 'high_24h'), 'low': this.safeFloat (ticker, 'low_24h'), 'bid': this.safeFloat (ticker, 'best_bid'), 'bidVolume': undefined, 'ask': this.safeFloat (ticker, 'best_ask'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeFloat (ticker, 'base_volume_24h'), 'quoteVolume': this.safeFloat (ticker, 'quote_volume_24h'), 'info': ticker, }; } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const method = market['type'] + 'GetInstrumentsInstrumentIdTicker'; const request = { 'instrument_id': market['id'], }; const response = await this[method] (this.extend (request, params)); // // { best_ask: "0.02665472", // best_bid: "0.02665221", // instrument_id: "ETH-BTC", // product_id: "ETH-BTC", // last: "0.02665472", // ask: "0.02665472", // bid: "0.02665221", // open_24h: "0.02645482", // high_24h: "0.02714633", // low_24h: "0.02614109", // base_volume_24h: "572298.901923", // timestamp: "2018-12-17T21:20:07.856Z", // quote_volume_24h: "15094.86831261" } // return this.parseTicker (response); } async fetchTickersByType (type, symbols = undefined, params = {}) { await this.loadMarkets (); const method = type + 'GetInstrumentsTicker'; const response = await this[method] (params); const result = {}; for (let i = 0; i < response.length; i++) { const ticker = this.parseTicker (response[i]); const symbol = ticker['symbol']; result[symbol] = ticker; } return result; } async fetchTickers (symbols = undefined, params = {}) { const defaultType = this.safeString2 (this.options, 'fetchTickers', 'defaultType'); const type = this.safeString (params, 'type', defaultType); return await this.fetchTickersByType (type, symbols, this.omit (params, 'type')); } parseTrade (trade, market = undefined) { // // fetchTrades (public) // // spot trades // // { // time: "2018-12-17T23:31:08.268Z", // timestamp: "2018-12-17T23:31:08.268Z", // trade_id: "409687906", // price: "0.02677805", // size: "0.923467", // side: "sell" // } // // futures trades, swap trades // // { // trade_id: "1989230840021013", // side: "buy", // price: "92.42", // qty: "184", // missing in swap markets // size: "5", // missing in futures markets // timestamp: "2018-12-17T23:26:04.613Z" // } // // fetchOrderTrades (private) // // spot trades, margin trades // // { // "created_at":"2019-03-15T02:52:56.000Z", // "exec_type":"T", // whether the order is taker or maker // "fee":"0.00000082", // "instrument_id":"BTC-USDT", // "ledger_id":"3963052721", // "liquidity":"T", // whether the order is taker or maker // "order_id":"2482659399697408", // "price":"3888.6", // "product_id":"BTC-USDT", // "side":"buy", // "size":"0.00055306", // "timestamp":"2019-03-15T02:52:56.000Z" // }, // // futures trades, swap trades // // { // "trade_id":"197429674631450625", // "instrument_id":"EOS-USD-SWAP", // "order_id":"6a-7-54d663a28-0", // "price":"3.633", // "order_qty":"1.0000", // "fee":"-0.000551", // "created_at":"2019-03-21T04:41:58.0Z", // missing in swap trades // "timestamp":"2019-03-25T05:56:31.287Z", // missing in futures trades // "exec_type":"M", // whether the order is taker or maker // "side":"short", // "buy" in futures trades // } // let symbol = undefined; if (market !== undefined) { symbol = market['symbol']; } const timestamp = this.parse8601 (this.safeString2 (trade, 'timestamp', 'created_at')); const price = this.safeFloat (trade, 'price'); let amount = this.safeFloat2 (trade, 'size', 'qty'); amount = this.safeFloat (trade, 'order_qty', amount); let takerOrMaker = this.safeString2 (trade, 'exec_type', 'liquidity'); if (takerOrMaker ===