2723 lines
119 KiB
JavaScript
2723 lines
119 KiB
JavaScript
'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 === 'M') {
|
||
takerOrMaker = 'maker';
|
||
} else if (takerOrMaker === 'T') {
|
||
takerOrMaker = 'taker';
|
||
}
|
||
const side = this.safeString (trade, 'side');
|
||
let cost = undefined;
|
||
if (amount !== undefined) {
|
||
if (price !== undefined) {
|
||
cost = amount * price;
|
||
}
|
||
}
|
||
const feeCost = this.safeFloat (trade, 'fee');
|
||
let fee = undefined;
|
||
if (feeCost !== undefined) {
|
||
let feeCurrency = undefined;
|
||
if (market !== undefined) {
|
||
feeCurrency = (side === 'buy') ? market['base'] : market['quote'];
|
||
}
|
||
fee = {
|
||
// fee is either a positive number (invitation rebate)
|
||
// or a negative number (transaction fee deduction)
|
||
// therefore we need to invert the fee
|
||
// more about it https://github.com/ccxt/ccxt/issues/5909
|
||
'cost': -feeCost,
|
||
'currency': feeCurrency,
|
||
};
|
||
}
|
||
const orderId = this.safeString (trade, 'order_id');
|
||
return {
|
||
'info': trade,
|
||
'timestamp': timestamp,
|
||
'datetime': this.iso8601 (timestamp),
|
||
'symbol': symbol,
|
||
'id': this.safeString2 (trade, 'trade_id', 'ledger_id'),
|
||
'order': orderId,
|
||
'type': undefined,
|
||
'takerOrMaker': takerOrMaker,
|
||
'side': side,
|
||
'price': price,
|
||
'amount': amount,
|
||
'cost': cost,
|
||
'fee': fee,
|
||
};
|
||
}
|
||
|
||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||
await this.loadMarkets ();
|
||
const market = this.market (symbol);
|
||
const method = market['type'] + 'GetInstrumentsInstrumentIdTrades';
|
||
if ((limit === undefined) || (limit > 100)) {
|
||
limit = 100; // maximum = default = 100
|
||
}
|
||
const request = {
|
||
'instrument_id': market['id'],
|
||
'limit': limit,
|
||
// from: 'id',
|
||
// to: 'id',
|
||
};
|
||
const response = await this[method] (this.extend (request, params));
|
||
//
|
||
// spot markets
|
||
//
|
||
// [
|
||
// {
|
||
// 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 markets, swap markets
|
||
//
|
||
// [
|
||
// {
|
||
// 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"
|
||
// }
|
||
// ]
|
||
//
|
||
return this.parseTrades (response, market, since, limit);
|
||
}
|
||
|
||
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
|
||
//
|
||
// spot markets
|
||
//
|
||
// {
|
||
// close: "0.02684545",
|
||
// high: "0.02685084",
|
||
// low: "0.02683312",
|
||
// open: "0.02683894",
|
||
// time: "2018-12-17T20:28:00.000Z",
|
||
// volume: "101.457222"
|
||
// }
|
||
//
|
||
// futures markets
|
||
//
|
||
// [
|
||
// 1545072720000,
|
||
// 0.3159,
|
||
// 0.3161,
|
||
// 0.3144,
|
||
// 0.3149,
|
||
// 22886,
|
||
// 725179.26172331,
|
||
// ]
|
||
//
|
||
if (Array.isArray (ohlcv)) {
|
||
const numElements = ohlcv.length;
|
||
const volumeIndex = (numElements > 6) ? 6 : 5;
|
||
let timestamp = ohlcv[0];
|
||
if (typeof timestamp === 'string') {
|
||
timestamp = this.parse8601 (timestamp);
|
||
}
|
||
return [
|
||
timestamp, // timestamp
|
||
parseFloat (ohlcv[1]), // Open
|
||
parseFloat (ohlcv[2]), // High
|
||
parseFloat (ohlcv[3]), // Low
|
||
parseFloat (ohlcv[4]), // Close
|
||
// parseFloat (ohlcv[5]), // Quote Volume
|
||
// parseFloat (ohlcv[6]), // Base Volume
|
||
parseFloat (ohlcv[volumeIndex]), // Volume, okex will return base volume in the 7th element for future markets
|
||
];
|
||
} else {
|
||
return [
|
||
this.parse8601 (this.safeString (ohlcv, 'time')),
|
||
this.safeFloat (ohlcv, 'open'), // Open
|
||
this.safeFloat (ohlcv, 'high'), // High
|
||
this.safeFloat (ohlcv, 'low'), // Low
|
||
this.safeFloat (ohlcv, 'close'), // Close
|
||
this.safeFloat (ohlcv, 'volume'), // Base Volume
|
||
];
|
||
}
|
||
}
|
||
|
||
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
||
await this.loadMarkets ();
|
||
const market = this.market (symbol);
|
||
const method = market['type'] + 'GetInstrumentsInstrumentIdCandles';
|
||
const request = {
|
||
'instrument_id': market['id'],
|
||
'granularity': this.timeframes[timeframe],
|
||
};
|
||
if (since !== undefined) {
|
||
request['start'] = this.iso8601 (since);
|
||
}
|
||
const response = await this[method] (this.extend (request, params));
|
||
//
|
||
// spot markets
|
||
//
|
||
// [ { close: "0.02683401",
|
||
// high: "0.02683401",
|
||
// low: "0.02683401",
|
||
// open: "0.02683401",
|
||
// time: "2018-12-17T23:47:00.000Z",
|
||
// volume: "0" },
|
||
// ...
|
||
// { close: "0.02684545",
|
||
// high: "0.02685084",
|
||
// low: "0.02683312",
|
||
// open: "0.02683894",
|
||
// time: "2018-12-17T20:28:00.000Z",
|
||
// volume: "101.457222" } ]
|
||
//
|
||
// futures
|
||
//
|
||
// [ [ 1545090660000,
|
||
// 0.3171,
|
||
// 0.3174,
|
||
// 0.3171,
|
||
// 0.3173,
|
||
// 1648,
|
||
// 51930.38579450868 ],
|
||
// ...
|
||
// [ 1545072720000,
|
||
// 0.3159,
|
||
// 0.3161,
|
||
// 0.3144,
|
||
// 0.3149,
|
||
// 22886,
|
||
// 725179.26172331 ] ]
|
||
//
|
||
return this.parseOHLCVs (response, 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 };
|
||
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.safeFloat (balance, 'balance');
|
||
account['used'] = this.safeFloat (balance, 'hold');
|
||
account['free'] = this.safeFloat (balance, 'available');
|
||
result[code] = account;
|
||
}
|
||
return this.parseBalance (result);
|
||
}
|
||
|
||
parseMarginBalance (response) {
|
||
//
|
||
// [
|
||
// {
|
||
// "currency:BTC": {
|
||
// "available":"0",
|
||
// "balance":"0",
|
||
// "borrowed":"0",
|
||
// "can_withdraw":"0",
|
||
// "frozen":"0",
|
||
// "hold":"0",
|
||
// "holds":"0",
|
||
// "lending_fee":"0"
|
||
// },
|
||
// "currency:USDT": {
|
||
// "available":"100",
|
||
// "balance":"100",
|
||
// "borrowed":"0",
|
||
// "can_withdraw":"100",
|
||
// "frozen":"0",
|
||
// "hold":"0",
|
||
// "holds":"0",
|
||
// "lending_fee":"0"
|
||
// },
|
||
// "instrument_id":"BTC-USDT",
|
||
// "liquidation_price":"0",
|
||
// "product_id":"BTC-USDT",
|
||
// "risk_rate":""
|
||
// },
|
||
// ]
|
||
//
|
||
const result = { 'info': response };
|
||
for (let i = 0; i < response.length; i++) {
|
||
const balance = response[i];
|
||
const marketId = this.safeString (balance, 'instrument_id');
|
||
const market = this.safeValue (this.markets_by_id, marketId);
|
||
let symbol = undefined;
|
||
if (market === undefined) {
|
||
const [ baseId, quoteId ] = marketId.split ('-');
|
||
const base = this.safeCurrencyCode (baseId);
|
||
const quote = this.safeCurrencyCode (quoteId);
|
||
symbol = base + '/' + quote;
|
||
} else {
|
||
symbol = market['symbol'];
|
||
}
|
||
const omittedBalance = this.omit (balance, [
|
||
'instrument_id',
|
||
'liquidation_price',
|
||
'product_id',
|
||
'risk_rate',
|
||
'margin_ratio',
|
||
'maint_margin_ratio',
|
||
'tiers',
|
||
]);
|
||
const keys = Object.keys (omittedBalance);
|
||
const accounts = {};
|
||
for (let k = 0; k < keys.length; k++) {
|
||
const key = keys[k];
|
||
const marketBalance = balance[key];
|
||
if (key.indexOf (':') >= 0) {
|
||
const parts = key.split (':');
|
||
const currencyId = parts[1];
|
||
const code = this.safeCurrencyCode (currencyId);
|
||
const account = this.account ();
|
||
account['total'] = this.safeFloat (marketBalance, 'balance');
|
||
account['used'] = this.safeFloat (marketBalance, 'hold');
|
||
account['free'] = this.safeFloat (marketBalance, 'available');
|
||
accounts[code] = account;
|
||
} else {
|
||
throw new NotSupported (this.id + ' margin balance response format has changed!');
|
||
}
|
||
}
|
||
result[symbol] = this.parseBalance (accounts);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
parseFuturesBalance (response) {
|
||
//
|
||
// {
|
||
// "info":{
|
||
// "eos":{
|
||
// "auto_margin":"0",
|
||
// "contracts": [
|
||
// {
|
||
// "available_qty":"40.37069445",
|
||
// "fixed_balance":"0",
|
||
// "instrument_id":"EOS-USD-190329",
|
||
// "margin_for_unfilled":"0",
|
||
// "margin_frozen":"0",
|
||
// "realized_pnl":"0",
|
||
// "unrealized_pnl":"0"
|
||
// },
|
||
// {
|
||
// "available_qty":"40.37069445",
|
||
// "fixed_balance":"14.54895721",
|
||
// "instrument_id":"EOS-USD-190628",
|
||
// "margin_for_unfilled":"0",
|
||
// "margin_frozen":"10.64042157",
|
||
// "realized_pnl":"-3.90853564",
|
||
// "unrealized_pnl":"-0.259"
|
||
// },
|
||
// ],
|
||
// "equity":"50.75220665",
|
||
// "margin_mode":"fixed",
|
||
// "total_avail_balance":"40.37069445"
|
||
// },
|
||
// }
|
||
// }
|
||
//
|
||
// their root field name is "info", so our info will contain their info
|
||
const result = { 'info': response };
|
||
const info = this.safeValue (response, 'info', {});
|
||
const ids = Object.keys (info);
|
||
for (let i = 0; i < ids.length; i++) {
|
||
const id = ids[i];
|
||
const code = this.safeCurrencyCode (id);
|
||
const balance = this.safeValue (info, id, {});
|
||
const account = this.account ();
|
||
// it may be incorrect to use total, free and used for swap accounts
|
||
account['total'] = this.safeFloat (balance, 'equity');
|
||
account['free'] = this.safeFloat (balance, 'total_avail_balance');
|
||
result[code] = account;
|
||
}
|
||
return this.parseBalance (result);
|
||
}
|
||
|
||
parseSwapBalance (response) {
|
||
//
|
||
// {
|
||
// "info": [
|
||
// {
|
||
// "equity":"3.0139",
|
||
// "fixed_balance":"0.0000",
|
||
// "instrument_id":"EOS-USD-SWAP",
|
||
// "margin":"0.5523",
|
||
// "margin_frozen":"0.0000",
|
||
// "margin_mode":"crossed",
|
||
// "margin_ratio":"1.0913",
|
||
// "realized_pnl":"-0.0006",
|
||
// "timestamp":"2019-03-25T03:46:10.336Z",
|
||
// "total_avail_balance":"3.0000",
|
||
// "unrealized_pnl":"0.0145"
|
||
// }
|
||
// ]
|
||
// }
|
||
//
|
||
// their root field name is "info", so our info will contain their info
|
||
const result = { 'info': response };
|
||
const info = this.safeValue (response, 'info', []);
|
||
for (let i = 0; i < info.length; i++) {
|
||
const balance = info[i];
|
||
const marketId = this.safeString (balance, 'instrument_id');
|
||
let symbol = marketId;
|
||
if (marketId in this.markets_by_id) {
|
||
symbol = this.markets_by_id[marketId]['symbol'];
|
||
}
|
||
const account = this.account ();
|
||
// it may be incorrect to use total, free and used for swap accounts
|
||
account['total'] = this.safeFloat (balance, 'equity');
|
||
account['free'] = this.safeFloat (balance, 'total_avail_balance');
|
||
result[symbol] = account;
|
||
}
|
||
return this.parseBalance (result);
|
||
}
|
||
|
||
async fetchBalance (params = {}) {
|
||
await this.loadMarkets ();
|
||
const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType');
|
||
const type = this.safeString (params, 'type', defaultType);
|
||
if (type === undefined) {
|
||
throw new ArgumentsRequired (this.id + " fetchBalance requires a type parameter (one of 'account', 'spot', 'margin', 'futures', 'swap')");
|
||
}
|
||
const suffix = (type === 'account') ? 'Wallet' : 'Accounts';
|
||
const method = type + 'Get' + suffix;
|
||
const query = this.omit (params, 'type');
|
||
const response = await this[method] (query);
|
||
//
|
||
// 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"
|
||
// }
|
||
// ]
|
||
//
|
||
// margin
|
||
//
|
||
// [
|
||
// {
|
||
// "currency:BTC": {
|
||
// "available":"0",
|
||
// "balance":"0",
|
||
// "borrowed":"0",
|
||
// "can_withdraw":"0",
|
||
// "frozen":"0",
|
||
// "hold":"0",
|
||
// "holds":"0",
|
||
// "lending_fee":"0"
|
||
// },
|
||
// "currency:USDT": {
|
||
// "available":"100",
|
||
// "balance":"100",
|
||
// "borrowed":"0",
|
||
// "can_withdraw":"100",
|
||
// "frozen":"0",
|
||
// "hold":"0",
|
||
// "holds":"0",
|
||
// "lending_fee":"0"
|
||
// },
|
||
// "instrument_id":"BTC-USDT",
|
||
// "liquidation_price":"0",
|
||
// "product_id":"BTC-USDT",
|
||
// "risk_rate":""
|
||
// },
|
||
// ]
|
||
//
|
||
// futures
|
||
//
|
||
// {
|
||
// "info":{
|
||
// "eos":{
|
||
// "auto_margin":"0",
|
||
// "contracts": [
|
||
// {
|
||
// "available_qty":"40.37069445",
|
||
// "fixed_balance":"0",
|
||
// "instrument_id":"EOS-USD-190329",
|
||
// "margin_for_unfilled":"0",
|
||
// "margin_frozen":"0",
|
||
// "realized_pnl":"0",
|
||
// "unrealized_pnl":"0"
|
||
// },
|
||
// {
|
||
// "available_qty":"40.37069445",
|
||
// "fixed_balance":"14.54895721",
|
||
// "instrument_id":"EOS-USD-190628",
|
||
// "margin_for_unfilled":"0",
|
||
// "margin_frozen":"10.64042157",
|
||
// "realized_pnl":"-3.90853564",
|
||
// "unrealized_pnl":"-0.259"
|
||
// },
|
||
// ],
|
||
// "equity":"50.75220665",
|
||
// "margin_mode":"fixed",
|
||
// "total_avail_balance":"40.37069445"
|
||
// },
|
||
// }
|
||
// }
|
||
//
|
||
// swap
|
||
//
|
||
// {
|
||
// "info": [
|
||
// {
|
||
// "equity":"3.0139",
|
||
// "fixed_balance":"0.0000",
|
||
// "instrument_id":"EOS-USD-SWAP",
|
||
// "margin":"0.5523",
|
||
// "margin_frozen":"0.0000",
|
||
// "margin_mode":"crossed",
|
||
// "margin_ratio":"1.0913",
|
||
// "realized_pnl":"-0.0006",
|
||
// "timestamp":"2019-03-25T03:46:10.336Z",
|
||
// "total_avail_balance":"3.0000",
|
||
// "unrealized_pnl":"0.0145"
|
||
// }
|
||
// ]
|
||
// }
|
||
//
|
||
if ((type === 'account') || (type === 'spot')) {
|
||
return this.parseAccountBalance (response);
|
||
} else if (type === 'margin') {
|
||
return this.parseMarginBalance (response);
|
||
} else if (type === 'futures') {
|
||
return this.parseFuturesBalance (response);
|
||
} else if (type === 'swap') {
|
||
return this.parseSwapBalance (response);
|
||
}
|
||
throw new NotSupported (this.id + " fetchBalance does not support the '" + type + "' type (the type must be one of 'account', 'spot', 'margin', 'futures', 'swap')");
|
||
}
|
||
|
||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||
await this.loadMarkets ();
|
||
const market = this.market (symbol);
|
||
let request = {
|
||
'instrument_id': market['id'],
|
||
// 'client_oid': 'abcdef1234567890', // [a-z0-9]{1,32}
|
||
// 'order_type': '0', // 0: Normal limit order (Unfilled and 0 represent normal limit order) 1: Post only 2: Fill Or Kill 3: Immediatel Or Cancel
|
||
};
|
||
let method = undefined;
|
||
if (market['futures'] || market['swap']) {
|
||
const size = market['futures'] ? this.numberToString (amount) : this.amountToPrecision (symbol, amount);
|
||
request = this.extend (request, {
|
||
'type': type, // 1:open long 2:open short 3:close long 4:close short for futures
|
||
'size': size,
|
||
'price': this.priceToPrecision (symbol, price),
|
||
// 'match_price': '0', // Order at best counter party price? (0:no 1:yes). The default is 0. If it is set as 1, the price parameter will be ignored. When posting orders at best bid price, order_type can only be 0 (regular order).
|
||
});
|
||
if (market['futures']) {
|
||
request['leverage'] = '10'; // or '20'
|
||
}
|
||
method = market['type'] + 'PostOrder';
|
||
} else {
|
||
const marginTrading = this.safeString (params, 'margin_trading', '1'); // 1 = spot, 2 = margin
|
||
request = this.extend (request, {
|
||
'side': side,
|
||
'type': type, // limit/market
|
||
'margin_trading': marginTrading, // 1 = spot, 2 = margin
|
||
});
|
||
if (type === 'limit') {
|
||
request['price'] = this.priceToPrecision (symbol, price);
|
||
request['size'] = this.amountToPrecision (symbol, amount);
|
||
} else if (type === 'market') {
|
||
// for market buy it requires the amount of quote currency to spend
|
||
if (side === 'buy') {
|
||
let notional = this.safeFloat (params, 'notional');
|
||
const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true);
|
||
if (createMarketBuyOrderRequiresPrice) {
|
||
if (price !== undefined) {
|
||
if (notional === undefined) {
|
||
notional = amount * price;
|
||
}
|
||
} else if (notional === undefined) {
|
||
throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'notional' extra parameter (the exchange-specific behaviour)");
|
||
}
|
||
}
|
||
request['notional'] = this.costToPrecision (symbol, notional);
|
||
} else {
|
||
request['size'] = this.amountToPrecision (symbol, amount);
|
||
}
|
||
}
|
||
method = (marginTrading === '2') ? 'marginPostOrders' : 'spotPostOrders';
|
||
}
|
||
const response = await this[method] (this.extend (request, params));
|
||
//
|
||
// {
|
||
// "client_oid":"oktspot79",
|
||
// "error_code":"",
|
||
// "error_message":"",
|
||
// "order_id":"2510789768709120",
|
||
// "result":true
|
||
// }
|
||
//
|
||
const timestamp = this.milliseconds ();
|
||
const id = this.safeString (response, 'order_id');
|
||
return {
|
||
'info': response,
|
||
'id': id,
|
||
'timestamp': timestamp,
|
||
'datetime': this.iso8601 (timestamp),
|
||
'lastTradeTimestamp': undefined,
|
||
'status': undefined,
|
||
'symbol': symbol,
|
||
'type': type,
|
||
'side': side,
|
||
'price': price,
|
||
'amount': amount,
|
||
'filled': undefined,
|
||
'remaining': undefined,
|
||
'cost': undefined,
|
||
'trades': undefined,
|
||
'fee': undefined,
|
||
};
|
||
}
|
||
|
||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||
if (symbol === undefined) {
|
||
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
|
||
}
|
||
await this.loadMarkets ();
|
||
const market = this.market (symbol);
|
||
const type = market['type'];
|
||
let method = type + 'PostCancelOrder';
|
||
const request = {
|
||
'instrument_id': market['id'],
|
||
};
|
||
if (market['futures'] || market['swap']) {
|
||
method += 'InstrumentId';
|
||
} else {
|
||
method += 's';
|
||
}
|
||
const clientOid = this.safeString (params, 'client_oid');
|
||
if (clientOid !== undefined) {
|
||
method += 'ClientOid';
|
||
request['client_oid'] = clientOid;
|
||
} else {
|
||
method += 'OrderId';
|
||
request['order_id'] = id;
|
||
}
|
||
const query = this.omit (params, 'type');
|
||
const response = await this[method] (this.extend (request, query));
|
||
const result = ('result' in response) ? response : this.safeValue (response, market['id'], {});
|
||
//
|
||
// spot, margin
|
||
//
|
||
// {
|
||
// "btc-usdt": [
|
||
// {
|
||
// "result":true,
|
||
// "client_oid":"a123",
|
||
// "order_id": "2510832677225473"
|
||
// }
|
||
// ]
|
||
// }
|
||
//
|
||
// futures, swap
|
||
//
|
||
// {
|
||
// "result": true,
|
||
// "client_oid": "oktfuture10", // missing if requested by order_id
|
||
// "order_id": "2517535534836736",
|
||
// "instrument_id": "EOS-USD-190628"
|
||
// }
|
||
//
|
||
return this.parseOrder (result, market);
|
||
}
|
||
|
||
parseOrderStatus (status) {
|
||
const statuses = {
|
||
'-2': 'failed',
|
||
'-1': 'canceled',
|
||
'0': 'open',
|
||
'1': 'open',
|
||
'2': 'closed',
|
||
'3': 'open',
|
||
'4': 'canceled',
|
||
};
|
||
return this.safeString (statuses, status, status);
|
||
}
|
||
|
||
parseOrderSide (side) {
|
||
const sides = {
|
||
'1': 'buy', // open long
|
||
'2': 'sell', // open short
|
||
'3': 'sell', // close long
|
||
'4': 'buy', // close short
|
||
};
|
||
return this.safeString (sides, side, side);
|
||
}
|
||
|
||
parseOrder (order, market = undefined) {
|
||
//
|
||
// createOrder
|
||
//
|
||
// {
|
||
// "client_oid":"oktspot79",
|
||
// "error_code":"",
|
||
// "error_message":"",
|
||
// "order_id":"2510789768709120",
|
||
// "result":true
|
||
// }
|
||
//
|
||
// cancelOrder
|
||
//
|
||
// {
|
||
// "result": true,
|
||
// "client_oid": "oktfuture10", // missing if requested by order_id
|
||
// "order_id": "2517535534836736",
|
||
// // instrument_id is missing for spot/margin orders
|
||
// // available in futures and swap orders only
|
||
// "instrument_id": "EOS-USD-190628",
|
||
// }
|
||
//
|
||
// fetchOrder, fetchOrdersByState, fetchOpenOrders, fetchClosedOrders
|
||
//
|
||
// // spot and margin orders
|
||
//
|
||
// {
|
||
// "client_oid":"oktspot76",
|
||
// "created_at":"2019-03-18T07:26:49.000Z",
|
||
// "filled_notional":"3.9734",
|
||
// "filled_size":"0.001", // filled_qty in futures and swap orders
|
||
// "funds":"", // this is most likely the same as notional
|
||
// "instrument_id":"BTC-USDT",
|
||
// "notional":"",
|
||
// "order_id":"2500723297813504",
|
||
// "order_type":"0",
|
||
// "price":"4013",
|
||
// "product_id":"BTC-USDT", // missing in futures and swap orders
|
||
// "side":"buy",
|
||
// "size":"0.001",
|
||
// "status":"filled",
|
||
// "state": "2",
|
||
// "timestamp":"2019-03-18T07:26:49.000Z",
|
||
// "type":"limit"
|
||
// }
|
||
//
|
||
// // futures and swap orders
|
||
//
|
||
// {
|
||
// "instrument_id":"EOS-USD-190628",
|
||
// "size":"10",
|
||
// "timestamp":"2019-03-20T10:04:55.000Z",
|
||
// "filled_qty":"10", // filled_size in spot and margin orders
|
||
// "fee":"-0.00841043",
|
||
// "order_id":"2512669605501952",
|
||
// "price":"3.668",
|
||
// "price_avg":"3.567", // missing in spot and margin orders
|
||
// "status":"2",
|
||
// "state": "2",
|
||
// "type":"4",
|
||
// "contract_val":"10",
|
||
// "leverage":"10", // missing in swap, spot and margin orders
|
||
// "client_oid":"",
|
||
// "pnl":"1.09510794", // missing in swap, spo and margin orders
|
||
// "order_type":"0"
|
||
// }
|
||
//
|
||
const id = this.safeString (order, 'order_id');
|
||
const timestamp = this.parse8601 (this.safeString (order, 'timestamp'));
|
||
let side = this.safeString (order, 'side');
|
||
let type = this.safeString (order, 'type');
|
||
if ((side !== 'buy') && (side !== 'sell')) {
|
||
side = this.parseOrderSide (type);
|
||
}
|
||
if ((type !== 'limit') && (type !== 'market')) {
|
||
if ('pnl' in order) {
|
||
type = 'futures';
|
||
} else {
|
||
type = 'swap';
|
||
}
|
||
}
|
||
let symbol = undefined;
|
||
const marketId = this.safeString (order, 'instrument_id');
|
||
if (marketId in this.markets_by_id) {
|
||
market = this.markets_by_id[marketId];
|
||
symbol = market['symbol'];
|
||
} else {
|
||
symbol = marketId;
|
||
}
|
||
if (market !== undefined) {
|
||
if (symbol === undefined) {
|
||
symbol = market['symbol'];
|
||
}
|
||
}
|
||
let amount = this.safeFloat (order, 'size');
|
||
const filled = this.safeFloat2 (order, 'filled_size', 'filled_qty');
|
||
let remaining = undefined;
|
||
if (amount !== undefined) {
|
||
if (filled !== undefined) {
|
||
amount = Math.max (amount, filled);
|
||
remaining = Math.max (0, amount - filled);
|
||
}
|
||
}
|
||
if (type === 'market') {
|
||
remaining = 0;
|
||
}
|
||
let cost = this.safeFloat2 (order, 'filled_notional', 'funds');
|
||
const price = this.safeFloat (order, 'price');
|
||
let average = this.safeFloat (order, 'price_avg');
|
||
if (cost === undefined) {
|
||
if (filled !== undefined && average !== undefined) {
|
||
cost = average * filled;
|
||
}
|
||
} else {
|
||
if ((average === undefined) && (filled !== undefined) && (filled > 0)) {
|
||
average = cost / filled;
|
||
}
|
||
}
|
||
const status = this.parseOrderStatus (this.safeString (order, 'state'));
|
||
const feeCost = this.safeFloat (order, 'fee');
|
||
let fee = undefined;
|
||
if (feeCost !== undefined) {
|
||
const feeCurrency = undefined;
|
||
fee = {
|
||
'cost': feeCost,
|
||
'currency': feeCurrency,
|
||
};
|
||
}
|
||
return {
|
||
'info': order,
|
||
'id': id,
|
||
'timestamp': timestamp,
|
||
'datetime': this.iso8601 (timestamp),
|
||
'lastTradeTimestamp': undefined,
|
||
'symbol': symbol,
|
||
'type': type,
|
||
'side': side,
|
||
'price': price,
|
||
'average': average,
|
||
'cost': cost,
|
||
'amount': amount,
|
||
'filled': filled,
|
||
'remaining': remaining,
|
||
'status': status,
|
||
'fee': fee,
|
||
};
|
||
}
|
||
|
||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||
if (symbol === undefined) {
|
||
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
|
||
}
|
||
await this.loadMarkets ();
|
||
const market = this.market (symbol);
|
||
const defaultType = this.safeString2 (this.options, 'fetchOrder', 'defaultType', market['type']);
|
||
const type = this.safeString (params, 'type', defaultType);
|
||
if (type === undefined) {
|
||
throw new ArgumentsRequired (this.id + " fetchOrder requires a type parameter (one of 'spot', 'margin', 'futures', 'swap').");
|
||
}
|
||
const instrumentId = (market['futures'] || market['swap']) ? 'InstrumentId' : '';
|
||
let method = type + 'GetOrders' + instrumentId;
|
||
const request = {
|
||
'instrument_id': market['id'],
|
||
// 'client_oid': 'abcdef12345', // optional, [a-z0-9]{1,32}
|
||
// 'order_id': id,
|
||
};
|
||
const clientOid = this.safeString (params, 'client_oid');
|
||
if (clientOid !== undefined) {
|
||
method += 'ClientOid';
|
||
request['client_oid'] = clientOid;
|
||
} else {
|
||
method += 'OrderId';
|
||
request['order_id'] = id;
|
||
}
|
||
const query = this.omit (params, 'type');
|
||
const response = await this[method] (this.extend (request, query));
|
||
//
|
||
// spot, margin
|
||
//
|
||
// {
|
||
// "client_oid":"oktspot70",
|
||
// "created_at":"2019-03-15T02:52:56.000Z",
|
||
// "filled_notional":"3.8886",
|
||
// "filled_size":"0.001",
|
||
// "funds":"",
|
||
// "instrument_id":"BTC-USDT",
|
||
// "notional":"",
|
||
// "order_id":"2482659399697408",
|
||
// "order_type":"0",
|
||
// "price":"3927.3",
|
||
// "product_id":"BTC-USDT",
|
||
// "side":"buy",
|
||
// "size":"0.001",
|
||
// "status":"filled",
|
||
// "state": "2",
|
||
// "timestamp":"2019-03-15T02:52:56.000Z",
|
||
// "type":"limit"
|
||
// }
|
||
//
|
||
// futures, swap
|
||
//
|
||
// {
|
||
// "instrument_id":"EOS-USD-190628",
|
||
// "size":"10",
|
||
// "timestamp":"2019-03-20T02:46:38.000Z",
|
||
// "filled_qty":"10",
|
||
// "fee":"-0.0080819",
|
||
// "order_id":"2510946213248000",
|
||
// "price":"3.712",
|
||
// "price_avg":"3.712",
|
||
// "status":"2",
|
||
// "state": "2",
|
||
// "type":"2",
|
||
// "contract_val":"10",
|
||
// "leverage":"10",
|
||
// "client_oid":"", // missing in swap orders
|
||
// "pnl":"0", // missing in swap orders
|
||
// "order_type":"0"
|
||
// }
|
||
//
|
||
return this.parseOrder (response);
|
||
}
|
||
|
||
async fetchOrdersByState (state, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||
if (symbol === undefined) {
|
||
throw new ArgumentsRequired (this.id + ' fetchOrdersByState requires a symbol argument');
|
||
}
|
||
await this.loadMarkets ();
|
||
const market = this.market (symbol);
|
||
const type = market['type'];
|
||
const request = {
|
||
'instrument_id': market['id'],
|
||
// '-2': failed,
|
||
// '-1': cancelled,
|
||
// '0': open ,
|
||
// '1': partially filled,
|
||
// '2': fully filled,
|
||
// '3': submitting,
|
||
// '4': cancelling,
|
||
// '6': incomplete(open+partially filled),
|
||
// '7': complete(cancelled+fully filled),
|
||
'state': state,
|
||
};
|
||
let method = type + 'GetOrders';
|
||
if (market['futures'] || market['swap']) {
|
||
method += 'InstrumentId';
|
||
}
|
||
const query = this.omit (params, 'type');
|
||
const response = await this[method] (this.extend (request, query));
|
||
//
|
||
// spot, margin
|
||
//
|
||
// [
|
||
// // in fact, this documented API response does not correspond
|
||
// // to their actual API response for spot markets
|
||
// // OKEX v3 API returns a plain array of orders (see below)
|
||
// [
|
||
// {
|
||
// "client_oid":"oktspot76",
|
||
// "created_at":"2019-03-18T07:26:49.000Z",
|
||
// "filled_notional":"3.9734",
|
||
// "filled_size":"0.001",
|
||
// "funds":"",
|
||
// "instrument_id":"BTC-USDT",
|
||
// "notional":"",
|
||
// "order_id":"2500723297813504",
|
||
// "order_type":"0",
|
||
// "price":"4013",
|
||
// "product_id":"BTC-USDT",
|
||
// "side":"buy",
|
||
// "size":"0.001",
|
||
// "status":"filled",
|
||
// "state": "2",
|
||
// "timestamp":"2019-03-18T07:26:49.000Z",
|
||
// "type":"limit"
|
||
// },
|
||
// ],
|
||
// {
|
||
// "before":"2500723297813504",
|
||
// "after":"2500650881647616"
|
||
// }
|
||
// ]
|
||
//
|
||
// futures, swap
|
||
//
|
||
// {
|
||
// "result":true, // missing in swap orders
|
||
// "order_info": [
|
||
// {
|
||
// "instrument_id":"EOS-USD-190628",
|
||
// "size":"10",
|
||
// "timestamp":"2019-03-20T10:04:55.000Z",
|
||
// "filled_qty":"10",
|
||
// "fee":"-0.00841043",
|
||
// "order_id":"2512669605501952",
|
||
// "price":"3.668",
|
||
// "price_avg":"3.567",
|
||
// "status":"2",
|
||
// "state": "2",
|
||
// "type":"4",
|
||
// "contract_val":"10",
|
||
// "leverage":"10", // missing in swap orders
|
||
// "client_oid":"",
|
||
// "pnl":"1.09510794", // missing in swap orders
|
||
// "order_type":"0"
|
||
// },
|
||
// ]
|
||
// }
|
||
//
|
||
let orders = undefined;
|
||
if (market['type'] === 'swap' || market['type'] === 'futures') {
|
||
orders = this.safeValue (response, 'order_info', []);
|
||
} else {
|
||
orders = response;
|
||
const responseLength = response.length;
|
||
if (responseLength < 1) {
|
||
return [];
|
||
}
|
||
// in fact, this documented API response does not correspond
|
||
// to their actual API response for spot markets
|
||
// OKEX v3 API returns a plain array of orders
|
||
if (responseLength > 1) {
|
||
const before = this.safeValue (response[1], 'before');
|
||
if (before !== undefined) {
|
||
orders = response[0];
|
||
}
|
||
}
|
||
}
|
||
return this.parseOrders (orders, market, since, limit);
|
||
}
|
||
|
||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||
// '-2': failed,
|
||
// '-1': cancelled,
|
||
// '0': open ,
|
||
// '1': partially filled,
|
||
// '2': fully filled,
|
||
// '3': submitting,
|
||
// '4': cancelling,
|
||
// '6': incomplete(open+partially filled),
|
||
// '7': complete(cancelled+fully filled),
|
||
return await this.fetchOrdersByState ('6', symbol, since, limit, params);
|
||
}
|
||
|
||
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||
// '-2': failed,
|
||
// '-1': cancelled,
|
||
// '0': open ,
|
||
// '1': partially filled,
|
||
// '2': fully filled,
|
||
// '3': submitting,
|
||
// '4': cancelling,
|
||
// '6': incomplete(open+partially filled),
|
||
// '7': complete(cancelled+fully filled),
|
||
return await this.fetchOrdersByState ('7', symbol, since, limit, params);
|
||
}
|
||
|
||
parseDepositAddresses (addresses) {
|
||
const result = [];
|
||
for (let i = 0; i < addresses.length; i++) {
|
||
result.push (this.parseDepositAddress (addresses[i]));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
parseDepositAddress (depositAddress, currency = undefined) {
|
||
//
|
||
// {
|
||
// address: '0x696abb81974a8793352cbd33aadcf78eda3cfdfa',
|
||
// currency: 'eth'
|
||
// tag: 'abcde12345', // will be missing if the token does not require a deposit tag
|
||
// payment_id: 'abcde12345', // will not be returned if the token does not require a payment_id
|
||
// // can_deposit: 1, // 0 or 1, documented but missing
|
||
// // can_withdraw: 1, // 0 or 1, documented but missing
|
||
// }
|
||
//
|
||
const address = this.safeString (depositAddress, 'address');
|
||
let tag = this.safeString2 (depositAddress, 'tag', 'payment_id');
|
||
tag = this.safeString (depositAddress, 'memo', tag);
|
||
const currencyId = this.safeString (depositAddress, 'currency');
|
||
const code = this.safeCurrencyCode (currencyId);
|
||
this.checkAddress (address);
|
||
return {
|
||
'currency': code,
|
||
'address': address,
|
||
'tag': tag,
|
||
'info': depositAddress,
|
||
};
|
||
}
|
||
|
||
async fetchDepositAddress (code, params = {}) {
|
||
await this.loadMarkets ();
|
||
const currency = this.currency (code);
|
||
const request = {
|
||
'currency': currency['id'],
|
||
};
|
||
const response = await this.accountGetDepositAddress (this.extend (request, params));
|
||
//
|
||
// [
|
||
// {
|
||
// address: '0x696abb81974a8793352cbd33aadcf78eda3cfdfa',
|
||
// currency: 'eth'
|
||
// }
|
||
// ]
|
||
//
|
||
const addresses = this.parseDepositAddresses (response);
|
||
const numAddresses = addresses.length;
|
||
if (numAddresses < 1) {
|
||
throw new InvalidAddress (this.id + ' fetchDepositAddress cannot return nonexistent addresses, you should create withdrawal addresses with the exchange website first');
|
||
}
|
||
return addresses[0];
|
||
}
|
||
|
||
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
||
this.checkAddress (address);
|
||
await this.loadMarkets ();
|
||
const currency = this.currency (code);
|
||
if (tag) {
|
||
address = address + ':' + tag;
|
||
}
|
||
const fee = this.safeString (params, 'fee');
|
||
if (fee === undefined) {
|
||
throw new ExchangeError (this.id + " withdraw() requires a `fee` string parameter, network transaction fee must be ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set '0'. Withdrawing to external digital asset address requires network transaction fee.");
|
||
}
|
||
const request = {
|
||
'currency': currency['id'],
|
||
'to_address': address,
|
||
'destination': '4', // 2 = OKCoin International, 3 = OKEx 4 = others
|
||
'amount': this.numberToString (amount),
|
||
'fee': fee, // String. Network transaction fee ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set as 0. Withdrawal to external digital asset address requires network transaction fee.
|
||
};
|
||
if ('password' in params) {
|
||
request['trade_pwd'] = params['password'];
|
||
} else if ('trade_pwd' in params) {
|
||
request['trade_pwd'] = params['trade_pwd'];
|
||
} else if (this.password) {
|
||
request['trade_pwd'] = this.password;
|
||
}
|
||
const query = this.omit (params, [ 'fee', 'password', 'trade_pwd' ]);
|
||
if (!('trade_pwd' in request)) {
|
||
throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a password / trade_pwd parameter');
|
||
}
|
||
const response = await this.accountPostWithdrawal (this.extend (request, query));
|
||
//
|
||
// {
|
||
// "amount":"0.1",
|
||
// "withdrawal_id":"67485",
|
||
// "currency":"btc",
|
||
// "result":true
|
||
// }
|
||
//
|
||
return {
|
||
'info': response,
|
||
'id': this.safeString (response, 'withdrawal_id'),
|
||
};
|
||
}
|
||
|
||
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
|
||
await this.loadMarkets ();
|
||
const request = {};
|
||
let method = 'accountGetDepositHistory';
|
||
let currency = undefined;
|
||
if (code !== undefined) {
|
||
currency = this.currency (code);
|
||
request['code'] = currency['code'];
|
||
method += 'Currency';
|
||
}
|
||
const response = await this[method] (this.extend (request, params));
|
||
return this.parseTransactions (response, currency, since, limit, params);
|
||
}
|
||
|
||
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
|
||
await this.loadMarkets ();
|
||
const request = {};
|
||
let method = 'accountGetWithdrawalHistory';
|
||
let currency = undefined;
|
||
if (code !== undefined) {
|
||
currency = this.currency (code);
|
||
request['code'] = currency['code'];
|
||
method += 'Currency';
|
||
}
|
||
const response = await this[method] (this.extend (request, params));
|
||
return this.parseTransactions (response, 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': 'pending',
|
||
'-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
|
||
//
|
||
// {
|
||
// "amount":"0.1",
|
||
// "withdrawal_id":"67485",
|
||
// "currency":"btc",
|
||
// "result":true
|
||
// }
|
||
//
|
||
// fetchWithdrawals
|
||
//
|
||
// {
|
||
// amount: "4.72100000",
|
||
// withdrawal_id: "1729116",
|
||
// fee: "0.01000000eth",
|
||
// txid: "0xf653125bbf090bcfe4b5e8e7b8f586a9d87aa7de94598702758c0802b…",
|
||
// currency: "ETH",
|
||
// from: "7147338839",
|
||
// to: "0x26a3CB49578F07000575405a57888681249c35Fd",
|
||
// timestamp: "2018-08-17T07:03:42.000Z",
|
||
// status: "2"
|
||
// }
|
||
//
|
||
// fetchDeposits
|
||
//
|
||
// {
|
||
// amount: "0.47847546",
|
||
// txid: "1723573_3_0_0_WALLET",
|
||
// currency: "BTC",
|
||
// to: "",
|
||
// timestamp: "2018-08-16T03:41:10.000Z",
|
||
// status: "2"
|
||
// }
|
||
//
|
||
let type = undefined;
|
||
let id = undefined;
|
||
let address = undefined;
|
||
const withdrawalId = this.safeString (transaction, 'withdrawal_id');
|
||
const addressFrom = this.safeString (transaction, 'from');
|
||
const addressTo = this.safeString (transaction, 'to');
|
||
if (withdrawalId !== undefined) {
|
||
type = 'withdrawal';
|
||
id = withdrawalId;
|
||
address = addressTo;
|
||
} else {
|
||
// the payment_id will appear on new deposits but appears to be removed from the response after 2 months
|
||
id = this.safeString (transaction, 'payment_id');
|
||
type = 'deposit';
|
||
address = addressTo;
|
||
}
|
||
const currencyId = this.safeString (transaction, 'currency');
|
||
const code = this.safeCurrencyCode (currencyId);
|
||
const amount = this.safeFloat (transaction, 'amount');
|
||
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
|
||
const txid = this.safeString (transaction, 'txid');
|
||
const timestamp = this.parse8601 (this.safeString (transaction, 'timestamp'));
|
||
let feeCost = undefined;
|
||
if (type === 'deposit') {
|
||
feeCost = 0;
|
||
} else {
|
||
if (currencyId !== undefined) {
|
||
const feeWithCurrencyId = this.safeString (transaction, 'fee');
|
||
if (feeWithCurrencyId !== undefined) {
|
||
// https://github.com/ccxt/ccxt/pull/5748
|
||
const lowercaseCurrencyId = currencyId.toLowerCase ();
|
||
const feeWithoutCurrencyId = feeWithCurrencyId.replace (lowercaseCurrencyId, '');
|
||
feeCost = parseFloat (feeWithoutCurrencyId);
|
||
}
|
||
}
|
||
}
|
||
// todo parse tags
|
||
return {
|
||
'info': transaction,
|
||
'id': id,
|
||
'currency': code,
|
||
'amount': amount,
|
||
'addressFrom': addressFrom,
|
||
'addressTo': addressTo,
|
||
'address': address,
|
||
'tagFrom': undefined,
|
||
'tagTo': undefined,
|
||
'tag': undefined,
|
||
'status': status,
|
||
'type': type,
|
||
'updated': undefined,
|
||
'txid': txid,
|
||
'timestamp': timestamp,
|
||
'datetime': this.iso8601 (timestamp),
|
||
'fee': {
|
||
'currency': code,
|
||
'cost': feeCost,
|
||
},
|
||
};
|
||
}
|
||
|
||
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||
// okex actually returns ledger entries instead of fills here, so each fill in the order
|
||
// is represented by two trades with opposite buy/sell sides, not one :\
|
||
// this aspect renders the 'fills' endpoint unusable for fetchOrderTrades
|
||
// until either OKEX fixes the API or we workaround this on our side somehow
|
||
if (symbol === undefined) {
|
||
throw new ArgumentsRequired (this.id + ' fetchOrderTrades requires a symbol argument');
|
||
}
|
||
await this.loadMarkets ();
|
||
const market = this.market (symbol);
|
||
if ((limit === undefined) || (limit > 100)) {
|
||
limit = 100;
|
||
}
|
||
const request = {
|
||
'instrument_id': market['id'],
|
||
'order_id': id,
|
||
// from: '1', // return the page after the specified page number
|
||
// to: '1', // return the page before the specified page number
|
||
'limit': limit, // optional, number of results per request, default = maximum = 100
|
||
};
|
||
const defaultType = this.safeString2 (this.options, 'fetchMyTrades', 'defaultType');
|
||
const type = this.safeString (params, 'type', defaultType);
|
||
const query = this.omit (params, 'type');
|
||
const method = type + 'GetFills';
|
||
const response = await this[method] (this.extend (request, query));
|
||
//
|
||
// spot trades, margin trades
|
||
//
|
||
// [
|
||
// {
|
||
// "created_at":"2019-09-20T07:15:24.000Z",
|
||
// "exec_type":"T",
|
||
// "fee":"0",
|
||
// "instrument_id":"ETH-USDT",
|
||
// "ledger_id":"7173486113",
|
||
// "liquidity":"T",
|
||
// "order_id":"3553868136523776",
|
||
// "price":"217.59",
|
||
// "product_id":"ETH-USDT",
|
||
// "side":"sell",
|
||
// "size":"0.04619899",
|
||
// "timestamp":"2019-09-20T07:15:24.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
|
||
// }
|
||
// ]
|
||
//
|
||
return this.parseTrades (response, market, since, limit);
|
||
}
|
||
|
||
async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) {
|
||
await this.loadMarkets ();
|
||
const defaultType = this.safeString2 (this.options, 'fetchLedger', 'defaultType');
|
||
const type = this.safeString (params, 'type', defaultType);
|
||
const query = this.omit (params, 'type');
|
||
const suffix = (type === 'account') ? '' : 'Accounts';
|
||
let argument = '';
|
||
const request = {
|
||
// 'from': 'id',
|
||
// 'to': 'id',
|
||
};
|
||
if (limit !== undefined) {
|
||
request['limit'] = limit;
|
||
}
|
||
let currency = undefined;
|
||
if ((type === 'spot') || (type === 'futures')) {
|
||
if (code === undefined) {
|
||
throw new ArgumentsRequired (this.id + " fetchLedger requires a currency code argument for '" + type + "' markets");
|
||
}
|
||
argument = 'Currency';
|
||
currency = this.currency (code);
|
||
request['currency'] = currency['id'];
|
||
} else if ((type === 'margin') || (type === 'swap')) {
|
||
if (code === undefined) {
|
||
throw new ArgumentsRequired (this.id + " fetchLedger requires a code argument (a market symbol) for '" + type + "' markets");
|
||
}
|
||
argument = 'InstrumentId';
|
||
const market = this.market (code); // we intentionally put a market inside here for the margin and swap ledgers
|
||
currency = this.currency (market['base']);
|
||
request['instrument_id'] = market['id'];
|
||
//
|
||
// if (type === 'margin') {
|
||
// //
|
||
// // 3. Borrow
|
||
// // 4. Repayment
|
||
// // 5. Interest
|
||
// // 7. Buy
|
||
// // 8. Sell
|
||
// // 9. From capital account
|
||
// // 10. From C2C
|
||
// // 11. From Futures
|
||
// // 12. From Spot
|
||
// // 13. From ETT
|
||
// // 14. To capital account
|
||
// // 15. To C2C
|
||
// // 16. To Spot
|
||
// // 17. To Futures
|
||
// // 18. To ETT
|
||
// // 19. Mandatory Repayment
|
||
// // 20. From Piggybank
|
||
// // 21. To Piggybank
|
||
// // 22. From Perpetual
|
||
// // 23. To Perpetual
|
||
// // 24. Liquidation Fee
|
||
// // 54. Clawback
|
||
// // 59. Airdrop Return.
|
||
// //
|
||
// request['type'] = 'number'; // All types will be returned if this filed is left blank
|
||
// }
|
||
//
|
||
} else if (type === 'account') {
|
||
if (code !== undefined) {
|
||
currency = this.currency (code);
|
||
request['currency'] = currency['id'];
|
||
}
|
||
//
|
||
// //
|
||
// // 1. deposit
|
||
// // 2. withdrawal
|
||
// // 13. cancel withdrawal
|
||
// // 18. into futures account
|
||
// // 19. out of futures account
|
||
// // 20. into sub account
|
||
// // 21. out of sub account
|
||
// // 28. claim
|
||
// // 29. into ETT account
|
||
// // 30. out of ETT account
|
||
// // 31. into C2C account
|
||
// // 32. out of C2C account
|
||
// // 33. into margin account
|
||
// // 34. out of margin account
|
||
// // 37. into spot account
|
||
// // 38. out of spot account
|
||
// //
|
||
// request['type'] = 'number';
|
||
//
|
||
} else {
|
||
throw new NotSupported (this.id + " fetchLedger does not support the '" + type + "' type (the type must be one of 'account', 'spot', 'margin', 'futures', 'swap')");
|
||
}
|
||
const method = type + 'Get' + suffix + argument + 'Ledger';
|
||
const response = await this[method] (this.extend (request, query));
|
||
//
|
||
// transfer funds transfer in/out
|
||
// trade funds moved as a result of a trade, spot and margin accounts only
|
||
// rebate fee rebate as per fee schedule, spot and margin accounts only
|
||
// match open long/open short/close long/close short (futures) or a change in the amount because of trades (swap)
|
||
// fee fee, futures only
|
||
// settlement settlement/clawback/settle long/settle short
|
||
// liquidation force close long/force close short/deliver close long/deliver close short
|
||
// funding funding fee, swap only
|
||
// margin a change in the amount after adjusting margin, swap only
|
||
//
|
||
// account
|
||
//
|
||
// [
|
||
// {
|
||
// "amount":0.00051843,
|
||
// "balance":0.00100941,
|
||
// "currency":"BTC",
|
||
// "fee":0,
|
||
// "ledger_id":8987285,
|
||
// "timestamp":"2018-10-12T11:01:14.000Z",
|
||
// "typename":"Get from activity"
|
||
// }
|
||
// ]
|
||
//
|
||
// spot
|
||
//
|
||
// [
|
||
// {
|
||
// "timestamp":"2019-03-18T07:08:25.000Z",
|
||
// "ledger_id":"3995334780",
|
||
// "created_at":"2019-03-18T07:08:25.000Z",
|
||
// "currency":"BTC",
|
||
// "amount":"0.0009985",
|
||
// "balance":"0.0029955",
|
||
// "type":"trade",
|
||
// "details":{
|
||
// "instrument_id":"BTC-USDT",
|
||
// "order_id":"2500650881647616",
|
||
// "product_id":"BTC-USDT"
|
||
// }
|
||
// }
|
||
// ]
|
||
//
|
||
// margin
|
||
//
|
||
// [
|
||
// [
|
||
// {
|
||
// "created_at":"2019-03-20T03:45:05.000Z",
|
||
// "ledger_id":"78918186",
|
||
// "timestamp":"2019-03-20T03:45:05.000Z",
|
||
// "currency":"EOS",
|
||
// "amount":"0", // ?
|
||
// "balance":"0.59957711",
|
||
// "type":"transfer",
|
||
// "details":{
|
||
// "instrument_id":"EOS-USDT",
|
||
// "order_id":"787057",
|
||
// "product_id":"EOS-USDT"
|
||
// }
|
||
// }
|
||
// ],
|
||
// {
|
||
// "before":"78965766",
|
||
// "after":"78918186"
|
||
// }
|
||
// ]
|
||
//
|
||
// futures
|
||
//
|
||
// [
|
||
// {
|
||
// "ledger_id":"2508090544914461",
|
||
// "timestamp":"2019-03-19T14:40:24.000Z",
|
||
// "amount":"-0.00529521",
|
||
// "balance":"0",
|
||
// "currency":"EOS",
|
||
// "type":"fee",
|
||
// "details":{
|
||
// "order_id":"2506982456445952",
|
||
// "instrument_id":"EOS-USD-190628"
|
||
// }
|
||
// }
|
||
// ]
|
||
//
|
||
// swap
|
||
//
|
||
// [
|
||
// {
|
||
// "amount":"0.004742",
|
||
// "fee":"-0.000551",
|
||
// "type":"match",
|
||
// "instrument_id":"EOS-USD-SWAP",
|
||
// "ledger_id":"197429674941902848",
|
||
// "timestamp":"2019-03-25T05:56:31.286Z"
|
||
// },
|
||
// ]
|
||
//
|
||
const isArray = Array.isArray (response[0]);
|
||
const isMargin = (type === 'margin');
|
||
const entries = (isMargin && isArray) ? response[0] : response;
|
||
return this.parseLedger (entries, currency, since, limit);
|
||
}
|
||
|
||
parseLedgerEntryType (type) {
|
||
const types = {
|
||
'transfer': 'transfer', // // funds transfer in/out
|
||
'trade': 'trade', // funds moved as a result of a trade, spot and margin accounts only
|
||
'rebate': 'rebate', // fee rebate as per fee schedule, spot and margin accounts only
|
||
'match': 'trade', // open long/open short/close long/close short (futures) or a change in the amount because of trades (swap)
|
||
'fee': 'fee', // fee, futures only
|
||
'settlement': 'trade', // settlement/clawback/settle long/settle short
|
||
'liquidation': 'trade', // force close long/force close short/deliver close long/deliver close short
|
||
'funding': 'fee', // funding fee, swap only
|
||
'margin': 'margin', // a change in the amount after adjusting margin, swap only
|
||
};
|
||
return this.safeString (types, type, type);
|
||
}
|
||
|
||
parseLedgerEntry (item, currency = undefined) {
|
||
//
|
||
//
|
||
// account
|
||
//
|
||
// {
|
||
// "amount":0.00051843,
|
||
// "balance":0.00100941,
|
||
// "currency":"BTC",
|
||
// "fee":0,
|
||
// "ledger_id":8987285,
|
||
// "timestamp":"2018-10-12T11:01:14.000Z",
|
||
// "typename":"Get from activity"
|
||
// }
|
||
//
|
||
// spot
|
||
//
|
||
// {
|
||
// "timestamp":"2019-03-18T07:08:25.000Z",
|
||
// "ledger_id":"3995334780",
|
||
// "created_at":"2019-03-18T07:08:25.000Z",
|
||
// "currency":"BTC",
|
||
// "amount":"0.0009985",
|
||
// "balance":"0.0029955",
|
||
// "type":"trade",
|
||
// "details":{
|
||
// "instrument_id":"BTC-USDT",
|
||
// "order_id":"2500650881647616",
|
||
// "product_id":"BTC-USDT"
|
||
// }
|
||
// }
|
||
//
|
||
// margin
|
||
//
|
||
// {
|
||
// "created_at":"2019-03-20T03:45:05.000Z",
|
||
// "ledger_id":"78918186",
|
||
// "timestamp":"2019-03-20T03:45:05.000Z",
|
||
// "currency":"EOS",
|
||
// "amount":"0", // ?
|
||
// "balance":"0.59957711",
|
||
// "type":"transfer",
|
||
// "details":{
|
||
// "instrument_id":"EOS-USDT",
|
||
// "order_id":"787057",
|
||
// "product_id":"EOS-USDT"
|
||
// }
|
||
// }
|
||
//
|
||
// futures
|
||
//
|
||
// {
|
||
// "ledger_id":"2508090544914461",
|
||
// "timestamp":"2019-03-19T14:40:24.000Z",
|
||
// "amount":"-0.00529521",
|
||
// "balance":"0",
|
||
// "currency":"EOS",
|
||
// "type":"fee",
|
||
// "details":{
|
||
// "order_id":"2506982456445952",
|
||
// "instrument_id":"EOS-USD-190628"
|
||
// }
|
||
// }
|
||
//
|
||
// swap
|
||
//
|
||
// {
|
||
// "amount":"0.004742",
|
||
// "fee":"-0.000551",
|
||
// "type":"match",
|
||
// "instrument_id":"EOS-USD-SWAP",
|
||
// "ledger_id":"197429674941902848",
|
||
// "timestamp":"2019-03-25T05:56:31.286Z"
|
||
// },
|
||
//
|
||
const id = this.safeString (item, 'ledger_id');
|
||
const account = undefined;
|
||
const details = this.safeValue (item, 'details', {});
|
||
const referenceId = this.safeString (details, 'order_id');
|
||
const referenceAccount = undefined;
|
||
const type = this.parseLedgerEntryType (this.safeString (item, 'type'));
|
||
const code = this.safeCurrencyCode (this.safeString (item, 'currency'), currency);
|
||
const amount = this.safeFloat (item, 'amount');
|
||
const timestamp = this.parse8601 (this.safeString (item, 'timestamp'));
|
||
const fee = {
|
||
'cost': this.safeFloat (item, 'fee'),
|
||
'currency': code,
|
||
};
|
||
const before = undefined;
|
||
const after = this.safeFloat (item, 'balance');
|
||
const status = 'ok';
|
||
return {
|
||
'info': item,
|
||
'id': id,
|
||
'account': account,
|
||
'referenceId': referenceId,
|
||
'referenceAccount': referenceAccount,
|
||
'type': type,
|
||
'currency': code,
|
||
'amount': amount,
|
||
'before': before, // balance before
|
||
'after': after, // balance after
|
||
'status': status,
|
||
'timestamp': timestamp,
|
||
'datetime': this.iso8601 (timestamp),
|
||
'fee': fee,
|
||
};
|
||
}
|
||
|
||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||
const isArray = Array.isArray (params);
|
||
let request = '/api/' + api + '/' + this.version + '/';
|
||
request += isArray ? path : this.implodeParams (path, params);
|
||
const query = isArray ? params : this.omit (params, this.extractParams (path));
|
||
let url = this.urls['api'] + request;
|
||
const type = this.getPathAuthenticationType (path);
|
||
if (type === 'public') {
|
||
if (Object.keys (query).length) {
|
||
url += '?' + this.urlencode (query);
|
||
}
|
||
} else if (type === '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', 'base64');
|
||
headers['OK-ACCESS-SIGN'] = this.decode (signature);
|
||
}
|
||
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
||
}
|
||
|
||
getPathAuthenticationType (path) {
|
||
const auth = this.safeValue (this.options, 'auth', {});
|
||
const key = this.findBroadlyMatchedKey (auth, path);
|
||
return this.safeString (auth, key, 'private');
|
||
}
|
||
|
||
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
|
||
const feedback = this.id + ' ' + body;
|
||
if (code === 503) {
|
||
throw new ExchangeError (feedback);
|
||
}
|
||
if (!response) {
|
||
return; // fallback to default error handler
|
||
}
|
||
const message = this.safeString (response, 'message');
|
||
const errorCode = this.safeString2 (response, 'code', 'error_code');
|
||
if (message !== undefined) {
|
||
this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
|
||
this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
|
||
}
|
||
this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback);
|
||
if (message !== undefined) {
|
||
throw new ExchangeError (feedback); // unknown message
|
||
}
|
||
}
|
||
};
|