astro-perp-ccxt-dev
Version:
3,472 lines • 160 kB
JavaScript
'use strict';
var cryptocom$1 = require('./abstract/cryptocom.js');
var Precise = require('./base/Precise.js');
var errors = require('./base/errors.js');
var number = require('./base/functions/number.js');
var sha256 = require('./static_dependencies/noble-hashes/sha256.js');
// ----------------------------------------------------------------------------
/**
* @class cryptocom
* @augments Exchange
*/
class cryptocom extends cryptocom$1 {
describe() {
return this.deepExtend(super.describe(), {
'id': 'cryptocom',
'name': 'Crypto.com',
'countries': ['MT'],
'version': 'v2',
'rateLimit': 10,
'certified': true,
'pro': true,
'has': {
'CORS': false,
'spot': true,
'margin': true,
'swap': true,
'future': true,
'option': true,
'addMargin': false,
'cancelAllOrders': true,
'cancelOrder': true,
'cancelOrders': true,
'cancelOrdersForSymbols': true,
'closeAllPositions': false,
'closePosition': true,
'createMarketBuyOrderWithCost': false,
'createMarketOrderWithCost': false,
'createMarketSellOrderWithCost': false,
'createOrder': true,
'createOrders': true,
'createStopOrder': true,
'createTriggerOrder': true,
'editOrder': true,
'fetchAccounts': true,
'fetchBalance': true,
'fetchBidsAsks': false,
'fetchBorrowInterest': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchClosedOrders': 'emulated',
'fetchCrossBorrowRate': false,
'fetchCrossBorrowRates': false,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': true,
'fetchDeposits': true,
'fetchDepositsWithdrawals': false,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': true,
'fetchFundingRates': false,
'fetchGreeks': false,
'fetchIndexOHLCV': false,
'fetchIsolatedBorrowRate': false,
'fetchIsolatedBorrowRates': false,
'fetchLedger': true,
'fetchLeverage': false,
'fetchLeverageTiers': false,
'fetchMarginAdjustmentHistory': false,
'fetchMarginMode': false,
'fetchMarketLeverageTiers': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMySettlementHistory': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchPosition': true,
'fetchPositionHistory': false,
'fetchPositionMode': false,
'fetchPositions': true,
'fetchPositionsHistory': false,
'fetchPremiumIndexOHLCV': false,
'fetchSettlementHistory': true,
'fetchStatus': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': false,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': true,
'fetchTransactionFees': false,
'fetchTransactions': false,
'fetchTransfers': false,
'fetchUnderlyingAssets': false,
'fetchVolatilityHistory': false,
'fetchWithdrawals': true,
'reduceMargin': false,
'repayCrossMargin': false,
'repayIsolatedMargin': false,
'sandbox': true,
'setLeverage': false,
'setMarginMode': false,
'setPositionMode': false,
'transfer': false,
'withdraw': true,
},
'timeframes': {
'1m': '1m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'4h': '4h',
'6h': '6h',
'12h': '12h',
'1d': '1D',
'1w': '7D',
'2w': '14D',
'1M': '1M',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/147792121-38ed5e36-c229-48d6-b49a-48d05fc19ed4.jpeg',
'test': {
'v1': 'https://uat-api.3ona.co/exchange/v1',
'v2': 'https://uat-api.3ona.co/v2',
'derivatives': 'https://uat-api.3ona.co/v2',
},
'api': {
'base': 'https://api.crypto.com',
'v1': 'https://api.crypto.com/exchange/v1',
'v2': 'https://api.crypto.com/v2',
'derivatives': 'https://deriv-api.crypto.com/v1',
},
'www': 'https://crypto.com/',
'referral': {
'url': 'https://crypto.com/exch/kdacthrnxt',
'discount': 0.75,
},
'doc': [
'https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html',
'https://exchange-docs.crypto.com/spot/index.html',
'https://exchange-docs.crypto.com/derivatives/index.html',
],
'fees': 'https://crypto.com/exchange/document/fees-limits',
},
'api': {
'base': {
'public': {
'get': {
'v1/public/get-announcements': 1, // no description of rate limit
},
},
},
'v1': {
'public': {
'get': {
'public/auth': 10 / 3,
'public/get-instruments': 10 / 3,
'public/get-book': 1,
'public/get-candlestick': 1,
'public/get-trades': 1,
'public/get-tickers': 1,
'public/get-valuations': 1,
'public/get-expired-settlement-price': 10 / 3,
'public/get-insurance': 1,
'public/get-risk-parameters': 1,
},
'post': {
'public/staking/get-conversion-rate': 2,
},
},
'private': {
'post': {
'private/set-cancel-on-disconnect': 10 / 3,
'private/get-cancel-on-disconnect': 10 / 3,
'private/user-balance': 10 / 3,
'private/user-balance-history': 10 / 3,
'private/get-positions': 10 / 3,
'private/create-order': 2 / 3,
'private/amend-order': 4 / 3,
'private/create-order-list': 10 / 3,
'private/cancel-order': 2 / 3,
'private/cancel-order-list': 10 / 3,
'private/cancel-all-orders': 2 / 3,
'private/close-position': 10 / 3,
'private/get-order-history': 100,
'private/get-open-orders': 10 / 3,
'private/get-order-detail': 1 / 3,
'private/get-trades': 100,
'private/change-account-leverage': 10 / 3,
'private/get-transactions': 10 / 3,
'private/create-subaccount-transfer': 10 / 3,
'private/get-subaccount-balances': 10 / 3,
'private/get-order-list': 10 / 3,
'private/create-withdrawal': 10 / 3,
'private/get-currency-networks': 10 / 3,
'private/get-deposit-address': 10 / 3,
'private/get-accounts': 10 / 3,
'private/get-withdrawal-history': 10 / 3,
'private/get-deposit-history': 10 / 3,
'private/get-fee-rate': 2,
'private/get-instrument-fee-rate': 2,
'private/staking/stake': 2,
'private/staking/unstake': 2,
'private/staking/get-staking-position': 2,
'private/staking/get-staking-instruments': 2,
'private/staking/get-open-stake': 2,
'private/staking/get-stake-history': 2,
'private/staking/get-reward-history': 2,
'private/staking/convert': 2,
'private/staking/get-open-convert': 2,
'private/staking/get-convert-history': 2,
},
},
},
'v2': {
'public': {
'get': {
'public/auth': 1,
'public/get-instruments': 1,
'public/get-book': 1,
'public/get-candlestick': 1,
'public/get-ticker': 1,
'public/get-trades': 1,
'public/margin/get-transfer-currencies': 1,
'public/margin/get-load-currenices': 1,
'public/respond-heartbeat': 1,
},
},
'private': {
'post': {
'private/set-cancel-on-disconnect': 10 / 3,
'private/get-cancel-on-disconnect': 10 / 3,
'private/create-withdrawal': 10 / 3,
'private/get-withdrawal-history': 10 / 3,
'private/get-currency-networks': 10 / 3,
'private/get-deposit-history': 10 / 3,
'private/get-deposit-address': 10 / 3,
'private/export/create-export-request': 10 / 3,
'private/export/get-export-requests': 10 / 3,
'private/export/download-export-output': 10 / 3,
'private/get-account-summary': 10 / 3,
'private/create-order': 2 / 3,
'private/cancel-order': 2 / 3,
'private/cancel-all-orders': 2 / 3,
'private/create-order-list': 10 / 3,
'private/get-order-history': 10 / 3,
'private/get-open-orders': 10 / 3,
'private/get-order-detail': 1 / 3,
'private/get-trades': 100,
'private/get-accounts': 10 / 3,
'private/get-subaccount-balances': 10 / 3,
'private/create-subaccount-transfer': 10 / 3,
'private/otc/get-otc-user': 10 / 3,
'private/otc/get-instruments': 10 / 3,
'private/otc/request-quote': 100,
'private/otc/accept-quote': 100,
'private/otc/get-quote-history': 10 / 3,
'private/otc/get-trade-history': 10 / 3,
'private/otc/create-order': 10 / 3,
},
},
},
'derivatives': {
'public': {
'get': {
'public/auth': 10 / 3,
'public/get-instruments': 10 / 3,
'public/get-book': 1,
'public/get-candlestick': 1,
'public/get-trades': 1,
'public/get-tickers': 1,
'public/get-valuations': 1,
'public/get-expired-settlement-price': 10 / 3,
'public/get-insurance': 1,
},
},
'private': {
'post': {
'private/set-cancel-on-disconnect': 10 / 3,
'private/get-cancel-on-disconnect': 10 / 3,
'private/user-balance': 10 / 3,
'private/user-balance-history': 10 / 3,
'private/get-positions': 10 / 3,
'private/create-order': 2 / 3,
'private/create-order-list': 10 / 3,
'private/cancel-order': 2 / 3,
'private/cancel-order-list': 10 / 3,
'private/cancel-all-orders': 2 / 3,
'private/close-position': 10 / 3,
'private/convert-collateral': 10 / 3,
'private/get-order-history': 100,
'private/get-open-orders': 10 / 3,
'private/get-order-detail': 1 / 3,
'private/get-trades': 100,
'private/change-account-leverage': 10 / 3,
'private/get-transactions': 10 / 3,
'private/create-subaccount-transfer': 10 / 3,
'private/get-subaccount-balances': 10 / 3,
'private/get-order-list': 10 / 3,
},
},
},
},
'fees': {
'trading': {
'maker': this.parseNumber('0.004'),
'taker': this.parseNumber('0.004'),
'tiers': {
'maker': [
[this.parseNumber('0'), this.parseNumber('0.004')],
[this.parseNumber('25000'), this.parseNumber('0.0035')],
[this.parseNumber('50000'), this.parseNumber('0.0015')],
[this.parseNumber('100000'), this.parseNumber('0.001')],
[this.parseNumber('250000'), this.parseNumber('0.0009')],
[this.parseNumber('1000000'), this.parseNumber('0.0008')],
[this.parseNumber('20000000'), this.parseNumber('0.0007')],
[this.parseNumber('100000000'), this.parseNumber('0.0006')],
[this.parseNumber('200000000'), this.parseNumber('0.0004')],
],
'taker': [
[this.parseNumber('0'), this.parseNumber('0.004')],
[this.parseNumber('25000'), this.parseNumber('0.0035')],
[this.parseNumber('50000'), this.parseNumber('0.0025')],
[this.parseNumber('100000'), this.parseNumber('0.0016')],
[this.parseNumber('250000'), this.parseNumber('0.00015')],
[this.parseNumber('1000000'), this.parseNumber('0.00014')],
[this.parseNumber('20000000'), this.parseNumber('0.00013')],
[this.parseNumber('100000000'), this.parseNumber('0.00012')],
[this.parseNumber('200000000'), this.parseNumber('0.0001')],
],
},
},
},
'options': {
'defaultType': 'spot',
'accountsById': {
'funding': 'SPOT',
'spot': 'SPOT',
'margin': 'MARGIN',
'derivatives': 'DERIVATIVES',
'swap': 'DERIVATIVES',
'future': 'DERIVATIVES',
},
'networks': {
'BEP20': 'BSC',
'ERC20': 'ETH',
'TRC20': 'TRON',
},
'broker': 'CCXT',
},
'features': {
'default': {
'sandbox': true,
'createOrder': {
'marginMode': true,
'triggerPrice': true,
// todo: implementation fix
'triggerPriceType': {
'last': true,
'mark': true,
'index': true,
},
'triggerDirection': false,
'stopLossPrice': true,
'takeProfitPrice': true,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'hedged': false,
'selfTradePrevention': true,
'trailing': false,
'iceberg': false,
'leverage': false,
'marketBuyByCost': true,
'marketBuyRequiresPrice': true,
},
'createOrders': {
'max': 10,
},
'fetchMyTrades': {
'marginMode': false,
'limit': 100,
'daysBack': undefined,
'untilDays': 1,
'symbolRequired': false,
},
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOpenOrders': {
'marginMode': true,
'limit': 100,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': {
'marginMode': false,
'limit': 100,
'daysBack': undefined,
'untilDays': 1,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchClosedOrders': {
'marginMode': false,
'limit': 100,
'daysBack': undefined,
'daysBackCanceled': undefined,
'untilDays': 1,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 300,
},
},
'spot': {
'extends': 'default',
},
'swap': {
'linear': {
'extends': 'default',
},
'inverse': {
'extends': 'default',
},
},
'future': {
'linear': {
'extends': 'default',
},
'inverse': {
'extends': 'default',
},
},
},
// https://exchange-docs.crypto.com/spot/index.html#response-and-reason-codes
'commonCurrencies': {
'USD_STABLE_COIN': 'USDC',
},
'precisionMode': number.TICK_SIZE,
'exceptions': {
'exact': {
'219': errors.InvalidOrder,
'314': errors.InvalidOrder,
'325': errors.InvalidOrder,
'415': errors.InvalidOrder,
'10001': errors.ExchangeError,
'10002': errors.PermissionDenied,
'10003': errors.PermissionDenied,
'10004': errors.BadRequest,
'10005': errors.PermissionDenied,
'10006': errors.DDoSProtection,
'10007': errors.InvalidNonce,
'10008': errors.BadRequest,
'10009': errors.BadRequest,
'20001': errors.BadRequest,
'20002': errors.InsufficientFunds,
'20005': errors.AccountNotEnabled,
'30003': errors.BadSymbol,
'30004': errors.BadRequest,
'30005': errors.BadRequest,
'30006': errors.InvalidOrder,
'30007': errors.InvalidOrder,
'30008': errors.InvalidOrder,
'30009': errors.InvalidOrder,
'30010': errors.BadRequest,
'30013': errors.InvalidOrder,
'30014': errors.InvalidOrder,
'30016': errors.InvalidOrder,
'30017': errors.InvalidOrder,
'30023': errors.InvalidOrder,
'30024': errors.InvalidOrder,
'30025': errors.InvalidOrder,
'40001': errors.BadRequest,
'40002': errors.BadRequest,
'40003': errors.BadRequest,
'40004': errors.BadRequest,
'40005': errors.BadRequest,
'40006': errors.BadRequest,
'40007': errors.BadRequest,
'40101': errors.AuthenticationError,
'40102': errors.InvalidNonce,
'40103': errors.AuthenticationError,
'40104': errors.AuthenticationError,
'40107': errors.BadRequest,
'40401': errors.OrderNotFound,
'40801': errors.RequestTimeout,
'42901': errors.RateLimitExceeded,
'43005': errors.InvalidOrder,
'43003': errors.InvalidOrder,
'43004': errors.InvalidOrder,
'43012': errors.BadRequest,
'50001': errors.ExchangeError,
'9010001': errors.OnMaintenance, // {"code":9010001,"message":"SYSTEM_MAINTENANCE","details":"Crypto.com Exchange is currently under maintenance. Please refer to https://status.crypto.com for more details."}
},
'broad': {},
},
});
}
/**
* @method
* @name cryptocom#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-currency-networks
* @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
if (!this.checkRequiredCredentials(false)) {
return undefined;
}
let skipFetchCurrencies = false;
[skipFetchCurrencies, params] = this.handleOptionAndParams(params, 'fetchCurrencies', 'skipFetchCurrencies', false);
if (skipFetchCurrencies) {
// sub-accounts can't access this endpoint
return undefined;
}
let response = {};
try {
response = await this.v1PrivatePostPrivateGetCurrencyNetworks(params);
}
catch (e) {
if (e instanceof errors.ExchangeError) {
// sub-accounts can't access this endpoint
// {"code":"10001","msg":"SYS_ERROR"}
return undefined;
}
throw e;
// do nothing
// sub-accounts can't access this endpoint
}
//
// {
// "id": "1747502328559",
// "method": "private/get-currency-networks",
// "code": "0",
// "result": {
// "update_time": "1747502281000",
// "currency_map": {
// "USDT": {
// "full_name": "Tether USD",
// "default_network": "ETH",
// "network_list": [
// {
// "network_id": "ETH",
// "withdrawal_fee": "10.00000000",
// "withdraw_enabled": true,
// "min_withdrawal_amount": "20.0",
// "deposit_enabled": true,
// "confirmation_required": "32"
// },
// {
// "network_id": "CRONOS",
// "withdrawal_fee": "0.18000000",
// "withdraw_enabled": true,
// "min_withdrawal_amount": "0.35",
// "deposit_enabled": true,
// "confirmation_required": "15"
// },
// {
// "network_id": "SOL",
// "withdrawal_fee": "5.31000000",
// "withdraw_enabled": true,
// "min_withdrawal_amount": "10.62",
// "deposit_enabled": true,
// "confirmation_required": "1"
// }
// ]
// }
// }
// }
// }
//
const resultData = this.safeDict(response, 'result', {});
const currencyMap = this.safeDict(resultData, 'currency_map', {});
const keys = Object.keys(currencyMap);
const result = {};
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const currency = currencyMap[key];
const id = key;
const code = this.safeCurrencyCode(id);
const networks = {};
const chains = this.safeList(currency, 'network_list', []);
for (let j = 0; j < chains.length; j++) {
const chain = chains[j];
const networkId = this.safeString(chain, 'network_id');
const network = this.networkIdToCode(networkId);
networks[network] = {
'info': chain,
'id': networkId,
'network': network,
'active': undefined,
'deposit': this.safeBool(chain, 'deposit_enabled', false),
'withdraw': this.safeBool(chain, 'withdraw_enabled', false),
'fee': this.safeNumber(chain, 'withdrawal_fee'),
'precision': undefined,
'limits': {
'withdraw': {
'min': this.safeNumber(chain, 'min_withdrawal_amount'),
'max': undefined,
},
},
};
}
result[code] = this.safeCurrencyStructure({
'info': currency,
'id': id,
'code': code,
'name': this.safeString(currency, 'full_name'),
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'fee': undefined,
'precision': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
},
'type': 'crypto',
'networks': networks,
});
}
return result;
}
/**
* @method
* @name cryptocom#fetchMarkets
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-instruments
* @description retrieves data on all markets for cryptocom
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const response = await this.v1PublicGetPublicGetInstruments(params);
//
// {
// "id": 1,
// "method": "public/get-instruments",
// "code": 0,
// "result": {
// "data": [
// {
// "symbol": "BTC_USDT",
// "inst_type": "CCY_PAIR",
// "display_name": "BTC/USDT",
// "base_ccy": "BTC",
// "quote_ccy": "USDT",
// "quote_decimals": 2,
// "quantity_decimals": 5,
// "price_tick_size": "0.01",
// "qty_tick_size": "0.00001",
// "max_leverage": "50",
// "tradable": true,
// "expiry_timestamp_ms": 0,
// "beta_product": false,
// "margin_buy_enabled": false,
// "margin_sell_enabled": true
// },
// {
// "symbol": "RUNEUSD-PERP",
// "inst_type": "PERPETUAL_SWAP",
// "display_name": "RUNEUSD Perpetual",
// "base_ccy": "RUNE",
// "quote_ccy": "USD",
// "quote_decimals": 3,
// "quantity_decimals": 1,
// "price_tick_size": "0.001",
// "qty_tick_size": "0.1",
// "max_leverage": "50",
// "tradable": true,
// "expiry_timestamp_ms": 0,
// "beta_product": false,
// "underlying_symbol": "RUNEUSD-INDEX",
// "contract_size": "1",
// "margin_buy_enabled": false,
// "margin_sell_enabled": false
// },
// {
// "symbol": "ETHUSD-230825",
// "inst_type": "FUTURE",
// "display_name": "ETHUSD Futures 20230825",
// "base_ccy": "ETH",
// "quote_ccy": "USD",
// "quote_decimals": 2,
// "quantity_decimals": 4,
// "price_tick_size": "0.01",
// "qty_tick_size": "0.0001",
// "max_leverage": "100",
// "tradable": true,
// "expiry_timestamp_ms": 1692950400000,
// "beta_product": false,
// "underlying_symbol": "ETHUSD-INDEX",
// "contract_size": "1",
// "margin_buy_enabled": false,
// "margin_sell_enabled": false
// },
// {
// "symbol": "BTCUSD-230630-CW30000",
// "inst_type": "WARRANT",
// "display_name": "BTCUSD-230630-CW30000",
// "base_ccy": "BTC",
// "quote_ccy": "USD",
// "quote_decimals": 3,
// "quantity_decimals": 0,
// "price_tick_size": "0.001",
// "qty_tick_size": "10",
// "max_leverage": "50",
// "tradable": true,
// "expiry_timestamp_ms": 1688112000000,
// "beta_product": false,
// "underlying_symbol": "BTCUSD-INDEX",
// "put_call": "CALL",
// "strike": "30000",
// "contract_size": "0.0001",
// "margin_buy_enabled": false,
// "margin_sell_enabled": false
// },
// ]
// }
// }
//
const resultResponse = this.safeDict(response, 'result', {});
const data = this.safeList(resultResponse, 'data', []);
const result = [];
for (let i = 0; i < data.length; i++) {
const market = data[i];
const inst_type = this.safeString(market, 'inst_type');
const spot = inst_type === 'CCY_PAIR';
const swap = inst_type === 'PERPETUAL_SWAP';
const future = inst_type === 'FUTURE';
const option = inst_type === 'WARRANT';
const baseId = this.safeString(market, 'base_ccy');
const quoteId = this.safeString(market, 'quote_ccy');
const settleId = spot ? undefined : quoteId;
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
const settle = spot ? undefined : this.safeCurrencyCode(settleId);
const optionType = this.safeStringLower(market, 'put_call');
const strike = this.safeString(market, 'strike');
const marginBuyEnabled = this.safeBool(market, 'margin_buy_enabled');
const marginSellEnabled = this.safeBool(market, 'margin_sell_enabled');
const expiryString = this.omitZero(this.safeString(market, 'expiry_timestamp_ms'));
const expiry = (expiryString !== undefined) ? parseInt(expiryString) : undefined;
let symbol = base + '/' + quote;
let type = undefined;
let contract = undefined;
if (inst_type === 'CCY_PAIR') {
type = 'spot';
contract = false;
}
else if (inst_type === 'PERPETUAL_SWAP') {
type = 'swap';
symbol = symbol + ':' + quote;
contract = true;
}
else if (inst_type === 'FUTURE') {
type = 'future';
symbol = symbol + ':' + quote + '-' + this.yymmdd(expiry);
contract = true;
}
else if (inst_type === 'WARRANT') {
type = 'option';
const symbolOptionType = (optionType === 'call') ? 'C' : 'P';
symbol = symbol + ':' + quote + '-' + this.yymmdd(expiry) + '-' + strike + '-' + symbolOptionType;
contract = true;
}
result.push({
'id': this.safeString(market, 'symbol'),
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': type,
'spot': spot,
'margin': ((marginBuyEnabled) || (marginSellEnabled)),
'swap': swap,
'future': future,
'option': option,
'active': this.safeBool(market, 'tradable'),
'contract': contract,
'linear': (contract) ? true : undefined,
'inverse': (contract) ? false : undefined,
'contractSize': this.safeNumber(market, 'contract_size'),
'expiry': expiry,
'expiryDatetime': this.iso8601(expiry),
'strike': this.parseNumber(strike),
'optionType': optionType,
'precision': {
'price': this.parseNumber(this.safeString(market, 'price_tick_size')),
'amount': this.parseNumber(this.safeString(market, 'qty_tick_size')),
},
'limits': {
'leverage': {
'min': this.parseNumber('1'),
'max': this.safeNumber(market, 'max_leverage'),
},
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'created': undefined,
'info': market,
});
}
return result;
}
/**
* @method
* @name cryptocom#fetchTickers
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-tickers
* @see https://exchange-docs.crypto.com/derivatives/index.html#public-get-tickers
* @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTickers(symbols = undefined, params = {}) {
await this.loadMarkets();
let market = undefined;
const request = {};
if (symbols !== undefined) {
let symbol = undefined;
if (Array.isArray(symbols)) {
const symbolsLength = symbols.length;
if (symbolsLength > 1) {
throw new errors.BadRequest(this.id + ' fetchTickers() symbols argument cannot contain more than 1 symbol');
}
symbol = symbols[0];
}
else {
symbol = symbols;
}
market = this.market(symbol);
request['instrument_name'] = market['id'];
}
const response = await this.v1PublicGetPublicGetTickers(this.extend(request, params));
//
// {
// "id": -1,
// "method": "public/get-tickers",
// "code": 0,
// "result": {
// "data": [
// {
// "i": "AVAXUSD-PERP",
// "h": "13.209",
// "l": "12.148",
// "a": "13.209",
// "v": "1109.8",
// "vv": "14017.33",
// "c": "0.0732",
// "b": "13.210",
// "k": "13.230",
// "oi": "10888.9",
// "t": 1687402657575
// },
// ]
// }
// }
//
const result = this.safeDict(response, 'result', {});
const data = this.safeList(result, 'data', []);
return this.parseTickers(data, symbols);
}
/**
* @method
* @name cryptocom#fetchTicker
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-tickers
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTicker(symbol, params = {}) {
await this.loadMarkets();
symbol = this.symbol(symbol);
const tickers = await this.fetchTickers([symbol], params);
return this.safeValue(tickers, symbol);
}
/**
* @method
* @name cryptocom#fetchOrders
* @description fetches information on multiple orders made by the user
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-order-history
* @param {string} symbol unified market symbol of the market the orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for, max date range is one day
* @param {int} [limit] the maximum number of order structures to retrieve, default 100 max 100
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms for the ending date filter, default is the current time
* @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 {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchOrders', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchOrders', symbol, since, limit, params);
}
let market = undefined;
const request = {};
if (symbol !== undefined) {
market = this.market(symbol);
request['instrument_name'] = market['id'];
}
if (since !== undefined) {
request['start_time'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const until = this.safeInteger(params, 'until');
params = this.omit(params, ['until']);
if (until !== undefined) {
request['end_time'] = until;
}
const response = await this.v1PrivatePostPrivateGetOrderHistory(this.extend(request, params));
//
// {
// "id": 1686881486183,
// "method": "private/get-order-history",
// "code": 0,
// "result": {
// "data": [
// {
// "account_id": "ce075bef-1234-4321-bd6g-ff9007252e63",
// "order_id": "6142909895014042762",
// "client_oid": "4e918597-1234-4321-8201-a7577e1e1d91",
// "order_type": "MARKET",
// "time_in_force": "GOOD_TILL_CANCEL",
// "side": "SELL",
// "exec_inst": [ ],
// "quantity": "0.00024",
// "order_value": "5.7054672",
// "maker_fee_rate": "0",
// "taker_fee_rate": "0",
// "avg_price": "25023.97",
// "trigger_price": "0",
// "ref_price": "0",
// "ref_price_type": "NULL_VAL",
// "cumulative_quantity": "0.00024",
// "cumulative_value": "6.0057528",
// "cumulative_fee": "0.001501438200",
// "status": "FILLED",
// "update_user_id": "ce075bef-1234-4321-bd6g-ff9007252e63",
// "order_date": "2023-06-15",
// "instrument_name": "BTC_USD",
// "fee_instrument_name": "USD",
// "create_time": 1686805465891,
// "create_time_ns": "1686805465891812578",
// "update_time": 1686805465891
// }
// ]
// }
// }
//
const data = this.safeDict(response, 'result', {});
const orders = this.safeList(data, 'data', []);
return this.parseOrders(orders, market, since, limit);
}
/**
* @method
* @name cryptocom#fetchTrades
* @description get a list of the most recent trades for a particular symbol
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-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, maximum date range is one day
* @param {int} [limit] the maximum number of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms for the ending date filter, default is the current time
* @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=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.fetchPaginatedCallDynamic('fetchTrades', symbol, since, limit, params);
}
const market = this.market(symbol);
const request = {
'instrument_name': market['id'],
};
if (since !== undefined) {
request['start_ts'] = since;
}
if (limit !== undefined) {
request['count'] = limit;
}
const until = this.safeInteger(params, 'until');
params = this.omit(params, ['until']);
if (until !== undefined) {
request['end_ts'] = until;
}
const response = await this.v1PublicGetPublicGetTrades(this.extend(request, params));
//
// {
// "id": -1,
// "method": "public/get-trades",
// "code": 0,
// "result": {
// "data": [
// {
// "s": "sell",
// "p": "26386.00",
// "q": "0.00453",
// "t": 1686944282062,
// "tn" : 1704476468851524373,
// "d": "4611686018455979970",
// "i": "BTC_USD"
// },
// ]
// }
// }
//
const result = this.safeDict(response, 'result', {});
const trades = this.safeList(result, 'data', []);
return this.parseTrades(trades, market, since, limit);
}
/**
* @method
* @name cryptocom#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-candlestick
* @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 {int} [params.until] timestamp in ms for the ending date filter, default is the current time
* @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();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate', false);
if (paginate) {
return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 300);
}
const market = this.market(symbol);
const request = {
'instrument_name': market['id'],
'timeframe': this.safeString(this.timeframes, timeframe, timeframe),
};
if (limit !== undefined) {
if (limit > 300) {
limit = 300;
}
request['count'] = limit;
}
const now = this.microseconds();
const duration = this.parseTimeframe(timeframe);
const until = this.safeInteger(params, 'until', now);
params = this.omit(params, ['until']);
if (since !== undefined) {
request['start_ts'] = since - duration * 1000;
if (limit !== undefined) {
request['end_ts'] = this.sum(since, duration * limit * 1000);
}
else {
request['end_ts'] = until;
}
}
else {
request['end_ts'] = until;
}
const response = await this.v1PublicGetPublicGetCandlestick(this.extend(request, params));
//
// {
// "id": -1,
// "method": "public/get-candlestick",
// "code": 0,
// "result": {
// "interval": "1m",
// "data": [
// {
// "o": "26949.89",
// "h": "26957.64",
// "l": "26948.24",
// "c": "26950.00",
// "v": "0.0670",
// "t": 1687237080000
// },
// ],
// "instrument_name": "BTC_USD"
// }
// }
//
const result = this.safeDict(response, 'result', {});
const data = this.safeList(result, 'data', []);
return this.parseOHLCVs(data, market, timeframe, since, limit);
}
/**
* @method
* @name cryptocom#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-book
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the number of order book entries to return, max 50
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'instrument_name': market['id'],
};
if (limit) {
request['depth'] = limit;
}
const response = await this.v1PublicGetPublicGetBook(this.extend(request, params));
//
// {
// "id": -1,
// "method": "public/get-book",
// "code": 0,
// "result": {
// "depth": 3,
// "data": [
// {
// "bids": [ [ "30025.00", "0.00004", "1" ], [ "30020.15", "0.02498", "1" ], [ "30020.00", "0.00004", "1" ] ],
// "asks": [ [ "30025.01", "0.04090", "1" ], [ "30025.70", "0.01000", "1" ], [ "30026.94", "0.02681", "1" ] ],
// "t": 1687491287380
// }
// ],
// "instrument_name": "BTC_USD"
// }
// }
//
const result = this.safeDict(response, 'result', {});
const data = this.safeList(result, 'data', []);
const orderBook = this.safeValue(data, 0);
const timestamp = this.safeInteger(orderBook, 't');
return this.parseOrderBook(orderBook, symbol, timestamp);
}
parseBalance(response) {
const responseResult = this.safeDict(response, 'result', {});
const data = this.safeList(responseResult, 'data', []);
const positionBalances = this.safeValue(data[0], 'position_balances', []);
const result = { 'info': response };
for (let i = 0; i < positionBalances.length; i++) {
const balance = positionBalances[i];
const currencyId = this.safeString(balance, 'instrument_name');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
account['total'] = this.safeString(balance, 'quantity');
account['used'] = this.safeString(balance, 'reserved_qty');
result[code] = account;
}
return this.safeBalance(result);
}
/**
* @method
* @name cryptocom#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-user-balance
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
*/
async fetchBalance(params = {}) {
await this.loadMarkets();
const response = await this.v1PrivatePostPrivateUserBalance(params);
//
// {
// "id": 1687300499018,
// "method": "private/user-balance",
// "code": 0,
// "result": {
// "data": [
// {
// "total_available_balance": "5.84684368",
// "total_margin_balance": "5.84684368",
// "total_initial_margin": "0",
// "total_maintenance_margin": "0",
// "total_position_cost": "0",
// "total_cash_balance": "6.44412101",
// "total_collateral_value": "5.846843685",
// "total_session_unrealized_pnl": "0",
// "instrument_name": "USD",
// "total_session_realized_pnl": "0",
// "position_balances": [
// {
// "quantity": "0.0002119875",
// "reserved_qty": "0",
// "collateral_weight": "0.9",
// "collateral_amount": "5.37549592",
// "market_value": "5.97277325",
// "max_withdrawal_balance": "0.00021198",
// "instrument_name": "BTC",
// "hourly_interest_rate": "0"
// },
// ],
// "total_effective_leverage": "0",
// "position_limit": "3000000",
// "used_position_limit": "0",
// "total_borrow": "0",
// "margin_score": "0",
// "is_liquidating": false,
// "has_risk": false,
// "terminatable": true
// }
// ]
// }
// }
//
return this.parseBalance(response);
}
/**
* @method
* @name cryptocom#fetchOrder
* @description fetches information on an order made by the user
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-order-detail
* @param {string} id order id
* @param {string} symbol unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOrder(id, symbol = undefined, params = {}) {
await this.loadMarkets();
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
}
const request = {
'order_id': id,
};
const response = await this.v1PrivatePostPrivateGetOrderDetail(this.extend(request, params));
//
// {
// "id": 1686872583882,
// "method": "private/get-order-detail",
// "code": 0,
// "result": {
// "account_id": "ae075bef-1234-4321-bd6g-bb9007252a63",
// "order_id": "6142909895025252686",
// "client_oid": "CCXT_c2d2152cc32d40a3ae7fbf",
// "order_type": "LIMIT",
// "time_in_force": "GOOD_TILL_CANCEL",
// "side": "BUY",
// "exec_inst": [ ],
// "quantity": "0.00020",
// "limit_price": "20000.00",
// "order_value": "4",
// "avg_price": "0",
// "trigger_price": "0",
// "ref_price": "0",
// "cumulative_quantity": "0",
// "cumulative_value": "0",
// "cumulative_fee": "0",
// "status": "ACTIVE",
// "update_user_id": "ae075bef-1234-4321-bd6g-bb9007252a63",
// "order_date": "2023-06-15",
// "instrument_name": "BTC_USD",
// "fee_instrument_name": "BTC",
// "create_time": 1686870220684,
// "create_time_ns": "1686870220684239675",
// "update_time": 1686870220684
// }
// }
//
const order = this.safeDict(response, 'result', {});
return this.parseOrder(order, market);
}
createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
const market = this.market(symbol);
const uppercaseType = type.toUpperCase();
const request = {
'instrument_name': market['id'],
'side': side.toUpperCase(),
'quantity': this.amountToPrecision(symbol, amount),
};
if ((uppercaseType === 'LIMIT') || (uppercaseType === 'STOP_LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) {
request['price'] = this.priceToPrecision(symbol, price);
}
const broker = this.safeString(this.options, 'broker', 'CCXT');
request['broker_id'] = broker;
let marketType = undefined;
let marginMode = undefined;
[marketType, params] = this.handleMarketTypeAndParams('createOrder', market, params);
[marginMode, params] = this.customHandleMarginModeAndParams('createOrder', params);
if ((marketType === 'margin') || (marginMode !== undefined)) {
request['spot_margin'] = 'MARGIN';
}
else if (marketType === 'spot') {
request['spot_margin'] = 'SPOT';
}
const timeInForce = this.safeStringUpper2(params, 'timeInForce', 'time_in_force');
if (timeInForce !== undefined) {
if (timeInForce === 'GTC') {
request['time_in_force'] = 'GOOD_TILL_CANCEL';
}
else if (timeInForce === 'IOC') {
request['time_in_force'] = 'IMMEDIATE_OR_CANCEL';
}
else if (timeInForce === 'FOK') {
request['time_in_force'] = 'FILL_OR_KILL';
}
else {
request['time_in_force'] = timeInForce;
}
}
const postOnly = this.safeBool(params, 'postOnly', false);
if ((postOnly) || (timeInForce === 'PO')) {
request['exec_inst'] = ['POST_ONLY'];
request['time_in_force'] = 'GOOD_TILL_CANCEL';
}
const triggerPrice = this.safeStringN(params, ['stopPrice', 'triggerPrice', 'ref_price']);
const stopLossPrice = this.safeNumber(params, 'stopLossPrice');
const takeProfitPrice = this.safeNumber(params, 'takeProfitPrice');
const isTrigger = (triggerPrice !== undefined);
const isStopLossTrigger = (stopLossPrice !== undefined);
const isTakeProfitTrigger = (takeProfitPrice !== undefined);
if (isTrigger) {
request['ref_price'] = this.priceToPrecision(symbol, triggerPrice);
const priceString = this.numberToString(price);
if ((uppercaseType === 'LIMIT') || (uppercaseType === 'STOP_LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) {
if (side === 'buy') {
if (Precise["default"].stringLt(priceString, triggerPrice)) {
request['type'] = 'TAKE_PROFIT_LIMIT';
}
else {
request['type'] = 'STOP_LIMIT';
}
}
else {
if (Precise["default"].stringLt(priceString, triggerPrice)) {
request['type'] = 'STOP_LIMIT';
}
else {
request['type'] = 'TAKE_PROFIT_LIMIT';
}
}
}
else {
if (side === 'buy') {
if (Precise["default"].stringLt(priceString, triggerPrice)) {
request['type'] = 'TAKE_PROFIT';
}
else {
request['type'] = 'STOP_LOSS';
}
}
else {
if (Precise["default"].stringLt(priceString, triggerPrice)) {
request['type'] = 'STOP_LOSS';
}
else {
request['type'] = 'TAKE_PROFIT';
}
}
}
}
else if (isStopLossTrigger) {
if ((uppercaseType === 'LIMIT') || (uppercaseType === 'STOP_LIMIT')) {
request['type'] = 'STOP_LIMIT';
}
else {
request['type'] = 'STOP_LOSS';
}
request['ref_price'] = this.priceToPrecision(symbol, stopLossPrice);
}
else if (isTakeProfitTrigger) {
if ((uppercaseType === 'LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) {
request['type'] = 'TAKE_PROFIT_LIMIT';
}
else {
request['type'] = 'TAKE_PROFIT';
}
request['ref_price'] = this.priceToPrecision(symbol, takeProfitPrice);
}
else {
request['type'] = uppercaseType;
}
params = this.omit(params, ['postOnly', 'clientOrderId', 'timeInForce', 'stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice']);
return this.extend(request, params);
}
/**
* @method
* @name cryptocom#createOrder
* @description create a trade order
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-create-order
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market', 'limit', 'stop_loss', 'stop_limit', 'take_profit', 'take_profit_limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much 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 {string} [params.timeInForce] 'GTC', 'IOC', 'FOK' or 'PO'
* @param {string} [params.ref_price_type] 'MARK_PRICE', 'INDEX_PRICE', 'LAST_PRICE' which trigger price type to use, default is MARK_PRICE
* @param {float} [params.triggerPrice] price to trigger a trigger order
* @param {float} [params.stopLossPrice] price to trigger a stop-loss trigger order
* @param {float} [params.takeProfitPrice] price to trigger a take-profit trigger order
* @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);
const request = this.createOrderRequest(symbol, type, side, amount, price, params);
const response = await this.v1PrivatePostPrivateCreateOrder(request);
//
// {
// "id": 1686804664362,
// "method": "private/create-order",
// "code" : 0,
// "result": {
// "order_id": "6540219377766741832",
// "client_oid": "CCXT_d6ef7c3db6c1495aa8b757"
// }
// }
//
const result = this.safeDict(response, 'result', {});
return this.parseOrder(result, market);
}
/**
* @method
* @name cryptocom#createOrders
* @description create a list of trade orders
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-create-order-list-list
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-create-order-list-oco
* @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 orderRequest = this.createAdvancedOrderRequest(marketId, type, side, amount, price, orderParams);
ordersRequests.push(orderRequest);
}
const contigency = this.safeString(params, 'contingency_type', 'LIST');
const request = {
'contingency_type': contigency,
'order_list': ordersRequests,
};
const response = await this.v1PrivatePostPrivateCreateOrderList(this.extend(request, params));
//
// {
// "id": 12,
// "method": "private/create-order-list",
// "code": 10001,
// "result": {
// "result_list": [
// {
// "index": 0,
// "code": 0,
// "order_id": "2015106383706015873",
// "client_oid": "my_order_0001"
// },
// {
// "index": 1,
// "code": 20007,
// "message": "INVALID_REQUEST",
// "client_oid": "my_order_0002"
// }
// ]
// }
// }
//
// {
// "id" : 1698068111133,
// "method" : "private/create-order-list",
// "code" : 0,
// "result" : [ {
// "code" : 0,
// "index" : 0,
// "client_oid" : "1698068111133_0",
// "order_id" : "6142909896519488206"
// }, {
// "code" : 306,
// "index" : 1,
// "client_oid" : "1698068111133_1",
// "message" : "INSUFFICIENT_AVAILABLE_BALANCE",
// "order_id" : "6142909896519488207"
// } ]
// }
//
const result = this.safeValue(response, 'result', []);
const listId = this.safeString(result, 'list_id');
if (listId !== undefined) {
const ocoOrders = [{ 'order_id': listId }];
return this.parseOrders(ocoOrders);
}
return this.parseOrders(result);
}
createAdvancedOrderRequest(symbol, type, side, amount, price = undefined, params = {}) {
// differs slightly from createOrderRequest
// since the advanced order endpoint requires a different set of parameters
// namely here we don't support ref_price or spot_margin
// and market-buy orders need to send notional instead of quantity
const market = this.market(symbol);
const uppercaseType = type.toUpperCase();
const request = {
'instrument_name': market['id'],
'side': side.toUpperCase(),
};
if ((uppercaseType === 'LIMIT') || (uppercaseType === 'STOP_LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) {
request['price'] = this.priceToPrecision(symbol, price);
}
const broker = this.safeString(this.options, 'broker', 'CCXT');
request['broker_id'] = broker;
const timeInForce = this.safeStringUpper2(params, 'timeInForce', 'time_in_force');
if (timeInForce !== undefined) {
if (timeInForce === 'GTC') {
request['time_in_force'] = 'GOOD_TILL_CANCEL';
}
else if (timeInForce === 'IOC') {
request['time_in_force'] = 'IMMEDIATE_OR_CANCEL';
}
else if (timeInForce === 'FOK') {
request['time_in_force'] = 'FILL_OR_KILL';
}
else {
request['time_in_force'] = timeInForce;
}
}
const postOnly = this.safeBool(params, 'postOnly', false);
if ((postOnly) || (timeInForce === 'PO')) {
request['exec_inst'] = ['POST_ONLY'];
request['time_in_force'] = 'GOOD_TILL_CANCEL';
}
const triggerPrice = this.safeStringN(params, ['stopPrice', 'triggerPrice', 'ref_price']);
const stopLossPrice = this.safeNumber(params, 'stopLossPrice');
const takeProfitPrice = this.safeNumber(params, 'takeProfitPrice');
const isTrigger = (triggerPrice !== undefined);
const isStopLossTrigger = (stopLossPrice !== undefined);
const isTakeProfitTrigger = (takeProfitPrice !== undefined);
if (isTrigger) {
const priceString = this.numberToString(price);
if ((uppercaseType === 'LIMIT') || (uppercaseType === 'STOP_LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) {
if (side === 'buy') {
if (Precise["default"].stringLt(priceString, triggerPrice)) {
request['type'] = 'TAKE_PROFIT_LIMIT';
}
else {
request['type'] = 'STOP_LIMIT';
}
}
else {
if (Precise["default"].stringLt(priceString, triggerPrice)) {
request['type'] = 'STOP_LIMIT';
}
else {
request['type'] = 'TAKE_PROFIT_LIMIT';
}
}
}
else {
if (side === 'buy') {
if (Precise["default"].stringLt(priceString, triggerPrice)) {
request['type'] = 'TAKE_PROFIT';
}
else {
request['type'] = 'STOP_LOSS';
}
}
else {
if (Precise["default"].stringLt(priceString, triggerPrice)) {
request['type'] = 'STOP_LOSS';
}
else {
request['type'] = 'TAKE_PROFIT';
}
}
}
}
else if (isStopLossTrigger) {
if ((uppercaseType === 'LIMIT') || (uppercaseType === 'STOP_LIMIT')) {
request['type'] = 'STOP_LIMIT';
}
else {
request['type'] = 'STOP_LOSS';
}
}
else if (isTakeProfitTrigger) {
if ((uppercaseType === 'LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) {
request['type'] = 'TAKE_PROFIT_LIMIT';
}
else {
request['type'] = 'TAKE_PROFIT';
}
}
else {
request['type'] = uppercaseType;
}
if ((side === 'buy') && ((uppercaseType === 'MARKET') || (uppercaseType === 'STOP_LOSS') || (uppercaseType === 'TAKE_PROFIT'))) {
// use createmarketBuy logic here
let quoteAmount = undefined;
let createMarketBuyOrderRequiresPrice = true;
[createMarketBuyOrderRequiresPrice, params] = this.handleOptionAndParams(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true);
const cost = this.safeNumber2(params, 'cost', 'notional');
params = this.omit(params, 'cost');
if (cost !== undefined) {
quoteAmount = this.costToPrecision(symbol, cost);
}
else if (createMarketBuyOrderRequiresPrice) {
if (price === undefined) {
throw new errors.InvalidOrder(this.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend (amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to false and pass the cost to spend (quote quantity) in the amount argument');
}
else {
const amountString = this.numberToString(amount);
const priceString = this.numberToString(price);
const costRequest = Precise["default"].stringMul(amountString, priceString);
quoteAmount = this.costToPrecision(symbol, costRequest);
}
}
else {
quoteAmount = this.costToPrecision(symbol, amount);
}
request['notional'] = quoteAmount;
}
else {
request['quantity'] = this.amountToPrecision(symbol, amount);
}
params = this.omit(params, ['postOnly', 'clientOrderId', 'timeInForce', 'stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice']);
return this.extend(request, params);
}
/**
* @method
* @name cryptocom#editOrder
* @description edit a trade order
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-amend-order
* @param {string} id order id
* @param {string} symbol unified market symbol of the order to edit
* @param {string} [type] not used by cryptocom editOrder
* @param {string} [side] not used by cryptocom editOrder
* @param {float} amount (mandatory) how much of the currency you want to trade in units of the base currency
* @param {float} price (mandatory) the price for the order, 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] the original client order id of the order to edit, required if id is not provided
* @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 request = this.editOrderRequest(id, symbol, amount, price, params);
const response = await this.v1PrivatePostPrivateAmendOrder(request);
const result = this.safeDict(response, 'result', {});
return this.parseOrder(result);
}
editOrderRequest(id, symbol, amount, price = undefined, params = {}) {
const request = {};
if (id !== undefined) {
request['order_id'] = id;
}
else {
const originalClientOrderId = this.safeString2(params, 'orig_client_oid', 'clientOrderId');
if (originalClientOrderId === undefined) {
throw new errors.ArgumentsRequired(this.id + ' editOrder() requires an id argument or orig_client_oid parameter');
}
else {
request['orig_client_oid'] = originalClientOrderId;
params = this.omit(params, ['orig_client_oid', 'clientOrderId']);
}
}
if ((amount === undefined) || (price === undefined)) {
throw new errors.ArgumentsRequired(this.id + ' editOrder() requires both amount and price arguments. If you do not want to change the amount or price, you should pass the original values');
}
request['new_quantity'] = this.amountToPrecision(symbol, amount);
request['new_price'] = this.priceToPrecision(symbol, price);
return this.extend(request, params);
}
/**
* @method
* @name cryptocom#cancelAllOrders
* @description cancel all open orders
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-cancel-all-orders
* @param {string} symbol unified market symbol of the orders to cancel
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} Returns exchange raw message{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelAllOrders(symbol = undefined, params = {}) {
await this.loadMarkets();
let market = undefined;
const request = {};
if (symbol !== undefined) {
market = this.market(symbol);
request['instrument_name'] = market['id'];
}
return await this.v1PrivatePostPrivateCancelAllOrders(this.extend(request, params));
}
/**
* @method
* @name cryptocom#cancelOrder
* @description cancels an open order
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-cancel-order
* @param {string} id the order id of the order to cancel
* @param {string} [symbol] unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrder(id, symbol = undefined, params = {}) {
await this.loadMarkets();
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
}
const request = {
'order_id': id,
};
const response = await this.v1PrivatePostPrivateCancelOrder(this.extend(request, params));
//
// {
// "id": 1686882846638,
// "method": "private/cancel-order",
// "code": 0,
// "message": "NO_ERROR",
// "result": {
// "client_oid": "CCXT_c2d2152cc32d40a3ae7fbf",
// "order_id": "6142909895025252686"
// }
// }
//
const result = this.safeDict(response, 'result', {});
return this.parseOrder(result, market);
}
/**
* @method
* @name cryptocom#cancelOrders
* @description cancel multiple orders
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-cancel-order-list-list
* @param {string[]} ids order ids
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrders(ids, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new errors.ArgumentsRequired(this.id + ' cancelOrders() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
const orderRequests = [];
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
const order = {
'instrument_name': market['id'],
'order_id': id.toString(),
};
orderRequests.push(order);
}
const request = {
'contingency_type': 'LIST',
'order_list': orderRequests,
};
const response = await this.v1PrivatePostPrivateCancelOrderList(this.extend(request, params));
const result = this.safeList(response, 'result', []);
return this.parseOrders(result, market, undefined, undefined, params);
}
/**
* @method
* @name cryptocom#cancelOrdersForSymbols
* @description cancel multiple orders for multiple symbols
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-cancel-order-list-list
* @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
* @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async cancelOrdersForSymbols(orders, params = {}) {
await this.loadMarkets();
const orderRequests = [];
for (let i = 0; i < orders.length; i++) {
const order = orders[i];
const id = this.safeString(order, 'id');
const symbol = this.safeString(order, 'symbol');
const market = this.market(symbol);
const orderItem = {
'instrument_name': market['id'],
'order_id': id.toString(),
};
orderRequests.push(orderItem);
}
const request = {
'contingency_type': 'LIST',
'order_list': orderRequests,
};
const response = await this.v1PrivatePostPrivateCancelOrderList(this.extend(request, params));
const result = this.safeList(response, 'result', []);
return this.parseOrders(result, undefined, undefined, undefined, params);
}
/**
* @method
* @name cryptocom#fetchOpenOrders
* @description fetch all unfilled currently open orders
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-open-orders
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch open orders for
* @param {int} [limit] the maximum number of open order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @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 market = undefined;
const request = {};
if (symbol !== undefined) {
market = this.market(symbol);
request['instrument_name'] = market['id'];
}
const response = await this.v1PrivatePostPrivateGetOpenOrders(this.extend(request, params));
//
// {
// "id": 1686806134961,
// "method": "private/get-open-orders",
// "code": 0,
// "result": {
// "data": [
// {
// "account_id": "ce075bef-1234-4321-bd6g-ff9007252e63",
// "order_id": "6530219477767564494",
// "client_oid": "CCXT_7ce730f0388441df9bc218",
// "order_type": "LIMIT",
// "time_in_force": "GOOD_TILL_CANCEL",
// "side": "BUY",
// "exec_inst": [ ],
// "quantity": "0.00020",
// "limit_price": "20000.00",
// "order_value": "4",
// "avg_price": "0",
// "trigger_price": "0",
// "ref_price": "0",
// "cumulative_quantity": "0",
// "cumulative_value": "0",
// "cumulative_fee": "0",
// "status": "ACTIVE",
// "update_user_id": "ce075bef-1234-4321-bd6g-gg9007252e63",
// "order_date": "2023-06-15",
// "instrument_name": "BTC_USD",
// "fee_instrument_name": "BTC",
// "create_time": 1686806053992,
// "create_time_ns": "1686806053992921880",
// "update_time": 1686806053993
// }
// ]
// }
// }
//
const data = this.safeDict(response, 'result', {});
const orders = this.safeList(data, 'data', []);
return this.parseOrders(orders, market, since, limit);
}
/**
* @method
* @name cryptocom#fetchMyTrades
* @description fetch all trades made by the user
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-trades
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for, maximum date range is one day
* @param {int} [limit] the maximum number of trade structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms for the ending date filter, default is the current time
* @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, 100);
}
const request = {};
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['instrument_name'] = market['id'];
}
if (since !== undefined) {
request['start_time'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const until = this.safeInteger(params, 'until');
params = this.omit(params, ['until']);
if (until !== undefined) {
request['end_time'] = until;
}
const response = await this.v1PrivatePostPrivateGetTrades(this.extend(request, params));
//
// {
// "id": 1686942003520,
// "method": "private/get-trades",
// "code": 0,
// "result": {
// "data": [
// {
// "account_id": "ds075abc-1234-4321-bd6g-ff9007252r63",
// "event_date": "2023-06-16",
// "journal_type": "TRADING",
// "side": "BUY",
// "instrument_name": "BTC_USD",
// "fees": "-0.0000000525",
// "trade_id": "6142909898247428343",
// "trade_match_id": "4611686018455978480",
// "create_time": 1686941992887,
// "traded_price": "26347.16",
// "traded_quantity": "0.00021",
// "fee_instrument_name": "BTC",
// "client_oid": "d1c70a60-810e-4c92-b2a0-72b931cb31e0",
// "taker_side": "TAKER",
// "order_id": "6142909895036331486",
// "create_time_ns": "1686941992887207066"
// }
// ]
// }
// }
//
const result = this.safeDict(response, 'result', {});
const trades = this.safeList(result, 'data', []);
return this.parseTrades(trades, market, since, limit);
}
parseAddress(addressString) {
let address = undefined;
let tag = undefined;
let rawTag = undefined;
if (addressString.indexOf('?') > 0) {
[address, rawTag] = addressString.split('?');
const splitted = rawTag.split('=');
tag = splitted[1];
}
else {
address = addressString;
}
return [address, tag];
}
/**
* @method
* @name cryptocom#withdraw
* @description make a withdrawal
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-create-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);
await this.loadMarkets();
const currency = this.safeCurrency(code); // for instance, USDC is not inferred from markets but it's still available
const request = {
'currency': currency['id'],
'amount': amount,
'address': address,
};
if (tag !== undefined) {
request['address_tag'] = tag;
}
let networkCode = undefined;
[networkCode, params] = this.handleNetworkCodeAndParams(params);
const networkId = this.networkCodeToId(networkCode);
if (networkId !== undefined) {
request['network_id'] = networkId;
}
const response = await this.v1PrivatePostPrivateCreateWithdrawal(this.extend(request, params));
//
// {
// "id":-1,
// "method":"private/create-withdrawal",
// "code":0,
// "result": {
// "id": 2220,
// "amount": 1,
// "fee": 0.0004,
// "symbol": "BTC",
// "address": "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBf",
// "client_wid": "my_withdrawal_002",
// "create_time":1607063412000
// }
// }
//
const result = this.safeDict(response, 'result');
return this.parseTransaction(result, currency);
}
/**
* @method
* @name cryptocom#fetchDepositAddressesByNetwork
* @description fetch a dictionary of addresses for a currency, indexed by network
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-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.safeCurrency(code);
const request = {
'currency': currency['id'],
};
const response = await this.v1PrivatePostPrivateGetDepositAddress(this.extend(request, params));
//
// {
// "id": 1234555011221,
// "method": "private/get-deposit-address",
// "code": 0,
// "result": {
// "deposit_address_list": [
// {
// "currency": "BTC",
// "create_time": 1686730755000,
// "id": "3737377",
// "address": "3N9afggxTSmJ3H4jaMQuWyEiLBzZdAbK6d",
// "status":"1",
// "network": "BTC"
// },
// ]
// }
// }
//
const data = this.safeDict(response, 'result', {});
const addresses = this.safeList(data, 'deposit_address_list', []);
const addressesLength = addresses.length;
if (addressesLength === 0) {
throw new errors.ExchangeError(this.id + ' fetchDepositAddressesByNetwork() generating address...');
}
const result = {};
for (let i = 0; i < addressesLength; i++) {
const value = this.safeDict(addresses, i);
const addressString = this.safeString(value, 'address');
const currencyId = this.safeString(value, 'currency');
const responseCode = this.safeCurrencyCode(currencyId);
const [address, tag] = this.parseAddress(addressString);
this.checkAddress(address);
const networkId = this.safeString(value, 'network');
const network = this.networkIdToCode(networkId, responseCode);
result[network] = {
'info': value,
'currency': responseCode,
'network': network,
'address': address,
'tag': tag,
};
}
return result;
}
/**
* @method
* @name cryptocom#fetchDepositAddress
* @description fetch the deposit address for a currency associated with this account
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-deposit-address
* @param {string} code unified currency code
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
*/
async fetchDepositAddress(code, params = {}) {
const network = this.safeStringUpper(params, 'network');
params = this.omit(params, ['network']);
const depositAddresses = await this.fetchDepositAddressesByNetwork(code, params);
if (network in depositAddresses) {
return depositAddresses[network];
}
else {
const keys = Object.keys(depositAddresses);
return depositAddresses[keys[0]];
}
}
/**
* @method
* @name cryptocom#fetchDeposits
* @description fetch all deposits made to an account
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-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] timestamp in ms for the ending date filter, default is the current time
* @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 currency = undefined;
const request = {};
if (code !== undefined) {
currency = this.safeCurrency(code);
request['currency'] = currency['id'];
}
if (since !== undefined) {
// 90 days date range
request['start_ts'] = since;
}
if (limit !== undefined) {
request['page_size'] = limit;
}
const until = this.safeInteger(params, 'until');
params = this.omit(params, ['until']);
if (until !== undefined) {
request['end_ts'] = until;
}
const response = await this.v1PrivatePostPrivateGetDepositHistory(this.extend(request, params));
//
// {
// "id": 1688701375714,
// "method": "private/get-deposit-history",
// "code": 0,
// "result": {
// "deposit_list": [
// {
// "currency": "BTC",
// "fee": 0,
// "create_time": 1688023659000,
// "id": "6201135",
// "update_time": 1688178509000,
// "amount": 0.00114571,
// "address": "1234fggxTSmJ3H4jaMQuWyEiLBzZdAbK6d",
// "status": "1",
// "txid": "f0ae4202b76eb999c301eccdde44dc639bee42d1fdd5974105286ca3393f6065/2"
// },
// ]
// }
// }
//
const data = this.safeDict(response, 'result', {});
const depositList = this.safeList(data, 'deposit_list', []);
return this.parseTransactions(depositList, currency, since, limit);
}
/**
* @method
* @name cryptocom#fetchWithdrawals
* @description fetch all withdrawals made from an account
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-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] timestamp in ms for the ending date filter, default is the current time
* @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 currency = undefined;
const request = {};
if (code !== undefined) {
currency = this.safeCurrency(code);
request['currency'] = currency['id'];
}
if (since !== undefined) {
// 90 days date range
request['start_ts'] = since;
}
if (limit !== undefined) {
request['page_size'] = limit;
}
const until = this.safeInteger(params, 'until');
params = this.omit(params, ['until']);
if (until !== undefined) {
request['end_ts'] = until;
}
const response = await this.v1PrivatePostPrivateGetWithdrawalHistory(this.extend(request, params));
//
// {
// "id": 1688613879534,
// "method": "private/get-withdrawal-history",
// "code": 0,
// "result": {
// "withdrawal_list": [
// {
// "currency": "BTC",
// "client_wid": "",
// "fee": 0.0005,
// "create_time": 1688613850000,
// "id": "5275977",
// "update_time": 1688613850000,
// "amount": 0.0005,
// "address": "1234NMEWbiF8ZkwUMxmfzMxi2A1MQ44bMn",
// "status": "1",
// "txid": "",
// "network_id": "BTC"
// }
// ]
// }
// }
//
const data = this.safeDict(response, 'result', {});
const withdrawalList = this.safeList(data, 'withdrawal_list', []);
return this.parseTransactions(withdrawalList, currency, since, limit);
}
parseTicker(ticker, market = undefined) {
//
// fetchTicker
//
// {
// "i": "BTC_USD",
// "h": "30821.45",
// "l": "28685.11",
// "a": "30446.00",
// "v": "1767.8734",
// "vv": "52436726.42",
// "c": "0.0583",
// "b": "30442.00",
// "k": "30447.66",
// "t": 1687403045415
// }
//
// fetchTickers
//
// {
// "i": "AVAXUSD-PERP",
// "h": "13.209",
// "l": "12.148",
// "a": "13.209",
// "v": "1109.8",
// "vv": "14017.33",
// "c": "0.0732",
// "b": "13.210",
// "k": "13.230",
// "oi": "10888.9",
// "t": 1687402657575
// }
//
const timestamp = this.safeInteger(ticker, 't');
const marketId = this.safeString(ticker, 'i');
market = this.safeMarket(marketId, market, '_');
const quote = this.safeString(market, 'quote');
const last = this.safeString(ticker, 'a');
return this.safeTicker({
'symbol': market['symbol'],
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'high': this.safeNumber(ticker, 'h'),
'low': this.safeNumber(ticker, 'l'),
'bid': this.safeNumber(ticker, 'b'),
'bidVolume': undefined,
'ask': this.safeNumber(ticker, 'k'),
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': this.safeString(ticker, 'c'),
'average': undefined,
'baseVolume': this.safeString(ticker, 'v'),
'quoteVolume': (quote === 'USD') ? this.safeString(ticker, 'vv') : undefined,
'info': ticker,
}, market);
}
parseTrade(trade, market = undefined) {
//
// fetchTrades
//
// {
// "s": "sell",
// "p": "26386.00",
// "q": "0.00453",
// "tn": 1686944282062,
// "tn": 1704476468851524373,
// "d": "4611686018455979970",
// "i": "BTC_USD"
// }
//
// fetchMyTrades
//
// {
// "account_id": "ds075abc-1234-4321-bd6g-ff9007252r63",
// "event_date": "2023-06-16",
// "journal_type": "TRADING",
// "side": "BUY",
// "instrument_name": "BTC_USD",
// "fees": "-0.0000000525",
// "trade_id": "6142909898247428343",
// "trade_match_id": "4611686018455978480",
// "create_time": 1686941992887,
// "traded_price": "26347.16",
// "traded_quantity": "0.00021",
// "fee_instrument_name": "BTC",
// "client_oid": "d1c70a60-1234-4c92-b2a0-72b931cb31e0",
// "taker_side": "TAKER",
// "order_id": "6142909895036331486",
// "create_time_ns": "1686941992887207066"
// }
//
const timestamp = this.safeInteger2(trade, 't', 'create_time');
const marketId = this.safeString2(trade, 'i', 'instrument_name');
market = this.safeMarket(marketId, market, '_');
const feeCurrency = this.safeString(trade, 'fee_instrument_name');
const feeCostString = this.safeString(trade, 'fees');
return this.safeTrade({
'info': trade,
'id': this.safeString2(trade, 'd', 'trade_id'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': market['symbol'],
'order': this.safeString(trade, 'order_id'),
'side': this.safeStringLower2(trade, 's', 'side'),
'takerOrMaker': this.safeStringLower(trade, 'taker_side'),
'price': this.safeNumber2(trade, 'p', 'traded_price'),
'amount': this.safeNumber2(trade, 'q', 'traded_quantity'),
'cost': undefined,
'type': undefined,
'fee': {
'currency': this.safeCurrencyCode(feeCurrency),
'cost': this.parseNumber(Precise["default"].stringNeg(feeCostString)),
},
}, market);
}
parseOHLCV(ohlcv, market = undefined) {
//
// {
// "o": "26949.89",
// "h": "26957.64",
// "l": "26948.24",
// "c": "26950.00",
// "v": "0.0670",
// "t": 1687237080000
// }
//
return [
this.safeInteger(ohlcv, 't'),
this.safeNumber(ohlcv, 'o'),
this.safeNumber(ohlcv, 'h'),
this.safeNumber(ohlcv, 'l'),
this.safeNumber(ohlcv, 'c'),
this.safeNumber(ohlcv, 'v'),
];
}
parseOrderStatus(status) {
const statuses = {
'ACTIVE': 'open',
'CANCELED': 'canceled',
'FILLED': 'closed',
'REJECTED': 'rejected',
'EXPIRED': 'expired',
};
return this.safeString(statuses, status, status);
}
parseTimeInForce(timeInForce) {
const timeInForces = {
'GOOD_TILL_CANCEL': 'GTC',
'IMMEDIATE_OR_CANCEL': 'IOC',
'FILL_OR_KILL': 'FOK',
};
return this.safeString(timeInForces, timeInForce, timeInForce);
}
parseOrder(order, market = undefined) {
//
// createOrder, cancelOrder
//
// {
// "order_id": "6540219377766741832",
// "client_oid": "CCXT_d6ef7c3db6c1495aa8b757"
// }
//
// fetchOpenOrders, fetchOrder, fetchOrders
//
// {
// "account_id": "ce075bef-1234-4321-bd6g-ff9007252e63",
// "order_id": "6530219477767564494",
// "client_oid": "CCXT_7ce730f0388441df9bc218",
// "order_type": "LIMIT",
// "time_in_force": "GOOD_TILL_CANCEL",
// "side": "BUY",
// "exec_inst": [ ],
// "quantity": "0.00020",
// "limit_price": "20000.00",
// "order_value": "4",
// "avg_price": "0",
// "trigger_price": "0",
// "ref_price": "0",
// "cumulative_quantity": "0",
// "cumulative_value": "0",
// "cumulative_fee": "0",
// "status": "ACTIVE",
// "update_user_id": "ce075bef-1234-4321-bd6g-gg9007252e63",
// "order_date": "2023-06-15",
// "instrument_name": "BTC_USD",
// "fee_instrument_name": "BTC",
// "create_time": 1686806053992,
// "create_time_ns": "1686806053992921880",
// "update_time": 1686806053993
// }
//
// createOrders
// {
// "code" : 306,
// "index" : 1,
// "client_oid" : "1698068111133_1",
// "message" : "INSUFFICIENT_AVAILABLE_BALANCE",
// "order_id" : "6142909896519488207"
// }
//
const code = this.safeInteger(order, 'code');
if ((code !== undefined) && (code !== 0)) {
return this.safeOrder({
'id': this.safeString(order, 'order_id'),
'clientOrderId': this.safeString(order, 'client_oid'),
'info': order,
'status': 'rejected',
});
}
const created = this.safeInteger(order, 'create_time');
const marketId = this.safeString(order, 'instrument_name');
const symbol = this.safeSymbol(marketId, market);
const execInst = this.safeValue(order, 'exec_inst');
let postOnly = undefined;
if (execInst !== undefined) {
postOnly = false;
for (let i = 0; i < execInst.length; i++) {
const inst = execInst[i];
if (inst === 'POST_ONLY') {
postOnly = true;
break;
}
}
}
const feeCurrency = this.safeString(order, 'fee_instrument_name');
return this.safeOrder({
'info': order,
'id': this.safeString(order, 'order_id'),
'clientOrderId': this.safeString(order, 'client_oid'),
'timestamp': created,
'datetime': this.iso8601(created),
'lastTradeTimestamp': this.safeInteger(order, 'update_time'),
'status': this.parseOrderStatus(this.safeString(order, 'status')),
'symbol': symbol,
'type': this.safeStringLower(order, 'order_type'),
'timeInForce': this.parseTimeInForce(this.safeString(order, 'time_in_force')),
'postOnly': postOnly,
'side': this.safeStringLower(order, 'side'),
'price': this.safeNumber(order, 'limit_price'),
'amount': this.safeNumber(order, 'quantity'),
'filled': this.safeNumber(order, 'cumulative_quantity'),
'remaining': undefined,
'average': this.safeNumber(order, 'avg_price'),
'cost': this.safeNumber(order, 'cumulative_value'),
'fee': {
'currency': this.safeCurrencyCode(feeCurrency),
'cost': this.safeNumber(order, 'cumulative_fee'),
},
'trades': [],
}, market);
}
parseDepositStatus(status) {
const statuses = {
'0': 'pending',
'1': 'ok',
'2': 'failed',
'3': 'pending',
};
return this.safeString(statuses, status, status);
}
parseWithdrawalStatus(status) {
const statuses = {
'0': 'pending',
'1': 'pending',
'2': 'failed',
'3': 'pending',
'4': 'failed',
'5': 'ok',
'6': 'canceled',
};
return this.safeString(statuses, status, status);
}
parseTransaction(transaction, currency = undefined) {
//
// fetchDeposits
//
// {
// "currency": "BTC",
// "fee": 0,
// "create_time": 1688023659000,
// "id": "6201135",
// "update_time": 1688178509000,
// "amount": 0.00114571,
// "address": "1234fggxTSmJ3H4jaMQuWyEiLBzZdAbK6d",
// "status": "1",
// "txid": "f0ae4202b76eb999c301eccdde44dc639bee42d1fdd5974105286ca3393f6065/2"
// }
//
// fetchWithdrawals
//
// {
// "currency": "BTC",
// "client_wid": "",
// "fee": 0.0005,
// "create_time": 1688613850000,
// "id": "5775977",
// "update_time": 1688613850000,
// "amount": 0.0005,
// "address": "1234NMEWbiF8ZkwUMxmfzMxi2A1MQ44bMn",
// "status": "1",
// "txid": "",
// "network_id": "BTC"
// }
//
// withdraw
//
// {
// "id": 2220,
// "amount": 1,
// "fee": 0.0004,
// "symbol": "BTC",
// "address": "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBf",
// "client_wid": "my_withdrawal_002",
// "create_time":1607063412000
// }
//
let type = undefined;
const rawStatus = this.safeString(transaction, 'status');
let status = undefined;
if ('client_wid' in transaction) {
type = 'withdrawal';
status = this.parseWithdrawalStatus(rawStatus);
}
else {
type = 'deposit';
status = this.parseDepositStatus(rawStatus);
}
const addressString = this.safeString(transaction, 'address');
const [address, tag] = this.parseAddress(addressString);
const currencyId = this.safeString(transaction, 'currency');
const code = this.safeCurrencyCode(currencyId, currency);
const timestamp = this.safeInteger(transaction, 'create_time');
const feeCost = this.safeNumber(transaction, 'fee');
let fee = undefined;
if (feeCost !== undefined) {
fee = { 'currency': code, 'cost': feeCost };
}
return {
'info': transaction,
'id': this.safeString(transaction, 'id'),
'txid': this.safeString(transaction, 'txid'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'network': undefined,
'address': address,
'addressTo': address,
'addressFrom': undefined,
'tag': tag,
'tagTo': tag,
'tagFrom': undefined,
'type': type,
'amount': this.safeNumber(transaction, 'amount'),
'currency': code,
'status': status,
'updated': this.safeInteger(transaction, 'update_time'),
'internal': undefined,
'comment': this.safeString(transaction, 'client_wid'),
'fee': fee,
};
}
customHandleMarginModeAndParams(methodName, params = {}) {
/**
* @ignore
* @method
* @description marginMode specified by params["marginMode"], this.options["marginMode"], this.options["defaultMarginMode"], params["margin"] = true or this.options["defaultType"] = 'margin'
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Array} the marginMode in lowercase
*/
const defaultType = this.safeString(this.options, 'defaultType');
const isMargin = this.safeBool(params, 'margin', false);
params = this.omit(params, 'margin');
let marginMode = undefined;
[marginMode, params] = this.handleMarginModeAndParams(methodName, params);
if (marginMode !== undefined) {
if (marginMode !== 'cross') {
throw new errors.NotSupported(this.id + ' only cross margin is supported');
}
}
else {
if ((defaultType === 'margin') || (isMargin === true)) {
marginMode = 'cross';
}
}
return [marginMode, params];
}
parseDepositWithdrawFee(fee, currency = undefined) {
//
// {
// "full_name": "Alchemix",
// "default_network": "ETH",
// "network_list": [
// {
// "network_id": "ETH",
// "withdrawal_fee": "0.25000000",
// "withdraw_enabled": true,
// "min_withdrawal_amount": "0.5",
// "deposit_enabled": true,
// "confirmation_required": "0"
// }
// ]
// }
//
const networkList = this.safeList(fee, 'network_list', []);
const networkListLength = networkList.length;
const result = {
'info': fee,
'withdraw': {
'fee': undefined,
'percentage': undefined,
},
'deposit': {
'fee': undefined,
'percentage': undefined,
},
'networks': {},
};
if (networkList !== undefined) {
for (let i = 0; i < networkListLength; i++) {
const networkInfo = networkList[i];
const networkId = this.safeString(networkInfo, 'network_id');
const currencyCode = this.safeString(currency, 'code');
const networkCode = this.networkIdToCode(networkId, currencyCode);
result['networks'][networkCode] = {
'deposit': { 'fee': undefined, 'percentage': undefined },
'withdraw': { 'fee': this.safeNumber(networkInfo, 'withdrawal_fee'), 'percentage': false },
};
if (networkListLength === 1) {
result['withdraw']['fee'] = this.safeNumber(networkInfo, 'withdrawal_fee');
result['withdraw']['percentage'] = false;
}
}
}
return result;
}
/**
* @method
* @name cryptocom#fetchDepositWithdrawFees
* @description fetch deposit and withdraw fees
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-currency-networks
* @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 [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure}
*/
async fetchDepositWithdrawFees(codes = undefined, params = {}) {
await this.loadMarkets();
const response = await this.v1PrivatePostPrivateGetCurrencyNetworks(params);
const data = this.safeValue(response, 'result');
const currencyMap = this.safeList(data, 'currency_map');
return this.parseDepositWithdrawFees(currencyMap, codes, 'full_name');
}
/**
* @method
* @name cryptocom#fetchLedger
* @description fetch the history of changes, actions done by the user or operations that altered the balance of the user
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-transactions
* @param {string} [code] unified currency code
* @param {int} [since] timestamp in ms of the earliest ledger entry
* @param {int} [limit] max number of ledger entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms for the ending date filter, default is the current time
* @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger}
*/
async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const request = {};
let currency = undefined;
if (code !== undefined) {
currency = this.safeCurrency(code);
}
if (since !== undefined) {
request['start_time'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const until = this.safeInteger(params, 'until');
params = this.omit(params, ['until']);
if (until !== undefined) {
request['end_time'] = until;
}
const response = await this.v1PrivatePostPrivateGetTransactions(this.extend(request, params));
//
// {
// "id": 1686813195698,
// "method": "private/get-transactions",
// "code": 0,
// "result": {
// "data": [
// {
// "account_id": "ce075cef-1234-4321-bd6e-gf9007351e64",
// "event_date": "2023-06-15",
// "journal_type": "TRADING",
// "journal_id": "6530219460124075091",
// "transaction_qty": "6.0091224",
// "transaction_cost": "6.0091224",
// "realized_pnl": "0",
// "order_id": "6530219477766741833",
// "trade_id": "6530219495775954765",
// "trade_match_id": "4611686018455865176",
// "event_timestamp_ms": 1686804665013,
// "event_timestamp_ns": "1686804665013642422",
// "client_oid": "CCXT_d6ea7c5db6c1495aa8b758",
// "taker_side": "",
// "side": "BUY",
// "instrument_name": "USD"
// },
// ]
// }
// }
//
const result = this.safeDict(response, 'result', {});
const ledger = this.safeList(result, 'data', []);
return this.parseLedger(ledger, currency, since, limit);
}
parseLedgerEntry(item, currency = undefined) {
//
// {
// "account_id": "ce075cef-1234-4321-bd6e-gf9007351e64",
// "event_date": "2023-06-15",
// "journal_type": "TRADING",
// "journal_id": "6530219460124075091",
// "transaction_qty": "6.0091224",
// "transaction_cost": "6.0091224",
// "realized_pnl": "0",
// "order_id": "6530219477766741833",
// "trade_id": "6530219495775954765",
// "trade_match_id": "4611686018455865176",
// "event_timestamp_ms": 1686804665013,
// "event_timestamp_ns": "1686804665013642422",
// "client_oid": "CCXT_d6ea7c5db6c1495aa8b758",
// "taker_side": "",
// "side": "BUY",
// "instrument_name": "USD"
// }
//
const timestamp = this.safeInteger(item, 'event_timestamp_ms');
const currencyId = this.safeString(item, 'instrument_name');
const code = this.safeCurrencyCode(currencyId, currency);
currency = this.safeCurrency(currencyId, currency);
let amount = this.safeString(item, 'transaction_qty');
let direction = undefined;
if (Precise["default"].stringLt(amount, '0')) {
direction = 'out';
amount = Precise["default"].stringAbs(amount);
}
else {
direction = 'in';
}
return this.safeLedgerEntry({
'info': item,
'id': this.safeString(item, 'order_id'),
'direction': direction,
'account': this.safeString(item, 'account_id'),
'referenceId': this.safeString(item, 'trade_id'),
'referenceAccount': this.safeString(item, 'trade_match_id'),
'type': this.parseLedgerEntryType(this.safeString(item, 'journal_type')),
'currency': code,
'amount': this.parseNumber(amount),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'before': undefined,
'after': undefined,
'status': undefined,
'fee': {
'currency': undefined,
'cost': undefined,
},
}, currency);
}
parseLedgerEntryType(type) {
const ledgerType = {
'TRADING': 'trade',
'TRADE_FEE': 'fee',
'WITHDRAW_FEE': 'fee',
'WITHDRAW': 'withdrawal',
'DEPOSIT': 'deposit',
'ROLLBACK_WITHDRAW': 'rollback',
'ROLLBACK_DEPOSIT': 'rollback',
'FUNDING': 'fee',
'REALIZED_PNL': 'trade',
'INSURANCE_FUND': 'insurance',
'SOCIALIZED_LOSS': 'trade',
'LIQUIDATION_FEE': 'fee',
'SESSION_RESET': 'reset',
'ADJUSTMENT': 'adjustment',
'SESSION_SETTLE': 'settlement',
'UNCOVERED_LOSS': 'trade',
'ADMIN_ADJUSTMENT': 'adjustment',
'DELIST': 'delist',
'SETTLEMENT_FEE': 'fee',
'AUTO_CONVERSION': 'conversion',
'MANUAL_CONVERSION': 'conversion',
};
return this.safeString(ledgerType, type, type);
}
/**
* @method
* @name cryptocom#fetchAccounts
* @description fetch all the accounts associated with a profile
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-accounts
* @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 = {}) {
await this.loadMarkets();
const response = await this.v1PrivatePostPrivateGetAccounts(params);
//
// {
// "id": 1234567894321,
// "method": "private/get-accounts",
// "code": 0,
// "result": {
// "master_account": {
// "uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
// "user_uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
// "enabled": true,
// "tradable": true,
// "name": "YOUR_NAME",
// "country_code": "CAN",
// "phone_country_code": "CAN",
// "incorp_country_code": "",
// "margin_access": "DEFAULT",
// "derivatives_access": "DEFAULT",
// "create_time": 1656445188000,
// "update_time": 1660794567262,
// "two_fa_enabled": true,
// "kyc_level": "ADVANCED",
// "suspended": false,
// "terminated": false,
// "spot_enabled": false,
// "margin_enabled": false,
// "derivatives_enabled": false
// },
// "sub_account_list": []
// }
// }
//
const result = this.safeDict(response, 'result', {});
const masterAccount = this.safeDict(result, 'master_account', {});
const accounts = this.safeList(result, 'sub_account_list', []);
accounts.push(masterAccount);
return this.parseAccounts(accounts, params);
}
parseAccount(account) {
//
// {
// "uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
// "user_uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
// "master_account_uuid": "a1234abc-1234-4321-q5r7-b1ab0a0b12b",
// "label": "FORMER_MASTER_MARGIN",
// "enabled": true,
// "tradable": true,
// "name": "YOUR_NAME",
// "country_code": "YOUR_COUNTRY_CODE",
// "incorp_country_code": "",
// "margin_access": "DEFAULT",
// "derivatives_access": "DEFAULT",
// "create_time": 1656481992000,
// "update_time": 1667272884594,
// "two_fa_enabled": false,
// "kyc_level": "ADVANCED",
// "suspended": false,
// "terminated": false,
// "spot_enabled": false,
// "margin_enabled": false,
// "derivatives_enabled": false,
// "system_label": "FORMER_MASTER_MARGIN"
// }
//
return {
'id': this.safeString(account, 'uuid'),
'type': this.safeString(account, 'label'),
'code': undefined,
'info': account,
};
}
/**
* @method
* @name cryptocom#fetchSettlementHistory
* @description fetches historical settlement records
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-expired-settlement-price
* @param {string} symbol unified market symbol of the settlement history
* @param {int} [since] timestamp in ms
* @param {int} [limit] number of records
* @param {object} [params] exchange specific params
* @param {int} [params.type] 'future', 'option'
* @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 = {}) {
await this.loadMarkets();
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
}
let type = undefined;
[type, params] = this.handleMarketTypeAndParams('fetchSettlementHistory', market, params);
this.checkRequiredArgument('fetchSettlementHistory', type, 'type', ['future', 'option', 'WARRANT', 'FUTURE']);
if (type === 'option') {
type = 'WARRANT';
}
const request = {
'instrument_type': type.toUpperCase(),
};
const response = await this.v1PublicGetPublicGetExpiredSettlementPrice(this.extend(request, params));
//
// {
// "id": -1,
// "method": "public/get-expired-settlement-price",
// "code": 0,
// "result": {
// "data": [
// {
// "i": "BTCUSD-230526",
// "x": 1685088000000,
// "v": "26464.1",
// "t": 1685087999500
// }
// ]
// }
// }
//
const result = this.safeDict(response, 'result', {});
const data = this.safeList(result, 'data', []);
const settlements = this.parseSettlements(data, market);
const sorted = this.sortBy(settlements, 'timestamp');
return this.filterBySymbolSinceLimit(sorted, symbol, since, limit);
}
parseSettlement(settlement, market) {
//
// {
// "i": "BTCUSD-230526",
// "x": 1685088000000,
// "v": "26464.1",
// "t": 1685087999500
// }
//
const timestamp = this.safeInteger(settlement, 'x');
const marketId = this.safeString(settlement, 'i');
return {
'info': settlement,
'symbol': this.safeSymbol(marketId, market),
'price': this.safeNumber(settlement, 'v'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
};
}
parseSettlements(settlements, market) {
//
// [
// {
// "i": "BTCUSD-230526",
// "x": 1685088000000,
// "v": "26464.1",
// "t": 1685087999500
// }
// ]
//
const result = [];
for (let i = 0; i < settlements.length; i++) {
result.push(this.parseSettlement(settlements[i], market));
}
return result;
}
/**
* @method
* @name cryptocom#fetchFundingRateHistory
* @description fetches historical funding rates
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#public-get-valuations
* @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] to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms for the ending date filter, default is the current time
* @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);
}
const market = this.market(symbol);
if (!market['swap']) {
throw new errors.BadSymbol(this.id + ' fetchFundingRateHistory() supports swap contracts only');
}
const request = {
'instrument_name': market['id'],
'valuation_type': 'funding_hist',
};
if (since !== undefined) {
request['start_ts'] = since;
}
if (limit !== undefined) {
request['count'] = limit;
}
const until = this.safeInteger(params, 'until');
params = this.omit(params, ['until']);
if (until !== undefined) {
request['end_ts'] = until;
}
const response = await this.v1PublicGetPublicGetValuations(this.extend(request, params));
//
// {
// "id": -1,
// "method": "public/get-valuations",
// "code": 0,
// "result": {
// "data": [
// {
// "v": "-0.000001884",
// "t": 1687892400000
// },
// ],
// "instrument_name": "BTCUSD-PERP"
// }
// }
//
const result = this.safeDict(response, 'result', {});
const data = this.safeList(result, 'data', []);
const marketId = this.safeString(result, 'instrument_name');
const rates = [];
for (let i = 0; i < data.length; i++) {
const entry = data[i];
const timestamp = this.safeInteger(entry, 't');
rates.push({
'info': entry,
'symbol': this.safeSymbol(marketId, market),
'fundingRate': this.safeNumber(entry, 'v'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
});
}
const sorted = this.sortBy(rates, 'timestamp');
return this.filterBySymbolSinceLimit(sorted, market['symbol'], since, limit);
}
/**
* @method
* @name cryptocom#fetchPosition
* @description fetch data on a single open contract trade position
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-positions
* @param {string} symbol unified market symbol of the market the position is held in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @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 request = {
'instrument_name': market['id'],
};
const response = await this.v1PrivatePostPrivateGetPositions(this.extend(request, params));
//
// {
// "id": 1688015952050,
// "method": "private/get-positions",
// "code": 0,
// "result": {
// "data": [
// {
// "account_id": "ce075bef-b600-4277-bd6e-ff9007251e63",
// "quantity": "0.0001",
// "cost": "3.02392",
// "open_pos_cost": "3.02392",
// "open_position_pnl": "-0.0010281328",
// "session_pnl": "-0.0010281328",
// "update_timestamp_ms": 1688015919091,
// "instrument_name": "BTCUSD-PERP",
// "type": "PERPETUAL_SWAP"
// }
// ]
// }
// }
//
const result = this.safeDict(response, 'result', {});
const data = this.safeList(result, 'data', []);
return this.parsePosition(this.safeDict(data, 0), market);
}
/**
* @method
* @name cryptocom#fetchPositions
* @description fetch all open positions
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-positions
* @param {string[]|undefined} symbols list of unified market symbols
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
*/
async fetchPositions(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols);
const request = {};
let market = undefined;
if (symbols !== undefined) {
let symbol = undefined;
if (Array.isArray(symbols)) {
const symbolsLength = symbols.length;
if (symbolsLength > 1) {
throw new errors.BadRequest(this.id + ' fetchPositions() symbols argument cannot contain more than 1 symbol');
}
symbol = symbols[0];
}
else {
symbol = symbols;
}
market = this.market(symbol);
request['instrument_name'] = market['id'];
}
const response = await this.v1PrivatePostPrivateGetPositions(this.extend(request, params));
//
// {
// "id": 1688015952050,
// "method": "private/get-positions",
// "code": 0,
// "result": {
// "data": [
// {
// "account_id": "ce075bef-b600-4277-bd6e-ff9007251e63",
// "quantity": "0.0001",
// "cost": "3.02392",
// "open_pos_cost": "3.02392",
// "open_position_pnl": "-0.0010281328",
// "session_pnl": "-0.0010281328",
// "update_timestamp_ms": 1688015919091,
// "instrument_name": "BTCUSD-PERP",
// "type": "PERPETUAL_SWAP"
// }
// ]
// }
// }
//
const responseResult = this.safeDict(response, 'result', {});
const positions = this.safeList(responseResult, 'data', []);
const result = [];
for (let i = 0; i < positions.length; i++) {
const entry = positions[i];
const marketId = this.safeString(entry, 'instrument_name');
const marketInner = this.safeMarket(marketId, undefined, undefined, 'contract');
result.push(this.parsePosition(entry, marketInner));
}
return this.filterByArrayPositions(result, 'symbol', undefined, false);
}
parsePosition(position, market = undefined) {
//
// {
// "account_id": "ce075bef-b600-4277-bd6e-ff9007251e63",
// "quantity": "0.0001",
// "cost": "3.02392",
// "open_pos_cost": "3.02392",
// "open_position_pnl": "-0.0010281328",
// "session_pnl": "-0.0010281328",
// "update_timestamp_ms": 1688015919091,
// "instrument_name": "BTCUSD-PERP",
// "type": "PERPETUAL_SWAP"
// }
//
const marketId = this.safeString(position, 'instrument_name');
market = this.safeMarket(marketId, market, undefined, 'contract');
const symbol = this.safeSymbol(marketId, market, undefined, 'contract');
const timestamp = this.safeInteger(position, 'update_timestamp_ms');
const amount = this.safeString(position, 'quantity');
return this.safePosition({
'info': position,
'id': undefined,
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'hedged': undefined,
'side': Precise["default"].stringGt(amount, '0') ? 'buy' : 'sell',
'contracts': Precise["default"].stringAbs(amount),
'contractSize': market['contractSize'],
'entryPrice': undefined,
'markPrice': undefined,
'notional': undefined,
'leverage': undefined,
'collateral': this.safeNumber(position, 'open_pos_cost'),
'initialMargin': this.safeNumber(position, 'cost'),
'maintenanceMargin': undefined,
'initialMarginPercentage': undefined,
'maintenanceMarginPercentage': undefined,
'unrealizedPnl': this.safeNumber(position, 'open_position_pnl'),
'liquidationPrice': undefined,
'marginMode': undefined,
'percentage': undefined,
'marginRatio': undefined,
'stopLossPrice': undefined,
'takeProfitPrice': undefined,
});
}
nonce() {
return this.milliseconds();
}
paramsToString(object, level) {
const maxLevel = 3;
if (level >= maxLevel) {
return object.toString();
}
if (typeof object === 'string') {
return object;
}
let returnString = '';
let paramsKeys = undefined;
if (Array.isArray(object)) {
paramsKeys = object;
}
else {
const sorted = this.keysort(object);
paramsKeys = Object.keys(sorted);
}
for (let i = 0; i < paramsKeys.length; i++) {
const key = paramsKeys[i];
returnString += key;
const value = object[key];
if (value === 'undefined') {
returnString += 'null';
}
else if (Array.isArray(value)) {
for (let j = 0; j < value.length; j++) {
returnString += this.paramsToString(value[j], level + 1);
}
}
else {
returnString += value.toString();
}
}
return returnString;
}
/**
* @method
* @name cryptocom#closePositions
* @description closes open positions for a market
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-close-position
* @param {string} symbol Unified CCXT market symbol
* @param {string} [side] not used by cryptocom.closePositions
* @param {object} [params] extra parameters specific to the okx api endpoint
*
* EXCHANGE SPECIFIC PARAMETERS
* @param {string} [params.type] LIMIT or MARKET
* @param {number} [params.price] for limit orders only
* @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 request = {
'instrument_name': market['id'],
'type': 'MARKET',
};
const type = this.safeStringUpper(params, 'type');
const price = this.safeString(params, 'price');
if (type !== undefined) {
request['type'] = type;
}
if (price !== undefined) {
request['price'] = this.priceToPrecision(market['symbol'], price);
}
const response = await this.v1PrivatePostPrivateClosePosition(this.extend(request, params));
//
// {
// "id" : 1700830813298,
// "method" : "private/close-position",
// "code" : 0,
// "result" : {
// "client_oid" : "179a909d-5614-655b-0d0e-9e85c9a25c85",
// "order_id" : "6142909897021751347"
// }
// }
//
const result = this.safeDict(response, 'result');
return this.parseOrder(result, market);
}
/**
* @method
* @name cryptocom#fetchTradingFee
* @description fetch the trading fees for a market
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-instrument-fee-rate
* @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 = {
'instrument_name': market['id'],
};
const response = await this.v1PrivatePostPrivateGetInstrumentFeeRate(this.extend(request, params));
//
// {
// "id": 1,
// "code": 0,
// "method": "private/staking/unstake",
// "result": {
// "staking_id": "1",
// "instrument_name": "SOL.staked",
// "status": "NEW",
// "quantity": "1",
// "underlying_inst_name": "SOL",
// "reason": "NO_ERROR"
// }
// }
//
const data = this.safeDict(response, 'result', {});
return this.parseTradingFee(data, market);
}
/**
* @method
* @name cryptocom#fetchTradingFees
* @see https://exchange-docs.crypto.com/exchange/v1/rest-ws/index.html#private-get-fee-rate
* @description fetch the trading fees for multiple markets
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols
*/
async fetchTradingFees(params = {}) {
await this.loadMarkets();
const response = await this.v1PrivatePostPrivateGetFeeRate(params);
//
// {
// "id": 1,
// "method": "/private/get-fee-rate",
// "code": 0,
// "result": {
// "spot_tier": "3",
// "deriv_tier": "3",
// "effective_spot_maker_rate_bps": "6.5",
// "effective_spot_taker_rate_bps": "6.9",
// "effective_deriv_maker_rate_bps": "1.1",
// "effective_deriv_taker_rate_bps": "3"
// }
// }
//
const result = this.safeDict(response, 'result', {});
return this.parseTradingFees(result);
}
parseTradingFees(response) {
//
// {
// "spot_tier": "3",
// "deriv_tier": "3",
// "effective_spot_maker_rate_bps": "6.5",
// "effective_spot_taker_rate_bps": "6.9",
// "effective_deriv_maker_rate_bps": "1.1",
// "effective_deriv_taker_rate_bps": "3"
// }
//
const result = {};
result['info'] = response;
for (let i = 0; i < this.symbols.length; i++) {
const symbol = this.symbols[i];
const market = this.market(symbol);
const isSwap = market['swap'];
const takerFeeKey = isSwap ? 'effective_deriv_taker_rate_bps' : 'effective_spot_taker_rate_bps';
const makerFeeKey = isSwap ? 'effective_deriv_maker_rate_bps' : 'effective_spot_maker_rate_bps';
const tradingFee = {
'info': response,
'symbol': symbol,
'maker': this.parseNumber(Precise["default"].stringDiv(this.safeString(response, makerFeeKey), '10000')),
'taker': this.parseNumber(Precise["default"].stringDiv(this.safeString(response, takerFeeKey), '10000')),
'percentage': undefined,
'tierBased': undefined,
};
result[symbol] = tradingFee;
}
return result;
}
parseTradingFee(fee, market = undefined) {
//
// {
// "instrument_name": "BTC_USD",
// "effective_maker_rate_bps": "6.5",
// "effective_taker_rate_bps": "6.9"
// }
//
const marketId = this.safeString(fee, 'instrument_name');
const symbol = this.safeSymbol(marketId, market);
return {
'info': fee,
'symbol': symbol,
'maker': this.parseNumber(Precise["default"].stringDiv(this.safeString(fee, 'effective_maker_rate_bps'), '10000')),
'taker': this.parseNumber(Precise["default"].stringDiv(this.safeString(fee, 'effective_taker_rate_bps'), '10000')),
'percentage': undefined,
'tierBased': undefined,
};
}
sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
const type = this.safeString(api, 0);
const access = this.safeString(api, 1);
let url = this.urls['api'][type] + '/' + path;
const query = this.omit(params, this.extractParams(path));
if (access === 'public') {
if (Object.keys(query).length) {
url += '?' + this.urlencode(query);
}
}
else {
this.checkRequiredCredentials();
const nonce = this.nonce().toString();
const requestParams = this.extend({}, params);
const paramsKeys = Object.keys(requestParams);
const strSortKey = this.paramsToString(requestParams, 0);
const payload = path + nonce + this.apiKey + strSortKey + nonce;
const signature = this.hmac(this.encode(payload), this.encode(this.secret), sha256.sha256);
const paramsKeysLength = paramsKeys.length;
body = this.json({
'id': nonce,
'method': path,
'params': params,
'api_key': this.apiKey,
'sig': signature,
'nonce': nonce,
});
// fix issue https://github.com/ccxt/ccxt/issues/11179
// php always encodes dictionaries as arrays
// if an array is empty, php will put it in square brackets
// python and js will put it in curly brackets
// the code below checks and replaces those brackets in empty requests
if (paramsKeysLength === 0) {
const paramsString = '{}';
const arrayString = '[]';
body = body.replace(arrayString, paramsString);
}
headers = {
'Content-Type': 'application/json',
};
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
const errorCode = this.safeString(response, 'code');
if (errorCode !== '0') {
const feedback = this.id + ' ' + body;
this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
throw new errors.ExchangeError(this.id + ' ' + body);
}
return undefined;
}
}
module.exports = cryptocom;