astro-perp-ccxt-dev
Version:
8,834 lines • 408 kB
JavaScript
'use strict';
var okx$1 = require('./abstract/okx.js');
var errors = require('./base/errors.js');
var Precise = require('./base/Precise.js');
var number = require('./base/functions/number.js');
var sha256 = require('./static_dependencies/noble-hashes/sha256.js');
// ----------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/**
* @class okx
* @augments Exchange
*/
class okx extends okx$1 {
describe() {
return this.deepExtend(super.describe(), {
'id': 'okx',
'name': 'OKX',
'countries': ['CN', 'US'],
'version': 'v5',
'rateLimit': 100 * 1.03,
'pro': true,
'certified': true,
'has': {
'CORS': undefined,
'spot': true,
'margin': true,
'swap': true,
'future': true,
'option': true,
'addMargin': true,
'cancelAllOrders': false,
'cancelAllOrdersAfter': true,
'cancelOrder': true,
'cancelOrders': true,
'cancelOrdersForSymbols': true,
'closeAllPositions': false,
'closePosition': true,
'createConvertTrade': true,
'createDepositAddress': false,
'createMarketBuyOrderWithCost': true,
'createMarketSellOrderWithCost': true,
'createOrder': true,
'createOrders': true,
'createOrderWithTakeProfitAndStopLoss': true,
'createPostOnlyOrder': true,
'createReduceOnlyOrder': true,
'createStopLimitOrder': true,
'createStopLossOrder': true,
'createStopMarketOrder': true,
'createStopOrder': true,
'createTakeProfitOrder': true,
'createTrailingPercentOrder': true,
'createTriggerOrder': true,
'editOrder': true,
'fetchAccounts': true,
'fetchBalance': true,
'fetchBidsAsks': undefined,
'fetchBorrowInterest': true,
'fetchBorrowRateHistories': true,
'fetchBorrowRateHistory': true,
'fetchCanceledOrders': true,
'fetchClosedOrder': undefined,
'fetchClosedOrders': true,
'fetchConvertCurrencies': true,
'fetchConvertQuote': true,
'fetchConvertTrade': true,
'fetchConvertTradeHistory': true,
'fetchCrossBorrowRate': true,
'fetchCrossBorrowRates': true,
'fetchCurrencies': true,
'fetchDeposit': true,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': true,
'fetchDeposits': true,
'fetchDepositsWithdrawals': false,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': true,
'fetchFundingHistory': true,
'fetchFundingInterval': true,
'fetchFundingIntervals': false,
'fetchFundingRate': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': true,
'fetchGreeks': true,
'fetchIndexOHLCV': true,
'fetchIsolatedBorrowRate': false,
'fetchIsolatedBorrowRates': false,
'fetchL3OrderBook': false,
'fetchLedger': true,
'fetchLedgerEntry': undefined,
'fetchLeverage': true,
'fetchLeverageTiers': false,
'fetchLongShortRatio': false,
'fetchLongShortRatioHistory': true,
'fetchMarginAdjustmentHistory': true,
'fetchMarketLeverageTiers': true,
'fetchMarkets': true,
'fetchMarkOHLCV': true,
'fetchMarkPrice': true,
'fetchMarkPrices': true,
'fetchMySettlementHistory': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterest': true,
'fetchOpenInterestHistory': true,
'fetchOpenInterests': true,
'fetchOpenOrder': undefined,
'fetchOpenOrders': true,
'fetchOption': true,
'fetchOptionChain': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrderBooks': false,
'fetchOrders': false,
'fetchOrderTrades': true,
'fetchPosition': true,
'fetchPositionHistory': 'emulated',
'fetchPositions': true,
'fetchPositionsForSymbol': true,
'fetchPositionsHistory': true,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchSettlementHistory': true,
'fetchStatus': true,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': false,
'fetchTradingLimits': false,
'fetchTransactionFee': false,
'fetchTransactionFees': false,
'fetchTransactions': false,
'fetchTransfer': true,
'fetchTransfers': true,
'fetchUnderlyingAssets': true,
'fetchVolatilityHistory': false,
'fetchWithdrawal': true,
'fetchWithdrawals': true,
'fetchWithdrawalWhitelist': false,
'reduceMargin': true,
'repayCrossMargin': true,
'sandbox': true,
'setLeverage': true,
'setMargin': false,
'setMarginMode': true,
'setPositionMode': true,
'signIn': false,
'transfer': true,
'withdraw': true,
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1H',
'2h': '2H',
'4h': '4H',
'6h': '6H',
'12h': '12H',
'1d': '1D',
'1w': '1W',
'1M': '1M',
'3M': '3M',
},
'hostname': 'www.okx.com',
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/152485636-38b19e4a-bece-4dec-979a-5982859ffc04.jpg',
'api': {
'rest': 'https://{hostname}',
},
'www': 'https://www.okx.com',
'doc': 'https://www.okx.com/docs-v5/en/',
'fees': 'https://www.okx.com/pages/products/fees.html',
'referral': {
// old reflink 0% discount https://www.okx.com/join/1888677
// new reflink 20% discount https://www.okx.com/join/CCXT2023
'url': 'https://www.okx.com/join/CCXT2023',
'discount': 0.2,
},
'test': {
'rest': 'https://{hostname}',
},
},
'api': {
'public': {
'get': {
'market/books-full': 2,
'market/tickers': 1,
'market/ticker': 1,
'market/index-tickers': 1,
'market/books': 1 / 2,
'market/books-lite': 5 / 3,
'market/candles': 1 / 2,
'market/history-candles': 1,
'market/index-candles': 1,
'market/history-index-candles': 2,
'market/mark-price-candles': 1,
'market/history-mark-price-candles': 2,
'market/trades': 1 / 5,
'market/history-trades': 2,
'market/option/instrument-family-trades': 1,
'market/platform-24-volume': 10,
'market/open-oracle': 50,
'market/exchange-rate': 20,
'market/index-components': 1,
'public/economic-calendar': 50,
'market/block-tickers': 1,
'market/block-ticker': 1,
'public/block-trades': 1,
'public/instruments': 1,
'public/delivery-exercise-history': 1 / 2,
'public/open-interest': 1,
'public/funding-rate': 1,
'public/funding-rate-history': 1,
'public/price-limit': 1,
'public/opt-summary': 1,
'public/estimated-price': 2,
'public/discount-rate-interest-free-quota': 10,
'public/time': 2,
'public/mark-price': 2,
'public/position-tiers': 2,
'public/interest-rate-loan-quota': 10,
'public/vip-interest-rate-loan-quota': 10,
'public/underlying': 1,
'public/insurance-fund': 2,
'public/convert-contract-coin': 2,
'public/option-trades': 1,
'public/instrument-tick-bands': 4,
'rubik/stat/trading-data/support-coin': 4,
'rubik/stat/taker-volume': 4,
'rubik/stat/margin/loan-ratio': 4,
// long/short
'rubik/stat/contracts/long-short-account-ratio': 4,
'rubik/stat/contracts/long-short-account-ratio-contract': 4,
'rubik/stat/contracts/open-interest-volume': 4,
'rubik/stat/option/open-interest-volume': 4,
// put/call
'rubik/stat/option/open-interest-volume-ratio': 4,
'rubik/stat/option/open-interest-volume-expiry': 4,
'rubik/stat/option/open-interest-volume-strike': 4,
'rubik/stat/option/taker-block-volume': 4,
'system/status': 50,
// public api
'sprd/spreads': 1,
'sprd/books': 1 / 2,
'sprd/ticker': 1,
'sprd/public-trades': 1 / 5,
'market/sprd-ticker': 2,
'market/sprd-candles': 2,
'market/sprd-history-candles': 2,
'tradingBot/grid/ai-param': 1,
'tradingBot/grid/min-investment': 1,
'tradingBot/public/rsi-back-testing': 1,
'asset/exchange-list': 5 / 3,
'finance/staking-defi/eth/apy-history': 5 / 3,
'finance/staking-defi/sol/apy-history': 5 / 3,
'finance/savings/lending-rate-summary': 5 / 3,
'finance/savings/lending-rate-history': 5 / 3,
'finance/fixed-loan/lending-offers': 10 / 3,
'finance/fixed-loan/lending-apy-history': 10 / 3,
'finance/fixed-loan/pending-lending-volume': 10 / 3,
// public broker
'finance/sfp/dcd/products': 2 / 3,
// copytrading
'copytrading/public-lead-traders': 4,
'copytrading/public-weekly-pnl': 4,
'copytrading/public-stats': 4,
'copytrading/public-preference-currency': 4,
'copytrading/public-current-subpositions': 4,
'copytrading/public-subpositions-history': 4,
'support/announcements-types': 20,
},
},
'private': {
'get': {
// rfq
'rfq/counterparties': 4,
'rfq/maker-instrument-settings': 4,
'rfq/mmp-config': 4,
'rfq/rfqs': 10,
'rfq/quotes': 10,
'rfq/trades': 4,
'rfq/public-trades': 4,
// sprd
'sprd/order': 1 / 3,
'sprd/orders-pending': 1 / 3,
'sprd/orders-history': 1 / 2,
'sprd/orders-history-archive': 1 / 2,
'sprd/trades': 1 / 3,
// trade
'trade/order': 1 / 3,
'trade/orders-pending': 1 / 3,
'trade/orders-history': 1 / 2,
'trade/orders-history-archive': 1,
'trade/fills': 1 / 3,
'trade/fills-history': 2.2,
'trade/fills-archive': 2,
'trade/order-algo': 1,
'trade/orders-algo-pending': 1,
'trade/orders-algo-history': 1,
'trade/easy-convert-currency-list': 20,
'trade/easy-convert-history': 20,
'trade/one-click-repay-currency-list': 20,
'trade/one-click-repay-currency-list-v2': 20,
'trade/one-click-repay-history': 20,
'trade/one-click-repay-history-v2': 20,
'trade/account-rate-limit': 1,
// asset
'asset/currencies': 5 / 3,
'asset/balances': 5 / 3,
'asset/non-tradable-assets': 5 / 3,
'asset/asset-valuation': 10,
'asset/transfer-state': 10,
'asset/bills': 5 / 3,
'asset/deposit-lightning': 5,
'asset/deposit-address': 5 / 3,
'asset/deposit-history': 5 / 3,
'asset/withdrawal-history': 5 / 3,
'asset/deposit-withdraw-status': 20,
'asset/convert/currencies': 5 / 3,
'asset/convert/currency-pair': 5 / 3,
'asset/convert/history': 5 / 3,
'asset/monthly-statement': 2,
// account
'account/instruments': 1,
'account/balance': 2,
'account/positions': 2,
'account/positions-history': 100,
'account/account-position-risk': 2,
'account/bills': 5 / 3,
'account/bills-archive': 5 / 3,
'account/bills-history-archive': 2,
'account/config': 4,
'account/max-size': 1,
'account/max-avail-size': 1,
'account/leverage-info': 1,
'account/adjust-leverage-info': 4,
'account/max-loan': 1,
'account/trade-fee': 4,
'account/interest-accrued': 4,
'account/interest-rate': 4,
'account/max-withdrawal': 1,
'account/risk-state': 2,
'account/quick-margin-borrow-repay-history': 4,
'account/borrow-repay-history': 4,
'account/vip-interest-accrued': 4,
'account/vip-interest-deducted': 4,
'account/vip-loan-order-list': 4,
'account/vip-loan-order-detail': 4,
'account/interest-limits': 4,
'account/greeks': 2,
'account/position-tiers': 2,
'account/mmp-config': 4,
'account/fixed-loan/borrowing-limit': 4,
'account/fixed-loan/borrowing-quote': 5,
'account/fixed-loan/borrowing-orders-list': 5,
'account/spot-manual-borrow-repay': 10,
'account/set-auto-repay': 4,
'account/spot-borrow-repay-history': 4,
'account/move-positions-history': 10,
// subaccount
'users/subaccount/list': 10,
'account/subaccount/balances': 10 / 3,
'asset/subaccount/balances': 10 / 3,
'account/subaccount/max-withdrawal': 1,
'asset/subaccount/bills': 5 / 3,
'asset/subaccount/managed-subaccount-bills': 5 / 3,
'users/entrust-subaccount-list': 10,
'account/subaccount/interest-limits': 4,
'users/subaccount/apikey': 10,
// grid trading
'tradingBot/grid/orders-algo-pending': 1,
'tradingBot/grid/orders-algo-history': 1,
'tradingBot/grid/orders-algo-details': 1,
'tradingBot/grid/sub-orders': 1,
'tradingBot/grid/positions': 1,
'tradingBot/grid/ai-param': 1,
'tradingBot/signal/signals': 1,
'tradingBot/signal/orders-algo-details': 1,
'tradingBot/signal/orders-algo-history': 1,
'tradingBot/signal/positions': 1,
'tradingBot/signal/positions-history': 1,
'tradingBot/signal/sub-orders': 1,
'tradingBot/signal/event-history': 1,
'tradingBot/recurring/orders-algo-pending': 1,
'tradingBot/recurring/orders-algo-history': 1,
'tradingBot/recurring/orders-algo-details': 1,
'tradingBot/recurring/sub-orders': 1,
// earn
'finance/savings/balance': 5 / 3,
'finance/savings/lending-history': 5 / 3,
'finance/staking-defi/offers': 10 / 3,
'finance/staking-defi/orders-active': 10 / 3,
'finance/staking-defi/orders-history': 10 / 3,
// eth staking
'finance/staking-defi/eth/balance': 5 / 3,
'finance/staking-defi/eth/purchase-redeem-history': 5 / 3,
'finance/staking-defi/eth/product-info': 3,
'finance/staking-defi/sol/balance': 5 / 3,
'finance/staking-defi/sol/purchase-redeem-history': 5 / 3,
// copytrading
'copytrading/current-subpositions': 1,
'copytrading/subpositions-history': 1,
'copytrading/instruments': 4,
'copytrading/profit-sharing-details': 4,
'copytrading/total-profit-sharing': 4,
'copytrading/unrealized-profit-sharing-details': 4,
'copytrading/copy-settings': 4,
'copytrading/batch-leverage-info': 4,
'copytrading/current-lead-traders': 4,
'copytrading/lead-traders-history': 4,
// broker
'broker/nd/info': 10,
'broker/nd/subaccount-info': 10,
'broker/nd/subaccount/apikey': 10,
'asset/broker/nd/subaccount-deposit-address': 5 / 3,
'asset/broker/nd/subaccount-deposit-history': 4,
'asset/broker/nd/subaccount-withdrawal-history': 4,
'broker/nd/rebate-daily': 100,
'broker/nd/rebate-per-orders': 300,
'finance/sfp/dcd/order': 2,
'finance/sfp/dcd/orders': 2,
'broker/fd/rebate-per-orders': 300,
'broker/fd/if-rebate': 5,
// affiliate
'affiliate/invitee/detail': 1,
'users/partner/if-rebate': 1,
'support/announcements': 4,
},
'post': {
// rfq
'rfq/create-rfq': 4,
'rfq/cancel-rfq': 4,
'rfq/cancel-batch-rfqs': 10,
'rfq/cancel-all-rfqs': 10,
'rfq/execute-quote': 15,
'rfq/maker-instrument-settings': 4,
'rfq/mmp-reset': 4,
'rfq/mmp-config': 100,
'rfq/create-quote': 0.4,
'rfq/cancel-quote': 0.4,
'rfq/cancel-batch-quotes': 10,
'rfq/cancel-all-quotes': 10,
// sprd
'sprd/order': 1,
'sprd/cancel-order': 1,
'sprd/mass-cancel': 1,
'sprd/amend-order': 1,
'sprd/cancel-all-after': 10,
// trade
'trade/order': 1 / 3,
'trade/batch-orders': 1 / 15,
'trade/cancel-order': 1 / 3,
'trade/cancel-batch-orders': 1 / 15,
'trade/amend-order': 1 / 3,
'trade/amend-batch-orders': 1 / 150,
'trade/close-position': 1,
'trade/fills-archive': 172800,
'trade/order-algo': 1,
'trade/cancel-algos': 1,
'trade/amend-algos': 1,
'trade/cancel-advance-algos': 1,
'trade/easy-convert': 20,
'trade/one-click-repay': 20,
'trade/one-click-repay-v2': 20,
'trade/mass-cancel': 4,
'trade/cancel-all-after': 10,
// asset
'asset/transfer': 10,
'asset/withdrawal': 5 / 3,
'asset/withdrawal-lightning': 5,
'asset/cancel-withdrawal': 5 / 3,
'asset/convert-dust-assets': 10,
'asset/convert/estimate-quote': 1,
'asset/convert/trade': 1,
'asset/monthly-statement': 1,
// account
'account/set-position-mode': 4,
'account/set-leverage': 1,
'account/position/margin-balance': 1,
'account/set-greeks': 4,
'account/set-isolated-mode': 4,
'account/quick-margin-borrow-repay': 4,
'account/borrow-repay': 5 / 3,
'account/simulated_margin': 10,
'account/position-builder': 10,
'account/set-riskOffset-type': 2,
'account/activate-option': 4,
'account/set-auto-loan': 4,
'account/set-account-level': 4,
'account/mmp-reset': 4,
'account/mmp-config': 100,
'account/fixed-loan/borrowing-order': 5,
'account/fixed-loan/amend-borrowing-order': 5,
'account/fixed-loan/manual-reborrow': 5,
'account/fixed-loan/repay-borrowing-order': 5,
'account/bills-history-archive': 72000,
'account/move-positions': 10,
// subaccount
'users/subaccount/modify-apikey': 10,
'asset/subaccount/transfer': 10,
'users/subaccount/set-transfer-out': 10,
'account/subaccount/set-loan-allocation': 4,
'users/subaccount/create-subaccount': 10,
'users/subaccount/subaccount-apikey': 10,
'users/subaccount/delete-apikey': 10,
// grid trading
'tradingBot/grid/order-algo': 1,
'tradingBot/grid/amend-order-algo': 1,
'tradingBot/grid/stop-order-algo': 1,
'tradingBot/grid/close-position': 1,
'tradingBot/grid/cancel-close-order': 1,
'tradingBot/grid/order-instant-trigger': 1,
'tradingBot/grid/withdraw-income': 1,
'tradingBot/grid/compute-margin-balance': 1,
'tradingBot/grid/margin-balance': 1,
'tradingBot/grid/min-investment': 1,
'tradingBot/grid/adjust-investment': 1,
'tradingBot/signal/create-signal': 1,
'tradingBot/signal/order-algo': 1,
'tradingBot/signal/stop-order-algo': 1,
'tradingBot/signal/margin-balance': 1,
'tradingBot/signal/amendTPSL': 1,
'tradingBot/signal/set-instruments': 1,
'tradingBot/signal/close-position': 1,
'tradingBot/signal/sub-order': 1,
'tradingBot/signal/cancel-sub-order': 1,
'tradingBot/recurring/order-algo': 1,
'tradingBot/recurring/amend-order-algo': 1,
'tradingBot/recurring/stop-order-algo': 1,
// earn
'finance/savings/purchase-redempt': 5 / 3,
'finance/savings/set-lending-rate': 5 / 3,
'finance/staking-defi/purchase': 3,
'finance/staking-defi/redeem': 3,
'finance/staking-defi/cancel': 3,
// eth staking
'finance/staking-defi/eth/purchase': 5,
'finance/staking-defi/eth/redeem': 5,
'finance/staking-defi/sol/purchase': 5,
'finance/staking-defi/sol/redeem': 5,
// copytrading
'copytrading/algo-order': 1,
'copytrading/close-subposition': 1,
'copytrading/set-instruments': 4,
'copytrading/first-copy-settings': 4,
'copytrading/amend-copy-settings': 4,
'copytrading/stop-copy-trading': 4,
'copytrading/batch-set-leverage': 4,
// broker
'broker/nd/create-subaccount': 0.25,
'broker/nd/delete-subaccount': 1,
'broker/nd/subaccount/apikey': 0.25,
'broker/nd/subaccount/modify-apikey': 1,
'broker/nd/subaccount/delete-apikey': 1,
'broker/nd/set-subaccount-level': 4,
'broker/nd/set-subaccount-fee-rate': 4,
'broker/nd/set-subaccount-assets': 0.25,
'asset/broker/nd/subaccount-deposit-address': 1,
'asset/broker/nd/modify-subaccount-deposit-address': 5 / 3,
'broker/nd/rebate-per-orders': 36000,
'finance/sfp/dcd/quote': 10,
'finance/sfp/dcd/order': 10,
'broker/nd/report-subaccount-ip': 0.25,
'broker/fd/rebate-per-orders': 36000,
},
},
},
'fees': {
'trading': {
'taker': this.parseNumber('0.0015'),
'maker': this.parseNumber('0.0010'),
},
'spot': {
'taker': this.parseNumber('0.0015'),
'maker': this.parseNumber('0.0010'),
},
'future': {
'taker': this.parseNumber('0.0005'),
'maker': this.parseNumber('0.0002'),
},
'swap': {
'taker': this.parseNumber('0.00050'),
'maker': this.parseNumber('0.00020'),
},
},
'requiredCredentials': {
'apiKey': true,
'secret': true,
'password': true,
},
'exceptions': {
'exact': {
// Public error codes from 50000-53999
// General Class
'1': errors.ExchangeError,
'2': errors.ExchangeError,
'4088': errors.ManualInteractionNeeded,
'50000': errors.BadRequest,
'50001': errors.OnMaintenance,
'50002': errors.BadRequest,
'50004': errors.RequestTimeout,
'50005': errors.ExchangeNotAvailable,
'50006': errors.BadRequest,
'50007': errors.AccountSuspended,
'50008': errors.AuthenticationError,
'50009': errors.AccountSuspended,
'50010': errors.ExchangeError,
'50011': errors.RateLimitExceeded,
'50012': errors.ExchangeError,
'50013': errors.ExchangeNotAvailable,
'50014': errors.BadRequest,
'50015': errors.ExchangeError,
'50016': errors.ExchangeError,
'50017': errors.ExchangeError,
'50018': errors.ExchangeError,
'50019': errors.ExchangeError,
'50020': errors.ExchangeError,
'50021': errors.ExchangeError,
'50022': errors.ExchangeError,
'50023': errors.ExchangeError,
'50024': errors.BadRequest,
'50025': errors.ExchangeError,
'50026': errors.ExchangeNotAvailable,
'50027': errors.PermissionDenied,
'50028': errors.ExchangeError,
'50044': errors.BadRequest,
'50061': errors.ExchangeError,
'50062': errors.ExchangeError,
// API Class
'50100': errors.ExchangeError,
'50101': errors.AuthenticationError,
'50102': errors.InvalidNonce,
'50103': errors.AuthenticationError,
'50104': errors.AuthenticationError,
'50105': errors.AuthenticationError,
'50106': errors.AuthenticationError,
'50107': errors.AuthenticationError,
'50108': errors.ExchangeError,
'50109': errors.ExchangeError,
'50110': errors.PermissionDenied,
'50111': errors.AuthenticationError,
'50112': errors.AuthenticationError,
'50113': errors.AuthenticationError,
'50114': errors.AuthenticationError,
'50115': errors.BadRequest,
// Trade Class
'51000': errors.BadRequest,
'51001': errors.BadSymbol,
'51002': errors.BadSymbol,
'51003': errors.BadRequest,
'51004': errors.InvalidOrder,
'51005': errors.InvalidOrder,
'51006': errors.InvalidOrder,
'51007': errors.InvalidOrder,
'51008': errors.InsufficientFunds,
'51009': errors.AccountSuspended,
'51010': errors.AccountNotEnabled,
'51011': errors.InvalidOrder,
'51012': errors.BadSymbol,
'51014': errors.BadSymbol,
'51015': errors.BadSymbol,
'51016': errors.InvalidOrder,
'51017': errors.ExchangeError,
'51018': errors.ExchangeError,
'51019': errors.ExchangeError,
'51020': errors.InvalidOrder,
'51021': errors.ContractUnavailable,
'51022': errors.ContractUnavailable,
'51023': errors.ExchangeError,
'51024': errors.AccountSuspended,
'51025': errors.ExchangeError,
'51026': errors.BadSymbol,
'51027': errors.ContractUnavailable,
'51028': errors.ContractUnavailable,
'51029': errors.ContractUnavailable,
'51030': errors.ContractUnavailable,
'51031': errors.InvalidOrder,
'51046': errors.InvalidOrder,
'51047': errors.InvalidOrder,
'51072': errors.InvalidOrder,
'51073': errors.InvalidOrder,
'51074': errors.InvalidOrder,
'51090': errors.InvalidOrder,
'51091': errors.InvalidOrder,
'51092': errors.InvalidOrder,
'51093': errors.InvalidOrder,
'51094': errors.InvalidOrder,
'51095': errors.InvalidOrder,
'51096': errors.InvalidOrder,
'51098': errors.InvalidOrder,
'51099': errors.InvalidOrder,
'51100': errors.InvalidOrder,
'51101': errors.InvalidOrder,
'51102': errors.InvalidOrder,
'51103': errors.InvalidOrder,
'51104': errors.InvalidOrder,
'51105': errors.InvalidOrder,
'51106': errors.InvalidOrder,
'51107': errors.InvalidOrder,
'51108': errors.InvalidOrder,
'51109': errors.InvalidOrder,
'51110': errors.InvalidOrder,
'51111': errors.BadRequest,
'51112': errors.InvalidOrder,
'51113': errors.RateLimitExceeded,
'51115': errors.InvalidOrder,
'51116': errors.InvalidOrder,
'51117': errors.InvalidOrder,
'51118': errors.InvalidOrder,
'51119': errors.InsufficientFunds,
'51120': errors.InvalidOrder,
'51121': errors.InvalidOrder,
'51122': errors.InvalidOrder,
'51124': errors.InvalidOrder,
'51125': errors.InvalidOrder,
'51126': errors.InvalidOrder,
'51127': errors.InsufficientFunds,
'51128': errors.InvalidOrder,
'51129': errors.InvalidOrder,
'51130': errors.BadSymbol,
'51131': errors.InsufficientFunds,
'51132': errors.InvalidOrder,
'51133': errors.InvalidOrder,
'51134': errors.InvalidOrder,
'51135': errors.InvalidOrder,
'51136': errors.InvalidOrder,
'51137': errors.InvalidOrder,
'51138': errors.InvalidOrder,
'51139': errors.InvalidOrder,
'51156': errors.BadRequest,
'51159': errors.BadRequest,
'51162': errors.InvalidOrder,
'51163': errors.InvalidOrder,
'51166': errors.InvalidOrder,
'51174': errors.InvalidOrder,
'51185': errors.InvalidOrder,
'51201': errors.InvalidOrder,
'51202': errors.InvalidOrder,
'51203': errors.InvalidOrder,
'51204': errors.InvalidOrder,
'51205': errors.InvalidOrder,
'51250': errors.InvalidOrder,
'51251': errors.InvalidOrder,
'51252': errors.InvalidOrder,
'51253': errors.InvalidOrder,
'51254': errors.InvalidOrder,
'51255': errors.InvalidOrder,
'51256': errors.InvalidOrder,
'51257': errors.InvalidOrder,
'51258': errors.InvalidOrder,
'51259': errors.InvalidOrder,
'51260': errors.InvalidOrder,
'51261': errors.InvalidOrder,
'51262': errors.InvalidOrder,
'51263': errors.InvalidOrder,
'51264': errors.InvalidOrder,
'51265': errors.InvalidOrder,
'51267': errors.InvalidOrder,
'51268': errors.InvalidOrder,
'51269': errors.InvalidOrder,
'51270': errors.InvalidOrder,
'51271': errors.InvalidOrder,
'51272': errors.InvalidOrder,
'51273': errors.InvalidOrder,
'51274': errors.InvalidOrder,
'51275': errors.InvalidOrder,
'51276': errors.InvalidOrder,
'51277': errors.InvalidOrder,
'51278': errors.InvalidOrder,
'51279': errors.InvalidOrder,
'51280': errors.InvalidOrder,
'51321': errors.InvalidOrder,
'51322': errors.InvalidOrder,
'51323': errors.BadRequest,
'51324': errors.BadRequest,
'51325': errors.InvalidOrder,
'51327': errors.InvalidOrder,
'51328': errors.InvalidOrder,
'51329': errors.InvalidOrder,
'51330': errors.InvalidOrder,
'51400': errors.OrderNotFound,
'51401': errors.OrderNotFound,
'51402': errors.OrderNotFound,
'51403': errors.InvalidOrder,
'51404': errors.InvalidOrder,
'51405': errors.ExchangeError,
'51406': errors.ExchangeError,
'51407': errors.BadRequest,
'51408': errors.ExchangeError,
'51409': errors.ExchangeError,
'51410': errors.CancelPending,
'51500': errors.ExchangeError,
'51501': errors.ExchangeError,
'51502': errors.InsufficientFunds,
'51503': errors.ExchangeError,
'51506': errors.ExchangeError,
'51508': errors.ExchangeError,
'51509': errors.ExchangeError,
'51510': errors.ExchangeError,
'51511': errors.ExchangeError,
'51600': errors.ExchangeError,
'51601': errors.ExchangeError,
'51602': errors.ExchangeError,
'51603': errors.OrderNotFound,
'51732': errors.AuthenticationError,
'51733': errors.AuthenticationError,
'51734': errors.AuthenticationError,
'51735': errors.ExchangeError,
'51736': errors.InsufficientFunds,
// Data class
'52000': errors.ExchangeError,
// SPOT/MARGIN error codes 54000-54999
'54000': errors.ExchangeError,
'54001': errors.ExchangeError,
'54008': errors.InvalidOrder,
'54009': errors.InvalidOrder,
'54011': errors.InvalidOrder,
// Trading bot Error Code from 55100 to 55999
'55100': errors.InvalidOrder,
'55101': errors.InvalidOrder,
'55102': errors.InvalidOrder,
'55103': errors.InvalidOrder,
'55104': errors.InvalidOrder,
'55111': errors.InvalidOrder,
'55112': errors.InvalidOrder,
'55113': errors.InvalidOrder,
// FUNDING error codes 58000-58999
'58000': errors.ExchangeError,
'58001': errors.AuthenticationError,
'58002': errors.PermissionDenied,
'58003': errors.ExchangeError,
'58004': errors.AccountSuspended,
'58005': errors.ExchangeError,
'58006': errors.ExchangeError,
'58007': errors.ExchangeError,
'58100': errors.ExchangeError,
'58101': errors.AccountSuspended,
'58102': errors.RateLimitExceeded,
'58103': errors.ExchangeError,
'58104': errors.ExchangeError,
'58105': errors.ExchangeError,
'58106': errors.ExchangeError,
'58107': errors.ExchangeError,
'58108': errors.ExchangeError,
'58109': errors.ExchangeError,
'58110': errors.ExchangeError,
'58111': errors.ExchangeError,
'58112': errors.ExchangeError,
'58114': errors.ExchangeError,
'58115': errors.ExchangeError,
'58116': errors.ExchangeError,
'58117': errors.ExchangeError,
'58125': errors.BadRequest,
'58126': errors.BadRequest,
'58127': errors.BadRequest,
'58128': errors.BadRequest,
'58200': errors.ExchangeError,
'58201': errors.ExchangeError,
'58202': errors.ExchangeError,
'58203': errors.InvalidAddress,
'58204': errors.AccountSuspended,
'58205': errors.ExchangeError,
'58206': errors.ExchangeError,
'58207': errors.InvalidAddress,
'58208': errors.ExchangeError,
'58209': errors.ExchangeError,
'58210': errors.ExchangeError,
'58211': errors.ExchangeError,
'58212': errors.ExchangeError,
'58213': errors.AuthenticationError,
'58221': errors.BadRequest,
'58222': errors.BadRequest,
'58224': errors.BadRequest,
'58227': errors.BadRequest,
'58228': errors.BadRequest,
'58229': errors.InsufficientFunds,
'58300': errors.ExchangeError,
'58350': errors.InsufficientFunds,
// Account error codes 59000-59999
'59000': errors.ExchangeError,
'59001': errors.ExchangeError,
'59100': errors.ExchangeError,
'59101': errors.ExchangeError,
'59102': errors.ExchangeError,
'59103': errors.InsufficientFunds,
'59104': errors.ExchangeError,
'59105': errors.ExchangeError,
'59106': errors.ExchangeError,
'59107': errors.ExchangeError,
'59108': errors.InsufficientFunds,
'59109': errors.ExchangeError,
'59128': errors.InvalidOrder,
'59200': errors.InsufficientFunds,
'59201': errors.InsufficientFunds,
'59216': errors.BadRequest,
'59260': errors.PermissionDenied,
'59262': errors.PermissionDenied,
'59300': errors.ExchangeError,
'59301': errors.ExchangeError,
'59313': errors.ExchangeError,
'59401': errors.ExchangeError,
'59410': errors.OperationRejected,
'59411': errors.InsufficientFunds,
'59412': errors.OperationRejected,
'59413': errors.OperationRejected,
'59414': errors.BadRequest,
'59500': errors.ExchangeError,
'59501': errors.ExchangeError,
'59502': errors.ExchangeError,
'59503': errors.ExchangeError,
'59504': errors.ExchangeError,
'59505': errors.ExchangeError,
'59506': errors.ExchangeError,
'59507': errors.ExchangeError,
'59508': errors.AccountSuspended,
'59515': errors.ExchangeError,
'59516': errors.ExchangeError,
'59517': errors.ExchangeError,
'59518': errors.ExchangeError,
'59519': errors.ExchangeError,
'59642': errors.BadRequest,
'59643': errors.ExchangeError,
// WebSocket error Codes from 60000-63999
'60001': errors.AuthenticationError,
'60002': errors.AuthenticationError,
'60003': errors.AuthenticationError,
'60004': errors.AuthenticationError,
'60005': errors.AuthenticationError,
'60006': errors.InvalidNonce,
'60007': errors.AuthenticationError,
'60008': errors.AuthenticationError,
'60009': errors.AuthenticationError,
'60010': errors.AuthenticationError,
'60011': errors.AuthenticationError,
'60012': errors.BadRequest,
'60013': errors.BadRequest,
'60014': errors.RateLimitExceeded,
'60015': errors.NetworkError,
'60016': errors.ExchangeNotAvailable,
'60017': errors.BadRequest,
'60018': errors.BadRequest,
'60019': errors.BadRequest,
'60020': errors.ExchangeError,
'60021': errors.AccountNotEnabled,
'60022': errors.AuthenticationError,
'60023': errors.DDoSProtection,
'60024': errors.AuthenticationError,
'60025': errors.ExchangeError,
'60026': errors.AuthenticationError,
'60027': errors.ArgumentsRequired,
'60028': errors.NotSupported,
'60029': errors.AccountNotEnabled,
'60030': errors.AccountNotEnabled,
'60031': errors.AuthenticationError,
'60032': errors.AuthenticationError,
'63999': errors.ExchangeError,
'64000': errors.BadRequest,
'64001': errors.BadRequest,
'64002': errors.BadRequest,
'64003': errors.AccountNotEnabled,
'70010': errors.BadRequest,
'70013': errors.BadRequest,
'70016': errors.BadRequest,
'70060': errors.BadRequest,
'70061': errors.BadRequest,
'70062': errors.BadRequest,
'70064': errors.BadRequest,
'70065': errors.BadRequest,
'70066': errors.BadRequest,
'70067': errors.BadRequest,
'1009': errors.BadRequest,
'4001': errors.AuthenticationError,
'4002': errors.BadRequest,
'4003': errors.RateLimitExceeded,
'4004': errors.NetworkError,
'4005': errors.ExchangeNotAvailable,
'4006': errors.BadRequest,
'4007': errors.AuthenticationError,
'4008': errors.RateLimitExceeded, // The number of subscribed channels exceeds the maximum limit.
},
'broad': {
'Internal Server Error': errors.ExchangeNotAvailable,
'server error': errors.ExchangeNotAvailable, // {"code":500,"data":{},"detailMsg":"","error_code":"500","error_message":"server error 1236805249","msg":"server error 1236805249"}
},
},
'httpExceptions': {
'429': errors.ExchangeNotAvailable, // https://github.com/ccxt/ccxt/issues/9612
},
'precisionMode': number.TICK_SIZE,
'options': {
'sandboxMode': false,
'defaultNetwork': 'ERC20',
'defaultNetworks': {
'ETH': 'ERC20',
'BTC': 'BTC',
'USDT': 'TRC20',
},
'networks': {
'BTC': 'Bitcoin',
'BTCLN': 'Lightning',
'BTCLIGHTNING': 'Lightning',
'BEP20': 'BSC',
'BRC20': 'BRC20',
'ERC20': 'ERC20',
'TRC20': 'TRC20',
'CRC20': 'Crypto',
'ACA': 'Acala',
'ALGO': 'Algorand',
'APT': 'Aptos',
'SCROLL': 'Scroll',
'ARBONE': 'Arbitrum One',
'AVAXC': 'Avalanche C-Chain',
'AVAXX': 'Avalanche X-Chain',
'BASE': 'Base',
'SUI': 'SUI',
'ZKSYNCERA': 'zkSync Era',
'LINEA': 'Linea',
'AR': 'Arweave',
'ASTR': 'Astar',
'BCH': 'BitcoinCash',
'BSV': 'Bitcoin SV',
'ADA': 'Cardano',
'CSPR': 'Casper',
'CELO': 'CELO',
'XCH': 'Chia',
// 'CHZ': 'Chiliz', TBD: Chiliz 2.0 Chain vs Chiliz Chain
'ATOM': 'Cosmos',
'DGB': 'Digibyte',
'DOGE': 'Dogecoin',
'EGLD': 'Elrond',
'CFX': 'Conflux',
'EOS': 'EOS',
'CORE': 'CORE',
'ETC': 'Ethereum Classic',
'ETHW': 'EthereumPow',
// 'FTM': 'Fantom', 'Sonic' TBD
'FIL': 'Filecoin',
'ONE': 'Harmony',
'HBAR': 'Hedera',
'ICX': 'ICON',
'ICP': 'Dfinity',
'IOST': 'IOST',
'IOTA': 'MIOTA',
'KLAY': 'Klaytn',
'KSM': 'Kusama',
'LSK': 'Lisk',
'LTC': 'Litecoin',
'METIS': 'Metis',
'MINA': 'Mina',
'GLRM': 'Moonbeam',
'MOVR': 'Moonriver',
'NANO': 'Nano',
'NEAR': 'NEAR',
'NULS': 'NULS',
'OASYS': 'OASYS',
'ONT': 'Ontology',
'OPTIMISM': 'Optimism',
// 'OP': 'Optimism', or Optimism (V2), TBD
'LAT': 'PlatON',
'DOT': 'Polkadot',
'MATIC': 'Polygon',
'RVN': 'Ravencoin',
'XRP': 'Ripple',
'SC': 'Siacoin',
'SOL': 'Solana',
'STX': 'l-Stacks',
'XLM': 'Stellar Lumens',
'XTZ': 'Tezos',
'TON': 'TON',
'THETA': 'Theta',
'WAX': 'Wax',
'ZIL': 'Zilliqa',
// non-supported known network: CRP. KAVA, TAIKO, BOB, GNO, BLAST, RSK, SEI, MANTLE, HYPE, RUNE, OSMO, XIN, WEMIX, HT, FSN, NEO, TLOS, CANTO, SCRT, AURORA, XMR
// others:
// "OKTC",
// "X Layer",
// "Polygon (Bridged)",
// "BTCK-OKTC",
// "ETHK-OKTC",
// "Starknet",
// "LTCK-OKTC",
// "XRPK-OKTC",
// "BCHK-OKTC",
// "ETCK-OKTC",
// "Endurance Smart Chain",
// "Berachain",
// "CELO-TOKEN",
// "CFX_EVM",
// "Cortex",
// "DAIK-OKTC",
// "Dora Vota Mainnet",
// "DOTK-OKTC",
// "DYDX",
// "AELF",
// "Enjin Relay Chain",
// "FEVM",
// "FILK-OKTC",
// "Flare",
// "Gravity Alpha Mainnet",
// "INJ",
// "Story",
// "LINKK-OKTC",
// "Terra",
// "Terra Classic",
// "Terra Classic (USTC)",
// "MERLIN Network",
// "Layer 3",
// "PI",
// "Ronin",
// "Quantum",
// "SHIBK-OKTC",
// "SUSHIK-OKTC",
// "Celestia",
// "TRXK-OKTC",
// "UNIK-OKTC",
// "Venom",
// "WBTCK-OKTC",
// "ZetaChain",
},
'fetchOpenInterestHistory': {
'timeframes': {
'5m': '5m',
'1h': '1H',
'8h': '8H',
'1d': '1D',
'5M': '5m',
'1H': '1H',
'8H': '8H',
'1D': '1D',
},
},
'fetchOHLCV': {
// 'type': 'Candles', // Candles or HistoryCandles, IndexCandles, MarkPriceCandles
'timezone': 'UTC', // UTC, HK
},
'fetchPositions': {
'method': 'privateGetAccountPositions', // privateGetAccountPositions or privateGetAccountPositionsHistory
},
'createOrder': 'privatePostTradeBatchOrders',
'createMarketBuyOrderRequiresPrice': false,
'fetchMarkets': ['spot', 'future', 'swap', 'option'],
'timeDifference': 0,
'adjustForTimeDifference': false,
'defaultType': 'spot',
// 'fetchBalance': {
// 'type': 'spot', // 'funding', 'trading', 'spot'
// },
'fetchLedger': {
'method': 'privateGetAccountBills', // privateGetAccountBills, privateGetAccountBillsArchive, privateGetAssetBills
},
// 6: Funding account, 18: Trading account
'fetchOrder': {
'method': 'privateGetTradeOrder', // privateGetTradeOrdersAlgoHistory
},
'fetchOpenOrders': {
'method': 'privateGetTradeOrdersPending', // privateGetTradeOrdersAlgoPending
},
'cancelOrders': {
'method': 'privatePostTradeCancelBatchOrders', // privatePostTradeCancelAlgos
},
'fetchCanceledOrders': {
'method': 'privateGetTradeOrdersHistory', // privateGetTradeOrdersAlgoHistory
},
'fetchClosedOrders': {
'method': 'privateGetTradeOrdersHistory', // privateGetTradeOrdersAlgoHistory
},
'withdraw': {
// a funding password credential is required by the exchange for the
// withdraw call (not to be confused with the api password credential)
'password': undefined,
'pwd': undefined, // password or pwd both work
},
'algoOrderTypes': {
'conditional': true,
'trigger': true,
'oco': true,
'move_order_stop': true,
'iceberg': true,
'twap': true,
},
'accountsByType': {
'funding': '6',
'trading': '18',
'spot': '18',
'future': '18',
'futures': '18',
'margin': '18',
'swap': '18',
'option': '18',
},
'accountsById': {
'6': 'funding',
'18': 'trading', // unified trading account
},
'exchangeType': {
'spot': 'SPOT',
'margin': 'MARGIN',
'swap': 'SWAP',
'future': 'FUTURES',
'futures': 'FUTURES',
'option': 'OPTION',
'SPOT': 'SPOT',
'MARGIN': 'MARGIN',
'SWAP': 'SWAP',
'FUTURES': 'FUTURES',
'OPTION': 'OPTION',
},
'brokerId': 'e847386590ce4dBC',
},
'features': {
'default': {
'sandbox': true,
'createOrder': {
'marginMode': true,
'triggerPrice': true,
'triggerPriceType': {
'last': true,
'mark': true,
'index': true,
},
'triggerDirection': false,
'stopLossPrice': true,
'takeProfitPrice': true,
'attachedStopLossTakeProfit': {
'triggerPriceType': {
'last': true,
'mark': true,
'index': true,
},
'price': true,
},
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'hedged': true,
'trailing': true,
'iceberg': true,
'leverage': false,
'selfTradePrevention': true,
'marketBuyByCost': true,
'marketBuyRequiresPrice': false,
},
'createOrders': {
'max': 20,
},
'fetchMyTrades': {
'marginMode': false,
'daysBack': 90,
'limit': 100,
'untilDays': 10000,
'symbolRequired': false,
},
'fetchOrder': {
'marginMode': false,
'trigger': true,
'trailing': true,
'symbolRequired': true,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': 100,
'trigger': true,
'trailing': true,
'symbolRequired': false,
},
'fetchOrders': undefined,
'fetchClosedOrders': {
'marginMode': false,
'limit': 100,
'daysBack': 90,
'daysBackCanceled': 1 / 12,
'untilDays': undefined,
'trigger': true,
'trailing': true,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 300,
'historical': 100,
},
},
'spot': {
'extends': 'default',
},
'swap': {
'linear': {
'extends': 'default',
},
'inverse': {
'extends': 'default',
},
},
'future': {
'linear': {
'extends': 'default',
},
'inverse': {
'extends': 'default',
},
},
},
'commonCurrencies': {
// the exchange refers to ERC20 version of Aeternity (AEToken)
'AE': 'AET',
'WIN': 'WINTOKEN', // https://github.com/ccxt/ccxt/issues/5701
},
});
}
handleMarketTypeAndParams(methodName, market = undefined, params = {}, defaultValue = undefined) {
const instType = this.safeString(params, 'instType');
params = this.omit(params, 'instType');
const type = this.safeString(params, 'type');
if ((type === undefined) && (instType !== undefined)) {
params['type'] = instType;
}
return super.handleMarketTypeAndParams(methodName, market, params, defaultValue);
}
convertToInstrumentType(type) {
const exchangeTypes = this.safeDict(this.options, 'exchangeType', {});
return this.safeString(exchangeTypes, type, type);
}
createExpiredOptionMarket(symbol) {
// support expired option contracts
const quote = 'USD';
const optionParts = symbol.split('-');
const symbolBase = symbol.split('/');
let base = undefined;
if (symbol.indexOf('/') > -1) {
base = this.safeString(symbolBase, 0);
}
else {
base = this.safeString(optionParts, 0);
}
const settle = base;
const expiry = this.safeString(optionParts, 2);
const strike = this.safeString(optionParts, 3);
const optionType = this.safeString(optionParts, 4);
const datetime = this.convertExpireDate(expiry);
const timestamp = this.parse8601(datetime);
return {
'id': base + '-' + quote + '-' + expiry + '-' + strike + '-' + optionType,
'symbol': base + '/' + quote + ':' + settle + '-' + expiry + '-' + strike + '-' + optionType,
'base': base,
'quote': quote,
'settle': settle,
'baseId': base,
'quoteId': quote,
'settleId': settle,
'active': false,
'type': 'option',
'linear': undefined,
'inverse': undefined,
'spot': false,
'swap': false,
'future': false,
'option': true,
'margin': false,
'contract': true,
'contractSize': this.parseNumber('1'),
'expiry': timestamp,
'expiryDatetime': datetime,
'optionType': (optionType === 'C') ? 'call' : 'put',
'strike': this.parseNumber(strike),
'precision': {
'amount': undefined,
'price': undefined,
},
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': undefined,
};
}
safeMarket(marketId = undefined, market = undefined, delimiter = undefined, marketType = undefined) {
const isOption = (marketId !== undefined) && ((marketId.indexOf('-C') > -1) || (marketId.indexOf('-P') > -1));
if (isOption && !(marketId in this.markets_by_id)) {
// handle expired option contracts
return this.createExpiredOptionMarket(marketId);
}
return super.safeMarket(marketId, market, delimiter, marketType);
}
/**
* @method
* @name okx#fetchStatus
* @description the latest known information on the availability of the exchange API
* @see https://www.okx.com/docs-v5/en/#status-get-status
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure}
*/
async fetchStatus(params = {}) {
const response = await this.publicGetSystemStatus(params);
//
// Note, if there is no maintenance around, the 'data' array is empty
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "begin": "1621328400000",
// "end": "1621329000000",
// "href": "https://www.okx.com/support/hc/en-us/articles/360060882172",
// "scheDesc": "",
// "serviceType": "1", // 0 WebSocket, 1 Spot/Margin, 2 Futures, 3 Perpetual, 4 Options, 5 Trading service
// "state": "scheduled", // ongoing, completed, canceled
// "system": "classic", // classic, unified
// "title": "Classic Spot System Upgrade"
// },
// ]
// }
//
const data = this.safeList(response, 'data', []);
const dataLength = data.length;
const update = {
'updated': undefined,
'status': (dataLength === 0) ? 'ok' : 'maintenance',
'eta': undefined,
'url': undefined,
'info': response,
};
for (let i = 0; i < data.length; i++) {
const event = data[i];
const state = this.safeString(event, 'state');
update['eta'] = this.safeInteger(event, 'end');
update['url'] = this.safeString(event, 'href');
if (state === 'ongoing') {
update['status'] = 'maintenance';
}
else if (state === 'scheduled') {
update['status'] = 'ok';
}
else if (state === 'completed') {
update['status'] = 'ok';
}
else if (state === 'canceled') {
update['status'] = 'ok';
}
}
return update;
}
/**
* @method
* @name okx#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-system-time
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
async fetchTime(params = {}) {
const response = await this.publicGetPublicTime(params);
//
// {
// "code": "0",
// "data": [
// {"ts": "1621247923668"}
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
return this.safeInteger(first, 'ts');
}
/**
* @method
* @name okx#fetchAccounts
* @description fetch all the accounts associated with a profile
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-account-configuration
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [account structures]{@link https://docs.ccxt.com/#/?id=account-structure} indexed by the account type
*/
async fetchAccounts(params = {}) {
const response = await this.privateGetAccountConfig(params);
//
// {
// "code": "0",
// "data": [
// {
// "acctLv": "2",
// "autoLoan": false,
// "ctIsoMode": "automatic",
// "greeksType": "PA",
// "level": "Lv1",
// "levelTmp": "",
// "mgnIsoMode": "automatic",
// "posMode": "long_short_mode",
// "uid": "88018754289672195"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const result = [];
for (let i = 0; i < data.length; i++) {
const account = data[i];
const accountId = this.safeString(account, 'uid');
const type = this.safeString(account, 'acctLv');
result.push({
'id': accountId,
'type': type,
'currency': undefined,
'info': account,
'code': undefined,
});
}
return result;
}
nonce() {
return this.milliseconds() - this.options['timeDifference'];
}
/**
* @method
* @name okx#fetchMarkets
* @description retrieves data on all markets for okx
* @see https://www.okx.com/docs-v5/en/#rest-api-public-data-get-instruments
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
if (this.options['adjustForTimeDifference']) {
await this.loadTimeDifference();
}
const types = this.safeList(this.options, 'fetchMarkets', []);
let promises = [];
let result = [];
for (let i = 0; i < types.length; i++) {
promises.push(this.fetchMarketsByType(types[i], params));
}
promises = await Promise.all(promises);
for (let i = 0; i < promises.length; i++) {
result = this.arrayConcat(result, promises[i]);
}
return result;
}
parseMarket(market) {
//
// {
// "alias": "", // this_week, next_week, quarter, next_quarter
// "baseCcy": "BTC",
// "category": "1",
// "ctMult": "",
// "ctType": "", // inverse, linear
// "ctVal": "",
// "ctValCcy": "",
// "expTime": "",
// "instId": "BTC-USDT", // BTC-USD-210521, CSPR-USDT-SWAP, BTC-USD-210517-44000-C
// "instType": "SPOT", // SPOT, FUTURES, SWAP, OPTION
// "lever": "10",
// "listTime": "1548133413000",
// "lotSz": "0.00000001",
// "minSz": "0.00001",
// "optType": "",
// "quoteCcy": "USDT",
// "settleCcy": "",
// "state": "live",
// "stk": "",
// "tickSz": "0.1",
// "uly": ""
// }
//
// {
// "alias": "",
// "baseCcy": "",
// "category": "1",
// "ctMult": "0.1",
// "ctType": "",
// "ctVal": "1",
// "ctValCcy": "BTC",
// "expTime": "1648195200000",
// "instId": "BTC-USD-220325-194000-P",
// "instType": "OPTION",
// "lever": "",
// "listTime": "1631262612280",
// "contTdSwTime": "1631262812280",
// "lotSz": "1",
// "minSz": "1",
// "optType": "P",
// "quoteCcy": "",
// "settleCcy": "BTC",
// "state": "live",
// "stk": "194000",
// "tickSz": "0.0005",
// "uly": "BTC-USD"
// }
//
const id = this.safeString(market, 'instId');
let type = this.safeStringLower(market, 'instType');
if (type === 'futures') {
type = 'future';
}
const spot = (type === 'spot');
const future = (type === 'future');
const swap = (type === 'swap');
const option = (type === 'option');
const contract = swap || future || option;
let baseId = this.safeString(market, 'baseCcy', ''); // defaulting to '' because some weird preopen markets have empty baseId
let quoteId = this.safeString(market, 'quoteCcy', '');
const settleId = this.safeString(market, 'settleCcy');
const settle = this.safeCurrencyCode(settleId);
const underlying = this.safeString(market, 'uly');
if ((underlying !== undefined) && !spot) {
const parts = underlying.split('-');
baseId = this.safeString(parts, 0);
quoteId = this.safeString(parts, 1);
}
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
let symbol = base + '/' + quote;
let expiry = undefined;
let strikePrice = undefined;
let optionType = undefined;
if (contract) {
if (settle !== undefined) {
symbol = symbol + ':' + settle;
}
if (future) {
expiry = this.safeInteger(market, 'expTime');
if (expiry !== undefined) {
const ymd = this.yymmdd(expiry);
symbol = symbol + '-' + ymd;
}
}
else if (option) {
expiry = this.safeInteger(market, 'expTime');
strikePrice = this.safeString(market, 'stk');
optionType = this.safeString(market, 'optType');
if (expiry !== undefined) {
const ymd = this.yymmdd(expiry);
symbol = symbol + '-' + ymd + '-' + strikePrice + '-' + optionType;
optionType = (optionType === 'P') ? 'put' : 'call';
}
}
}
const fees = this.safeDict2(this.fees, type, 'trading', {});
let maxLeverage = this.safeString(market, 'lever', '1');
maxLeverage = Precise["default"].stringMax(maxLeverage, '1');
const maxSpotCost = this.safeNumber(market, 'maxMktSz');
return this.extend(fees, {
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': type,
'spot': spot,
'margin': spot && (Precise["default"].stringGt(maxLeverage, '1')),
'swap': swap,
'future': future,
'option': option,
'active': true,
'contract': contract,
'linear': contract ? (quoteId === settleId) : undefined,
'inverse': contract ? (baseId === settleId) : undefined,
'contractSize': contract ? this.safeNumber(market, 'ctVal') : undefined,
'expiry': expiry,
'expiryDatetime': this.iso8601(expiry),
'strike': this.parseNumber(strikePrice),
'optionType': optionType,
'created': this.safeInteger2(market, 'contTdSwTime', 'listTime'),
'precision': {
'amount': this.safeNumber(market, 'lotSz'),
'price': this.safeNumber(market, 'tickSz'),
},
'limits': {
'leverage': {
'min': this.parseNumber('1'),
'max': this.parseNumber(maxLeverage),
},
'amount': {
'min': this.safeNumber(market, 'minSz'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': contract ? undefined : maxSpotCost,
},
},
'info': market,
});
}
async fetchMarketsByType(type, params = {}) {
const request = {
'instType': this.convertToInstrumentType(type),
};
if (type === 'option') {
const optionsUnderlying = this.safeList(this.options, 'defaultUnderlying', ['BTC-USD', 'ETH-USD']);
const promises = [];
for (let i = 0; i < optionsUnderlying.length; i++) {
const underlying = optionsUnderlying[i];
request['uly'] = underlying;
promises.push(this.publicGetPublicInstruments(this.extend(request, params)));
}
const promisesResult = await Promise.all(promises);
let markets = [];
for (let i = 0; i < promisesResult.length; i++) {
const res = this.safeDict(promisesResult, i, {});
const options = this.safeList(res, 'data', []);
markets = this.arrayConcat(markets, options);
}
return this.parseMarkets(markets);
}
const response = await this.publicGetPublicInstruments(this.extend(request, params));
//
// spot, future, swap, option
//
// {
// "code": "0",
// "data": [
// {
// "alias": "", // this_week, next_week, quarter, next_quarter
// "baseCcy": "BTC",
// "category": "1",
// "ctMult": "",
// "ctType": "", // inverse, linear
// "ctVal": "",
// "ctValCcy": "",
// "expTime": "",
// "instId": "BTC-USDT", // BTC-USD-210521, CSPR-USDT-SWAP, BTC-USD-210517-44000-C
// "instType": "SPOT", // SPOT, FUTURES, SWAP, OPTION
// "lever": "10",
// "listTime": "1548133413000",
// "lotSz": "0.00000001",
// "minSz": "0.00001",
// "optType": "",
// "quoteCcy": "USDT",
// "settleCcy": "",
// "state": "live",
// "stk": "",
// "tickSz": "0.1",
// "uly": ""
// }
// ],
// "msg": ""
// }
//
const dataResponse = this.safeList(response, 'data', []);
return this.parseMarkets(dataResponse);
}
/**
* @method
* @name okx#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://www.okx.com/docs-v5/en/#rest-api-funding-get-currencies
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an associative dictionary of currencies
*/
async fetchCurrencies(params = {}) {
// this endpoint requires authentication
// while fetchCurrencies is a public API method by design
// therefore we check the keys here
// and fallback to generating the currencies from the markets
const isSandboxMode = this.safeBool(this.options, 'sandboxMode', false);
if (!this.checkRequiredCredentials(false) || isSandboxMode) {
return undefined;
}
//
// has['fetchCurrencies'] is currently set to true, but an unauthorized request returns
//
// {"msg":"Request header “OK_ACCESS_KEY“ can't be empty.","code":"50103"}
//
const response = await this.privateGetAssetCurrencies(params);
//
// {
// "code": "0",
// "data": [
// {
// "canDep": true,
// "canInternal": false,
// "canWd": true,
// "ccy": "USDT",
// "chain": "USDT-TRC20",
// "logoLink": "https://static.coinall.ltd/cdn/assets/imgs/221/5F74EB20302D7761.png",
// "mainNet": false,
// "maxFee": "1.6",
// "maxWd": "8852150",
// "minFee": "0.8",
// "minWd": "2",
// "name": "Tether",
// "usedWdQuota": "0",
// "wdQuota": "500",
// "wdTickSz": "3"
// },
// {
// "canDep": true,
// "canInternal": false,
// "canWd": true,
// "ccy": "USDT",
// "chain": "USDT-ERC20",
// "logoLink": "https://static.coinall.ltd/cdn/assets/imgs/221/5F74EB20302D7761.png",
// "mainNet": false,
// "maxFee": "16",
// "maxWd": "8852150",
// "minFee": "8",
// "minWd": "2",
// "name": "Tether",
// "usedWdQuota": "0",
// "wdQuota": "500",
// "wdTickSz": "3"
// },
// ...
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const result = {};
const dataByCurrencyId = this.groupBy(data, 'ccy');
const currencyIds = Object.keys(dataByCurrencyId);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const currency = this.safeCurrency(currencyId);
const code = currency['code'];
const chains = dataByCurrencyId[currencyId];
const networks = {};
let type = 'crypto';
const chainsLength = chains.length;
for (let j = 0; j < chainsLength; j++) {
const chain = chains[j];
// allow empty string for rare fiat-currencies, e.g. TRY
const networkId = this.safeString(chain, 'chain', ''); // USDT-BEP20, USDT-Avalance-C, etc
if (networkId === '') {
// only happens for fiat 'TRY' currency
type = 'fiat';
}
const idParts = networkId.split('-');
const parts = this.arraySlice(idParts, 1);
const chainPart = parts.join('-');
const networkCode = this.networkIdToCode(chainPart, currency['code']);
networks[networkCode] = {
'id': networkId,
'network': networkCode,
'active': undefined,
'deposit': this.safeBool(chain, 'canDep'),
'withdraw': this.safeBool(chain, 'canWd'),
'fee': this.safeNumber(chain, 'fee'),
'precision': this.parseNumber(this.parsePrecision(this.safeString(chain, 'wdTickSz'))),
'limits': {
'withdraw': {
'min': this.safeNumber(chain, 'minWd'),
'max': this.safeNumber(chain, 'maxWd'),
},
},
'info': chain,
};
}
const firstChain = this.safeDict(chains, 0, {});
result[code] = this.safeCurrencyStructure({
'info': chains,
'code': code,
'id': currencyId,
'name': this.safeString(firstChain, 'name'),
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'fee': undefined,
'precision': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
},
'type': type,
'networks': networks,
});
}
return result;
}
/**
* @method
* @name okx#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://www.okx.com/docs-v5/en/#order-book-trading-market-data-get-order-book
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.method] 'publicGetMarketBooksFull' or 'publicGetMarketBooks' default is 'publicGetMarketBooks'
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
let method = undefined;
[method, params] = this.handleOptionAndParams(params, 'fetchOrderBook', 'method', 'publicGetMarketBooks');
if (method === 'publicGetMarketBooksFull' && limit === undefined) {
limit = 5000;
}
limit = (limit === undefined) ? 100 : limit;
if (limit !== undefined) {
request['sz'] = limit; // max 400
}
let response = undefined;
if ((method === 'publicGetMarketBooksFull') || (limit > 400)) {
response = await this.publicGetMarketBooksFull(this.extend(request, params));
}
else {
response = await this.publicGetMarketBooks(this.extend(request, params));
}
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "asks": [
// ["0.07228","4.211619","0","2"], // price, amount, liquidated orders, total open orders
// ["0.0723","299.880364","0","2"],
// ["0.07231","3.72832","0","1"],
// ],
// "bids": [
// ["0.07221","18.5","0","1"],
// ["0.0722","18.5","0","1"],
// ["0.07219","0.505407","0","1"],
// ],
// "ts": "1621438475342"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
const timestamp = this.safeInteger(first, 'ts');
return this.parseOrderBook(first, symbol, timestamp);
}
parseTicker(ticker, market = undefined) {
//
// {
// "instType":"SWAP",
// "instId":"BTC-USDT-SWAP",
// "markPx":"200",
// "ts":"1597026383085"
// }
//
// {
// "instType": "SPOT",
// "instId": "ETH-BTC",
// "last": "0.07319",
// "lastSz": "0.044378",
// "askPx": "0.07322",
// "askSz": "4.2",
// "bidPx": "0.0732",
// "bidSz": "6.050058",
// "open24h": "0.07801",
// "high24h": "0.07975",
// "low24h": "0.06019",
// "volCcy24h": "11788.887619",
// "vol24h": "167493.829229",
// "ts": "1621440583784",
// "sodUtc0": "0.07872",
// "sodUtc8": "0.07345"
// }
// {
// instId: 'LTC-USDT',
// idxPx: '65.74',
// open24h: '65.37',
// high24h: '66.15',
// low24h: '64.97',
// sodUtc0: '65.68',
// sodUtc8: '65.54',
// ts: '1728467346900'
// },
//
const timestamp = this.safeInteger(ticker, 'ts');
const marketId = this.safeString(ticker, 'instId');
market = this.safeMarket(marketId, market, '-');
const symbol = market['symbol'];
const last = this.safeString(ticker, 'last');
const open = this.safeString(ticker, 'open24h');
const spot = this.safeBool(market, 'spot', false);
const quoteVolume = spot ? this.safeString(ticker, 'volCcy24h') : undefined;
const baseVolume = this.safeString(ticker, 'vol24h');
const high = this.safeString(ticker, 'high24h');
const low = this.safeString(ticker, 'low24h');
return this.safeTicker({
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'high': high,
'low': low,
'bid': this.safeString(ticker, 'bidPx'),
'bidVolume': this.safeString(ticker, 'bidSz'),
'ask': this.safeString(ticker, 'askPx'),
'askVolume': this.safeString(ticker, 'askSz'),
'vwap': undefined,
'open': open,
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': undefined,
'average': undefined,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'markPrice': this.safeString(ticker, 'markPx'),
'indexPrice': this.safeString(ticker, 'idxPx'),
'info': ticker,
}, market);
}
/**
* @method
* @name okx#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://www.okx.com/docs-v5/en/#order-book-trading-market-data-get-ticker
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTicker(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
const response = await this.publicGetMarketTicker(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "instType": "SPOT",
// "instId": "ETH-BTC",
// "last": "0.07319",
// "lastSz": "0.044378",
// "askPx": "0.07322",
// "askSz": "4.2",
// "bidPx": "0.0732",
// "bidSz": "6.050058",
// "open24h": "0.07801",
// "high24h": "0.07975",
// "low24h": "0.06019",
// "volCcy24h": "11788.887619",
// "vol24h": "167493.829229",
// "ts": "1621440583784",
// "sodUtc0": "0.07872",
// "sodUtc8": "0.07345"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
return this.parseTicker(first, market);
}
/**
* @method
* @name okx#fetchTickers
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @see https://www.okx.com/docs-v5/en/#order-book-trading-market-data-get-tickers
* @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTickers(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols);
const market = this.getMarketFromSymbols(symbols);
let marketType = undefined;
[marketType, params] = this.handleMarketTypeAndParams('fetchTickers', market, params);
const request = {
'instType': this.convertToInstrumentType(marketType),
};
if (marketType === 'option') {
const defaultUnderlying = this.safeString(this.options, 'defaultUnderlying', 'BTC-USD');
const currencyId = this.safeString2(params, 'uly', 'marketId', defaultUnderlying);
if (currencyId === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchTickers() requires an underlying uly or marketId parameter for options markets');
}
else {
request['uly'] = currencyId;
}
}
const response = await this.publicGetMarketTickers(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "instType": "SPOT",
// "instId": "BCD-BTC",
// "last": "0.0000769",
// "lastSz": "5.4788",
// "askPx": "0.0000777",
// "askSz": "3.2197",
// "bidPx": "0.0000757",
// "bidSz": "4.7509",
// "open24h": "0.0000885",
// "high24h": "0.0000917",
// "low24h": "0.0000596",
// "volCcy24h": "9.2877",
// "vol24h": "124824.1985",
// "ts": "1621441741434",
// "sodUtc0": "0.0000905",
// "sodUtc8": "0.0000729"
// },
// ]
// }
//
const tickers = this.safeList(response, 'data', []);
return this.parseTickers(tickers, symbols);
}
/**
* @method
* @name okx#fetchMarkPrice
* @description fetches mark price for the market
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-mark-price
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchMarkPrice(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
const response = await this.publicGetPublicMarkPrice(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "instId": "ETH-USDT",
// "instType": "MARGIN",
// "markPx": "2403.98",
// "ts": "1728578500703"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data');
return this.parseTicker(this.safeDict(data, 0), market);
}
/**
* @method
* @name okx#fetchMarkPrices
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-mark-price
* @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchMarkPrices(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols);
const market = this.getMarketFromSymbols(symbols);
let marketType = undefined;
[marketType, params] = this.handleMarketTypeAndParams('fetchTickers', market, params, 'swap');
const request = {
'instType': this.convertToInstrumentType(marketType),
};
if (marketType === 'option') {
const defaultUnderlying = this.safeString(this.options, 'defaultUnderlying', 'BTC-USD');
const currencyId = this.safeString2(params, 'uly', 'marketId', defaultUnderlying);
if (currencyId === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchMarkPrices() requires an underlying uly or marketId parameter for options markets');
}
else {
request['uly'] = currencyId;
}
}
const response = await this.publicGetPublicMarkPrice(this.extend(request, params));
const tickers = this.safeList(response, 'data', []);
return this.parseTickers(tickers, symbols);
}
parseTrade(trade, market = undefined) {
//
// public fetchTrades
//
// {
// "instId": "ETH-BTC",
// "side": "sell",
// "sz": "0.119501",
// "px": "0.07065",
// "tradeId": "15826757",
// "ts": "1621446178316"
// }
//
// option: fetchTrades
//
// {
// "fillVol": "0.46387625976562497",
// "fwdPx": "26299.754935451125",
// "indexPx": "26309.7",
// "instFamily": "BTC-USD",
// "instId": "BTC-USD-230526-26000-C",
// "markPx": "0.042386283557554236",
// "optType": "C",
// "px": "0.0415",
// "side": "sell",
// "sz": "90",
// "tradeId": "112",
// "ts": "1683907480154"
// }
//
// private fetchMyTrades
//
// {
// "side": "buy",
// "fillSz": "0.007533",
// "fillPx": "2654.98",
// "fee": "-0.000007533",
// "ordId": "317321390244397056",
// "instType": "SPOT",
// "instId": "ETH-USDT",
// "clOrdId": "",
// "posSide": "net",
// "billId": "317321390265368576",
// "tag": "0",
// "execType": "T",
// "tradeId": "107601752",
// "feeCcy": "ETH",
// "ts": "1621927314985"
// }
//
const id = this.safeString(trade, 'tradeId');
const marketId = this.safeString(trade, 'instId');
market = this.safeMarket(marketId, market, '-');
const symbol = market['symbol'];
const timestamp = this.safeInteger(trade, 'ts');
const price = this.safeString2(trade, 'fillPx', 'px');
const amount = this.safeString2(trade, 'fillSz', 'sz');
const side = this.safeString(trade, 'side');
const orderId = this.safeString(trade, 'ordId');
const feeCostString = this.safeString(trade, 'fee');
let fee = undefined;
if (feeCostString !== undefined) {
const feeCostSigned = Precise["default"].stringNeg(feeCostString);
const feeCurrencyId = this.safeString(trade, 'feeCcy');
const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId);
fee = {
'cost': feeCostSigned,
'currency': feeCurrencyCode,
};
}
let takerOrMaker = this.safeString(trade, 'execType');
if (takerOrMaker === 'T') {
takerOrMaker = 'taker';
}
else if (takerOrMaker === 'M') {
takerOrMaker = 'maker';
}
return this.safeTrade({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': symbol,
'id': id,
'order': orderId,
'type': undefined,
'takerOrMaker': takerOrMaker,
'side': side,
'price': price,
'amount': amount,
'cost': undefined,
'fee': fee,
}, market);
}
/**
* @method
* @name okx#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://www.okx.com/docs-v5/en/#rest-api-market-data-get-trades
* @see https://www.okx.com/docs-v5/en/#rest-api-public-data-get-option-trades
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.method] 'publicGetMarketTrades' or 'publicGetMarketHistoryTrades' default is 'publicGetMarketTrades'
* @param {boolean} [params.paginate] *only applies to publicGetMarketHistoryTrades* default false, when true will automatically paginate by calling this endpoint multiple times
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchTrades', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallCursor('fetchTrades', symbol, since, limit, params, 'tradeId', 'after', undefined, 100);
}
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
let response = undefined;
if (market['option']) {
response = await this.publicGetPublicOptionTrades(this.extend(request, params));
}
else {
if (limit !== undefined) {
request['limit'] = limit; // default 100
}
let method = undefined;
[method, params] = this.handleOptionAndParams(params, 'fetchTrades', 'method', 'publicGetMarketTrades');
if (method === 'publicGetMarketTrades') {
response = await this.publicGetMarketTrades(this.extend(request, params));
}
else if (method === 'publicGetMarketHistoryTrades') {
response = await this.publicGetMarketHistoryTrades(this.extend(request, params));
}
}
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {"instId":"ETH-BTC","side":"sell","sz":"0.119501","px":"0.07065","tradeId":"15826757","ts":"1621446178316"},
// {"instId":"ETH-BTC","side":"sell","sz":"0.03","px":"0.07068","tradeId":"15826756","ts":"1621446178066"},
// {"instId":"ETH-BTC","side":"buy","sz":"0.507","px":"0.07069","tradeId":"15826755","ts":"1621446175085"},
// ]
// }
//
// option
//
// {
// "code": "0",
// "data": [
// {
// "fillVol": "0.46387625976562497",
// "fwdPx": "26299.754935451125",
// "indexPx": "26309.7",
// "instFamily": "BTC-USD",
// "instId": "BTC-USD-230526-26000-C",
// "markPx": "0.042386283557554236",
// "optType": "C",
// "px": "0.0415",
// "side": "sell",
// "sz": "90",
// "tradeId": "112",
// "ts": "1683907480154"
// },
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTrades(data, market, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
//
// [
// "1678928760000", // timestamp
// "24341.4", // open
// "24344", // high
// "24313.2", // low
// "24323", // close
// "628", // contract volume
// "2.5819", // base volume
// "62800", // quote volume
// "0" // candlestick state
// ]
//
const res = this.handleMarketTypeAndParams('fetchOHLCV', market, undefined);
const type = res[0];
const volumeIndex = (type === 'spot') ? 5 : 6;
return [
this.safeInteger(ohlcv, 0),
this.safeNumber(ohlcv, 1),
this.safeNumber(ohlcv, 2),
this.safeNumber(ohlcv, 3),
this.safeNumber(ohlcv, 4),
this.safeNumber(ohlcv, volumeIndex),
];
}
/**
* @method
* @name okx#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://www.okx.com/docs-v5/en/#rest-api-market-data-get-candlesticks
* @see https://www.okx.com/docs-v5/en/#rest-api-market-data-get-candlesticks-history
* @see https://www.okx.com/docs-v5/en/#rest-api-market-data-get-mark-price-candlesticks
* @see https://www.okx.com/docs-v5/en/#rest-api-market-data-get-mark-price-candlesticks-history
* @see https://www.okx.com/docs-v5/en/#rest-api-market-data-get-index-candlesticks
* @see https://www.okx.com/docs-v5/en/#rest-api-market-data-get-index-candlesticks-history
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.price] "mark" or "index" for mark price and index price candles
* @param {int} [params.until] timestamp in ms of the latest candle to fetch
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 200);
}
const price = this.safeString(params, 'price');
params = this.omit(params, 'price');
const options = this.safeDict(this.options, 'fetchOHLCV', {});
const timezone = this.safeString(options, 'timezone', 'UTC');
if (limit === undefined) {
limit = 100; // default 100, max 100
}
else {
limit = Math.min(limit, 300); // max 100
}
const duration = this.parseTimeframe(timeframe);
let bar = this.safeString(this.timeframes, timeframe, timeframe);
if ((timezone === 'UTC') && (duration >= 21600)) { // if utc and timeframe >= 6h
bar += timezone.toLowerCase();
}
const request = {
'instId': market['id'],
'bar': bar,
'limit': limit,
};
let defaultType = 'Candles';
if (since !== undefined) {
const now = this.milliseconds();
const durationInMilliseconds = duration * 1000;
// switch to history candles if since is past the cutoff for current candles
const historyBorder = now - ((1440 - 1) * durationInMilliseconds);
if (since < historyBorder) {
defaultType = 'HistoryCandles';
limit = Math.min(limit, 100); // max 100 for historical endpoint
}
const startTime = Math.max(since - 1, 0);
request['before'] = startTime;
request['after'] = this.sum(since, durationInMilliseconds * limit);
}
const until = this.safeInteger(params, 'until');
if (until !== undefined) {
request['after'] = until;
params = this.omit(params, 'until');
}
defaultType = this.safeString(options, 'type', defaultType); // Candles or HistoryCandles
const type = this.safeString(params, 'type', defaultType);
params = this.omit(params, 'type');
const isHistoryCandles = (type === 'HistoryCandles');
let response = undefined;
if (price === 'mark') {
if (isHistoryCandles) {
response = await this.publicGetMarketHistoryMarkPriceCandles(this.extend(request, params));
}
else {
response = await this.publicGetMarketMarkPriceCandles(this.extend(request, params));
}
}
else if (price === 'index') {
request['instId'] = market['info']['instFamily']; // okx index candles require instFamily instead of instId
if (isHistoryCandles) {
response = await this.publicGetMarketHistoryIndexCandles(this.extend(request, params));
}
else {
response = await this.publicGetMarketIndexCandles(this.extend(request, params));
}
}
else {
if (isHistoryCandles) {
response = await this.publicGetMarketHistoryCandles(this.extend(request, params));
}
else {
response = await this.publicGetMarketCandles(this.extend(request, params));
}
}
//
// {
// "code": "0",
// "msg": "",
// "data": [
// ["1678928760000","24341.4","24344","24313.2","24323","628","2.5819","62800","0"],
// ["1678928700000","24324.1","24347.6","24321.7","24341.4","2565","10.5401","256500","1"],
// ["1678928640000","24300.2","24324.1","24288","24324.1","3304","13.5937","330400","1"],
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseOHLCVs(data, market, timeframe, since, limit);
}
/**
* @method
* @name okx#fetchFundingRateHistory
* @description fetches historical funding rate prices
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-funding-rate-history
* @param {string} symbol unified symbol of the market to fetch the funding rate history for
* @param {int} [since] timestamp in ms of the earliest funding rate to fetch
* @param {int} [limit] the maximum amount of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure} to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {object[]} a list of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure}
*/
async fetchFundingRateHistory(symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchFundingRateHistory() requires a symbol argument');
}
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchFundingRateHistory', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDeterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params, 100);
}
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
if (since !== undefined) {
request['before'] = Math.max(since - 1, 0);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetPublicFundingRateHistory(this.extend(request, params));
//
// {
// "code":"0",
// "msg":"",
// "data":[
// {
// "instType":"SWAP",
// "instId":"BTC-USDT-SWAP",
// "fundingRate":"0.018",
// "realizedRate":"0.017",
// "fundingTime":"1597026383085"
// },
// {
// "instType":"SWAP",
// "instId":"BTC-USDT-SWAP",
// "fundingRate":"0.018",
// "realizedRate":"0.017",
// "fundingTime":"1597026383085"
// }
// ]
// }
//
const rates = [];
const data = this.safeList(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const rate = data[i];
const timestamp = this.safeInteger(rate, 'fundingTime');
rates.push({
'info': rate,
'symbol': this.safeSymbol(this.safeString(rate, 'instId')),
'fundingRate': this.safeNumber(rate, 'realizedRate'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
});
}
const sorted = this.sortBy(rates, 'timestamp');
return this.filterBySymbolSinceLimit(sorted, market['symbol'], since, limit);
}
parseBalanceByType(type, response) {
if (type === 'funding') {
return this.parseFundingBalance(response);
}
else {
return this.parseTradingBalance(response);
}
}
parseTradingBalance(response) {
const result = { 'info': response };
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
const timestamp = this.safeInteger(first, 'uTime');
const details = this.safeList(first, 'details', []);
for (let i = 0; i < details.length; i++) {
const balance = details[i];
const currencyId = this.safeString(balance, 'ccy');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
// it may be incorrect to use total, free and used for swap accounts
const eq = this.safeString(balance, 'eq');
const availEq = this.safeString(balance, 'availEq');
if ((eq === undefined) || (availEq === undefined)) {
account['free'] = this.safeString(balance, 'availBal');
account['used'] = this.safeString(balance, 'frozenBal');
}
else {
account['total'] = eq;
account['free'] = availEq;
}
result[code] = account;
}
result['timestamp'] = timestamp;
result['datetime'] = this.iso8601(timestamp);
return this.safeBalance(result);
}
parseFundingBalance(response) {
const result = { 'info': response };
const data = this.safeList(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const balance = data[i];
const currencyId = this.safeString(balance, 'ccy');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
// it may be incorrect to use total, free and used for swap accounts
account['total'] = this.safeString(balance, 'bal');
account['free'] = this.safeString(balance, 'availBal');
account['used'] = this.safeString(balance, 'frozenBal');
result[code] = account;
}
return this.safeBalance(result);
}
parseTradingFee(fee, market = undefined) {
// https://www.okx.com/docs-v5/en/#rest-api-account-get-fee-rates
//
// {
// "category": "1",
// "delivery": "",
// "exercise": "",
// "instType": "SPOT",
// "level": "Lv1",
// "maker": "-0.0008",
// "taker": "-0.001",
// "ts": "1639043138472"
// }
//
return {
'info': fee,
'symbol': this.safeSymbol(undefined, market),
// OKX returns the fees as negative values opposed to other exchanges, so the sign needs to be flipped
'maker': this.parseNumber(Precise["default"].stringNeg(this.safeString2(fee, 'maker', 'makerU'))),
'taker': this.parseNumber(Precise["default"].stringNeg(this.safeString2(fee, 'taker', 'takerU'))),
'percentage': undefined,
'tierBased': undefined,
};
}
/**
* @method
* @name okx#fetchTradingFee
* @description fetch the trading fees for a market
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-fee-rates
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [fee structure]{@link https://docs.ccxt.com/#/?id=fee-structure}
*/
async fetchTradingFee(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instType': this.convertToInstrumentType(market['type']), // SPOT, MARGIN, SWAP, FUTURES, OPTION
// "instId": market["id"], // only applicable to SPOT/MARGIN
// "uly": market["id"], // only applicable to FUTURES/SWAP/OPTION
// "category": "1", // 1 = Class A, 2 = Class B, 3 = Class C, 4 = Class D
};
if (market['spot']) {
request['instId'] = market['id'];
}
else if (market['swap'] || market['future'] || market['option']) {
request['uly'] = market['baseId'] + '-' + market['quoteId'];
}
else {
throw new errors.NotSupported(this.id + ' fetchTradingFee() supports spot, swap, future or option markets only');
}
const response = await this.privateGetAccountTradeFee(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "category": "1",
// "delivery": "",
// "exercise": "",
// "instType": "SPOT",
// "level": "Lv1",
// "maker": "-0.0008",
// "taker": "-0.001",
// "ts": "1639043138472"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
return this.parseTradingFee(first, market);
}
/**
* @method
* @name okx#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-balance
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-balance
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.type] wallet type, ['funding' or 'trading'] default is 'trading'
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
*/
async fetchBalance(params = {}) {
await this.loadMarkets();
const [marketType, query] = this.handleMarketTypeAndParams('fetchBalance', undefined, params);
const request = {
// 'ccy': 'BTC,ETH', // comma-separated list of currency ids
};
let response = undefined;
if (marketType === 'funding') {
response = await this.privateGetAssetBalances(this.extend(request, query));
}
else {
response = await this.privateGetAccountBalance(this.extend(request, query));
}
//
// {
// "code": "0",
// "data": [
// {
// "adjEq": "",
// "details": [
// {
// "availBal": "",
// "availEq": "28.21006347",
// "cashBal": "28.21006347",
// "ccy": "USDT",
// "crossLiab": "",
// "disEq": "28.2687404020176",
// "eq":"28 .21006347",
// "eqUsd": "28.2687404020176",
// "frozenBal": "0",
// "interest": "",
// "isoEq": "0",
// "isoLiab": "",
// "liab": "",
// "maxLoan": "",
// "mgnRatio": "",
// "notionalLever": "0",
// "ordFrozen": "0",
// "twap": "0",
// "uTime": "1621556539861",
// "upl": "0",
// "uplLiab": ""
// }
// ],
// "imr": "",
// "isoEq": "0",
// "mgnRatio": "",
// "mmr": "",
// "notionalUsd": "",
// "ordFroz": "",
// "totalEq": "28.2687404020176",
// "uTime": "1621556553510"
// }
// ],
// "msg": ""
// }
//
// {
// "code": "0",
// "data": [
// {
// "adjEq": "",
// "details": [
// {
// "availBal": "0.049",
// "availEq": "",
// "cashBal": "0.049",
// "ccy": "BTC",
// "crossLiab": "",
// "disEq": "1918.55678",
// "eq": "0.049",
// "eqUsd": "1918.55678",
// "frozenBal": "0",
// "interest": "",
// "isoEq": "",
// "isoLiab": "",
// "liab": "",
// "maxLoan": "",
// "mgnRatio": "",
// "notionalLever": "",
// "ordFrozen": "0",
// "twap": "0",
// "uTime": "1621973128591",
// "upl": "",
// "uplLiab": ""
// }
// ],
// "imr": "",
// "isoEq": "",
// "mgnRatio": "",
// "mmr": "",
// "notionalUsd": "",
// "ordFroz": "",
// "totalEq": "1918.55678",
// "uTime": "1622045126908"
// }
// ],
// "msg": ""
// }
//
// funding
//
// {
// "code": "0",
// "data": [
// {
// "availBal": "0.00005426",
// "bal": 0.0000542600000000,
// "ccy": "BTC",
// "frozenBal": "0"
// }
// ],
// "msg": ""
// }
//
return this.parseBalanceByType(marketType, response);
}
/**
* @method
* @name okx#createMarketBuyOrderWithCost
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-place-order
* @description create a market buy order by providing the symbol and cost
* @param {string} symbol unified symbol of the market to create an order in
* @param {float} cost how much you want to trade in units of the quote currency
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async createMarketBuyOrderWithCost(symbol, cost, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['spot']) {
throw new errors.NotSupported(this.id + ' createMarketBuyOrderWithCost() supports spot markets only');
}
const req = {
'createMarketBuyOrderRequiresPrice': false,
'tgtCcy': 'quote_ccy',
};
return await this.createOrder(symbol, 'market', 'buy', cost, undefined, this.extend(req, params));
}
/**
* @method
* @name okx#createMarketSellOrderWithCost
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-place-order
* @description create a market buy order by providing the symbol and cost
* @param {string} symbol unified symbol of the market to create an order in
* @param {float} cost how much you want to trade in units of the quote currency
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async createMarketSellOrderWithCost(symbol, cost, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['spot']) {
throw new errors.NotSupported(this.id + ' createMarketSellOrderWithCost() supports spot markets only');
}
const req = {
'createMarketBuyOrderRequiresPrice': false,
'tgtCcy': 'quote_ccy',
};
return await this.createOrder(symbol, 'market', 'sell', cost, undefined, this.extend(req, params));
}
createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
const market = this.market(symbol);
const request = {
'instId': market['id'],
// 'ccy': currency['id'], // only applicable to cross MARGIN orders in single-currency margin
// 'clOrdId': clientOrderId, // up to 32 characters, must be unique
// 'tag': tag, // up to 8 characters
'side': side,
// 'posSide': 'long', // long, short, // required in the long/short mode, and can only be long or short (only for future or swap)
'ordType': type,
// 'ordType': type, // privatePostTradeOrder: market, limit, post_only, fok, ioc, optimal_limit_ioc
// 'ordType': type, // privatePostTradeOrderAlgo: conditional, oco, trigger, move_order_stop, iceberg, twap
'sz': this.amountToPrecision(symbol, amount),
// 'px': this.priceToPrecision (symbol, price), // limit orders only
// 'reduceOnly': false,
//
// 'triggerPx': 10, // stopPrice (trigger orders)
// 'orderPx': 10, // Order price if -1, the order will be executed at the market price. (trigger orders)
// 'triggerPxType': 'last', // Conditional default is last, mark or index (trigger orders)
//
// 'tpTriggerPx': 10, // takeProfitPrice (conditional orders)
// 'tpTriggerPxType': 'last', // Conditional default is last, mark or index (conditional orders)
// 'tpOrdPx': 10, // Order price for Take-Profit orders, if -1 will be executed at market price (conditional orders)
//
// 'slTriggerPx': 10, // stopLossPrice (conditional orders)
// 'slTriggerPxType': 'last', // Conditional default is last, mark or index (conditional orders)
// 'slOrdPx': 10, // Order price for Stop-Loss orders, if -1 will be executed at market price (conditional orders)
};
const spot = market['spot'];
const contract = market['contract'];
const triggerPrice = this.safeValueN(params, ['triggerPrice', 'stopPrice', 'triggerPx']);
const timeInForce = this.safeString(params, 'timeInForce', 'GTC');
const takeProfitPrice = this.safeValue2(params, 'takeProfitPrice', 'tpTriggerPx');
const tpOrdPx = this.safeValue(params, 'tpOrdPx', price);
const tpTriggerPxType = this.safeString(params, 'tpTriggerPxType', 'last');
const stopLossPrice = this.safeValue2(params, 'stopLossPrice', 'slTriggerPx');
const slOrdPx = this.safeValue(params, 'slOrdPx', price);
const slTriggerPxType = this.safeString(params, 'slTriggerPxType', 'last');
const clientOrderId = this.safeString2(params, 'clOrdId', 'clientOrderId');
const stopLoss = this.safeValue(params, 'stopLoss');
const stopLossDefined = (stopLoss !== undefined);
const takeProfit = this.safeValue(params, 'takeProfit');
const takeProfitDefined = (takeProfit !== undefined);
const trailingPercent = this.safeString2(params, 'trailingPercent', 'callbackRatio');
const isTrailingPercentOrder = trailingPercent !== undefined;
const trigger = (triggerPrice !== undefined) || (type === 'trigger');
const isReduceOnly = this.safeValue(params, 'reduceOnly', false);
const defaultMarginMode = this.safeString2(this.options, 'defaultMarginMode', 'marginMode', 'cross');
let marginMode = this.safeString2(params, 'marginMode', 'tdMode'); // cross or isolated, tdMode not ommited so as to be extended into the request
let margin = false;
if ((marginMode !== undefined) && (marginMode !== 'cash')) {
margin = true;
}
else {
marginMode = defaultMarginMode;
margin = this.safeBool(params, 'margin', false);
}
if (spot) {
if (margin) {
const defaultCurrency = (side === 'buy') ? market['quote'] : market['base'];
const currency = this.safeString(params, 'ccy', defaultCurrency);
request['ccy'] = this.safeCurrencyCode(currency);
}
const tradeMode = margin ? marginMode : 'cash';
request['tdMode'] = tradeMode;
}
else if (contract) {
if (market['swap'] || market['future']) {
let positionSide = undefined;
[positionSide, params] = this.handleOptionAndParams(params, 'createOrder', 'positionSide');
if (positionSide !== undefined) {
request['posSide'] = positionSide;
}
else {
let hedged = undefined;
[hedged, params] = this.handleOptionAndParams(params, 'createOrder', 'hedged');
if (hedged) {
const isBuy = (side === 'buy');
const isProtective = (takeProfitPrice !== undefined) || (stopLossPrice !== undefined) || isReduceOnly;
if (isProtective) {
// in case of protective orders, the posSide should be opposite of position side
// reduceOnly is emulated and not natively supported by the exchange
request['posSide'] = isBuy ? 'short' : 'long';
if (isReduceOnly) {
params = this.omit(params, 'reduceOnly');
}
}
else {
request['posSide'] = isBuy ? 'long' : 'short';
}
}
}
}
request['tdMode'] = marginMode;
}
const isMarketOrder = type === 'market';
let postOnly = false;
[postOnly, params] = this.handlePostOnly(isMarketOrder, type === 'post_only', params);
params = this.omit(params, ['currency', 'ccy', 'marginMode', 'timeInForce', 'stopPrice', 'triggerPrice', 'clientOrderId', 'stopLossPrice', 'takeProfitPrice', 'slOrdPx', 'tpOrdPx', 'margin', 'stopLoss', 'takeProfit', 'trailingPercent']);
const ioc = (timeInForce === 'IOC') || (type === 'ioc');
const fok = (timeInForce === 'FOK') || (type === 'fok');
const conditional = (stopLossPrice !== undefined) || (takeProfitPrice !== undefined) || (type === 'conditional');
const marketIOC = (isMarketOrder && ioc) || (type === 'optimal_limit_ioc');
const defaultTgtCcy = this.safeString(this.options, 'tgtCcy', 'base_ccy');
const tgtCcy = this.safeString(params, 'tgtCcy', defaultTgtCcy);
if ((!contract) && (!margin)) {
request['tgtCcy'] = tgtCcy;
}
if (isMarketOrder || marketIOC) {
request['ordType'] = 'market';
if (spot && (side === 'buy')) {
// spot market buy: "sz" can refer either to base currency units or to quote currency units
// see documentation: https://www.okx.com/docs-v5/en/#rest-api-trade-place-order
if (tgtCcy === 'quote_ccy') {
// quote_ccy: sz refers to units of quote currency
let createMarketBuyOrderRequiresPrice = true;
[createMarketBuyOrderRequiresPrice, params] = this.handleOptionAndParams(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true);
let notional = this.safeNumber2(params, 'cost', 'sz');
params = this.omit(params, ['cost', 'sz']);
if (createMarketBuyOrderRequiresPrice) {
if (price !== undefined) {
if (notional === undefined) {
const amountString = this.numberToString(amount);
const priceString = this.numberToString(price);
const quoteAmount = Precise["default"].stringMul(amountString, priceString);
notional = this.parseNumber(quoteAmount);
}
}
else if (notional === undefined) {
throw new errors.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 'amount' argument or in the 'cost' unified extra parameter or in exchange-specific 'sz' extra parameter (the exchange-specific behaviour)");
}
}
else {
notional = (notional === undefined) ? amount : notional;
}
request['sz'] = this.costToPrecision(symbol, notional);
}
}
if (marketIOC && contract) {
request['ordType'] = 'optimal_limit_ioc';
}
}
else {
if ((!trigger) && (!conditional)) {
request['px'] = this.priceToPrecision(symbol, price);
}
}
if (postOnly) {
request['ordType'] = 'post_only';
}
else if (ioc && !marketIOC) {
request['ordType'] = 'ioc';
}
else if (fok) {
request['ordType'] = 'fok';
}
if (isTrailingPercentOrder) {
const convertedTrailingPercent = Precise["default"].stringDiv(trailingPercent, '100');
request['callbackRatio'] = convertedTrailingPercent;
request['ordType'] = 'move_order_stop';
}
else if (stopLossDefined || takeProfitDefined) {
if (stopLossDefined) {
const stopLossTriggerPrice = this.safeValueN(stopLoss, ['triggerPrice', 'stopPrice', 'slTriggerPx']);
if (stopLossTriggerPrice === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a trigger price in params["stopLoss"]["triggerPrice"], or params["stopLoss"]["stopPrice"], or params["stopLoss"]["slTriggerPx"] for a stop loss order');
}
request['slTriggerPx'] = this.priceToPrecision(symbol, stopLossTriggerPrice);
const stopLossLimitPrice = this.safeValueN(stopLoss, ['price', 'stopLossPrice', 'slOrdPx']);
const stopLossOrderType = this.safeString(stopLoss, 'type');
if (stopLossOrderType !== undefined) {
const stopLossLimitOrderType = (stopLossOrderType === 'limit');
const stopLossMarketOrderType = (stopLossOrderType === 'market');
if ((!stopLossLimitOrderType) && (!stopLossMarketOrderType)) {
throw new errors.InvalidOrder(this.id + ' createOrder() params["stopLoss"]["type"] must be either "limit" or "market"');
}
else if (stopLossLimitOrderType) {
if (stopLossLimitPrice === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a limit price in params["stopLoss"]["price"] or params["stopLoss"]["slOrdPx"] for a stop loss limit order');
}
else {
request['slOrdPx'] = this.priceToPrecision(symbol, stopLossLimitPrice);
}
}
else if (stopLossOrderType === 'market') {
request['slOrdPx'] = '-1';
}
}
else if (stopLossLimitPrice !== undefined) {
request['slOrdPx'] = this.priceToPrecision(symbol, stopLossLimitPrice); // limit sl order
}
else {
request['slOrdPx'] = '-1'; // market sl order
}
const stopLossTriggerPriceType = this.safeString2(stopLoss, 'triggerPriceType', 'slTriggerPxType', 'last');
if (stopLossTriggerPriceType !== undefined) {
if ((stopLossTriggerPriceType !== 'last') && (stopLossTriggerPriceType !== 'index') && (stopLossTriggerPriceType !== 'mark')) {
throw new errors.InvalidOrder(this.id + ' createOrder() stop loss trigger price type must be one of "last", "index" or "mark"');
}
request['slTriggerPxType'] = stopLossTriggerPriceType;
}
}
if (takeProfitDefined) {
const takeProfitTriggerPrice = this.safeValueN(takeProfit, ['triggerPrice', 'stopPrice', 'tpTriggerPx']);
if (takeProfitTriggerPrice === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a trigger price in params["takeProfit"]["triggerPrice"], or params["takeProfit"]["stopPrice"], or params["takeProfit"]["tpTriggerPx"] for a take profit order');
}
request['tpTriggerPx'] = this.priceToPrecision(symbol, takeProfitTriggerPrice);
const takeProfitLimitPrice = this.safeValueN(takeProfit, ['price', 'takeProfitPrice', 'tpOrdPx']);
const takeProfitOrderType = this.safeString2(takeProfit, 'type', 'tpOrdKind');
if (takeProfitOrderType !== undefined) {
const takeProfitLimitOrderType = (takeProfitOrderType === 'limit');
const takeProfitMarketOrderType = (takeProfitOrderType === 'market');
if ((!takeProfitLimitOrderType) && (!takeProfitMarketOrderType)) {
throw new errors.InvalidOrder(this.id + ' createOrder() params["takeProfit"]["type"] must be either "limit" or "market"');
}
else if (takeProfitLimitOrderType) {
if (takeProfitLimitPrice === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires a limit price in params["takeProfit"]["price"] or params["takeProfit"]["tpOrdPx"] for a take profit limit order');
}
else {
request['tpOrdKind'] = takeProfitOrderType;
request['tpOrdPx'] = this.priceToPrecision(symbol, takeProfitLimitPrice);
}
}
else if (takeProfitOrderType === 'market') {
request['tpOrdPx'] = '-1';
}
}
else if (takeProfitLimitPrice !== undefined) {
request['tpOrdKind'] = 'limit';
request['tpOrdPx'] = this.priceToPrecision(symbol, takeProfitLimitPrice); // limit tp order
}
else {
request['tpOrdPx'] = '-1'; // market tp order
}
const takeProfitTriggerPriceType = this.safeString2(takeProfit, 'triggerPriceType', 'tpTriggerPxType', 'last');
if (takeProfitTriggerPriceType !== undefined) {
if ((takeProfitTriggerPriceType !== 'last') && (takeProfitTriggerPriceType !== 'index') && (takeProfitTriggerPriceType !== 'mark')) {
throw new errors.InvalidOrder(this.id + ' createOrder() take profit trigger price type must be one of "last", "index" or "mark"');
}
request['tpTriggerPxType'] = takeProfitTriggerPriceType;
}
}
}
else if (trigger) {
request['ordType'] = 'trigger';
request['triggerPx'] = this.priceToPrecision(symbol, triggerPrice);
request['orderPx'] = isMarketOrder ? '-1' : this.priceToPrecision(symbol, price);
}
else if (conditional) {
request['ordType'] = 'conditional';
const twoWayCondition = ((takeProfitPrice !== undefined) && (stopLossPrice !== undefined));
// if TP and SL are sent together
// as ordType 'conditional' only stop-loss order will be applied
// tpOrdKind is 'condition' which is the default
if (twoWayCondition) {
request['ordType'] = 'oco';
}
if (takeProfitPrice !== undefined) {
request['tpTriggerPx'] = this.priceToPrecision(symbol, takeProfitPrice);
let tpOrdPxReq = '-1';
if (tpOrdPx !== undefined) {
tpOrdPxReq = this.priceToPrecision(symbol, tpOrdPx);
}
request['tpOrdPx'] = tpOrdPxReq;
request['tpTriggerPxType'] = tpTriggerPxType;
}
if (stopLossPrice !== undefined) {
request['slTriggerPx'] = this.priceToPrecision(symbol, stopLossPrice);
let slOrdPxReq = '-1';
if (slOrdPx !== undefined) {
slOrdPxReq = this.priceToPrecision(symbol, slOrdPx);
}
request['slOrdPx'] = slOrdPxReq;
request['slTriggerPxType'] = slTriggerPxType;
}
}
if (clientOrderId === undefined) {
const brokerId = this.safeString(this.options, 'brokerId');
if (brokerId !== undefined) {
request['clOrdId'] = brokerId + this.uuid16();
request['tag'] = brokerId;
}
}
else {
request['clOrdId'] = clientOrderId;
params = this.omit(params, ['clOrdId', 'clientOrderId']);
}
return this.extend(request, params);
}
/**
* @method
* @name okx#createOrder
* @description create a trade order
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-place-order
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-place-multiple-orders
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-post-place-algo-order
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {bool} [params.reduceOnly] a mark to reduce the position size for margin, swap and future orders
* @param {bool} [params.postOnly] true to place a post only order
* @param {object} [params.takeProfit] *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered (perpetual swap markets only)
* @param {float} [params.takeProfit.triggerPrice] take profit trigger price
* @param {float} [params.takeProfit.price] used for take profit limit orders, not used for take profit market price orders
* @param {string} [params.takeProfit.type] 'market' or 'limit' used to specify the take profit price type
* @param {object} [params.stopLoss] *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered (perpetual swap markets only)
* @param {float} [params.stopLoss.triggerPrice] stop loss trigger price
* @param {float} [params.stopLoss.price] used for stop loss limit orders, not used for stop loss market price orders
* @param {string} [params.stopLoss.type] 'market' or 'limit' used to specify the stop loss price type
* @param {string} [params.positionSide] if position mode is one-way: set to 'net', if position mode is hedge-mode: set to 'long' or 'short'
* @param {string} [params.trailingPercent] the percent to trail away from the current market price
* @param {string} [params.tpOrdKind] 'condition' or 'limit', the default is 'condition'
* @param {bool} [params.hedged] *swap and future only* true for hedged mode, false for one way mode
* @param {string} [params.marginMode] 'cross' or 'isolated', the default is 'cross'
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let request = this.createOrderRequest(symbol, type, side, amount, price, params);
let method = this.safeString(this.options, 'createOrder', 'privatePostTradeBatchOrders');
const requestOrdType = this.safeString(request, 'ordType');
if ((requestOrdType === 'trigger') || (requestOrdType === 'conditional') || (requestOrdType === 'move_order_stop') || (type === 'move_order_stop') || (type === 'oco') || (type === 'iceberg') || (type === 'twap')) {
method = 'privatePostTradeOrderAlgo';
}
if ((method !== 'privatePostTradeOrder') && (method !== 'privatePostTradeOrderAlgo') && (method !== 'privatePostTradeBatchOrders')) {
throw new errors.ExchangeError(this.id + ' createOrder() this.options["createOrder"] must be either privatePostTradeBatchOrders or privatePostTradeOrder or privatePostTradeOrderAlgo');
}
if (method === 'privatePostTradeBatchOrders') {
// keep the request body the same
// submit a single order in an array to the batch order endpoint
// because it has a lower ratelimit
request = [request];
}
let response = undefined;
if (method === 'privatePostTradeOrder') {
response = await this.privatePostTradeOrder(request);
}
else if (method === 'privatePostTradeOrderAlgo') {
response = await this.privatePostTradeOrderAlgo(request);
}
else {
response = await this.privatePostTradeBatchOrders(request);
}
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
const order = this.parseOrder(first, market);
order['type'] = type;
order['side'] = side;
return order;
}
/**
* @method
* @name okx#createOrders
* @description create a list of trade orders
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-place-multiple-orders
* @param {Array} orders list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async createOrders(orders, params = {}) {
await this.loadMarkets();
const ordersRequests = [];
for (let i = 0; i < orders.length; i++) {
const rawOrder = orders[i];
const marketId = this.safeString(rawOrder, 'symbol');
const type = this.safeString(rawOrder, 'type');
const side = this.safeString(rawOrder, 'side');
const amount = this.safeValue(rawOrder, 'amount');
const price = this.safeValue(rawOrder, 'price');
const orderParams = this.safeDict(rawOrder, 'params', {});
const extendedParams = this.extend(orderParams, params); // the request does not accept extra params since it's a list, so we're extending each order with the common params
const orderRequest = this.createOrderRequest(marketId, type, side, amount, price, extendedParams);
ordersRequests.push(orderRequest);
}
const response = await this.privatePostTradeBatchOrders(ordersRequests);
// {
// "code": "0",
// "data": [
// {
// "clOrdId": "e847386590ce4dBCc7f2a1b4c4509f82",
// "ordId": "636305438765568000",
// "sCode": "0",
// "sMsg": "Order placed",
// "tag": "e847386590ce4dBC"
// },
// {
// "clOrdId": "e847386590ce4dBC0b9993fe642d8f62",
// "ordId": "636305438765568001",
// "sCode": "0",
// "sMsg": "Order placed",
// "tag": "e847386590ce4dBC"
// }
// ],
// "inTime": "1697979038584486",
// "msg": "",
// "outTime": "1697979038586493"
// }
const data = this.safeList(response, 'data', []);
return this.parseOrders(data);
}
editOrderRequest(id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
let isAlgoOrder = undefined;
if ((type === 'trigger') || (type === 'conditional') || (type === 'move_order_stop') || (type === 'oco') || (type === 'iceberg') || (type === 'twap')) {
isAlgoOrder = true;
}
const clientOrderId = this.safeString2(params, 'clOrdId', 'clientOrderId');
if (clientOrderId !== undefined) {
if (isAlgoOrder) {
request['algoClOrdId'] = clientOrderId;
}
else {
request['clOrdId'] = clientOrderId;
}
}
else {
if (isAlgoOrder) {
request['algoId'] = id;
}
else {
request['ordId'] = id;
}
}
let stopLossTriggerPrice = this.safeValue2(params, 'stopLossPrice', 'newSlTriggerPx');
let stopLossPrice = this.safeValue(params, 'newSlOrdPx');
const stopLossTriggerPriceType = this.safeString(params, 'newSlTriggerPxType', 'last');
let takeProfitTriggerPrice = this.safeValue2(params, 'takeProfitPrice', 'newTpTriggerPx');
let takeProfitPrice = this.safeValue(params, 'newTpOrdPx');
const takeProfitTriggerPriceType = this.safeString(params, 'newTpTriggerPxType', 'last');
const stopLoss = this.safeValue(params, 'stopLoss');
const takeProfit = this.safeValue(params, 'takeProfit');
const stopLossDefined = (stopLoss !== undefined);
const takeProfitDefined = (takeProfit !== undefined);
if (isAlgoOrder) {
if ((stopLossTriggerPrice === undefined) && (takeProfitTriggerPrice === undefined)) {
throw new errors.BadRequest(this.id + ' editOrder() requires a stopLossPrice or takeProfitPrice parameter for editing an algo order');
}
if (stopLossTriggerPrice !== undefined) {
if (stopLossPrice === undefined) {
throw new errors.BadRequest(this.id + ' editOrder() requires a newSlOrdPx parameter for editing an algo order');
}
request['newSlTriggerPx'] = this.priceToPrecision(symbol, stopLossTriggerPrice);
request['newSlOrdPx'] = (type === 'market') ? '-1' : this.priceToPrecision(symbol, stopLossPrice);
request['newSlTriggerPxType'] = stopLossTriggerPriceType;
}
if (takeProfitTriggerPrice !== undefined) {
if (takeProfitPrice === undefined) {
throw new errors.BadRequest(this.id + ' editOrder() requires a newTpOrdPx parameter for editing an algo order');
}
request['newTpTriggerPx'] = this.priceToPrecision(symbol, takeProfitTriggerPrice);
request['newTpOrdPx'] = (type === 'market') ? '-1' : this.priceToPrecision(symbol, takeProfitPrice);
request['newTpTriggerPxType'] = takeProfitTriggerPriceType;
}
}
else {
if (stopLossTriggerPrice !== undefined) {
request['newSlTriggerPx'] = this.priceToPrecision(symbol, stopLossTriggerPrice);
request['newSlOrdPx'] = (type === 'market') ? '-1' : this.priceToPrecision(symbol, stopLossPrice);
request['newSlTriggerPxType'] = stopLossTriggerPriceType;
}
if (takeProfitTriggerPrice !== undefined) {
request['newTpTriggerPx'] = this.priceToPrecision(symbol, takeProfitTriggerPrice);
request['newTpOrdPx'] = (type === 'market') ? '-1' : this.priceToPrecision(symbol, takeProfitPrice);
request['newTpTriggerPxType'] = takeProfitTriggerPriceType;
}
if (stopLossDefined) {
stopLossTriggerPrice = this.safeValue(stopLoss, 'triggerPrice');
stopLossPrice = this.safeValue(stopLoss, 'price');
const stopLossType = this.safeString(stopLoss, 'type');
request['newSlTriggerPx'] = this.priceToPrecision(symbol, stopLossTriggerPrice);
request['newSlOrdPx'] = (stopLossType === 'market') ? '-1' : this.priceToPrecision(symbol, stopLossPrice);
request['newSlTriggerPxType'] = stopLossTriggerPriceType;
}
if (takeProfitDefined) {
takeProfitTriggerPrice = this.safeValue(takeProfit, 'triggerPrice');
takeProfitPrice = this.safeValue(takeProfit, 'price');
const takeProfitType = this.safeString(takeProfit, 'type');
request['newTpOrdKind'] = (takeProfitType === 'limit') ? takeProfitType : 'condition';
request['newTpTriggerPx'] = this.priceToPrecision(symbol, takeProfitTriggerPrice);
request['newTpOrdPx'] = (takeProfitType === 'market') ? '-1' : this.priceToPrecision(symbol, takeProfitPrice);
request['newTpTriggerPxType'] = takeProfitTriggerPriceType;
}
}
if (amount !== undefined) {
request['newSz'] = this.amountToPrecision(symbol, amount);
}
if (!isAlgoOrder) {
if (price !== undefined) {
request['newPx'] = this.priceToPrecision(symbol, price);
}
}
params = this.omit(params, ['clOrdId', 'clientOrderId', 'takeProfitPrice', 'stopLossPrice', 'stopLoss', 'takeProfit', 'postOnly']);
return this.extend(request, params);
}
/**
* @method
* @name okx#editOrder
* @description edit a trade order
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-amend-order
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-post-amend-algo-order
* @param {string} id order id
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much of the currency you want to trade in units of the base currency
* @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.clientOrderId] client order id, uses id if not passed
* @param {float} [params.stopLossPrice] stop loss trigger price
* @param {float} [params.newSlOrdPx] the stop loss order price, set to stopLossPrice if the type is market
* @param {string} [params.newSlTriggerPxType] 'last', 'index' or 'mark' used to specify the stop loss trigger price type, default is 'last'
* @param {float} [params.takeProfitPrice] take profit trigger price
* @param {float} [params.newTpOrdPx] the take profit order price, set to takeProfitPrice if the type is market
* @param {string} [params.newTpTriggerPxType] 'last', 'index' or 'mark' used to specify the take profit trigger price type, default is 'last'
* @param {object} [params.stopLoss] *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered
* @param {float} [params.stopLoss.triggerPrice] stop loss trigger price
* @param {float} [params.stopLoss.price] used for stop loss limit orders, not used for stop loss market price orders
* @param {string} [params.stopLoss.type] 'market' or 'limit' used to specify the stop loss price type
* @param {object} [params.takeProfit] *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered
* @param {float} [params.takeProfit.triggerPrice] take profit trigger price
* @param {float} [params.takeProfit.price] used for take profit limit orders, not used for take profit market price orders
* @param {string} [params.takeProfit.type] 'market' or 'limit' used to specify the take profit price type
* @param {string} [params.newTpOrdKind] 'condition' or 'limit', the default is 'condition'
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = this.editOrderRequest(id, symbol, type, side, amount, price, params);
let isAlgoOrder = undefined;
if ((type === 'trigger') || (type === 'conditional') || (type === 'move_order_stop') || (type === 'oco') || (type === 'iceberg') || (type === 'twap')) {
isAlgoOrder = true;
}
let response = undefined;
if (isAlgoOrder) {
response = await this.privatePostTradeAmendAlgos(this.extend(request, params));
}
else {
response = await this.privatePostTradeAmendOrder(this.extend(request, params));
}
//
// {
// "code": "0",
// "data": [
// {
// "clOrdId": "e847386590ce4dBCc1a045253497a547",
// "ordId": "559176536793178112",
// "reqId": "",
// "sCode": "0",
// "sMsg": ""
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const first = this.safeDict(data, 0, {});
const order = this.parseOrder(first, market);
order['type'] = type;
order['side'] = side;
return order;
}
/**
* @method
* @name okx#cancelOrder
* @description cancels an open order
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-cancel-order
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-post-cancel-algo-order
* @param {string} id order id
* @param {string} symbol unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.trigger] true if trigger orders
* @param {boolean} [params.trailing] set to true if you want to cancel a trailing order
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrder(id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument');
}
const trigger = this.safeValue2(params, 'stop', 'trigger');
const trailing = this.safeBool(params, 'trailing', false);
if (trigger || trailing) {
const orderInner = await this.cancelOrders([id], symbol, params);
return this.safeValue(orderInner, 0);
}
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
// 'ordId': id, // either ordId or clOrdId is required
// 'clOrdId': clientOrderId,
};
const clientOrderId = this.safeString2(params, 'clOrdId', 'clientOrderId');
if (clientOrderId !== undefined) {
request['clOrdId'] = clientOrderId;
}
else {
request['ordId'] = id;
}
const query = this.omit(params, ['clOrdId', 'clientOrderId']);
const response = await this.privatePostTradeCancelOrder(this.extend(request, query));
// {"code":"0","data":[{"clOrdId":"","ordId":"317251910906576896","sCode":"0","sMsg":""}],"msg":""}
const data = this.safeValue(response, 'data', []);
const order = this.safeDict(data, 0);
return this.parseOrder(order, market);
}
parseIds(ids) {
/**
* @ignore
* @method
* @name okx#parseIds
* @param {string[]|string} ids order ids
* @returns {string[]} list of order ids
*/
if ((ids !== undefined) && typeof ids === 'string') {
return ids.split(',');
}
else {
return ids;
}
}
/**
* @method
* @name okx#cancelOrders
* @description cancel multiple orders
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-cancel-multiple-orders
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-post-cancel-algo-order
* @param {string[]} ids order ids
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.trigger] whether the order is a stop/trigger order
* @param {boolean} [params.trailing] set to true if you want to cancel trailing orders
* @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrders(ids, symbol = undefined, params = {}) {
// TODO : the original endpoint signature differs, according to that you can skip individual symbol and assign ids in batch. At this moment, `params` is not being used too.
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' cancelOrders() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
const request = [];
const options = this.safeValue(this.options, 'cancelOrders', {});
const defaultMethod = this.safeString(options, 'method', 'privatePostTradeCancelBatchOrders');
let method = this.safeString(params, 'method', defaultMethod);
const clientOrderIds = this.parseIds(this.safeValue2(params, 'clOrdId', 'clientOrderId'));
const algoIds = this.parseIds(this.safeValue(params, 'algoId'));
const trigger = this.safeValue2(params, 'stop', 'trigger');
const trailing = this.safeBool(params, 'trailing', false);
if (trigger || trailing) {
method = 'privatePostTradeCancelAlgos';
}
if (clientOrderIds === undefined) {
ids = this.parseIds(ids);
if (algoIds !== undefined) {
for (let i = 0; i < algoIds.length; i++) {
request.push({
'algoId': algoIds[i],
'instId': market['id'],
});
}
}
for (let i = 0; i < ids.length; i++) {
if (trailing || trigger) {
request.push({
'algoId': ids[i],
'instId': market['id'],
});
}
else {
request.push({
'ordId': ids[i],
'instId': market['id'],
});
}
}
}
else {
for (let i = 0; i < clientOrderIds.length; i++) {
request.push({
'instId': market['id'],
'clOrdId': clientOrderIds[i],
});
}
}
let response = undefined;
if (method === 'privatePostTradeCancelAlgos') {
response = await this.privatePostTradeCancelAlgos(request); // * dont extend with params, otherwise ARRAY will be turned into OBJECT
}
else {
response = await this.privatePostTradeCancelBatchOrders(request); // * dont extend with params, otherwise ARRAY will be turned into OBJECT
}
//
// {
// "code": "0",
// "data": [
// {
// "clOrdId": "e123456789ec4dBC1123456ba123b45e",
// "ordId": "405071912345641543",
// "sCode": "0",
// "sMsg": ""
// },
// ...
// ],
// "msg": ""
// }
//
// Algo order
//
// {
// "code": "0",
// "data": [
// {
// "algoId": "431375349042380800",
// "sCode": "0",
// "sMsg": ""
// }
// ],
// "msg": ""
// }
//
const ordersData = this.safeList(response, 'data', []);
return this.parseOrders(ordersData, market, undefined, undefined, params);
}
/**
* @method
* @name okx#cancelOrdersForSymbols
* @description cancel multiple orders for multiple symbols
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-cancel-multiple-orders
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-post-cancel-algo-order
* @param {CancellationRequest[]} orders each order should contain the parameters required by cancelOrder namely id and symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.trigger] whether the order is a stop/trigger order
* @param {boolean} [params.trailing] set to true if you want to cancel trailing orders
* @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrdersForSymbols(orders, params = {}) {
await this.loadMarkets();
const request = [];
const options = this.safeDict(this.options, 'cancelOrders', {});
const defaultMethod = this.safeString(options, 'method', 'privatePostTradeCancelBatchOrders');
let method = this.safeString(params, 'method', defaultMethod);
const trigger = this.safeBool2(params, 'stop', 'trigger');
const trailing = this.safeBool(params, 'trailing', false);
const isStopOrTrailing = trigger || trailing;
if (isStopOrTrailing) {
method = 'privatePostTradeCancelAlgos';
}
for (let i = 0; i < orders.length; i++) {
const order = orders[i];
const id = this.safeString(order, 'id');
const clientOrderId = this.safeString2(order, 'clOrdId', 'clientOrderId');
const symbol = this.safeString(order, 'symbol');
const market = this.market(symbol);
let idKey = 'ordId';
if (isStopOrTrailing) {
idKey = 'algoId';
}
else if (clientOrderId !== undefined) {
idKey = 'clOrdId';
}
const requestItem = {
'instId': market['id'],
};
requestItem[idKey] = (clientOrderId !== undefined) ? clientOrderId : id;
request.push(requestItem);
}
let response = undefined;
if (method === 'privatePostTradeCancelAlgos') {
response = await this.privatePostTradeCancelAlgos(request); // * dont extend with params, otherwise ARRAY will be turned into OBJECT
}
else {
response = await this.privatePostTradeCancelBatchOrders(request); // * dont extend with params, otherwise ARRAY will be turned into OBJECT
}
//
// {
// "code": "0",
// "data": [
// {
// "clOrdId": "e123456789ec4dBC1123456ba123b45e",
// "ordId": "405071912345641543",
// "sCode": "0",
// "sMsg": ""
// },
// ...
// ],
// "msg": ""
// }
//
// Algo order
//
// {
// "code": "0",
// "data": [
// {
// "algoId": "431375349042380800",
// "sCode": "0",
// "sMsg": ""
// }
// ],
// "msg": ""
// }
//
const ordersData = this.safeList(response, 'data', []);
return this.parseOrders(ordersData, undefined, undefined, undefined, params);
}
/**
* @method
* @name okx#cancelAllOrdersAfter
* @description dead man's switch, cancel all orders after the given timeout
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-cancel-all-after
* @param {number} timeout time in milliseconds, 0 represents cancel the timer
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} the api result
*/
async cancelAllOrdersAfter(timeout, params = {}) {
await this.loadMarkets();
const request = {
'timeOut': (timeout > 0) ? this.parseToInt(timeout / 1000) : 0,
};
const response = await this.privatePostTradeCancelAllAfter(this.extend(request, params));
//
// {
// "code":"0",
// "msg":"",
// "data":[
// {
// "triggerTime":"1587971460",
// "ts":"1587971400"
// }
// ]
// }
//
return response;
}
parseOrderStatus(status) {
const statuses = {
'canceled': 'canceled',
'order_failed': 'canceled',
'live': 'open',
'partially_filled': 'open',
'filled': 'closed',
'effective': 'closed',
};
return this.safeString(statuses, status, status);
}
parseOrder(order, market = undefined) {
//
// createOrder
//
// {
// "clOrdId": "oktswap6",
// "ordId": "312269865356374016",
// "tag": "",
// "sCode": "0",
// "sMsg": ""
// }
//
// editOrder
//
// {
// "clOrdId": "e847386590ce4dBCc1a045253497a547",
// "ordId": "559176536793178112",
// "reqId": "",
// "sCode": "0",
// "sMsg": ""
// }
//
// Spot and Swap fetchOrder, fetchOpenOrders
//
// {
// "accFillSz": "0",
// "avgPx": "",
// "cTime": "1621910749815",
// "category": "normal",
// "ccy": "",
// "clOrdId": "",
// "fee": "0",
// "feeCcy": "ETH",
// "fillPx": "",
// "fillSz": "0",
// "fillTime": "",
// "instId": "ETH-USDT",
// "instType": "SPOT",
// "lever": "",
// "ordId": "317251910906576896",
// "ordType": "limit",
// "pnl": "0",
// "posSide": "net",
// "px": "2000",
// "rebate": "0",
// "rebateCcy": "USDT",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "state": "live",
// "sz": "0.001",
// "tag": "",
// "tdMode": "cash",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tradeId": "",
// "uTime": "1621910749815"
// }
//
// Algo Order fetchOpenOrders, fetchCanceledOrders, fetchClosedOrders
//
// {
// "activePx": "",
// "activePxType": "",
// "actualPx": "",
// "actualSide": "buy",
// "actualSz": "0",
// "algoId": "431375349042380800",
// "cTime": "1649119897778",
// "callbackRatio": "",
// "callbackSpread": "",
// "ccy": "",
// "ctVal": "0.01",
// "instId": "BTC-USDT-SWAP",
// "instType": "SWAP",
// "last": "46538.9",
// "lever": "125",
// "moveTriggerPx": "",
// "notionalUsd": "467.059",
// "ordId": "",
// "ordPx": "50000",
// "ordType": "trigger",
// "posSide": "long",
// "pxLimit": "",
// "pxSpread": "",
// "pxVar": "",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "slTriggerPxType": "",
// "state": "live",
// "sz": "1",
// "szLimit": "",
// "tag": "",
// "tdMode": "isolated",
// "tgtCcy": "",
// "timeInterval": "",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tpTriggerPxType": "",
// "triggerPx": "50000",
// "triggerPxType": "last",
// "triggerTime": "",
// "uly": "BTC-USDT"
// }
//
const scode = this.safeString(order, 'sCode');
if ((scode !== undefined) && (scode !== '0')) {
return this.safeOrder({
'id': this.safeString(order, 'ordId'),
'clientOrderId': this.safeString(order, 'clOrdId'),
'status': 'rejected',
'info': order,
});
}
const id = this.safeString2(order, 'algoId', 'ordId');
const timestamp = this.safeInteger(order, 'cTime');
const lastUpdateTimestamp = this.safeInteger(order, 'uTime');
const lastTradeTimestamp = this.safeInteger(order, 'fillTime');
const side = this.safeString(order, 'side');
let type = this.safeString(order, 'ordType');
let postOnly = undefined;
let timeInForce = undefined;
if (type === 'post_only') {
postOnly = true;
type = 'limit';
}
else if (type === 'fok') {
timeInForce = 'FOK';
type = 'limit';
}
else if (type === 'ioc') {
timeInForce = 'IOC';
type = 'limit';
}
const marketId = this.safeString(order, 'instId');
market = this.safeMarket(marketId, market);
const symbol = this.safeSymbol(marketId, market, '-');
const filled = this.safeString(order, 'accFillSz');
const price = this.safeString2(order, 'px', 'ordPx');
const average = this.safeString(order, 'avgPx');
const status = this.parseOrderStatus(this.safeString(order, 'state'));
const feeCostString = this.safeString(order, 'fee');
let amount = undefined;
let cost = undefined;
// spot market buy: "sz" can refer either to base currency units or to quote currency units
// see documentation: https://www.okx.com/docs-v5/en/#rest-api-trade-place-order
const defaultTgtCcy = this.safeString(this.options, 'tgtCcy', 'base_ccy');
const tgtCcy = this.safeString(order, 'tgtCcy', defaultTgtCcy);
const instType = this.safeString(order, 'instType');
if ((side === 'buy') && (type === 'market') && (instType === 'SPOT') && (tgtCcy === 'quote_ccy')) {
// "sz" refers to the cost
cost = this.safeString(order, 'sz');
}
else {
// "sz" refers to the trade currency amount
amount = this.safeString(order, 'sz');
}
let fee = undefined;
if (feeCostString !== undefined) {
const feeCostSigned = Precise["default"].stringNeg(feeCostString);
const feeCurrencyId = this.safeString(order, 'feeCcy');
const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId);
fee = {
'cost': this.parseNumber(feeCostSigned),
'currency': feeCurrencyCode,
};
}
let clientOrderId = this.safeString(order, 'clOrdId');
if ((clientOrderId !== undefined) && (clientOrderId.length < 1)) {
clientOrderId = undefined; // fix empty clientOrderId string
}
const stopLossPrice = this.safeNumber2(order, 'slTriggerPx', 'slOrdPx');
const takeProfitPrice = this.safeNumber2(order, 'tpTriggerPx', 'tpOrdPx');
const reduceOnlyRaw = this.safeString(order, 'reduceOnly');
let reduceOnly = false;
if (reduceOnly !== undefined) {
reduceOnly = (reduceOnlyRaw === 'true');
}
return this.safeOrder({
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
'lastUpdateTimestamp': lastUpdateTimestamp,
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopLossPrice': stopLossPrice,
'takeProfitPrice': takeProfitPrice,
'triggerPrice': this.safeNumberN(order, ['triggerPx', 'moveTriggerPx']),
'average': average,
'cost': cost,
'amount': amount,
'filled': filled,
'remaining': undefined,
'status': status,
'fee': fee,
'trades': undefined,
'reduceOnly': reduceOnly,
}, market);
}
/**
* @method
* @name okx#fetchOrder
* @description fetch an order by the id
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-get-order-details
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-get-algo-order-details
* @param {string} id the order id
* @param {string} symbol unified market symbol
* @param {object} [params] extra and exchange specific parameters
* @param {boolean} [params.trigger] true if fetching trigger orders
* @returns [an order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOrder(id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
// 'clOrdId': 'abcdef12345', // optional, [a-z0-9]{1,32}
// 'ordId': id,
// 'instType': // spot, swap, futures, margin
};
const clientOrderId = this.safeString2(params, 'clOrdId', 'clientOrderId');
const options = this.safeValue(this.options, 'fetchOrder', {});
const defaultMethod = this.safeString(options, 'method', 'privateGetTradeOrder');
let method = this.safeString(params, 'method', defaultMethod);
const trigger = this.safeValue2(params, 'stop', 'trigger');
if (trigger) {
method = 'privateGetTradeOrderAlgo';
if (clientOrderId !== undefined) {
request['algoClOrdId'] = clientOrderId;
}
else {
request['algoId'] = id;
}
}
else {
if (clientOrderId !== undefined) {
request['clOrdId'] = clientOrderId;
}
else {
request['ordId'] = id;
}
}
const query = this.omit(params, ['method', 'clOrdId', 'clientOrderId', 'stop', 'trigger']);
let response = undefined;
if (method === 'privateGetTradeOrderAlgo') {
response = await this.privateGetTradeOrderAlgo(this.extend(request, query));
}
else {
response = await this.privateGetTradeOrder(this.extend(request, query));
}
//
// Spot and Swap
//
// {
// "code": "0",
// "data": [
// {
// "accFillSz": "0",
// "avgPx": "",
// "cTime": "1621910749815",
// "category": "normal",
// "ccy": "",
// "clOrdId": "",
// "fee": "0",
// "feeCcy": "ETH",
// "fillPx": "",
// "fillSz": "0",
// "fillTime": "",
// "instId": "ETH-USDT",
// "instType": "SPOT",
// "lever": "",
// "ordId": "317251910906576896",
// "ordType": "limit",
// "pnl": "0",
// "posSide": "net",
// "px":"20 00",
// "rebate": "0",
// "rebateCcy": "USDT",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "state": "live",
// "sz":"0. 001",
// "tag": "",
// "tdMode": "cash",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tradeId": "",
// "uTime": "1621910749815"
// }
// ],
// "msg": ""
// }
//
// Algo order
// {
// "code":"0",
// "msg":"",
// "data":[
// {
// "instType":"FUTURES",
// "instId":"BTC-USD-200329",
// "ordId":"123445",
// "ccy":"BTC",
// "clOrdId":"",
// "algoId":"1234",
// "sz":"999",
// "closeFraction":"",
// "ordType":"oco",
// "side":"buy",
// "posSide":"long",
// "tdMode":"cross",
// "tgtCcy": "",
// "state":"effective",
// "lever":"20",
// "tpTriggerPx":"",
// "tpTriggerPxType":"",
// "tpOrdPx":"",
// "slTriggerPx":"",
// "slTriggerPxType":"",
// "triggerPx":"99",
// "triggerPxType":"last",
// "ordPx":"12",
// "actualSz":"",
// "actualPx":"",
// "actualSide":"",
// "pxVar":"",
// "pxSpread":"",
// "pxLimit":"",
// "szLimit":"",
// "tag": "adadadadad",
// "timeInterval":"",
// "callbackRatio":"",
// "callbackSpread":"",
// "activePx":"",
// "moveTriggerPx":"",
// "reduceOnly": "false",
// "triggerTime":"1597026383085",
// "last": "16012",
// "failCode": "",
// "algoClOrdId": "",
// "cTime":"1597026383000"
// }
// ]
// }
//
const data = this.safeValue(response, 'data', []);
const order = this.safeDict(data, 0);
return this.parseOrder(order, market);
}
/**
* @method
* @name okx#fetchOpenOrders
* @description fetch all unfilled currently open orders
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-get-order-list
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-get-algo-order-list
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch open orders for
* @param {int} [limit] the maximum number of open orders structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {bool} [params.trigger] True if fetching trigger or conditional orders
* @param {string} [params.ordType] "conditional", "oco", "trigger", "move_order_stop", "iceberg", or "twap"
* @param {string} [params.algoId] Algo ID "'433845797218942976'"
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @param {boolean} [params.trailing] set to true if you want to fetch trailing orders
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchOpenOrders', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchOpenOrders', symbol, since, limit, params);
}
const request = {
// 'instType': 'SPOT', // SPOT, MARGIN, SWAP, FUTURES, OPTION
// 'uly': currency['id'],
// 'instId': market['id'],
// 'ordType': 'limit', // market, limit, post_only, fok, ioc, comma-separated, stop orders: conditional, oco, trigger, move_order_stop, iceberg, or twap
// 'state': 'live', // live, partially_filled
// 'after': orderId,
// 'before': orderId,
// 'limit': limit, // default 100, max 100
};
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['instId'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
const options = this.safeValue(this.options, 'fetchOpenOrders', {});
const algoOrderTypes = this.safeValue(this.options, 'algoOrderTypes', {});
const defaultMethod = this.safeString(options, 'method', 'privateGetTradeOrdersPending');
let method = this.safeString(params, 'method', defaultMethod);
const ordType = this.safeString(params, 'ordType');
const trigger = this.safeValue2(params, 'stop', 'trigger');
const trailing = this.safeBool(params, 'trailing', false);
if (trailing || trigger || (ordType in algoOrderTypes)) {
method = 'privateGetTradeOrdersAlgoPending';
}
if (trailing) {
request['ordType'] = 'move_order_stop';
}
else if (trigger && (ordType === undefined)) {
request['ordType'] = 'trigger';
}
const query = this.omit(params, ['method', 'stop', 'trigger', 'trailing']);
let response = undefined;
if (method === 'privateGetTradeOrdersAlgoPending') {
response = await this.privateGetTradeOrdersAlgoPending(this.extend(request, query));
}
else {
response = await this.privateGetTradeOrdersPending(this.extend(request, query));
}
//
// {
// "code": "0",
// "data": [
// {
// "accFillSz": "0",
// "avgPx": "",
// "cTime": "1621910749815",
// "category": "normal",
// "ccy": "",
// "clOrdId": "",
// "fee": "0",
// "feeCcy": "ETH",
// "fillPx": "",
// "fillSz": "0",
// "fillTime": "",
// "instId": "ETH-USDT",
// "instType": "SPOT",
// "lever": "",
// "ordId": "317251910906576896",
// "ordType": "limit",
// "pnl": "0",
// "posSide": "net",
// "px":"20 00",
// "rebate": "0",
// "rebateCcy": "USDT",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "state": "live",
// "sz":"0. 001",
// "tag": "",
// "tdMode": "cash",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tradeId": "",
// "uTime": "1621910749815"
// }
// ],
// "msg":""
// }
//
// Algo order
//
// {
// "code": "0",
// "data": [
// {
// "activePx": "",
// "activePxType": "",
// "actualPx": "",
// "actualSide": "buy",
// "actualSz": "0",
// "algoId": "431375349042380800",
// "cTime": "1649119897778",
// "callbackRatio": "",
// "callbackSpread": "",
// "ccy": "",
// "ctVal": "0.01",
// "instId": "BTC-USDT-SWAP",
// "instType": "SWAP",
// "last": "46538.9",
// "lever": "125",
// "moveTriggerPx": "",
// "notionalUsd": "467.059",
// "ordId": "",
// "ordPx": "50000",
// "ordType": "trigger",
// "posSide": "long",
// "pxLimit": "",
// "pxSpread": "",
// "pxVar": "",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "slTriggerPxType": "",
// "state": "live",
// "sz": "1",
// "szLimit": "",
// "tag": "",
// "tdMode": "isolated",
// "tgtCcy": "",
// "timeInterval": "",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tpTriggerPxType": "",
// "triggerPx": "50000",
// "triggerPxType": "last",
// "triggerTime": "",
// "uly": "BTC-USDT"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseOrders(data, market, since, limit);
}
/**
* @method
* @name okx#fetchCanceledOrders
* @description fetches information on multiple canceled orders made by the user
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-get-order-history-last-7-days
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-get-algo-order-history
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] timestamp in ms of the earliest order, default is undefined
* @param {int} [limit] max number of orders to return, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {bool} [params.trigger] True if fetching trigger or conditional orders
* @param {string} [params.ordType] "conditional", "oco", "trigger", "move_order_stop", "iceberg", or "twap"
* @param {string} [params.algoId] Algo ID "'433845797218942976'"
* @param {int} [params.until] timestamp in ms to fetch orders for
* @param {boolean} [params.trailing] set to true if you want to fetch trailing orders
* @returns {object} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchCanceledOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const request = {
// 'instType': type.toUpperCase (), // SPOT, MARGIN, SWAP, FUTURES, OPTION
// 'uly': currency['id'],
// 'instId': market['id'],
// 'ordType': 'limit', // market, limit, post_only, fok, ioc, comma-separated stop orders: conditional, oco, trigger, move_order_stop, iceberg, or twap
// 'state': 'canceled', // filled, canceled
// 'after': orderId,
// 'before': orderId,
// 'limit': limit, // default 100, max 100
// 'algoId': "'433845797218942976'", // Algo order
};
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['instId'] = market['id'];
}
let type = undefined;
let query = undefined;
[type, query] = this.handleMarketTypeAndParams('fetchCanceledOrders', market, params);
request['instType'] = this.convertToInstrumentType(type);
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
request['state'] = 'canceled';
const options = this.safeValue(this.options, 'fetchCanceledOrders', {});
const algoOrderTypes = this.safeValue(this.options, 'algoOrderTypes', {});
const defaultMethod = this.safeString(options, 'method', 'privateGetTradeOrdersHistory');
let method = this.safeString(params, 'method', defaultMethod);
const ordType = this.safeString(params, 'ordType');
const trigger = this.safeValue2(params, 'stop', 'trigger');
const trailing = this.safeBool(params, 'trailing', false);
if (trailing) {
method = 'privateGetTradeOrdersAlgoHistory';
request['ordType'] = 'move_order_stop';
}
else if (trigger || (ordType in algoOrderTypes)) {
method = 'privateGetTradeOrdersAlgoHistory';
const algoId = this.safeString(params, 'algoId');
if (algoId !== undefined) {
request['algoId'] = algoId;
params = this.omit(params, 'algoId');
}
if (trigger) {
if (ordType === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchCanceledOrders() requires an "ordType" string parameter, "conditional", "oco", "trigger", "move_order_stop", "iceberg", or "twap"');
}
}
}
else {
if (since !== undefined) {
request['begin'] = since;
}
const until = this.safeInteger(query, 'until');
if (until !== undefined) {
request['end'] = until;
query = this.omit(query, ['until']);
}
}
const send = this.omit(query, ['method', 'stop', 'trigger', 'trailing']);
let response = undefined;
if (method === 'privateGetTradeOrdersAlgoHistory') {
response = await this.privateGetTradeOrdersAlgoHistory(this.extend(request, send));
}
else {
response = await this.privateGetTradeOrdersHistory(this.extend(request, send));
}
//
// {
// "code": "0",
// "data": [
// {
// "accFillSz": "0",
// "avgPx": "",
// "cTime": "1644037822494",
// "category": "normal",
// "ccy": "",
// "clOrdId": "",
// "fee": "0",
// "feeCcy": "BTC",
// "fillPx": "",
// "fillSz": "0",
// "fillTime": "",
// "instId": "BTC-USDT",
// "instType": "SPOT",
// "lever": "",
// "ordId": "410059580352409602",
// "ordType": "limit",
// "pnl": "0",
// "posSide": "net",
// "px": "30000",
// "rebate": "0",
// "rebateCcy": "USDT",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "slTriggerPxType": "",
// "source": "",
// "state": "canceled",
// "sz": "0.0005452",
// "tag": "",
// "tdMode": "cash",
// "tgtCcy": "",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tpTriggerPxType": "",
// "tradeId": "",
// "uTime": "1644038165667"
// }
// ],
// "msg": ""
// }
//
// Algo order
//
// {
// "code": "0",
// "data": [
// {
// "activePx": "",
// "activePxType": "",
// "actualPx": "",
// "actualSide": "buy",
// "actualSz": "0",
// "algoId": "433845797218942976",
// "cTime": "1649708898523",
// "callbackRatio": "",
// "callbackSpread": "",
// "ccy": "",
// "ctVal": "0.01",
// "instId": "BTC-USDT-SWAP",
// "instType": "SWAP",
// "last": "39950.4",
// "lever": "125",
// "moveTriggerPx": "",
// "notionalUsd": "1592.1760000000002",
// "ordId": "",
// "ordPx": "29000",
// "ordType": "trigger",
// "posSide": "long",
// "pxLimit": "",
// "pxSpread": "",
// "pxVar": "",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "slTriggerPxType": "",
// "state": "canceled",
// "sz": "4",
// "szLimit": "",
// "tag": "",
// "tdMode": "isolated",
// "tgtCcy": "",
// "timeInterval": "",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tpTriggerPxType": "",
// "triggerPx": "30000",
// "triggerPxType": "last",
// "triggerTime": "",
// "uly": "BTC-USDT"
// },
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseOrders(data, market, since, limit);
}
/**
* @method
* @name okx#fetchClosedOrders
* @description fetches information on multiple closed orders made by the user
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-get-order-history-last-7-days
* @see https://www.okx.com/docs-v5/en/#order-book-trading-algo-trading-get-algo-order-history
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-get-order-history-last-3-months
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {bool} [params.trigger] True if fetching trigger or conditional orders
* @param {string} [params.ordType] "conditional", "oco", "trigger", "move_order_stop", "iceberg", or "twap"
* @param {string} [params.algoId] Algo ID "'433845797218942976'"
* @param {int} [params.until] timestamp in ms to fetch orders for
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @param {string} [params.method] method to be used, either 'privateGetTradeOrdersHistory', 'privateGetTradeOrdersHistoryArchive' or 'privateGetTradeOrdersAlgoHistory' default is 'privateGetTradeOrdersHistory'
* @param {boolean} [params.trailing] set to true if you want to fetch trailing orders
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchClosedOrders', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchClosedOrders', symbol, since, limit, params);
}
const request = {
// 'instType': type.toUpperCase (), // SPOT, MARGIN, SWAP, FUTURES, OPTION
// 'uly': currency['id'],
// 'instId': market['id'],
// 'ordType': 'limit', // market, limit, post_only, fok, ioc, comma-separated stop orders: conditional, oco, trigger, move_order_stop, iceberg, or twap
// 'state': 'filled', // filled, effective
// 'after': orderId,
// 'before': orderId,
// 'limit': limit, // default 100, max 100
// 'algoId': "'433845797218942976'", // Algo order
};
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['instId'] = market['id'];
}
let type = undefined;
let query = undefined;
[type, query] = this.handleMarketTypeAndParams('fetchClosedOrders', market, params);
request['instType'] = this.convertToInstrumentType(type);
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
const options = this.safeDict(this.options, 'fetchClosedOrders', {});
const algoOrderTypes = this.safeDict(this.options, 'algoOrderTypes', {});
const defaultMethod = this.safeString(options, 'method', 'privateGetTradeOrdersHistory');
let method = this.safeString(params, 'method', defaultMethod);
const ordType = this.safeString(params, 'ordType');
const trigger = this.safeBool2(params, 'stop', 'trigger');
const trailing = this.safeBool(params, 'trailing', false);
if (trailing || trigger || (ordType in algoOrderTypes)) {
method = 'privateGetTradeOrdersAlgoHistory';
request['state'] = 'effective';
}
if (trailing) {
request['ordType'] = 'move_order_stop';
}
else if (trigger) {
if (ordType === undefined) {
request['ordType'] = 'trigger';
}
}
else {
if (since !== undefined) {
request['begin'] = since;
}
const until = this.safeInteger(query, 'until');
if (until !== undefined) {
request['end'] = until;
query = this.omit(query, ['until']);
}
request['state'] = 'filled';
}
const send = this.omit(query, ['method', 'stop', 'trigger', 'trailing']);
let response = undefined;
if (method === 'privateGetTradeOrdersAlgoHistory') {
response = await this.privateGetTradeOrdersAlgoHistory(this.extend(request, send));
}
else if (method === 'privateGetTradeOrdersHistoryArchive') {
response = await this.privateGetTradeOrdersHistoryArchive(this.extend(request, send));
}
else {
response = await this.privateGetTradeOrdersHistory(this.extend(request, send));
}
//
// {
// "code": "0",
// "data": [
// {
// "accFillSz": "0",
// "avgPx": "",
// "cTime": "1621910749815",
// "category": "normal",
// "ccy": "",
// "clOrdId": "",
// "fee": "0",
// "feeCcy": "ETH",
// "fillPx": "",
// "fillSz": "0",
// "fillTime": "",
// "instId": "ETH-USDT",
// "instType": "SPOT",
// "lever": "",
// "ordId": "317251910906576896",
// "ordType": "limit",
// "pnl": "0",
// "posSide": "net",
// "px": "2000",
// "rebate": "0",
// "rebateCcy": "USDT",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "state": "live",
// "sz": "0.001",
// "tag": "",
// "tdMode": "cash",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tradeId": "",
// "uTime": "1621910749815"
// }
// ],
// "msg": ""
// }
//
// Algo order
//
// {
// "code": "0",
// "data": [
// {
// "activePx": "",
// "activePxType": "",
// "actualPx": "",
// "actualSide": "buy",
// "actualSz": "0",
// "algoId": "433845797218942976",
// "cTime": "1649708898523",
// "callbackRatio": "",
// "callbackSpread": "",
// "ccy": "",
// "ctVal": "0.01",
// "instId": "BTC-USDT-SWAP",
// "instType": "SWAP",
// "last": "39950.4",
// "lever": "125",
// "moveTriggerPx": "",
// "notionalUsd": "1592.1760000000002",
// "ordId": "",
// "ordPx": "29000",
// "ordType": "trigger",
// "posSide": "long",
// "pxLimit": "",
// "pxSpread": "",
// "pxVar": "",
// "side": "buy",
// "slOrdPx": "",
// "slTriggerPx": "",
// "slTriggerPxType": "",
// "state": "effective",
// "sz": "4",
// "szLimit": "",
// "tag": "",
// "tdMode": "isolated",
// "tgtCcy": "",
// "timeInterval": "",
// "tpOrdPx": "",
// "tpTriggerPx": "",
// "tpTriggerPxType": "",
// "triggerPx": "30000",
// "triggerPxType": "last",
// "triggerTime": "",
// "uly": "BTC-USDT"
// },
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseOrders(data, market, since, limit);
}
/**
* @method
* @name okx#fetchMyTrades
* @description fetch all trades made by the user
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-get-transaction-details-last-3-months
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] Timestamp in ms of the latest time to retrieve trades for
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
*/
async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchMyTrades', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchMyTrades', symbol, since, limit, params);
}
let request = {
// 'instType': 'SPOT', // SPOT, MARGIN, SWAP, FUTURES, OPTION
// 'uly': currency['id'],
// 'instId': market['id'],
// 'ordId': orderId,
// 'after': billId,
// 'before': billId,
// 'limit': limit, // default 100, max 100
};
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['instId'] = market['id'];
}
if (since !== undefined) {
request['begin'] = since;
}
[request, params] = this.handleUntilOption('end', request, params);
const [type, query] = this.handleMarketTypeAndParams('fetchMyTrades', market, params);
request['instType'] = this.convertToInstrumentType(type);
if ((limit !== undefined) && (since === undefined)) { // let limit = n, okx will return the n most recent results, instead of the n results after limit, so limit should only be sent when since is undefined
request['limit'] = limit; // default 100, max 100
}
const response = await this.privateGetTradeFillsHistory(this.extend(request, query));
//
// {
// "code": "0",
// "data": [
// {
// "side": "buy",
// "fillSz": "0.007533",
// "fillPx": "2654.98",
// "fee": "-0.000007533",
// "ordId": "317321390244397056",
// "instType": "SPOT",
// "instId": "ETH-USDT",
// "clOrdId": "",
// "posSide": "net",
// "billId": "317321390265368576",
// "tag": "0",
// "execType": "T",
// "tradeId": "107601752",
// "feeCcy": "ETH",
// "ts": "1621927314985"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTrades(data, market, since, limit, query);
}
/**
* @method
* @name okx#fetchOrderTrades
* @description fetch all the trades made from a single order
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-get-transaction-details-last-3-months
* @param {string} id order id
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
*/
async fetchOrderTrades(id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
const request = {
// 'instrument_id': market['id'],
'ordId': id,
// 'after': '1', // return the page after the specified page number
// 'before': '1', // return the page before the specified page number
// 'limit': limit, // optional, number of results per request, default = maximum = 100
};
return await this.fetchMyTrades(symbol, since, limit, this.extend(request, params));
}
/**
* @method
* @name okx#fetchLedger
* @description fetch the history of changes, actions done by the user or operations that altered balance of the user
* @see https://www.okx.com/docs-v5/en/#rest-api-account-get-bills-details-last-7-days
* @see https://www.okx.com/docs-v5/en/#rest-api-account-get-bills-details-last-3-months
* @see https://www.okx.com/docs-v5/en/#rest-api-funding-asset-bills-details
* @param {string} [code] unified currency code, default is undefined
* @param {int} [since] timestamp in ms of the earliest ledger entry, default is undefined
* @param {int} [limit] max number of ledger entries to return, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.marginMode] 'cross' or 'isolated'
* @param {int} [params.until] the latest time in ms to fetch entries for
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger}
*/
async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchLedger', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchLedger', code, since, limit, params);
}
const options = this.safeDict(this.options, 'fetchLedger', {});
let method = this.safeString(options, 'method');
method = this.safeString(params, 'method', method);
params = this.omit(params, 'method');
let request = {
// 'instType': undefined, // 'SPOT', 'MARGIN', 'SWAP', 'FUTURES", 'OPTION'
// 'ccy': undefined, // currency['id'],
// 'mgnMode': undefined, // 'isolated', 'cross'
// 'ctType': undefined, // 'linear', 'inverse', only applicable to FUTURES/SWAP
// 'type': varies depending the 'method' endpoint :
// - https://www.okx.com/docs-v5/en/#rest-api-account-get-bills-details-last-7-days
// - https://www.okx.com/docs-v5/en/#rest-api-funding-asset-bills-details
// - https://www.okx.com/docs-v5/en/#rest-api-account-get-bills-details-last-3-months
// 'after': 'id', // return records earlier than the requested bill id
// 'before': 'id', // return records newer than the requested bill id
// 'limit': 100, // default 100, max 100
};
let marginMode = undefined;
[marginMode, params] = this.handleMarginModeAndParams('fetchLedger', params);
if (marginMode === undefined) {
marginMode = this.safeString(params, 'mgnMode');
}
if (method !== 'privateGetAssetBills') {
if (marginMode !== undefined) {
request['mgnMode'] = marginMode;
}
}
const [type, query] = this.handleMarketTypeAndParams('fetchLedger', undefined, params);
if (type !== undefined) {
request['instType'] = this.convertToInstrumentType(type);
}
if (limit !== undefined) {
request['limit'] = limit;
}
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
[request, params] = this.handleUntilOption('end', request, params);
let response = undefined;
if (method === 'privateGetAccountBillsArchive') {
response = await this.privateGetAccountBillsArchive(this.extend(request, query));
}
else if (method === 'privateGetAssetBills') {
response = await this.privateGetAssetBills(this.extend(request, query));
}
else {
response = await this.privateGetAccountBills(this.extend(request, query));
}
//
// privateGetAccountBills, privateGetAccountBillsArchive
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "bal": "0.0000819307998198",
// "balChg": "-664.2679586599999802",
// "billId": "310394313544966151",
// "ccy": "USDT",
// "fee": "0",
// "from": "",
// "instId": "LTC-USDT",
// "instType": "SPOT",
// "mgnMode": "cross",
// "notes": "",
// "ordId": "310394313519800320",
// "pnl": "0",
// "posBal": "0",
// "posBalChg": "0",
// "subType": "2",
// "sz": "664.26795866",
// "to": "",
// "ts": "1620275771196",
// "type": "2"
// }
// ]
// }
//
// privateGetAssetBills
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "billId": "12344",
// "ccy": "BTC",
// "balChg": "2",
// "bal": "12",
// "type": "1",
// "ts": "1597026383085"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseLedger(data, currency, since, limit);
}
parseLedgerEntryType(type) {
const types = {
'1': 'transfer',
'2': 'trade',
'3': 'trade',
'4': 'rebate',
'5': 'trade',
'6': 'transfer',
'7': 'trade',
'8': 'fee',
'9': 'trade',
'10': 'trade',
'11': 'trade', // system token conversion
};
return this.safeString(types, type, type);
}
parseLedgerEntry(item, currency = undefined) {
//
// privateGetAccountBills, privateGetAccountBillsArchive
//
// {
// "bal": "0.0000819307998198",
// "balChg": "-664.2679586599999802",
// "billId": "310394313544966151",
// "ccy": "USDT",
// "fee": "0",
// "from": "",
// "instId": "LTC-USDT",
// "instType": "SPOT",
// "mgnMode": "cross",
// "notes": "",
// "ordId": "310394313519800320",
// "pnl": "0",
// "posBal": "0",
// "posBalChg": "0",
// "subType": "2",
// "sz": "664.26795866",
// "to": "",
// "ts": "1620275771196",
// "type": "2"
// }
//
// privateGetAssetBills
//
// {
// "billId": "12344",
// "ccy": "BTC",
// "balChg": "2",
// "bal": "12",
// "type": "1",
// "ts": "1597026383085"
// }
//
const currencyId = this.safeString(item, 'ccy');
const code = this.safeCurrencyCode(currencyId, currency);
currency = this.safeCurrency(currencyId, currency);
const timestamp = this.safeInteger(item, 'ts');
const feeCostString = this.safeString(item, 'fee');
let fee = undefined;
if (feeCostString !== undefined) {
fee = {
'cost': this.parseNumber(Precise["default"].stringNeg(feeCostString)),
'currency': code,
};
}
const marketId = this.safeString(item, 'instId');
const symbol = this.safeSymbol(marketId, undefined, '-');
return this.safeLedgerEntry({
'info': item,
'id': this.safeString(item, 'billId'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'account': undefined,
'referenceId': this.safeString(item, 'ordId'),
'referenceAccount': undefined,
'type': this.parseLedgerEntryType(this.safeString(item, 'type')),
'currency': code,
'symbol': symbol,
'amount': this.safeNumber(item, 'balChg'),
'before': undefined,
'after': this.safeNumber(item, 'bal'),
'status': 'ok',
'fee': fee,
}, currency);
}
parseDepositAddress(depositAddress, currency = undefined) {
//
// {
// "addr": "okbtothemoon",
// "memo": "971668", // may be missing
// "tag":"52055", // may be missing
// "pmtId": "", // may be missing
// "ccy": "BTC",
// "to": "6", // 1 SPOT, 3 FUTURES, 6 FUNDING, 9 SWAP, 12 OPTION, 18 Unified account
// "selected": true
// }
//
// {
// "ccy":"usdt-erc20",
// "to":"6",
// "addr":"0x696abb81974a8793352cbd33aadcf78eda3cfdfa",
// "selected":true
// }
//
// {
// "chain": "ETH-OKExChain",
// "addrEx": { "comment": "6040348" }, // some currencies like TON may have this field,
// "ctAddr": "72315c",
// "ccy": "ETH",
// "to": "6",
// "addr": "0x1c9f2244d1ccaa060bd536827c18925db10db102",
// "selected": true
// }
//
const address = this.safeString(depositAddress, 'addr');
let tag = this.safeStringN(depositAddress, ['tag', 'pmtId', 'memo']);
if (tag === undefined) {
const addrEx = this.safeValue(depositAddress, 'addrEx', {});
tag = this.safeString(addrEx, 'comment');
}
const currencyId = this.safeString(depositAddress, 'ccy');
currency = this.safeCurrency(currencyId, currency);
const code = currency['code'];
const chain = this.safeString(depositAddress, 'chain');
const networks = this.safeValue(currency, 'networks', {});
const networksById = this.indexBy(networks, 'id');
let networkData = this.safeValue(networksById, chain);
// inconsistent naming responses from exchange
// with respect to network naming provided in currency info vs address chain-names and ids
//
// response from address endpoint:
// {
// "chain": "USDT-Polygon",
// "ctAddr": "",
// "ccy": "USDT",
// "to":"6" ,
// "addr": "0x1903441e386cc49d937f6302955b5feb4286dcfa",
// "selected": true
// }
// network information from currency['networks'] field:
// Polygon: {
// info: {
// canDep: false,
// canInternal: false,
// canWd: false,
// ccy: 'USDT',
// chain: 'USDT-Polygon-Bridge',
// mainNet: false,
// maxFee: '26.879528',
// minFee: '13.439764',
// minWd: '0.001',
// name: ''
// },
// id: 'USDT-Polygon-Bridge',
// network: 'Polygon',
// active: false,
// deposit: false,
// withdraw: false,
// fee: 13.439764,
// precision: undefined,
// limits: {
// withdraw: {
// min: 0.001,
// max: undefined
// }
// }
// },
//
if (chain === 'USDT-Polygon') {
networkData = this.safeValue2(networksById, 'USDT-Polygon-Bridge', 'USDT-Polygon');
}
const network = this.safeString(networkData, 'network');
const networkCode = this.networkIdToCode(network, code);
this.checkAddress(address);
return {
'info': depositAddress,
'currency': code,
'network': networkCode,
'address': address,
'tag': tag,
};
}
/**
* @method
* @name okx#fetchDepositAddressesByNetwork
* @description fetch a dictionary of addresses for a currency, indexed by network
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-deposit-address
* @param {string} code unified currency code of the currency for the deposit address
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [address structures]{@link https://docs.ccxt.com/#/?id=address-structure} indexed by the network
*/
async fetchDepositAddressesByNetwork(code, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'ccy': currency['id'],
};
const response = await this.privateGetAssetDepositAddress(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "addr": "okbtothemoon",
// "memo": "971668", // may be missing
// "tag":"52055", // may be missing
// "pmtId": "", // may be missing
// "ccy": "BTC",
// "to": "6", // 1 SPOT, 3 FUTURES, 6 FUNDING, 9 SWAP, 12 OPTION, 18 Unified account
// "selected": true
// },
// // {"ccy":"usdt-erc20","to":"6","addr":"0x696abb81974a8793352cbd33aadcf78eda3cfdfa","selected":true},
// // {"ccy":"usdt-trc20","to":"6","addr":"TRrd5SiSZrfQVRKm4e9SRSbn2LNTYqCjqx","selected":true},
// // {"ccy":"usdt_okexchain","to":"6","addr":"0x696abb81974a8793352cbd33aadcf78eda3cfdfa","selected":true},
// // {"ccy":"usdt_kip20","to":"6","addr":"0x696abb81974a8793352cbd33aadcf78eda3cfdfa","selected":true},
// ]
// }
//
const data = this.safeList(response, 'data', []);
const filtered = this.filterBy(data, 'selected', true);
const parsed = this.parseDepositAddresses(filtered, [currency['code']], false);
return this.indexBy(parsed, 'network');
}
/**
* @method
* @name okx#fetchDepositAddress
* @description fetch the deposit address for a currency associated with this account
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-deposit-address
* @param {string} code unified currency code
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.network] the network name for the deposit address
* @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
*/
async fetchDepositAddress(code, params = {}) {
await this.loadMarkets();
const rawNetwork = this.safeString(params, 'network'); // some networks are like "Dora Vota Mainnet"
params = this.omit(params, 'network');
code = this.safeCurrencyCode(code);
const network = this.networkIdToCode(rawNetwork, code);
const response = await this.fetchDepositAddressesByNetwork(code, params);
if (network !== undefined) {
const result = this.safeDict(response, network);
if (result === undefined) {
throw new errors.InvalidAddress(this.id + ' fetchDepositAddress() cannot find ' + network + ' deposit address for ' + code);
}
return result;
}
const codeNetwork = this.networkIdToCode(code, code);
if (codeNetwork in response) {
return response[codeNetwork];
}
// if the network is not specified, return the first address
const keys = Object.keys(response);
const first = this.safeString(keys, 0);
return this.safeDict(response, first);
}
/**
* @method
* @name okx#withdraw
* @description make a withdrawal
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-withdrawal
* @param {string} code unified currency code
* @param {float} amount the amount to withdraw
* @param {string} address the address to withdraw to
* @param {string} tag
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async withdraw(code, amount, address, tag = undefined, params = {}) {
[tag, params] = this.handleWithdrawTagAndParams(tag, params);
this.checkAddress(address);
await this.loadMarkets();
const currency = this.currency(code);
if ((tag !== undefined) && (tag.length > 0)) {
address = address + ':' + tag;
}
const request = {
'ccy': currency['id'],
'toAddr': address,
'dest': '4',
'amt': this.numberToString(amount),
};
let network = this.safeString(params, 'network'); // this line allows the user to specify either ERC20 or ETH
if (network !== undefined) {
const networks = this.safeDict(this.options, 'networks', {});
network = this.safeString(networks, network.toUpperCase(), network); // handle ETH>ERC20 alias
request['chain'] = currency['id'] + '-' + network;
params = this.omit(params, 'network');
}
let fee = this.safeString(params, 'fee');
if (fee === undefined) {
const currencies = await this.fetchCurrencies();
this.currencies = this.mapToSafeMap(this.deepExtend(this.currencies, currencies));
const targetNetwork = this.safeDict(currency['networks'], this.networkIdToCode(network), {});
fee = this.safeString(targetNetwork, 'fee');
if (fee === undefined) {
throw new errors.ArgumentsRequired(this.id + ' withdraw() requires a "fee" string parameter, network transaction fee must be ≥ 0. Withdrawals to OKCoin or OKX are fee-free, please set "0". Withdrawing to external digital asset address requires network transaction fee.');
}
}
request['fee'] = this.numberToString(fee); // withdrawals to OKCoin or OKX are fee-free, please set 0
const query = this.omit(params, ['fee']);
const response = await this.privatePostAssetWithdrawal(this.extend(request, query));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "amt": "0.1",
// "wdId": "67485",
// "ccy": "BTC"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
const transaction = this.safeDict(data, 0);
return this.parseTransaction(transaction, currency);
}
/**
* @method
* @name okx#fetchDeposits
* @description fetch all deposits made to an account
* @see https://www.okx.com/docs-v5/en/#rest-api-funding-get-deposit-history
* @param {string} code unified currency code
* @param {int} [since] the earliest time in ms to fetch deposits for
* @param {int} [limit] the maximum number of deposits structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] the latest time in ms to fetch entries for
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchDeposits', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchDeposits', code, since, limit, params);
}
let request = {
// 'ccy': currency['id'],
// 'state': 2, // 0 waiting for confirmation, 1 deposit credited, 2 deposit successful
// 'after': since,
// 'before' this.milliseconds (),
// 'limit': limit, // default 100, max 100
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
if (since !== undefined) {
request['before'] = Math.max(since - 1, 0);
}
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
[request, params] = this.handleUntilOption('after', request, params);
const response = await this.privateGetAssetDepositHistory(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "amt": "0.01044408",
// "txId": "1915737_3_0_0_asset",
// "ccy": "BTC",
// "from": "13801825426",
// "to": "",
// "ts": "1597026383085",
// "state": "2",
// "depId": "4703879"
// },
// {
// "amt": "491.6784211",
// "txId": "1744594_3_184_0_asset",
// "ccy": "OKB",
// "from": "",
// "to": "",
// "ts": "1597026383085",
// "state": "2",
// "depId": "4703809"
// },
// {
// "amt": "223.18782496",
// "txId": "6d892c669225b1092c780bf0da0c6f912fc7dc8f6b8cc53b003288624c",
// "ccy": "USDT",
// "from": "",
// "to": "39kK4XvgEuM7rX9frgyHoZkWqx4iKu1spD",
// "ts": "1597026383085",
// "state": "2",
// "depId": "4703779"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTransactions(data, currency, since, limit, params);
}
/**
* @method
* @name okx#fetchDeposit
* @description fetch data on a currency deposit via the deposit id
* @see https://www.okx.com/docs-v5/en/#rest-api-funding-get-deposit-history
* @param {string} id deposit id
* @param {string} code filter by currency code
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async fetchDeposit(id, code = undefined, params = {}) {
await this.loadMarkets();
const request = {
'depId': id,
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
const response = await this.privateGetAssetDepositHistory(this.extend(request, params));
const data = this.safeValue(response, 'data');
const deposit = this.safeDict(data, 0, {});
return this.parseTransaction(deposit, currency);
}
/**
* @method
* @name okx#fetchWithdrawals
* @description fetch all withdrawals made from an account
* @see https://www.okx.com/docs-v5/en/#rest-api-funding-get-withdrawal-history
* @param {string} code unified currency code
* @param {int} [since] the earliest time in ms to fetch withdrawals for
* @param {int} [limit] the maximum number of withdrawals structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] the latest time in ms to fetch entries for
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchWithdrawals', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchWithdrawals', code, since, limit, params);
}
let request = {
// 'ccy': currency['id'],
// 'state': 2, // -3: pending cancel, -2 canceled, -1 failed, 0, pending, 1 sending, 2 sent, 3 awaiting email verification, 4 awaiting manual verification, 5 awaiting identity verification
// 'after': since,
// 'before': this.milliseconds (),
// 'limit': limit, // default 100, max 100
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
if (since !== undefined) {
request['before'] = Math.max(since - 1, 0);
}
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 100
}
[request, params] = this.handleUntilOption('after', request, params);
const response = await this.privateGetAssetWithdrawalHistory(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "amt": "0.094",
// "wdId": "4703879",
// "fee": "0.01000000eth",
// "txId": "0x62477bac6509a04512819bb1455e923a60dea5966c7caeaa0b24eb8fb0432b85",
// "ccy": "ETH",
// "from": "13426335357",
// "to": "0xA41446125D0B5b6785f6898c9D67874D763A1519",
// "ts": "1597026383085",
// "state": "2"
// },
// {
// "amt": "0.01",
// "wdId": "4703879",
// "fee": "0.00000000btc",
// "txId": "",
// "ccy": "BTC",
// "from": "13426335357",
// "to": "13426335357",
// "ts": "1597026383085",
// "state": "2"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseTransactions(data, currency, since, limit, params);
}
/**
* @method
* @name okx#fetchWithdrawal
* @description fetch data on a currency withdrawal via the withdrawal id
* @see https://www.okx.com/docs-v5/en/#rest-api-funding-get-withdrawal-history
* @param {string} id withdrawal id
* @param {string} code unified currency code of the currency withdrawn, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
async fetchWithdrawal(id, code = undefined, params = {}) {
await this.loadMarkets();
const request = {
'wdId': id,
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
const response = await this.privateGetAssetWithdrawalHistory(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "chain": "USDT-TRC20",
// "clientId": '',
// "fee": "0.8",
// "ccy": "USDT",
// "amt": "54.561",
// "txId": "00cff6ec7fa7c7d7d184bd84e82b9ff36863f07c0421188607f87dfa94e06b70",
// "from": "example@email.com",
// "to": "TEY6qjnKDyyq5jDc3DJizWLCdUySrpQ4yp",
// "state": "2",
// "ts": "1641376485000",
// "wdId": "25147041"
// }
// ],
// "msg": ''
// }
//
const data = this.safeList(response, 'data', []);
const withdrawal = this.safeDict(data, 0, {});
return this.parseTransaction(withdrawal);
}
parseTransactionStatus(status) {
//
// deposit statuses
//
// {
// "0": "waiting for confirmation",
// "1": "deposit credited",
// "2": "deposit successful"
// }
//
// withdrawal statuses
//
// {
// '-3': "pending cancel",
// "-2": "canceled",
// "-1": "failed",
// "0": "pending",
// "1": "sending",
// "2": "sent",
// "3": "awaiting email verification",
// "4": "awaiting manual verification",
// "5": "awaiting identity verification"
// }
//
const statuses = {
'-3': 'pending',
'-2': 'canceled',
'-1': 'failed',
'0': 'pending',
'1': 'pending',
'2': 'ok',
'3': 'pending',
'4': 'pending',
'5': 'pending',
'6': 'pending',
'7': 'pending',
'8': 'pending',
'9': 'pending',
'10': 'pending',
'12': 'pending',
'15': 'pending',
'16': 'pending',
};
return this.safeString(statuses, status, status);
}
parseTransaction(transaction, currency = undefined) {
//
// withdraw
//
// {
// "amt": "0.1",
// "wdId": "67485",
// "ccy": "BTC"
// }
//
// fetchWithdrawals
//
// {
// "amt": "0.094",
// "wdId": "4703879",
// "fee": "0.01000000eth",
// "txId": "0x62477bac6509a04512819bb1455e923a60dea5966c7caeaa0b24eb8fb0432b85",
// "ccy": "ETH",
// "from": "13426335357",
// "to": "0xA41446125D0B5b6785f6898c9D67874D763A1519",
// "tag",
// "pmtId",
// "memo",
// "ts": "1597026383085",
// "state": "2"
// }
//
// fetchDeposits
//
// {
// "amt": "0.01044408",
// "txId": "1915737_3_0_0_asset",
// "ccy": "BTC",
// "from": "13801825426",
// "to": "",
// "ts": "1597026383085",
// "state": "2",
// "depId": "4703879"
// }
//
let type = undefined;
let id = undefined;
const withdrawalId = this.safeString(transaction, 'wdId');
const addressFrom = this.safeString(transaction, 'from');
const addressTo = this.safeString(transaction, 'to');
const address = addressTo;
let tagTo = this.safeString2(transaction, 'tag', 'memo');
tagTo = this.safeString2(transaction, 'pmtId', tagTo);
if (withdrawalId !== undefined) {
type = 'withdrawal';
id = withdrawalId;
}
else {
// the payment_id will appear on new deposits but appears to be removed from the response after 2 months
id = this.safeString(transaction, 'depId');
type = 'deposit';
}
const currencyId = this.safeString(transaction, 'ccy');
const code = this.safeCurrencyCode(currencyId);
const amount = this.safeNumber(transaction, 'amt');
const status = this.parseTransactionStatus(this.safeString(transaction, 'state'));
const txid = this.safeString(transaction, 'txId');
const timestamp = this.safeInteger(transaction, 'ts');
let feeCost = undefined;
if (type === 'deposit') {
feeCost = 0;
}
else {
feeCost = this.safeNumber(transaction, 'fee');
}
// todo parse tags
return {
'info': transaction,
'id': id,
'currency': code,
'amount': amount,
'network': undefined,
'addressFrom': addressFrom,
'addressTo': addressTo,
'address': address,
'tagFrom': undefined,
'tagTo': tagTo,
'tag': tagTo,
'status': status,
'type': type,
'updated': undefined,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'internal': undefined,
'comment': undefined,
'fee': {
'currency': code,
'cost': feeCost,
},
};
}
/**
* @method
* @name okx#fetchLeverage
* @description fetch the set leverage for a market
* @see https://www.okx.com/docs-v5/en/#rest-api-account-get-leverage
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.marginMode] 'cross' or 'isolated'
* @returns {object} a [leverage structure]{@link https://docs.ccxt.com/#/?id=leverage-structure}
*/
async fetchLeverage(symbol, params = {}) {
await this.loadMarkets();
let marginMode = undefined;
[marginMode, params] = this.handleMarginModeAndParams('fetchLeverage', params);
if (marginMode === undefined) {
marginMode = this.safeString(params, 'mgnMode', 'cross'); // cross as default marginMode
}
if ((marginMode !== 'cross') && (marginMode !== 'isolated')) {
throw new errors.BadRequest(this.id + ' fetchLeverage() requires a marginMode parameter that must be either cross or isolated');
}
const market = this.market(symbol);
const request = {
'instId': market['id'],
'mgnMode': marginMode,
};
const response = await this.privateGetAccountLeverageInfo(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "instId": "BTC-USDT-SWAP",
// "lever": "5.00000000",
// "mgnMode": "isolated",
// "posSide": "net"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseLeverage(data, market);
}
parseLeverage(leverage, market = undefined) {
let marketId = undefined;
let marginMode = undefined;
let longLeverage = undefined;
let shortLeverage = undefined;
for (let i = 0; i < leverage.length; i++) {
const entry = leverage[i];
marginMode = this.safeStringLower(entry, 'mgnMode');
marketId = this.safeString(entry, 'instId');
const positionSide = this.safeStringLower(entry, 'posSide');
if (positionSide === 'long') {
longLeverage = this.safeInteger(entry, 'lever');
}
else if (positionSide === 'short') {
shortLeverage = this.safeInteger(entry, 'lever');
}
else {
longLeverage = this.safeInteger(entry, 'lever');
shortLeverage = this.safeInteger(entry, 'lever');
}
}
return {
'info': leverage,
'symbol': this.safeSymbol(marketId, market),
'marginMode': marginMode,
'longLeverage': longLeverage,
'shortLeverage': shortLeverage,
};
}
/**
* @method
* @name okx#fetchPosition
* @description fetch data on a single open contract trade position
* @see https://www.okx.com/docs-v5/en/#rest-api-account-get-positions
* @param {string} symbol unified market symbol of the market the position is held in, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.instType] MARGIN, SWAP, FUTURES, OPTION
* @returns {object} a [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
*/
async fetchPosition(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const [type, query] = this.handleMarketTypeAndParams('fetchPosition', market, params);
const request = {
// instType String No Instrument type, MARGIN, SWAP, FUTURES, OPTION
'instId': market['id'],
// posId String No Single position ID or multiple position IDs (no more than 20) separated with comma
};
if (type !== undefined) {
request['instType'] = this.convertToInstrumentType(type);
}
const response = await this.privateGetAccountPositions(this.extend(request, query));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "adl": "1",
// "availPos": "1",
// "avgPx": "2566.31",
// "cTime": "1619507758793",
// "ccy": "ETH",
// "deltaBS": "",
// "deltaPA": "",
// "gammaBS": "",
// "gammaPA": "",
// "imr": "",
// "instId": "ETH-USD-210430",
// "instType": "FUTURES",
// "interest": "0",
// "last": "2566.22",
// "lever": "10",
// "liab": "",
// "liabCcy": "",
// "liqPx": "2352.8496681818233",
// "margin": "0.0003896645377994",
// "mgnMode": "isolated",
// "mgnRatio": "11.731726509588816",
// "mmr": "0.0000311811092368",
// "optVal": "",
// "pTime": "1619507761462",
// "pos": "1",
// "posCcy": "",
// "posId": "307173036051017730",
// "posSide": "long",
// "thetaBS": "",
// "thetaPA": "",
// "tradeId": "109844",
// "uTime": "1619507761462",
// "upl": "-0.0000009932766034",
// "uplRatio": "-0.0025490556801078",
// "vegaBS": "",
// "vegaPA": ""
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
const position = this.safeDict(data, 0);
if (position === undefined) {
return undefined;
}
return this.parsePosition(position, market);
}
/**
* @method
* @name okx#fetchPositions
* @see https://www.okx.com/docs-v5/en/#rest-api-account-get-positions
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-positions-history history
* @description fetch all open positions
* @param {string[]|undefined} symbols list of unified market symbols
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.instType] MARGIN, SWAP, FUTURES, OPTION
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
*/
async fetchPositions(symbols = undefined, params = {}) {
await this.loadMarkets();
const request = {
// 'instType': 'MARGIN', // optional string, MARGIN, SWAP, FUTURES, OPTION
// 'instId': market['id'], // optional string, e.g. 'BTC-USD-190927-5000-C'
// 'posId': '307173036051017730', // optional string, Single or multiple position IDs (no more than 20) separated with commas
};
if (symbols !== undefined) {
const marketIds = [];
for (let i = 0; i < symbols.length; i++) {
const entry = symbols[i];
const market = this.market(entry);
marketIds.push(market['id']);
}
const marketIdsLength = marketIds.length;
if (marketIdsLength > 0) {
request['instId'] = marketIds.join(',');
}
}
const fetchPositionsOptions = this.safeDict(this.options, 'fetchPositions', {});
const method = this.safeString(fetchPositionsOptions, 'method', 'privateGetAccountPositions');
let response = undefined;
if (method === 'privateGetAccountPositionsHistory') {
response = await this.privateGetAccountPositionsHistory(this.extend(request, params));
}
else {
response = await this.privateGetAccountPositions(this.extend(request, params));
}
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "adl": "1",
// "availPos": "1",
// "avgPx": "2566.31",
// "cTime": "1619507758793",
// "ccy": "ETH",
// "deltaBS": "",
// "deltaPA": "",
// "gammaBS": "",
// "gammaPA": "",
// "imr": "",
// "instId": "ETH-USD-210430",
// "instType": "FUTURES",
// "interest": "0",
// "last": "2566.22",
// "lever": "10",
// "liab": "",
// "liabCcy": "",
// "liqPx": "2352.8496681818233",
// "margin": "0.0003896645377994",
// "mgnMode": "isolated",
// "mgnRatio": "11.731726509588816",
// "mmr": "0.0000311811092368",
// "optVal": "",
// "pTime": "1619507761462",
// "pos": "1",
// "posCcy": "",
// "posId": "307173036051017730",
// "posSide": "long",
// "thetaBS": "",
// "thetaPA": "",
// "tradeId": "109844",
// "uTime": "1619507761462",
// "upl": "-0.0000009932766034",
// "uplRatio": "-0.0025490556801078",
// "vegaBS": "",
// "vegaPA": ""
// }
// ]
// }
//
const positions = this.safeList(response, 'data', []);
const result = [];
for (let i = 0; i < positions.length; i++) {
result.push(this.parsePosition(positions[i]));
}
return this.filterByArrayPositions(result, 'symbol', this.marketSymbols(symbols), false);
}
/**
* @method
* @name okx#fetchPositionsForSymbol
* @see https://www.okx.com/docs-v5/en/#rest-api-account-get-positions
* @description fetch all open positions for specific symbol
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.instType] MARGIN (if needed)
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
*/
async fetchPositionsForSymbol(symbol, params = {}) {
return await this.fetchPositions([symbol], params);
}
parsePosition(position, market = undefined) {
//
// {
// "adl": "3",
// "availPos": "1",
// "avgPx": "34131.1",
// "cTime": "1627227626502",
// "ccy": "USDT",
// "deltaBS": "",
// "deltaPA": "",
// "gammaBS": "",
// "gammaPA": "",
// "imr": "170.66093041794787",
// "instId": "BTC-USDT-SWAP",
// "instType": "SWAP",
// "interest": "0",
// "last": "34134.4",
// "lever": "2",
// "liab": "",
// "liabCcy": "",
// "liqPx": "12608.959083877446",
// "markPx": "4786.459271773621",
// "margin": "",
// "mgnMode": "cross",
// "mgnRatio": "140.49930117599155",
// "mmr": "1.3652874433435829",
// "notionalUsd": "341.5130010779638",
// "optVal": "",
// "pos": "1",
// "posCcy": "",
// "posId": "339552508062380036",
// "posSide": "long",
// "thetaBS": "",
// "thetaPA": "",
// "tradeId": "98617799",
// "uTime": "1627227626502",
// "upl": "0.0108608358957281",
// "uplRatio": "0.0000636418743944",
// "vegaBS": "",
// "vegaPA": ""
// }
// history
// {
// "cTime":"1708351230102",
// "ccy":"USDT",
// "closeAvgPx":"1.2567",
// "closeTotalPos":"40",
// "direction":"short",
// "fee":"-0.0351036",
// "fundingFee":"0",
// "instId":"SUSHI-USDT-SWAP",
// "instType":"SWAP",
// "lever":"10.0",
// "liqPenalty":"0",
// "mgnMode":"isolated",
// "openAvgPx":"1.2462",
// "openMaxPos":"40",
// "pnl":"-0.42",
// "pnlRatio":"-0.0912982667308618",
// "posId":"666159086676836352",
// "realizedPnl":"-0.4551036",
// "triggerPx":"",
// "type":"2",
// "uTime":"1708354805699",
// "uly":"SUSHI-USDT"
// }
//
const marketId = this.safeString(position, 'instId');
market = this.safeMarket(marketId, market, undefined, 'contract');
const symbol = market['symbol'];
const pos = this.safeString(position, 'pos'); // 'pos' field: One way mode: 0 if position is not open, 1 if open | Two way (hedge) mode: -1 if short, 1 if long, 0 if position is not open
const contractsAbs = Precise["default"].stringAbs(pos);
let side = this.safeString2(position, 'posSide', 'direction');
const hedged = side !== 'net';
const contracts = this.parseNumber(contractsAbs);
if (market['margin']) {
// margin position
if (side === 'net') {
const posCcy = this.safeString(position, 'posCcy');
const parsedCurrency = this.safeCurrencyCode(posCcy);
if (parsedCurrency !== undefined) {
side = (market['base'] === parsedCurrency) ? 'long' : 'short';
}
}
if (side === undefined) {
side = this.safeString(position, 'direction');
}
}
else {
if (pos !== undefined) {
if (side === 'net') {
if (Precise["default"].stringGt(pos, '0')) {
side = 'long';
}
else if (Precise["default"].stringLt(pos, '0')) {
side = 'short';
}
else {
side = undefined;
}
}
}
}
const contractSize = this.safeNumber(market, 'contractSize');
const contractSizeString = this.numberToString(contractSize);
const markPriceString = this.safeString(position, 'markPx');
let notionalString = this.safeString(position, 'notionalUsd');
if (market['inverse']) {
notionalString = Precise["default"].stringDiv(Precise["default"].stringMul(contractsAbs, contractSizeString), markPriceString);
}
const notional = this.parseNumber(notionalString);
const marginMode = this.safeString(position, 'mgnMode');
let initialMarginString = undefined;
const entryPriceString = this.safeString2(position, 'avgPx', 'openAvgPx');
const unrealizedPnlString = this.safeString(position, 'upl');
const leverageString = this.safeString(position, 'lever');
let initialMarginPercentage = undefined;
let collateralString = undefined;
if (marginMode === 'cross') {
initialMarginString = this.safeString(position, 'imr');
collateralString = Precise["default"].stringAdd(initialMarginString, unrealizedPnlString);
}
else if (marginMode === 'isolated') {
initialMarginPercentage = Precise["default"].stringDiv('1', leverageString);
collateralString = this.safeString(position, 'margin');
}
const maintenanceMarginString = this.safeString(position, 'mmr');
const maintenanceMargin = this.parseNumber(maintenanceMarginString);
const maintenanceMarginPercentageString = Precise["default"].stringDiv(maintenanceMarginString, notionalString);
if (initialMarginPercentage === undefined) {
initialMarginPercentage = this.parseNumber(Precise["default"].stringDiv(initialMarginString, notionalString, 4));
}
else if (initialMarginString === undefined) {
initialMarginString = Precise["default"].stringMul(initialMarginPercentage, notionalString);
}
const rounder = '0.00005'; // round to closest 0.01%
const maintenanceMarginPercentage = this.parseNumber(Precise["default"].stringDiv(Precise["default"].stringAdd(maintenanceMarginPercentageString, rounder), '1', 4));
const liquidationPrice = this.safeNumber(position, 'liqPx');
const percentageString = this.safeString(position, 'uplRatio');
const percentage = this.parseNumber(Precise["default"].stringMul(percentageString, '100'));
const timestamp = this.safeInteger(position, 'cTime');
const marginRatio = this.parseNumber(Precise["default"].stringDiv(maintenanceMarginString, collateralString, 4));
return this.safePosition({
'info': position,
'id': this.safeString(position, 'posId'),
'symbol': symbol,
'notional': notional,
'marginMode': marginMode,
'liquidationPrice': liquidationPrice,
'entryPrice': this.parseNumber(entryPriceString),
'unrealizedPnl': this.parseNumber(unrealizedPnlString),
'realizedPnl': this.safeNumber(position, 'realizedPnl'),
'percentage': percentage,
'contracts': contracts,
'contractSize': contractSize,
'markPrice': this.parseNumber(markPriceString),
'lastPrice': this.safeNumber(position, 'closeAvgPx'),
'side': side,
'hedged': hedged,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'lastUpdateTimestamp': this.safeInteger(position, 'uTime'),
'maintenanceMargin': maintenanceMargin,
'maintenanceMarginPercentage': maintenanceMarginPercentage,
'collateral': this.parseNumber(collateralString),
'initialMargin': this.parseNumber(initialMarginString),
'initialMarginPercentage': this.parseNumber(initialMarginPercentage),
'leverage': this.parseNumber(leverageString),
'marginRatio': marginRatio,
'stopLossPrice': undefined,
'takeProfitPrice': undefined,
});
}
/**
* @method
* @name okx#transfer
* @description transfer currency internally between wallets on the same account
* @see https://www.okx.com/docs-v5/en/#rest-api-funding-funds-transfer
* @param {string} code unified currency code
* @param {float} amount amount to transfer
* @param {string} fromAccount account to transfer from
* @param {string} toAccount account to transfer to
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [transfer structure]{@link https://docs.ccxt.com/#/?id=transfer-structure}
*/
async transfer(code, amount, fromAccount, toAccount, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const accountsByType = this.safeDict(this.options, 'accountsByType', {});
const fromId = this.safeString(accountsByType, fromAccount, fromAccount);
const toId = this.safeString(accountsByType, toAccount, toAccount);
const request = {
'ccy': currency['id'],
'amt': this.currencyToPrecision(code, amount),
'type': '0',
'from': fromId,
'to': toId, // beneficiary account, 6: Funding account, 18: Trading account
// 'subAcct': 'sub-account-name', // optional, only required when type is 1, 2 or 4
// 'loanTrans': false, // Whether or not borrowed coins can be transferred out under Multi-currency margin and Portfolio margin. The default is false
// 'clientId': 'client-supplied id', // A combination of case-sensitive alphanumerics, all numbers, or all letters of up to 32 characters
// 'omitPosRisk': false, // Ignore position risk. Default is false. Applicable to Portfolio margin
};
if (fromId === 'master') {
request['type'] = '1';
request['subAcct'] = toId;
request['from'] = this.safeString(params, 'from', '6');
request['to'] = this.safeString(params, 'to', '6');
}
else if (toId === 'master') {
request['type'] = '2';
request['subAcct'] = fromId;
request['from'] = this.safeString(params, 'from', '6');
request['to'] = this.safeString(params, 'to', '6');
}
const response = await this.privatePostAssetTransfer(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "transId": "754147",
// "ccy": "USDT",
// "from": "6",
// "amt": "0.1",
// "to": "18"
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
const rawTransfer = this.safeDict(data, 0, {});
return this.parseTransfer(rawTransfer, currency);
}
parseTransfer(transfer, currency = undefined) {
//
// transfer
//
// {
// "transId": "754147",
// "ccy": "USDT",
// "from": "6",
// "amt": "0.1",
// "to": "18"
// }
//
// fetchTransfer
//
// {
// "amt": "5",
// "ccy": "USDT",
// "from": "18",
// "instId": "",
// "state": "success",
// "subAcct": "",
// "to": "6",
// "toInstId": "",
// "transId": "464424732",
// "type": "0"
// }
//
// fetchTransfers
//
// {
// "bal": "70.6874353780312913",
// "balChg": "-4.0000000000000000", // negative means "to funding", positive meand "from funding"
// "billId": "588900695232225299",
// "ccy": "USDT",
// "execType": "",
// "fee": "",
// "from": "18",
// "instId": "",
// "instType": "",
// "mgnMode": "",
// "notes": "To Funding Account",
// "ordId": "",
// "pnl": "",
// "posBal": "",
// "posBalChg": "",
// "price": "0",
// "subType": "12",
// "sz": "-4",
// "to": "6",
// "ts": "1686676866989",
// "type": "1"
// }
//
const id = this.safeString2(transfer, 'transId', 'billId');
const currencyId = this.safeString(transfer, 'ccy');
const code = this.safeCurrencyCode(currencyId, currency);
let amount = this.safeNumber(transfer, 'amt');
const fromAccountId = this.safeString(transfer, 'from');
const toAccountId = this.safeString(transfer, 'to');
const accountsById = this.safeDict(this.options, 'accountsById', {});
const timestamp = this.safeInteger(transfer, 'ts');
const balanceChange = this.safeString(transfer, 'sz');
if (balanceChange !== undefined) {
amount = this.parseNumber(Precise["default"].stringAbs(balanceChange));
}
return {
'info': transfer,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'currency': code,
'amount': amount,
'fromAccount': this.safeString(accountsById, fromAccountId),
'toAccount': this.safeString(accountsById, toAccountId),
'status': this.parseTransferStatus(this.safeString(transfer, 'state')),
};
}
parseTransferStatus(status) {
const statuses = {
'success': 'ok',
};
return this.safeString(statuses, status, status);
}
async fetchTransfer(id, code = undefined, params = {}) {
await this.loadMarkets();
const request = {
'transId': id,
// 'type': 0, // default is 0 transfer within account, 1 master to sub, 2 sub to master
};
const response = await this.privateGetAssetTransferState(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "amt": "5",
// "ccy": "USDT",
// "from": "18",
// "instId": "",
// "state": "success",
// "subAcct": "",
// "to": "6",
// "toInstId": "",
// "transId": "464424732",
// "type": "0"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const transfer = this.safeDict(data, 0);
return this.parseTransfer(transfer);
}
/**
* @method
* @name okx#fetchTransfers
* @description fetch a history of internal transfers made on an account
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-bills-details-last-3-months
* @param {string} code unified currency code of the currency transferred
* @param {int} [since] the earliest time in ms to fetch transfers for
* @param {int} [limit] the maximum number of transfers structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [transfer structures]{@link https://docs.ccxt.com/#/?id=transfer-structure}
*/
async fetchTransfers(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let currency = undefined;
const request = {
'type': '1', // https://www.okx.com/docs-v5/en/#rest-api-account-get-bills-details-last-3-months
};
if (code !== undefined) {
currency = this.currency(code);
request['ccy'] = currency['id'];
}
if (since !== undefined) {
request['begin'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.privateGetAccountBillsArchive(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "bal": "70.6874353780312913",
// "balChg": "-4.0000000000000000",
// "billId": "588900695232225299",
// "ccy": "USDT",
// "execType": "",
// "fee": "",
// "from": "18",
// "instId": "",
// "instType": "",
// "mgnMode": "",
// "notes": "To Funding Account",
// "ordId": "",
// "pnl": "",
// "posBal": "",
// "posBalChg": "",
// "price": "0",
// "subType": "12",
// "sz": "-4",
// "to": "6",
// "ts": "1686676866989",
// "type": "1"
// },
// ...
// ],
// "msg": ""
// }
//
const transfers = this.safeList(response, 'data', []);
return this.parseTransfers(transfers, currency, since, limit, params);
}
sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
const isArray = Array.isArray(params);
const request = '/api/' + this.version + '/' + this.implodeParams(path, params);
const query = this.omit(params, this.extractParams(path));
let url = this.implodeHostname(this.urls['api']['rest']) + request;
// const type = this.getPathAuthenticationType (path);
if (api === 'public') {
if (Object.keys(query).length) {
url += '?' + this.urlencode(query);
}
}
else if (api === 'private') {
this.checkRequiredCredentials();
// inject id in implicit api call
if (method === 'POST' && (path === 'trade/batch-orders' || path === 'trade/order-algo' || path === 'trade/order')) {
const brokerId = this.safeString(this.options, 'brokerId', 'e847386590ce4dBC');
if (Array.isArray(params)) {
for (let i = 0; i < params.length; i++) {
const entry = params[i];
const clientOrderId = this.safeString(entry, 'clOrdId');
if (clientOrderId === undefined) {
entry['clOrdId'] = brokerId + this.uuid16();
entry['tag'] = brokerId;
params[i] = entry;
}
}
}
else {
const clientOrderId = this.safeString(params, 'clOrdId');
if (clientOrderId === undefined) {
params['clOrdId'] = brokerId + this.uuid16();
params['tag'] = brokerId;
}
}
}
const timestamp = this.iso8601(this.nonce());
headers = {
'OK-ACCESS-KEY': this.apiKey,
'OK-ACCESS-PASSPHRASE': this.password,
'OK-ACCESS-TIMESTAMP': timestamp,
// 'OK-FROM': '',
// 'OK-TO': '',
// 'OK-LIMIT': '',
};
let auth = timestamp + method + request;
if (method === 'GET') {
if (Object.keys(query).length) {
const urlencodedQuery = '?' + this.urlencode(query);
url += urlencodedQuery;
auth += urlencodedQuery;
}
}
else {
if (isArray || Object.keys(query).length) {
body = this.json(query);
auth += body;
}
headers['Content-Type'] = 'application/json';
}
const signature = this.hmac(this.encode(auth), this.encode(this.secret), sha256.sha256, 'base64');
headers['OK-ACCESS-SIGN'] = signature;
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
parseFundingRate(contract, market = undefined) {
//
// {
// "fundingRate": "0.00027815",
// "fundingTime": "1634256000000",
// "instId": "BTC-USD-SWAP",
// "instType": "SWAP",
// "nextFundingRate": "0.00017",
// "nextFundingTime": "1634284800000"
// }
// ws
// {
// "fundingRate":"0.0001875391284828",
// "fundingTime":"1700726400000",
// "instId":"BTC-USD-SWAP",
// "instType":"SWAP",
// "method": "next_period",
// "maxFundingRate":"0.00375",
// "minFundingRate":"-0.00375",
// "nextFundingRate":"0.0002608059239328",
// "nextFundingTime":"1700755200000",
// "premium": "0.0001233824646391",
// "settFundingRate":"0.0001699799259033",
// "settState":"settled",
// "ts":"1700724675402"
// }
//
// in the response above nextFundingRate is actually two funding rates from now
//
const nextFundingRateTimestamp = this.safeInteger(contract, 'nextFundingTime');
const marketId = this.safeString(contract, 'instId');
const symbol = this.safeSymbol(marketId, market);
const nextFundingRate = this.safeNumber(contract, 'nextFundingRate');
const fundingTime = this.safeInteger(contract, 'fundingTime');
const fundingTimeString = this.safeString(contract, 'fundingTime');
const nextFundingTimeString = this.safeString(contract, 'nextFundingTime');
const millisecondsInterval = Precise["default"].stringSub(nextFundingTimeString, fundingTimeString);
// https://www.okx.com/support/hc/en-us/articles/360053909272-Ⅸ-Introduction-to-perpetual-swap-funding-fee
// > The current interest is 0.
return {
'info': contract,
'symbol': symbol,
'markPrice': undefined,
'indexPrice': undefined,
'interestRate': this.parseNumber('0'),
'estimatedSettlePrice': undefined,
'timestamp': undefined,
'datetime': undefined,
'fundingRate': this.safeNumber(contract, 'fundingRate'),
'fundingTimestamp': fundingTime,
'fundingDatetime': this.iso8601(fundingTime),
'nextFundingRate': nextFundingRate,
'nextFundingTimestamp': nextFundingRateTimestamp,
'nextFundingDatetime': this.iso8601(nextFundingRateTimestamp),
'previousFundingRate': undefined,
'previousFundingTimestamp': undefined,
'previousFundingDatetime': undefined,
'interval': this.parseFundingInterval(millisecondsInterval),
};
}
parseFundingInterval(interval) {
const intervals = {
'3600000': '1h',
'14400000': '4h',
'28800000': '8h',
'57600000': '16h',
'86400000': '24h',
};
return this.safeString(intervals, interval, interval);
}
/**
* @method
* @name okx#fetchFundingInterval
* @description fetch the current funding rate interval
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-funding-rate
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [funding rate structure]{@link https://docs.ccxt.com/#/?id=funding-rate-structure}
*/
async fetchFundingInterval(symbol, params = {}) {
return await this.fetchFundingRate(symbol, params);
}
/**
* @method
* @name okx#fetchFundingRate
* @description fetch the current funding rate
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-funding-rate
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [funding rate structure]{@link https://docs.ccxt.com/#/?id=funding-rate-structure}
*/
async fetchFundingRate(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['swap']) {
throw new errors.ExchangeError(this.id + ' fetchFundingRate() is only valid for swap markets');
}
const request = {
'instId': market['id'],
};
const response = await this.publicGetPublicFundingRate(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "fundingRate": "0.00027815",
// "fundingTime": "1634256000000",
// "instId": "BTC-USD-SWAP",
// "instType": "SWAP",
// "nextFundingRate": "0.00017",
// "nextFundingTime": "1634284800000"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const entry = this.safeDict(data, 0, {});
return this.parseFundingRate(entry, market);
}
/**
* @method
* @name okx#fetchFundingRates
* @description fetches the current funding rates for multiple symbols
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-funding-rate
* @param {string[]} symbols unified market symbols
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [funding rates structure]{@link https://docs.ccxt.com/#/?id=funding-rates-structure}
*/
async fetchFundingRates(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols, 'swap', true);
const request = { 'instId': 'ANY' };
const response = await this.publicGetPublicFundingRate(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "fundingRate": "0.00027815",
// "fundingTime": "1634256000000",
// "instId": "BTC-USD-SWAP",
// "instType": "SWAP",
// "nextFundingRate": "0.00017",
// "nextFundingTime": "1634284800000"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseFundingRates(data, symbols);
}
/**
* @method
* @name okx#fetchFundingHistory
* @description fetch the history of funding payments paid and received on this account
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-bills-details-last-3-months
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch funding history for
* @param {int} [limit] the maximum number of funding history structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [funding history structure]{@link https://docs.ccxt.com/#/?id=funding-history-structure}
*/
async fetchFundingHistory(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const request = {
// 'instType': 'SPOT', // SPOT, MARGIN, SWAP, FUTURES, OPTION
// 'ccy': currency['id'],
// 'mgnMode': 'isolated', // isolated, cross
// 'ctType': 'linear', // linear, inverse, only applicable to FUTURES/SWAP
'type': '8',
//
// supported values for type
//
// 1 Transfer
// 2 Trade
// 3 Delivery
// 4 Auto token conversion
// 5 Liquidation
// 6 Margin transfer
// 7 Interest deduction
// 8 Funding fee
// 9 ADL
// 10 Clawback
// 11 System token conversion
// 12 Strategy transfer
// 13 ddh
//
// 'subType': '',
//
// supported values for subType
//
// 1 Buy
// 2 Sell
// 3 Open long
// 4 Open short
// 5 Close long
// 6 Close short
// 9 Interest deduction
// 11 Transfer in
// 12 Transfer out
// 160 Manual margin increase
// 161 Manual margin decrease
// 162 Auto margin increase
// 110 Auto buy
// 111 Auto sell
// 118 System token conversion transfer in
// 119 System token conversion transfer out
// 100 Partial liquidation close long
// 101 Partial liquidation close short
// 102 Partial liquidation buy
// 103 Partial liquidation sell
// 104 Liquidation long
// 105 Liquidation short
// 106 Liquidation buy
// 107 Liquidation sell
// 110 Liquidation transfer in
// 111 Liquidation transfer out
// 125 ADL close long
// 126 ADL close short
// 127 ADL buy
// 128 ADL sell
// 131 ddh buy
// 132 ddh sell
// 170 Exercised
// 171 Counterparty exercised
// 172 Expired OTM
// 112 Delivery long
// 113 Delivery short
// 117 Delivery/Exercise clawback
// 173 Funding fee expense
// 174 Funding fee income
// 200 System transfer in
// 201 Manually transfer in
// 202 System transfer out
// 203 Manually transfer out
//
// "after": "id", // earlier than the requested bill ID
// "before": "id", // newer than the requested bill ID
// "limit": "100", // default 100, max 100
};
if (limit !== undefined) {
request['limit'] = limit.toString(); // default 100, max 100
}
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
symbol = market['symbol'];
if (market['contract']) {
if (market['linear']) {
request['ctType'] = 'linear';
request['ccy'] = market['quoteId'];
}
else {
request['ctType'] = 'inverse';
request['ccy'] = market['baseId'];
}
}
}
const [type, query] = this.handleMarketTypeAndParams('fetchFundingHistory', market, params);
if (type === 'swap') {
request['instType'] = this.convertToInstrumentType(type);
}
// AccountBillsArchive has the same cost as AccountBills but supports three months of data
const response = await this.privateGetAccountBillsArchive(this.extend(request, query));
//
// {
// "bal": "0.0242946200998573",
// "balChg": "0.0000148752712240",
// "billId": "377970609204146187",
// "ccy": "ETH",
// "execType": "",
// "fee": "0",
// "from": "",
// "instId": "ETH-USD-SWAP",
// "instType": "SWAP",
// "mgnMode": "isolated",
// "notes": "",
// "ordId": "",
// "pnl": "0.000014875271224",
// "posBal": "0",
// "posBalChg": "0",
// "subType": "174",
// "sz": "9",
// "to": "",
// "ts": "1636387215588",
// "type": "8"
// }
//
const data = this.safeList(response, 'data', []);
const result = [];
for (let i = 0; i < data.length; i++) {
const entry = data[i];
const timestamp = this.safeInteger(entry, 'ts');
const instId = this.safeString(entry, 'instId');
const marketInner = this.safeMarket(instId);
const currencyId = this.safeString(entry, 'ccy');
const code = this.safeCurrencyCode(currencyId);
result.push({
'info': entry,
'symbol': marketInner['symbol'],
'code': code,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'id': this.safeString(entry, 'billId'),
'amount': this.safeNumber(entry, 'balChg'),
});
}
const sorted = this.sortBy(result, 'timestamp');
return this.filterBySymbolSinceLimit(sorted, symbol, since, limit);
}
/**
* @method
* @name okx#setLeverage
* @description set the level of leverage for a market
* @see https://www.okx.com/docs-v5/en/#rest-api-account-set-leverage
* @param {float} leverage the rate of leverage
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.marginMode] 'cross' or 'isolated'
* @param {string} [params.posSide] 'long' or 'short' or 'net' for isolated margin long/short mode on futures and swap markets, default is 'net'
* @returns {object} response from the exchange
*/
async setLeverage(leverage, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' setLeverage() requires a symbol argument');
}
// WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
// AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
if ((leverage < 1) || (leverage > 125)) {
throw new errors.BadRequest(this.id + ' setLeverage() leverage should be between 1 and 125');
}
await this.loadMarkets();
const market = this.market(symbol);
let marginMode = undefined;
[marginMode, params] = this.handleMarginModeAndParams('setLeverage', params);
if (marginMode === undefined) {
marginMode = this.safeString(params, 'mgnMode', 'cross'); // cross as default marginMode
}
if ((marginMode !== 'cross') && (marginMode !== 'isolated')) {
throw new errors.BadRequest(this.id + ' setLeverage() requires a marginMode parameter that must be either cross or isolated');
}
const request = {
'lever': leverage,
'mgnMode': marginMode,
'instId': market['id'],
};
const posSide = this.safeString(params, 'posSide', 'net');
if (marginMode === 'isolated') {
if (posSide !== 'long' && posSide !== 'short' && posSide !== 'net') {
throw new errors.BadRequest(this.id + ' setLeverage() requires the posSide argument to be either "long", "short" or "net"');
}
request['posSide'] = posSide;
}
const response = await this.privatePostAccountSetLeverage(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "instId": "BTC-USDT-SWAP",
// "lever": "5",
// "mgnMode": "isolated",
// "posSide": "long"
// }
// ],
// "msg": ""
// }
//
return response;
}
/**
* @method
* @name okx#fetchPositionMode
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-account-configuration
* @description fetchs the position mode, hedged or one way, hedged for binance is set identically for all linear markets or all inverse markets
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.accountId] if you have multiple accounts, you must specify the account id to fetch the position mode
* @returns {object} an object detailing whether the market is in hedged or one-way mode
*/
async fetchPositionMode(symbol = undefined, params = {}) {
const accounts = await this.fetchAccounts();
const length = accounts.length;
let selectedAccount = undefined;
if (length > 1) {
const accountId = this.safeString(params, 'accountId');
if (accountId === undefined) {
const accountIds = this.getListFromObjectValues(accounts, 'id');
throw new errors.ExchangeError(this.id + ' fetchPositionMode() can not detect position mode, because you have multiple accounts. Set params["accountId"] to desired id from: ' + accountIds.join(', '));
}
else {
const accountsById = this.indexBy(accounts, 'id');
selectedAccount = this.safeDict(accountsById, accountId);
}
}
else {
selectedAccount = accounts[0];
}
const mainAccount = selectedAccount['info'];
const posMode = this.safeString(mainAccount, 'posMode'); // long_short_mode, net_mode
const isHedged = posMode === 'long_short_mode';
return {
'info': mainAccount,
'hedged': isHedged,
};
}
/**
* @method
* @name okx#setPositionMode
* @description set hedged to true or false for a market
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-set-position-mode
* @param {bool} hedged set to true to use long_short_mode, false for net_mode
* @param {string} symbol not used by okx setPositionMode
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} response from the exchange
*/
async setPositionMode(hedged, symbol = undefined, params = {}) {
let hedgeMode = undefined;
if (hedged) {
hedgeMode = 'long_short_mode';
}
else {
hedgeMode = 'net_mode';
}
const request = {
'posMode': hedgeMode,
};
const response = await this.privatePostAccountSetPositionMode(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "posMode": "net_mode"
// }
// ],
// "msg": ""
// }
//
return response;
}
/**
* @method
* @name okx#setMarginMode
* @description set margin mode to 'cross' or 'isolated'
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-set-leverage
* @param {string} marginMode 'cross' or 'isolated'
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.leverage] leverage
* @returns {object} response from the exchange
*/
async setMarginMode(marginMode, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' setMarginMode() requires a symbol argument');
}
// WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
// AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
marginMode = marginMode.toLowerCase();
if ((marginMode !== 'cross') && (marginMode !== 'isolated')) {
throw new errors.BadRequest(this.id + ' setMarginMode() marginMode must be either cross or isolated');
}
await this.loadMarkets();
const market = this.market(symbol);
const lever = this.safeInteger2(params, 'lever', 'leverage');
if ((lever === undefined) || (lever < 1) || (lever > 125)) {
throw new errors.BadRequest(this.id + ' setMarginMode() params["lever"] should be between 1 and 125');
}
params = this.omit(params, ['leverage']);
const request = {
'lever': lever,
'mgnMode': marginMode,
'instId': market['id'],
};
const response = await this.privatePostAccountSetLeverage(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "instId": "BTC-USDT-SWAP",
// "lever": "5",
// "mgnMode": "isolated",
// "posSide": "long"
// }
// ],
// "msg": ""
// }
//
return response;
}
/**
* @method
* @name okx#fetchCrossBorrowRates
* @description fetch the borrow interest rates of all currencies
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-interest-rate
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a list of [borrow rate structures]{@link https://docs.ccxt.com/#/?id=borrow-rate-structure}
*/
async fetchCrossBorrowRates(params = {}) {
await this.loadMarkets();
const response = await this.privateGetAccountInterestRate(params);
//
// {
// "code": "0",
// "data": [
// {
// "ccy": "BTC",
// "interestRate": "0.00000833"
// }
// ...
// ],
// }
//
const data = this.safeList(response, 'data', []);
const rates = [];
for (let i = 0; i < data.length; i++) {
rates.push(this.parseBorrowRate(data[i]));
}
return rates;
}
/**
* @method
* @name okx#fetchCrossBorrowRate
* @description fetch the rate of interest to borrow a currency for margin trading
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-interest-rate
* @param {string} code unified currency code
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [borrow rate structure]{@link https://docs.ccxt.com/#/?id=borrow-rate-structure}
*/
async fetchCrossBorrowRate(code, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'ccy': currency['id'],
};
const response = await this.privateGetAccountInterestRate(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "ccy": "USDT",
// "interestRate": "0.00002065"
// }
// ...
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const rate = this.safeDict(data, 0, {});
return this.parseBorrowRate(rate);
}
parseBorrowRate(info, currency = undefined) {
//
// {
// "amt": "992.10341195",
// "ccy": "BTC",
// "rate": "0.01",
// "ts": "1643954400000"
// }
//
const ccy = this.safeString(info, 'ccy');
const timestamp = this.safeInteger(info, 'ts');
return {
'currency': this.safeCurrencyCode(ccy),
'rate': this.safeNumber2(info, 'interestRate', 'rate'),
'period': 86400000,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'info': info,
};
}
parseBorrowRateHistories(response, codes, since, limit) {
//
// [
// {
// "amt": "992.10341195",
// "ccy": "BTC",
// "rate": "0.01",
// "ts": "1643954400000"
// },
// ...
// ]
//
const borrowRateHistories = {};
for (let i = 0; i < response.length; i++) {
const item = response[i];
const code = this.safeCurrencyCode(this.safeString(item, 'ccy'));
if (codes === undefined || this.inArray(code, codes)) {
if (!(code in borrowRateHistories)) {
borrowRateHistories[code] = [];
}
const borrowRateStructure = this.parseBorrowRate(item);
const borrrowRateCode = borrowRateHistories[code];
borrrowRateCode.push(borrowRateStructure);
}
}
const keys = Object.keys(borrowRateHistories);
for (let i = 0; i < keys.length; i++) {
const code = keys[i];
borrowRateHistories[code] = this.filterByCurrencySinceLimit(borrowRateHistories[code], code, since, limit);
}
return borrowRateHistories;
}
/**
* @method
* @name okx#fetchBorrowRateHistories
* @description retrieves a history of a multiple currencies borrow interest rate at specific time slots, returns all currencies if no symbols passed, default is undefined
* @see https://www.okx.com/docs-v5/en/#financial-product-savings-get-public-borrow-history-public
* @param {string[]|undefined} codes list of unified currency codes, default is undefined
* @param {int} [since] timestamp in ms of the earliest borrowRate, default is undefined
* @param {int} [limit] max number of borrow rate prices to return, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [borrow rate structures]{@link https://docs.ccxt.com/#/?id=borrow-rate-structure} indexed by the market symbol
*/
async fetchBorrowRateHistories(codes = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const request = {
// 'ccy': currency['id'],
// 'after': this.milliseconds (), // Pagination of data to return records earlier than the requested ts,
// 'before': since, // Pagination of data to return records newer than the requested ts,
// 'limit': limit, // default is 100 and maximum is 100
};
if (since !== undefined) {
request['before'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetFinanceSavingsLendingRateHistory(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "amt": "992.10341195",
// "ccy": "BTC",
// "rate": "0.01",
// "ts": "1643954400000"
// },
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseBorrowRateHistories(data, codes, since, limit);
}
/**
* @method
* @name okx#fetchBorrowRateHistory
* @description retrieves a history of a currencies borrow interest rate at specific time slots
* @see https://www.okx.com/docs-v5/en/#financial-product-savings-get-public-borrow-history-public
* @param {string} code unified currency code
* @param {int} [since] timestamp for the earliest borrow rate
* @param {int} [limit] the maximum number of [borrow rate structures]{@link https://docs.ccxt.com/#/?id=borrow-rate-structure} to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of [borrow rate structures]{@link https://docs.ccxt.com/#/?id=borrow-rate-structure}
*/
async fetchBorrowRateHistory(code, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'ccy': currency['id'],
// 'after': this.milliseconds (), // Pagination of data to return records earlier than the requested ts,
// 'before': since, // Pagination of data to return records newer than the requested ts,
// 'limit': limit, // default is 100 and maximum is 100
};
if (since !== undefined) {
request['before'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetFinanceSavingsLendingRateHistory(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "amt": "992.10341195",
// "ccy": "BTC",
// "rate": "0.01",
// "ts": "1643954400000"
// },
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseBorrowRateHistory(data, code, since, limit);
}
async modifyMarginHelper(symbol, amount, type, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const posSide = this.safeString(params, 'posSide', 'net');
params = this.omit(params, ['posSide']);
const request = {
'instId': market['id'],
'amt': amount,
'type': type,
'posSide': posSide,
};
const response = await this.privatePostAccountPositionMarginBalance(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "amt": "0.01",
// "instId": "ETH-USD-SWAP",
// "posSide": "net",
// "type": "reduce"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const entry = this.safeDict(data, 0, {});
const errorCode = this.safeString(response, 'code');
return this.extend(this.parseMarginModification(entry, market), {
'status': (errorCode === '0') ? 'ok' : 'failed',
});
}
parseMarginModification(data, market = undefined) {
//
// addMargin/reduceMargin
//
// {
// "amt": "0.01",
// "instId": "ETH-USD-SWAP",
// "posSide": "net",
// "type": "reduce"
// }
//
// fetchMarginAdjustmentHistory
//
// {
// bal: '67621.4325135010619812',
// balChg: '-10.0000000000000000',
// billId: '691293628710342659',
// ccy: 'USDT',
// clOrdId: '',
// execType: '',
// fee: '0',
// fillFwdPx: '',
// fillIdxPx: '',
// fillMarkPx: '',
// fillMarkVol: '',
// fillPxUsd: '',
// fillPxVol: '',
// fillTime: '1711089244850',
// from: '',
// instId: 'XRP-USDT-SWAP',
// instType: 'SWAP',
// interest: '0',
// mgnMode: 'isolated',
// notes: '',
// ordId: '',
// pnl: '0',
// posBal: '73.12',
// posBalChg: '10.00',
// px: '',
// subType: '160',
// sz: '10',
// tag: '',
// to: '',
// tradeId: '0',
// ts: '1711089244699',
// type: '6'
// }
//
const amountRaw = this.safeString2(data, 'amt', 'posBalChg');
const typeRaw = this.safeString(data, 'type');
let type = undefined;
if (typeRaw === '6') {
type = Precise["default"].stringGt(amountRaw, '0') ? 'add' : 'reduce';
}
else {
type = typeRaw;
}
const amount = Precise["default"].stringAbs(amountRaw);
const marketId = this.safeString(data, 'instId');
const responseMarket = this.safeMarket(marketId, market);
const code = responseMarket['inverse'] ? responseMarket['base'] : responseMarket['quote'];
const timestamp = this.safeInteger(data, 'ts');
return {
'info': data,
'symbol': responseMarket['symbol'],
'type': type,
'marginMode': 'isolated',
'amount': this.parseNumber(amount),
'code': code,
'total': undefined,
'status': undefined,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
};
}
/**
* @method
* @name okx#reduceMargin
* @description remove margin from a position
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-increase-decrease-margin
* @param {string} symbol unified market symbol
* @param {float} amount the amount of margin to remove
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [margin structure]{@link https://docs.ccxt.com/#/?id=reduce-margin-structure}
*/
async reduceMargin(symbol, amount, params = {}) {
return await this.modifyMarginHelper(symbol, amount, 'reduce', params);
}
/**
* @method
* @name okx#addMargin
* @description add margin
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-increase-decrease-margin
* @param {string} symbol unified market symbol
* @param {float} amount amount of margin to add
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [margin structure]{@link https://docs.ccxt.com/#/?id=add-margin-structure}
*/
async addMargin(symbol, amount, params = {}) {
return await this.modifyMarginHelper(symbol, amount, 'add', params);
}
/**
* @method
* @name okx#fetchMarketLeverageTiers
* @description retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes for a single market
* @see https://www.okx.com/docs-v5/en/#rest-api-public-data-get-position-tiers
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.marginMode] 'cross' or 'isolated'
* @returns {object} a [leverage tiers structure]{@link https://docs.ccxt.com/#/?id=leverage-tiers-structure}
*/
async fetchMarketLeverageTiers(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const type = market['spot'] ? 'MARGIN' : this.convertToInstrumentType(market['type']);
const uly = this.safeString(market['info'], 'uly');
if (!uly) {
if (type !== 'MARGIN') {
throw new errors.BadRequest(this.id + ' fetchMarketLeverageTiers() cannot fetch leverage tiers for ' + symbol);
}
}
let marginMode = undefined;
[marginMode, params] = this.handleMarginModeAndParams('fetchMarketLeverageTiers', params);
if (marginMode === undefined) {
marginMode = this.safeString(params, 'tdMode', 'cross'); // cross as default marginMode
}
const request = {
'instType': type,
'tdMode': marginMode,
'uly': uly,
};
if (type === 'MARGIN') {
request['instId'] = market['id'];
}
const response = await this.publicGetPublicPositionTiers(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "baseMaxLoan": "500",
// "imr": "0.1",
// "instId": "ETH-USDT",
// "maxLever": "10",
// "maxSz": "500",
// "minSz": "0",
// "mmr": "0.03",
// "optMgnFactor": "0",
// "quoteMaxLoan": "200000",
// "tier": "1",
// "uly": ""
// },
// ...
// ]
// }
//
const data = this.safeList(response, 'data', []);
return this.parseMarketLeverageTiers(data, market);
}
parseMarketLeverageTiers(info, market = undefined) {
/**
* @ignore
* @method
* @param {object} info Exchange response for 1 market
* @param {object} market CCXT market
*/
//
// [
// {
// "baseMaxLoan": "500",
// "imr": "0.1",
// "instId": "ETH-USDT",
// "maxLever": "10",
// "maxSz": "500",
// "minSz": "0",
// "mmr": "0.03",
// "optMgnFactor": "0",
// "quoteMaxLoan": "200000",
// "tier": "1",
// "uly": ""
// },
// ...
// ]
//
const tiers = [];
for (let i = 0; i < info.length; i++) {
const tier = info[i];
const marketId = this.safeString(tier, 'instId');
tiers.push({
'tier': this.safeInteger(tier, 'tier'),
'symbol': this.safeSymbol(marketId, market),
'currency': market['quote'],
'minNotional': this.safeNumber(tier, 'minSz'),
'maxNotional': this.safeNumber(tier, 'maxSz'),
'maintenanceMarginRate': this.safeNumber(tier, 'mmr'),
'maxLeverage': this.safeNumber(tier, 'maxLever'),
'info': tier,
});
}
return tiers;
}
/**
* @method
* @name okx#fetchBorrowInterest
* @description fetch the interest owed b the user for borrowing currency for margin trading
* @see https://www.okx.com/docs-v5/en/#rest-api-account-get-interest-accrued-data
* @param {string} code the unified currency code for the currency of the interest
* @param {string} symbol the market symbol of an isolated margin market, if undefined, the interest for cross margin markets is returned
* @param {int} [since] timestamp in ms of the earliest time to receive interest records for
* @param {int} [limit] the number of [borrow interest structures]{@link https://docs.ccxt.com/#/?id=borrow-interest-structure} to retrieve
* @param {object} [params] exchange specific parameters
* @param {int} [params.type] Loan type 1 - VIP loans 2 - Market loans *Default is Market loans*
* @param {string} [params.marginMode] 'cross' or 'isolated'
* @returns {object[]} An list of [borrow interest structures]{@link https://docs.ccxt.com/#/?id=borrow-interest-structure}
*/
async fetchBorrowInterest(code = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let marginMode = undefined;
[marginMode, params] = this.handleMarginModeAndParams('fetchBorrowInterest', params);
if (marginMode === undefined) {
marginMode = this.safeString(params, 'mgnMode', 'cross'); // cross as default marginMode
}
const request = {
'mgnMode': marginMode,
};
let market = undefined;
if (code !== undefined) {
const currency = this.currency(code);
request['ccy'] = currency['id'];
}
if (since !== undefined) {
request['before'] = since - 1;
}
if (limit !== undefined) {
request['limit'] = limit;
}
if (symbol !== undefined) {
market = this.market(symbol);
request['instId'] = market['id'];
}
const response = await this.privateGetAccountInterestAccrued(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "ccy": "USDT",
// "instId": "",
// "interest": "0.0003960833333334",
// "interestRate": "0.0000040833333333",
// "liab": "97",
// "mgnMode": "",
// "ts": "1637312400000",
// "type": "1"
// },
// ...
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const interest = this.parseBorrowInterests(data);
return this.filterByCurrencySinceLimit(interest, code, since, limit);
}
parseBorrowInterest(info, market = undefined) {
const instId = this.safeString(info, 'instId');
if (instId !== undefined) {
market = this.safeMarket(instId, market);
}
const timestamp = this.safeInteger(info, 'ts');
return {
'info': info,
'symbol': this.safeString(market, 'symbol'),
'currency': this.safeCurrencyCode(this.safeString(info, 'ccy')),
'interest': this.safeNumber(info, 'interest'),
'interestRate': this.safeNumber(info, 'interestRate'),
'amountBorrowed': this.safeNumber(info, 'liab'),
'marginMode': this.safeString(info, 'mgnMode'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
};
}
/**
* @method
* @name okx#borrowCrossMargin
* @description create a loan to borrow margin (need to be VIP 5 and above)
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-vip-loans-borrow-and-repay
* @param {string} code unified currency code of the currency to borrow
* @param {float} amount the amount to borrow
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [margin loan structure]{@link https://docs.ccxt.com/#/?id=margin-loan-structure}
*/
async borrowCrossMargin(code, amount, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'ccy': currency['id'],
'amt': this.currencyToPrecision(code, amount),
'side': 'borrow',
};
const response = await this.privatePostAccountBorrowRepay(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "amt": "102",
// "ccy": "USDT",
// "ordId": "544199684697214976",
// "side": "borrow",
// "state": "1"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const loan = this.safeDict(data, 0, {});
return this.parseMarginLoan(loan, currency);
}
/**
* @method
* @name okx#repayCrossMargin
* @description repay borrowed margin and interest
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-vip-loans-borrow-and-repay
* @param {string} code unified currency code of the currency to repay
* @param {float} amount the amount to repay
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.id] the order ID of borrowing, it is necessary while repaying
* @returns {object} a [margin loan structure]{@link https://docs.ccxt.com/#/?id=margin-loan-structure}
*/
async repayCrossMargin(code, amount, params = {}) {
await this.loadMarkets();
const id = this.safeString2(params, 'id', 'ordId');
params = this.omit(params, 'id');
if (id === undefined) {
throw new errors.ArgumentsRequired(this.id + ' repayCrossMargin() requires an id parameter');
}
const currency = this.currency(code);
const request = {
'ccy': currency['id'],
'amt': this.currencyToPrecision(code, amount),
'side': 'repay',
'ordId': id,
};
const response = await this.privatePostAccountBorrowRepay(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "amt": "102",
// "ccy": "USDT",
// "ordId": "544199684697214976",
// "side": "repay",
// "state": "1"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const loan = this.safeDict(data, 0, {});
return this.parseMarginLoan(loan, currency);
}
parseMarginLoan(info, currency = undefined) {
//
// {
// "amt": "102",
// "availLoan": "97",
// "ccy": "USDT",
// "loanQuota": "6000000",
// "posLoan": "0",
// "side": "repay",
// "usedLoan": "97"
// }
//
const currencyId = this.safeString(info, 'ccy');
return {
'id': undefined,
'currency': this.safeCurrencyCode(currencyId, currency),
'amount': this.safeNumber(info, 'amt'),
'symbol': undefined,
'timestamp': undefined,
'datetime': undefined,
'info': info,
};
}
/**
* @method
* @name okx#fetchOpenInterest
* @description Retrieves the open interest of a currency
* @see https://www.okx.com/docs-v5/en/#rest-api-public-data-get-open-interest
* @param {string} symbol Unified CCXT market symbol
* @param {object} [params] exchange specific parameters
* @returns {object} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure}
*/
async fetchOpenInterest(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
if (!market['contract']) {
throw new errors.BadRequest(this.id + ' fetchOpenInterest() supports contract markets only');
}
const type = this.convertToInstrumentType(market['type']);
const uly = this.safeString(market['info'], 'uly');
const request = {
'instType': type,
'uly': uly,
'instId': market['id'],
};
const response = await this.publicGetPublicOpenInterest(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "instId": "BTC-USDT-SWAP",
// "instType": "SWAP",
// "oi": "2125419",
// "oiCcy": "21254.19",
// "ts": "1664005108969"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseOpenInterest(data[0], market);
}
/**
* @method
* @name okx#fetchOpenInterests
* @description Retrieves the open interests of some currencies
* @see https://www.okx.com/docs-v5/en/#rest-api-public-data-get-open-interest
* @param {string[]} symbols Unified CCXT market symbols
* @param {object} [params] exchange specific parameters
* @param {string} params.instType Instrument type, options: 'SWAP', 'FUTURES', 'OPTION', default to 'SWAP'
* @param {string} params.uly Underlying, Applicable to FUTURES/SWAP/OPTION, if instType is 'OPTION', either uly or instFamily is required
* @param {string} params.instFamily Instrument family, Applicable to FUTURES/SWAP/OPTION, if instType is 'OPTION', either uly or instFamily is required
* @returns {object} an dictionary of [open interest structures]{@link https://docs.ccxt.com/#/?id=open-interest-structure}
*/
async fetchOpenInterests(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols, undefined, true, true);
let market = undefined;
if (symbols !== undefined) {
market = this.market(symbols[0]);
}
let marketType = undefined;
[marketType, params] = this.handleSubTypeAndParams('fetchOpenInterests', market, params, 'swap');
let instType = 'SWAP';
if (marketType === 'future') {
instType = 'FUTURES';
}
else if (instType === 'option') {
instType = 'OPTION';
}
const request = { 'instType': instType };
const uly = this.safeString(params, 'uly');
if (uly !== undefined) {
request['uly'] = uly;
}
const instFamily = this.safeString(params, 'instFamily');
if (instFamily !== undefined) {
request['instFamily'] = instFamily;
}
if (instType === 'OPTION' && uly === undefined && instFamily === undefined) {
throw new errors.BadRequest(this.id + ' fetchOpenInterests() requires either uly or instFamily parameter for OPTION markets');
}
const response = await this.publicGetPublicOpenInterest(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "instId": "BTC-USDT-SWAP",
// "instType": "SWAP",
// "oi": "2125419",
// "oiCcy": "21254.19",
// "ts": "1664005108969"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
return this.parseOpenInterests(data, symbols);
}
/**
* @method
* @name okx#fetchOpenInterestHistory
* @description Retrieves the open interest history of a currency
* @see https://www.okx.com/docs-v5/en/#rest-api-trading-data-get-contracts-open-interest-and-volume
* @see https://www.okx.com/docs-v5/en/#rest-api-trading-data-get-options-open-interest-and-volume
* @param {string} symbol Unified CCXT currency code or unified symbol
* @param {string} timeframe "5m", "1h", or "1d" for option only "1d" or "8h"
* @param {int} [since] The time in ms of the earliest record to retrieve as a unix timestamp
* @param {int} [limit] Not used by okx, but parsed internally by CCXT
* @param {object} [params] Exchange specific parameters
* @param {int} [params.until] The time in ms of the latest record to retrieve as a unix timestamp
* @returns An array of [open interest structures]{@link https://docs.ccxt.com/#/?id=open-interest-structure}
*/
async fetchOpenInterestHistory(symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) {
const options = this.safeDict(this.options, 'fetchOpenInterestHistory', {});
const timeframes = this.safeDict(options, 'timeframes', {});
timeframe = this.safeString(timeframes, timeframe, timeframe);
if (timeframe !== '5m' && timeframe !== '1H' && timeframe !== '1D') {
throw new errors.BadRequest(this.id + ' fetchOpenInterestHistory cannot only use the 5m, 1h, and 1d timeframe');
}
await this.loadMarkets();
// handle unified currency code or symbol
let currencyId = undefined;
let market = undefined;
if ((symbol in this.markets) || (symbol in this.markets_by_id)) {
market = this.market(symbol);
currencyId = market['baseId'];
}
else {
const currency = this.currency(symbol);
currencyId = currency['id'];
}
const request = {
'ccy': currencyId,
'period': timeframe,
};
let type = undefined;
let response = undefined;
[type, params] = this.handleMarketTypeAndParams('fetchOpenInterestHistory', market, params);
if (type === 'option') {
response = await this.publicGetRubikStatOptionOpenInterestVolume(this.extend(request, params));
}
else {
if (since !== undefined) {
request['begin'] = since;
}
const until = this.safeInteger(params, 'until');
if (until !== undefined) {
request['end'] = until;
params = this.omit(params, ['until']);
}
response = await this.publicGetRubikStatContractsOpenInterestVolume(this.extend(request, params));
}
//
// {
// "code": "0",
// "data": [
// [
// "1648221300000", // timestamp
// "2183354317.945", // open interest (USD)
// "74285877.617", // volume (USD)
// ],
// ...
// ],
// "msg": ''
// }
//
const data = this.safeList(response, 'data', []);
return this.parseOpenInterestsHistory(data, undefined, since, limit);
}
parseOpenInterest(interest, market = undefined) {
//
// fetchOpenInterestHistory
//
// [
// "1648221300000", // timestamp
// "2183354317.945", // open interest (USD) - (coin) for options
// "74285877.617", // volume (USD) - (coin) for options
// ]
//
// fetchOpenInterest
//
// {
// "instId": "BTC-USD-230520-25500-P",
// "instType": "OPTION",
// "oi": "300",
// "oiCcy": "3",
// "oiUsd": "3",
// "ts": "1684551166251"
// }
//
const id = this.safeString(interest, 'instId');
market = this.safeMarket(id, market);
const time = this.safeInteger(interest, 'ts');
const timestamp = this.safeInteger(interest, 0, time);
let baseVolume = undefined;
let quoteVolume = undefined;
let openInterestAmount = undefined;
let openInterestValue = undefined;
const type = this.safeString(this.options, 'defaultType');
if (Array.isArray(interest)) {
if (type === 'option') {
openInterestAmount = this.safeNumber(interest, 1);
baseVolume = this.safeNumber(interest, 2);
}
else {
openInterestValue = this.safeNumber(interest, 1);
quoteVolume = this.safeNumber(interest, 2);
}
}
else {
baseVolume = this.safeNumber(interest, 'oiCcy');
openInterestAmount = this.safeNumber(interest, 'oi');
openInterestValue = this.safeNumber(interest, 'oiUsd');
}
return this.safeOpenInterest({
'symbol': this.safeSymbol(id),
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'openInterestAmount': openInterestAmount,
'openInterestValue': openInterestValue,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'info': interest,
}, market);
}
setSandboxMode(enable) {
super.setSandboxMode(enable);
this.options['sandboxMode'] = enable;
if (enable) {
this.headers['x-simulated-trading'] = '1';
}
else if ('x-simulated-trading' in this.headers) {
this.headers = this.omit(this.headers, 'x-simulated-trading');
}
}
/**
* @method
* @name okx#fetchDepositWithdrawFees
* @description fetch deposit and withdraw fees
* @see https://www.okx.com/docs-v5/en/#rest-api-funding-get-currencies
* @param {string[]|undefined} codes list of unified currency codes
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [fees structures]{@link https://docs.ccxt.com/#/?id=fee-structure}
*/
async fetchDepositWithdrawFees(codes = undefined, params = {}) {
await this.loadMarkets();
const request = {};
if (codes !== undefined) {
const ids = this.currencyIds(codes);
request['ccy'] = ids.join(',');
}
const response = await this.privateGetAssetCurrencies(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "canDep": true,
// "canInternal": false,
// "canWd": true,
// "ccy": "USDT",
// "chain": "USDT-TRC20",
// "logoLink": "https://static.coinall.ltd/cdn/assets/imgs/221/5F74EB20302D7761.png",
// "mainNet": false,
// "maxFee": "1.6",
// "maxWd": "8852150",
// "minFee": "0.8",
// "minWd": "2",
// "name": "Tether",
// "usedWdQuota": "0",
// "wdQuota": "500",
// "wdTickSz": "3"
// },
// {
// "canDep": true,
// "canInternal": false,
// "canWd": true,
// "ccy": "USDT",
// "chain": "USDT-ERC20",
// "logoLink": "https://static.coinall.ltd/cdn/assets/imgs/221/5F74EB20302D7761.png",
// "mainNet": false,
// "maxFee": "16",
// "maxWd": "8852150",
// "minFee": "8",
// "minWd": "2",
// "name": "Tether",
// "usedWdQuota": "0",
// "wdQuota": "500",
// "wdTickSz": "3"
// },
// ...
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data');
return this.parseDepositWithdrawFees(data, codes);
}
parseDepositWithdrawFees(response, codes = undefined, currencyIdKey = undefined) {
//
// [
// {
// "canDep": true,
// "canInternal": false,
// "canWd": true,
// "ccy": "USDT",
// "chain": "USDT-TRC20",
// "logoLink": "https://static.coinall.ltd/cdn/assets/imgs/221/5F74EB20302D7761.png",
// "mainNet": false,
// "maxFee": "1.6",
// "maxWd": "8852150",
// "minFee": "0.8",
// "minWd": "2",
// "name": "Tether",
// "usedWdQuota": "0",
// "wdQuota": "500",
// "wdTickSz": "3"
// }
// ]
//
const depositWithdrawFees = {};
codes = this.marketCodes(codes);
for (let i = 0; i < response.length; i++) {
const feeInfo = response[i];
const currencyId = this.safeString(feeInfo, 'ccy');
const code = this.safeCurrencyCode(currencyId);
if ((codes === undefined) || (this.inArray(code, codes))) {
const depositWithdrawFee = this.safeValue(depositWithdrawFees, code);
if (depositWithdrawFee === undefined) {
depositWithdrawFees[code] = this.depositWithdrawFee({});
}
depositWithdrawFees[code]['info'][currencyId] = feeInfo;
const chain = this.safeString(feeInfo, 'chain');
if (chain === undefined) {
continue;
}
const chainSplit = chain.split('-');
const networkId = this.safeValue(chainSplit, 1);
const withdrawFee = this.safeNumber(feeInfo, 'fee');
const withdrawResult = {
'fee': withdrawFee,
'percentage': (withdrawFee !== undefined) ? false : undefined,
};
const depositResult = {
'fee': undefined,
'percentage': undefined,
};
const networkCode = this.networkIdToCode(networkId, code);
depositWithdrawFees[code]['networks'][networkCode] = {
'withdraw': withdrawResult,
'deposit': depositResult,
};
}
}
const depositWithdrawCodes = Object.keys(depositWithdrawFees);
for (let i = 0; i < depositWithdrawCodes.length; i++) {
const code = depositWithdrawCodes[i];
const currency = this.currency(code);
depositWithdrawFees[code] = this.assignDefaultDepositWithdrawFees(depositWithdrawFees[code], currency);
}
return depositWithdrawFees;
}
/**
* @method
* @name okx#fetchSettlementHistory
* @description fetches historical settlement records
* @see https://www.okx.com/docs-v5/en/#rest-api-public-data-get-delivery-exercise-history
* @param {string} symbol unified market symbol to fetch the settlement history for
* @param {int} [since] timestamp in ms
* @param {int} [limit] number of records
* @param {object} [params] exchange specific params
* @returns {object[]} a list of [settlement history objects]{@link https://docs.ccxt.com/#/?id=settlement-history-structure}
*/
async fetchSettlementHistory(symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchSettlementHistory() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
let type = undefined;
[type, params] = this.handleMarketTypeAndParams('fetchSettlementHistory', market, params);
if (type !== 'future' && type !== 'option') {
throw new errors.NotSupported(this.id + ' fetchSettlementHistory() supports futures and options markets only');
}
const request = {
'instType': this.convertToInstrumentType(type),
'uly': market['baseId'] + '-' + market['quoteId'],
};
if (since !== undefined) {
request['before'] = since - 1;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetPublicDeliveryExerciseHistory(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "details": [
// {
// "insId": "BTC-USD-230523-25750-C",
// "px": "27290.1486867000556483",
// "type": "exercised"
// },
// ],
// "ts":"1684656000000"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const settlements = this.parseSettlements(data, market);
const sorted = this.sortBy(settlements, 'timestamp');
return this.filterBySymbolSinceLimit(sorted, market['symbol'], since, limit);
}
parseSettlement(settlement, market) {
//
// {
// "insId": "BTC-USD-230521-28500-P",
// "px": "27081.2007345984751516",
// "type": "exercised"
// }
//
const marketId = this.safeString(settlement, 'insId');
return {
'info': settlement,
'symbol': this.safeSymbol(marketId, market),
'price': this.safeNumber(settlement, 'px'),
'timestamp': undefined,
'datetime': undefined,
};
}
parseSettlements(settlements, market) {
//
// {
// "details": [
// {
// "insId": "BTC-USD-230523-25750-C",
// "px": "27290.1486867000556483",
// "type": "exercised"
// },
// ],
// "ts":"1684656000000"
// }
//
const result = [];
for (let i = 0; i < settlements.length; i++) {
const entry = settlements[i];
const timestamp = this.safeInteger(entry, 'ts');
const details = this.safeList(entry, 'details', []);
for (let j = 0; j < details.length; j++) {
const settlement = this.parseSettlement(details[j], market);
result.push(this.extend(settlement, {
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
}));
}
}
return result;
}
/**
* @method
* @name okx#fetchUnderlyingAssets
* @description fetches the market ids of underlying assets for a specific contract market type
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-underlying
* @param {object} [params] exchange specific params
* @param {string} [params.type] the contract market type, 'option', 'swap' or 'future', the default is 'option'
* @returns {object[]} a list of [underlying assets]{@link https://docs.ccxt.com/#/?id=underlying-assets-structure}
*/
async fetchUnderlyingAssets(params = {}) {
await this.loadMarkets();
let marketType = undefined;
[marketType, params] = this.handleMarketTypeAndParams('fetchUnderlyingAssets', undefined, params);
if ((marketType === undefined) || (marketType === 'spot')) {
marketType = 'option';
}
if ((marketType !== 'option') && (marketType !== 'swap') && (marketType !== 'future')) {
throw new errors.NotSupported(this.id + ' fetchUnderlyingAssets() supports contract markets only');
}
const request = {
'instType': this.convertToInstrumentType(marketType),
};
const response = await this.publicGetPublicUnderlying(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// [
// "BTC-USD",
// "ETH-USD"
// ]
// ],
// "msg": ""
// }
//
const underlyings = this.safeList(response, 'data', []);
return underlyings[0];
}
/**
* @method
* @name okx#fetchGreeks
* @description fetches an option contracts greeks, financial metrics used to measure the factors that affect the price of an options contract
* @see https://www.okx.com/docs-v5/en/#public-data-rest-api-get-option-market-data
* @param {string} symbol unified symbol of the market to fetch greeks for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [greeks structure]{@link https://docs.ccxt.com/#/?id=greeks-structure}
*/
async fetchGreeks(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const marketId = market['id'];
const optionParts = marketId.split('-');
const request = {
'uly': market['info']['uly'],
'instFamily': market['info']['instFamily'],
'expTime': this.safeString(optionParts, 2),
};
const response = await this.publicGetPublicOptSummary(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "askVol": "0",
// "bidVol": "0",
// "delta": "0.5105464486882039",
// "deltaBS": "0.7325502184143025",
// "fwdPx": "37675.80158694987186",
// "gamma": "-0.13183515090501083",
// "gammaBS": "0.000024139685826358558",
// "instId": "BTC-USD-240329-32000-C",
// "instType": "OPTION",
// "lever": "4.504428015946619",
// "markVol": "0.5916253554539876",
// "realVol": "0",
// "theta": "-0.0004202992014012855",
// "thetaBS": "-18.52354631567909",
// "ts": "1699586421976",
// "uly": "BTC-USD",
// "vega": "0.0020207455080045846",
// "vegaBS": "74.44022302387287",
// "volLv": "0.5948549730405797"
// },
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const entry = data[i];
const entryMarketId = this.safeString(entry, 'instId');
if (entryMarketId === marketId) {
return this.parseGreeks(entry, market);
}
}
return undefined;
}
parseGreeks(greeks, market = undefined) {
//
// {
// "askVol": "0",
// "bidVol": "0",
// "delta": "0.5105464486882039",
// "deltaBS": "0.7325502184143025",
// "fwdPx": "37675.80158694987186",
// "gamma": "-0.13183515090501083",
// "gammaBS": "0.000024139685826358558",
// "instId": "BTC-USD-240329-32000-C",
// "instType": "OPTION",
// "lever": "4.504428015946619",
// "markVol": "0.5916253554539876",
// "realVol": "0",
// "theta": "-0.0004202992014012855",
// "thetaBS": "-18.52354631567909",
// "ts": "1699586421976",
// "uly": "BTC-USD",
// "vega": "0.0020207455080045846",
// "vegaBS": "74.44022302387287",
// "volLv": "0.5948549730405797"
// }
//
const timestamp = this.safeInteger(greeks, 'ts');
const marketId = this.safeString(greeks, 'instId');
const symbol = this.safeSymbol(marketId, market);
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'delta': this.safeNumber(greeks, 'delta'),
'gamma': this.safeNumber(greeks, 'gamma'),
'theta': this.safeNumber(greeks, 'theta'),
'vega': this.safeNumber(greeks, 'vega'),
'rho': undefined,
'bidSize': undefined,
'askSize': undefined,
'bidImpliedVolatility': this.safeNumber(greeks, 'bidVol'),
'askImpliedVolatility': this.safeNumber(greeks, 'askVol'),
'markImpliedVolatility': this.safeNumber(greeks, 'markVol'),
'bidPrice': undefined,
'askPrice': undefined,
'markPrice': undefined,
'lastPrice': undefined,
'underlyingPrice': undefined,
'info': greeks,
};
}
/**
* @method
* @name okx#closePosition
* @description closes open positions for a market
* @see https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-close-positions
* @param {string} symbol Unified CCXT market symbol
* @param {string} [side] 'buy' or 'sell', leave as undefined in net mode
* @param {object} [params] extra parameters specific to the okx api endpoint
* @param {string} [params.clientOrderId] a unique identifier for the order
* @param {string} [params.marginMode] 'cross' or 'isolated', default is 'cross;
* @param {string} [params.code] *required in the case of closing cross MARGIN position for Single-currency margin* margin currency
*
* EXCHANGE SPECIFIC PARAMETERS
* @param {boolean} [params.autoCxl] whether any pending orders for closing out needs to be automatically canceled when close position via a market order. false or true, the default is false
* @param {string} [params.tag] order tag a combination of case-sensitive alphanumerics, all numbers, or all letters of up to 16 characters
* @returns {object[]} [A list of position structures]{@link https://docs.ccxt.com/#/?id=position-structure}
*/
async closePosition(symbol, side = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const clientOrderId = this.safeString(params, 'clientOrderId');
const code = this.safeString(params, 'code');
let marginMode = undefined;
[marginMode, params] = this.handleMarginModeAndParams('closePosition', params, 'cross');
const request = {
'instId': market['id'],
'mgnMode': marginMode,
};
if (side !== undefined) {
if ((side === 'buy')) {
request['posSide'] = 'long';
}
else if (side === 'sell') {
request['posSide'] = 'short';
}
else {
request['posSide'] = side;
}
}
if (clientOrderId !== undefined) {
request['clOrdId'] = clientOrderId;
}
if (code !== undefined) {
const currency = this.currency(code);
request['ccy'] = currency['id'];
}
const response = await this.privatePostTradeClosePosition(this.extend(request, params));
//
// {
// "code": "1",
// "data": [
// {
// "clOrdId":"e847386590ce4dBCe903bbc394dc88bf",
// "ordId":"",
// "sCode":"51000",
// "sMsg":"Parameter posSide error ",
// "tag":"e847386590ce4dBC"
// }
// ],
// "inTime": "1701877077101064",
// "msg": "All operations failed",
// "outTime": "1701877077102579"
// }
//
const data = this.safeList(response, 'data', []);
const order = this.safeDict(data, 0);
return this.parseOrder(order, market);
}
/**
* @method
* @name okx#fetchOption
* @description fetches option data that is commonly found in an option chain
* @see https://www.okx.com/docs-v5/en/#order-book-trading-market-data-get-ticker
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [option chain structure]{@link https://docs.ccxt.com/#/?id=option-chain-structure}
*/
async fetchOption(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
const response = await this.publicGetMarketTicker(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "instType": "OPTION",
// "instId": "BTC-USD-241227-60000-P",
// "last": "",
// "lastSz": "0",
// "askPx": "",
// "askSz": "0",
// "bidPx": "",
// "bidSz": "0",
// "open24h": "",
// "high24h": "",
// "low24h": "",
// "volCcy24h": "0",
// "vol24h": "0",
// "ts": "1711176035035",
// "sodUtc0": "",
// "sodUtc8": ""
// }
// ]
// }
//
const result = this.safeList(response, 'data', []);
const chain = this.safeDict(result, 0, {});
return this.parseOption(chain, undefined, market);
}
/**
* @method
* @name okx#fetchOptionChain
* @description fetches data for an underlying asset that is commonly found in an option chain
* @see https://www.okx.com/docs-v5/en/#order-book-trading-market-data-get-tickers
* @param {string} code base currency to fetch an option chain for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.uly] the underlying asset, can be obtained from fetchUnderlyingAssets ()
* @returns {object} a list of [option chain structures]{@link https://docs.ccxt.com/#/?id=option-chain-structure}
*/
async fetchOptionChain(code, params = {}) {
await this.loadMarkets();
const currency = this.currency(code);
const request = {
'uly': currency['code'] + '-USD',
'instType': 'OPTION',
};
const response = await this.publicGetMarketTickers(this.extend(request, params));
//
// {
// "code": "0",
// "msg": "",
// "data": [
// {
// "instType": "OPTION",
// "instId": "BTC-USD-240323-52000-C",
// "last": "",
// "lastSz": "0",
// "askPx": "",
// "askSz": "0",
// "bidPx": "",
// "bidSz": "0",
// "open24h": "",
// "high24h": "",
// "low24h": "",
// "volCcy24h": "0",
// "vol24h": "0",
// "ts": "1711176207008",
// "sodUtc0": "",
// "sodUtc8": ""
// },
// ]
// }
//
const result = this.safeList(response, 'data', []);
return this.parseOptionChain(result, undefined, 'instId');
}
parseOption(chain, currency = undefined, market = undefined) {
//
// {
// "instType": "OPTION",
// "instId": "BTC-USD-241227-60000-P",
// "last": "",
// "lastSz": "0",
// "askPx": "",
// "askSz": "0",
// "bidPx": "",
// "bidSz": "0",
// "open24h": "",
// "high24h": "",
// "low24h": "",
// "volCcy24h": "0",
// "vol24h": "0",
// "ts": "1711176035035",
// "sodUtc0": "",
// "sodUtc8": ""
// }
//
const marketId = this.safeString(chain, 'instId');
market = this.safeMarket(marketId, market);
const timestamp = this.safeInteger(chain, 'ts');
return {
'info': chain,
'currency': undefined,
'symbol': market['symbol'],
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'impliedVolatility': undefined,
'openInterest': undefined,
'bidPrice': this.safeNumber(chain, 'bidPx'),
'askPrice': this.safeNumber(chain, 'askPx'),
'midPrice': undefined,
'markPrice': undefined,
'lastPrice': this.safeNumber(chain, 'last'),
'underlyingPrice': undefined,
'change': undefined,
'percentage': undefined,
'baseVolume': this.safeNumber(chain, 'volCcy24h'),
'quoteVolume': undefined,
};
}
/**
* @method
* @name okx#fetchConvertQuote
* @description fetch a quote for converting from one currency to another
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-estimate-quote
* @param {string} fromCode the currency that you want to sell and convert from
* @param {string} toCode the currency that you want to buy and convert into
* @param {float} [amount] how much you want to trade in units of the from currency
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [conversion structure]{@link https://docs.ccxt.com/#/?id=conversion-structure}
*/
async fetchConvertQuote(fromCode, toCode, amount = undefined, params = {}) {
await this.loadMarkets();
const request = {
'baseCcy': fromCode.toUpperCase(),
'quoteCcy': toCode.toUpperCase(),
'rfqSzCcy': fromCode.toUpperCase(),
'rfqSz': this.numberToString(amount),
'side': 'sell',
};
const response = await this.privatePostAssetConvertEstimateQuote(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "baseCcy": "ETH",
// "baseSz": "0.01023052",
// "clQReqId": "",
// "cnvtPx": "2932.40104429",
// "origRfqSz": "30",
// "quoteCcy": "USDT",
// "quoteId": "quoterETH-USDT16461885104612381",
// "quoteSz": "30",
// "quoteTime": "1646188510461",
// "rfqSz": "30",
// "rfqSzCcy": "USDT",
// "side": "buy",
// "ttlMs": "10000"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const result = this.safeDict(data, 0, {});
const fromCurrencyId = this.safeString(result, 'baseCcy', fromCode);
const fromCurrency = this.currency(fromCurrencyId);
const toCurrencyId = this.safeString(result, 'quoteCcy', toCode);
const toCurrency = this.currency(toCurrencyId);
return this.parseConversion(result, fromCurrency, toCurrency);
}
/**
* @method
* @name okx#createConvertTrade
* @description convert from one currency to another
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-convert-trade
* @param {string} id the id of the trade that you want to make
* @param {string} fromCode the currency that you want to sell and convert from
* @param {string} toCode the currency that you want to buy and convert into
* @param {float} [amount] how much you want to trade in units of the from currency
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [conversion structure]{@link https://docs.ccxt.com/#/?id=conversion-structure}
*/
async createConvertTrade(id, fromCode, toCode, amount = undefined, params = {}) {
await this.loadMarkets();
const request = {
'quoteId': id,
'baseCcy': fromCode,
'quoteCcy': toCode,
'szCcy': fromCode,
'sz': this.numberToString(amount),
'side': 'sell',
};
const response = await this.privatePostAssetConvertTrade(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "baseCcy": "ETH",
// "clTReqId": "",
// "fillBaseSz": "0.01023052",
// "fillPx": "2932.40104429",
// "fillQuoteSz": "30",
// "instId": "ETH-USDT",
// "quoteCcy": "USDT",
// "quoteId": "quoterETH-USDT16461885104612381",
// "side": "buy",
// "state": "fullyFilled",
// "tradeId": "trader16461885203381437",
// "ts": "1646188520338"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const result = this.safeDict(data, 0, {});
const fromCurrencyId = this.safeString(result, 'baseCcy', fromCode);
const fromCurrency = this.currency(fromCurrencyId);
const toCurrencyId = this.safeString(result, 'quoteCcy', toCode);
const toCurrency = this.currency(toCurrencyId);
return this.parseConversion(result, fromCurrency, toCurrency);
}
/**
* @method
* @name okx#fetchConvertTrade
* @description fetch the data for a conversion trade
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-convert-history
* @param {string} id the id of the trade that you want to fetch
* @param {string} [code] the unified currency code of the conversion trade
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [conversion structure]{@link https://docs.ccxt.com/#/?id=conversion-structure}
*/
async fetchConvertTrade(id, code = undefined, params = {}) {
await this.loadMarkets();
const request = {
'clTReqId': id,
};
const response = await this.privateGetAssetConvertHistory(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "clTReqId": "",
// "instId": "ETH-USDT",
// "side": "buy",
// "fillPx": "2932.401044",
// "baseCcy": "ETH",
// "quoteCcy": "USDT",
// "fillBaseSz": "0.01023052",
// "state": "fullyFilled",
// "tradeId": "trader16461885203381437",
// "fillQuoteSz": "30",
// "ts": "1646188520000"
// }
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const result = this.safeDict(data, 0, {});
const fromCurrencyId = this.safeString(result, 'baseCcy');
const toCurrencyId = this.safeString(result, 'quoteCcy');
let fromCurrency = undefined;
let toCurrency = undefined;
if (fromCurrencyId !== undefined) {
fromCurrency = this.currency(fromCurrencyId);
}
if (toCurrencyId !== undefined) {
toCurrency = this.currency(toCurrencyId);
}
return this.parseConversion(result, fromCurrency, toCurrency);
}
/**
* @method
* @name okx#fetchConvertTradeHistory
* @description fetch the users history of conversion trades
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-convert-history
* @param {string} [code] the unified currency code
* @param {int} [since] the earliest time in ms to fetch conversions for
* @param {int} [limit] the maximum number of conversion structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest conversion to fetch
* @returns {object[]} a list of [conversion structures]{@link https://docs.ccxt.com/#/?id=conversion-structure}
*/
async fetchConvertTradeHistory(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let request = {};
[request, params] = this.handleUntilOption('after', request, params);
if (since !== undefined) {
request['before'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.privateGetAssetConvertHistory(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// {
// "clTReqId": "",
// "instId": "ETH-USDT",
// "side": "buy",
// "fillPx": "2932.401044",
// "baseCcy": "ETH",
// "quoteCcy": "USDT",
// "fillBaseSz": "0.01023052",
// "state": "fullyFilled",
// "tradeId": "trader16461885203381437",
// "fillQuoteSz": "30",
// "ts": "1646188520000"
// }
// ],
// "msg": ""
// }
//
const rows = this.safeList(response, 'data', []);
return this.parseConversions(rows, code, 'baseCcy', 'quoteCcy', since, limit);
}
parseConversion(conversion, fromCurrency = undefined, toCurrency = undefined) {
//
// fetchConvertQuote
//
// {
// "baseCcy": "ETH",
// "baseSz": "0.01023052",
// "clQReqId": "",
// "cnvtPx": "2932.40104429",
// "origRfqSz": "30",
// "quoteCcy": "USDT",
// "quoteId": "quoterETH-USDT16461885104612381",
// "quoteSz": "30",
// "quoteTime": "1646188510461",
// "rfqSz": "30",
// "rfqSzCcy": "USDT",
// "side": "buy",
// "ttlMs": "10000"
// }
//
// createConvertTrade
//
// {
// "baseCcy": "ETH",
// "clTReqId": "",
// "fillBaseSz": "0.01023052",
// "fillPx": "2932.40104429",
// "fillQuoteSz": "30",
// "instId": "ETH-USDT",
// "quoteCcy": "USDT",
// "quoteId": "quoterETH-USDT16461885104612381",
// "side": "buy",
// "state": "fullyFilled",
// "tradeId": "trader16461885203381437",
// "ts": "1646188520338"
// }
//
// fetchConvertTrade, fetchConvertTradeHistory
//
// {
// "clTReqId": "",
// "instId": "ETH-USDT",
// "side": "buy",
// "fillPx": "2932.401044",
// "baseCcy": "ETH",
// "quoteCcy": "USDT",
// "fillBaseSz": "0.01023052",
// "state": "fullyFilled",
// "tradeId": "trader16461885203381437",
// "fillQuoteSz": "30",
// "ts": "1646188520000"
// }
//
const timestamp = this.safeInteger2(conversion, 'quoteTime', 'ts');
const fromCoin = this.safeString(conversion, 'baseCcy');
const fromCode = this.safeCurrencyCode(fromCoin, fromCurrency);
const to = this.safeString(conversion, 'quoteCcy');
const toCode = this.safeCurrencyCode(to, toCurrency);
return {
'info': conversion,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'id': this.safeStringN(conversion, ['clQReqId', 'tradeId', 'quoteId']),
'fromCurrency': fromCode,
'fromAmount': this.safeNumber2(conversion, 'baseSz', 'fillBaseSz'),
'toCurrency': toCode,
'toAmount': this.safeNumber2(conversion, 'quoteSz', 'fillQuoteSz'),
'price': this.safeNumber2(conversion, 'cnvtPx', 'fillPx'),
'fee': undefined,
};
}
/**
* @method
* @name okx#fetchConvertCurrencies
* @description fetches all available currencies that can be converted
* @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-convert-currencies
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an associative dictionary of currencies
*/
async fetchConvertCurrencies(params = {}) {
await this.loadMarkets();
const response = await this.privateGetAssetConvertCurrencies(params);
//
// {
// "code": "0",
// "data": [
// {
// "ccy": "BTC",
// "max": "",
// "min": ""
// },
// ],
// "msg": ""
// }
//
const result = {};
const data = this.safeList(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const entry = data[i];
const id = this.safeString(entry, 'ccy');
const code = this.safeCurrencyCode(id);
result[code] = {
'info': entry,
'id': id,
'code': code,
'networks': undefined,
'type': undefined,
'name': undefined,
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'fee': undefined,
'precision': undefined,
'limits': {
'amount': {
'min': this.safeNumber(entry, 'min'),
'max': this.safeNumber(entry, 'max'),
},
'withdraw': {
'min': undefined,
'max': undefined,
},
'deposit': {
'min': undefined,
'max': undefined,
},
},
'created': undefined,
};
}
return result;
}
handleErrors(httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (!response) {
return undefined; // fallback to default error handler
}
//
// {
// "code": "1",
// "data": [
// {
// "clOrdId": "",
// "ordId": "",
// "sCode": "51119",
// "sMsg": "Order placement failed due to insufficient balance. ",
// "tag": ""
// }
// ],
// "msg": ""
// },
// {
// "code": "58001",
// "data": [],
// "msg": "Incorrect trade password"
// }
//
const code = this.safeString(response, 'code');
if ((code !== '0') && (code !== '2')) { // 2 means that bulk operation partially succeeded
const feedback = this.id + ' ' + body;
const data = this.safeList(response, 'data', []);
for (let i = 0; i < data.length; i++) {
const error = data[i];
const errorCode = this.safeString(error, 'sCode');
const message = this.safeString(error, 'sMsg');
this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
}
this.throwExactlyMatchedException(this.exceptions['exact'], code, feedback);
throw new errors.ExchangeError(feedback); // unknown message
}
return undefined;
}
/**
* @method
* @name okx#fetchMarginAdjustmentHistory
* @description fetches the history of margin added or reduced from contract isolated positions
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-bills-details-last-7-days
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-bills-details-last-3-months
* @param {string} [symbol] not used by okx fetchMarginAdjustmentHistory
* @param {string} [type] "add" or "reduce"
* @param {int} [since] the earliest time in ms to fetch margin adjustment history for
* @param {int} [limit] the maximum number of entries to retrieve
* @param {object} params extra parameters specific to the exchange api endpoint
* @param {boolean} [params.auto] true if fetching auto margin increases
* @returns {object[]} a list of [margin structures]{@link https://docs.ccxt.com/#/?id=margin-loan-structure}
*/
async fetchMarginAdjustmentHistory(symbol = undefined, type = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const auto = this.safeBool(params, 'auto');
if (type === undefined) {
throw new errors.ArgumentsRequired(this.id + ' fetchMarginAdjustmentHistory () requires a type argument');
}
const isAdd = type === 'add';
let subType = isAdd ? '160' : '161';
if (auto) {
if (isAdd) {
subType = '162';
}
else {
throw new errors.BadRequest(this.id + ' cannot fetch margin adjustments for type ' + type);
}
}
const request = {
'subType': subType,
'mgnMode': 'isolated',
};
const until = this.safeInteger(params, 'until');
params = this.omit(params, 'until');
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
if (until !== undefined) {
request['endTime'] = until;
}
let response = undefined;
const now = this.milliseconds();
const oneWeekAgo = now - 604800000;
const threeMonthsAgo = now - 7776000000;
if ((since === undefined) || (since > oneWeekAgo)) {
response = await this.privateGetAccountBills(this.extend(request, params));
}
else if (since > threeMonthsAgo) {
response = await this.privateGetAccountBillsArchive(this.extend(request, params));
}
else {
throw new errors.BadRequest(this.id + ' fetchMarginAdjustmentHistory () cannot fetch margin adjustments older than 3 months');
}
//
// {
// code: '0',
// data: [
// {
// bal: '67621.4325135010619812',
// balChg: '-10.0000000000000000',
// billId: '691293628710342659',
// ccy: 'USDT',
// clOrdId: '',
// execType: '',
// fee: '0',
// fillFwdPx: '',
// fillIdxPx: '',
// fillMarkPx: '',
// fillMarkVol: '',
// fillPxUsd: '',
// fillPxVol: '',
// fillTime: '1711089244850',
// from: '',
// instId: 'XRP-USDT-SWAP',
// instType: 'SWAP',
// interest: '0',
// mgnMode: 'isolated',
// notes: '',
// ordId: '',
// pnl: '0',
// posBal: '73.12',
// posBalChg: '10.00',
// px: '',
// subType: '160',
// sz: '10',
// tag: '',
// to: '',
// tradeId: '0',
// ts: '1711089244699',
// type: '6'
// }
// ],
// msg: ''
// }
//
const data = this.safeList(response, 'data');
const modifications = this.parseMarginModifications(data);
return this.filterBySymbolSinceLimit(modifications, symbol, since, limit);
}
/**
* @method
* @name okx#fetchPositionsHistory
* @description fetches historical positions
* @see https://www.okx.com/docs-v5/en/#trading-account-rest-api-get-positions-history
* @param {string} [symbols] unified market symbols
* @param {int} [since] timestamp in ms of the earliest position to fetch
* @param {int} [limit] the maximum amount of records to fetch, default=100, max=100
* @param {object} params extra parameters specific to the exchange api endpoint
* @param {string} [params.marginMode] "cross" or "isolated"
*
* EXCHANGE SPECIFIC PARAMETERS
* @param {string} [params.instType] margin, swap, futures or option
* @param {string} [params.type] the type of latest close position 1: close position partially, 2:close all, 3:liquidation, 4:partial liquidation; 5:adl, is it is the latest type if there are several types for the same position
* @param {string} [params.posId] position id, there is attribute expiration, the posid will be expired if it is more than 30 days after the last full close position, then position will use new posid
* @param {string} [params.before] timestamp in ms of the earliest position to fetch based on the last update time of the position
* @param {string} [params.after] timestamp in ms of the latest position to fetch based on the last update time of the position
* @returns {object[]} a list of [position structures]{@link https://docs.ccxt.com/#/?id=position-structure}
*/
async fetchPositionsHistory(symbols = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const marginMode = this.safeString(params, 'marginMode');
const instType = this.safeStringUpper(params, 'instType');
params = this.omit(params, ['until', 'marginMode', 'instType']);
if (limit === undefined) {
limit = 100;
}
const request = {
'limit': limit,
};
if (symbols !== undefined) {
const symbolsLength = symbols.length;
if (symbolsLength === 1) {
const market = this.market(symbols[0]);
request['instId'] = market['id'];
}
}
if (marginMode !== undefined) {
request['mgnMode'] = marginMode;
}
if (instType !== undefined) {
request['instType'] = instType;
}
const response = await this.privateGetAccountPositionsHistory(this.extend(request, params));
//
// {
// code: '0',
// data: [
// {
// cTime: '1708735940395',
// ccy: 'USDT',
// closeAvgPx: '0.6330444444444444',
// closeTotalPos: '27',
// direction: 'long',
// fee: '-1.69566',
// fundingFee: '-11.870404179341788',
// instId: 'XRP-USDT-SWAP',
// instType: 'SWAP',
// lever: '3.0',
// liqPenalty: '0',
// mgnMode: 'cross',
// openAvgPx: '0.623',
// openMaxPos: '15',
// pnl: '27.11999999999988',
// pnlRatio: '0.0241732402722634',
// posId: '681423155054862336',
// realizedPnl: '13.553935820658092',
// triggerPx: '',
// type: '2',
// uTime: '1711088748170',
// uly: 'XRP-USDT'
// },
// ...
// ],
// msg: ''
// }
//
const data = this.safeList(response, 'data');
const positions = this.parsePositions(data, symbols, params);
return this.filterBySinceLimit(positions, since, limit);
}
/**
* @method
* @name okx#fetchLongShortRatioHistory
* @description fetches the long short ratio history for a unified market symbol
* @see https://www.okx.com/docs-v5/en/#trading-statistics-rest-api-get-contract-long-short-ratio
* @param {string} symbol unified symbol of the market to fetch the long short ratio for
* @param {string} [timeframe] the period for the ratio
* @param {int} [since] the earliest time in ms to fetch ratios for
* @param {int} [limit] the maximum number of long short ratio structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest ratio to fetch
* @returns {object[]} an array of [long short ratio structures]{@link https://docs.ccxt.com/#/?id=long-short-ratio-structure}
*/
async fetchLongShortRatioHistory(symbol = undefined, timeframe = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instId': market['id'],
};
const until = this.safeString2(params, 'until', 'end');
params = this.omit(params, 'until');
if (until !== undefined) {
request['end'] = until;
}
if (timeframe !== undefined) {
request['period'] = timeframe;
}
if (since !== undefined) {
request['begin'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetRubikStatContractsLongShortAccountRatioContract(this.extend(request, params));
//
// {
// "code": "0",
// "data": [
// ["1729323600000", "0.9398602814619824"],
// ["1729323300000", "0.9398602814619824"],
// ["1729323000000", "0.9398602814619824"],
// ],
// "msg": ""
// }
//
const data = this.safeList(response, 'data', []);
const result = [];
for (let i = 0; i < data.length; i++) {
const entry = data[i];
result.push({
'timestamp': this.safeString(entry, 0),
'longShortRatio': this.safeString(entry, 1),
});
}
return this.parseLongShortRatioHistory(result, market);
}
parseLongShortRatio(info, market = undefined) {
const timestamp = this.safeInteger(info, 'timestamp');
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
return {
'info': info,
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'timeframe': undefined,
'longShortRatio': this.safeNumber(info, 'longShortRatio'),
};
}
}
module.exports = okx;