ccxt-xigua
Version:
1,938 lines • 84.4 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { BadSymbol, ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, BadRequest } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class currencycom extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'currencycom',
'name': 'Currency.com',
'countries': [ 'BY' ], // Belarus
'rateLimit': 100,
'certified': true,
'pro': true,
'version': 'v2',
// new metainfo interface
'has': {
'CORS': undefined,
'spot': true,
'margin': true,
'swap': true,
'future': false,
'option': false,
'addMargin': undefined,
'cancelAllOrders': undefined,
'cancelOrder': true,
'cancelOrders': undefined,
'createDepositAddress': undefined,
'createLimitOrder': true,
'createMarketOrder': true,
'createOrder': true,
'createStopLimitOrder': true,
'createStopMarketOrder': true,
'createStopOrder': true,
'editOrder': 'emulated',
'fetchAccounts': true,
'fetchBalance': true,
'fetchBidsAsks': undefined,
'fetchBorrowRate': undefined,
'fetchBorrowRateHistory': undefined,
'fetchBorrowRates': undefined,
'fetchBorrowRatesPerSymbol': undefined,
'fetchCanceledOrders': undefined,
'fetchClosedOrder': undefined,
'fetchClosedOrders': undefined,
'fetchCurrencies': true,
'fetchDeposit': undefined,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': false,
'fetchDeposits': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchL2OrderBook': true,
'fetchLedger': true,
'fetchLedgerEntry': false,
'fetchLeverage': true,
'fetchLeverageTiers': false,
'fetchMarginMode': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrder': undefined,
'fetchOpenOrders': true,
'fetchOrder': undefined,
'fetchOrderBook': true,
'fetchOrderBooks': undefined,
'fetchOrders': undefined,
'fetchOrderTrades': undefined,
'fetchPosition': undefined,
'fetchPositionMode': false,
'fetchPositions': true,
'fetchPositionsRisk': undefined,
'fetchPremiumIndexOHLCV': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': false,
'fetchTradingFees': true,
'fetchTradingLimits': undefined,
'fetchTransactionFee': undefined,
'fetchTransactionFees': undefined,
'fetchTransactions': true,
'fetchTransfers': undefined,
'fetchWithdrawal': undefined,
'fetchWithdrawals': true,
'reduceMargin': undefined,
'setLeverage': undefined,
'setMarginMode': undefined,
'setPositionMode': undefined,
'signIn': undefined,
'transfer': undefined,
'withdraw': undefined,
},
'timeframes': {
'1m': '1m',
'5m': '5m',
'10m': '10m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'4h': '4h',
'1d': '1d',
'1w': '1w',
},
'hostname': 'backend.currency.com',
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/83718672-36745c00-a63e-11ea-81a9-677b1f789a4d.jpg',
'api': {
'public': 'https://api-adapter.{hostname}/api',
'private': 'https://api-adapter.{hostname}/api',
'marketcap': 'https://marketcap.{hostname}/api',
},
'test': {
'public': 'https://demo-api-adapter.{hostname}/api',
'private': 'https://demo-api-adapter.{hostname}/api',
},
'www': 'https://www.currency.com',
'referral': 'https://currency.com/trading/signup?c=362jaimv&pid=referral',
'doc': [
'https://currency.com/api',
],
'fees': 'https://currency.com/fees-charges',
},
// rate-limits are described at: https://currency.com/api-get-started
'api': {
'public': {
'get': {
'v1/time': 1,
'v1/exchangeInfo': 1,
'v1/depth': 1,
'v1/aggTrades': 1,
'v1/klines': 1,
'v1/ticker/24hr': 1,
'v2/time': 1,
'v2/exchangeInfo': 1,
'v2/depth': 1,
'v2/aggTrades': 1,
'v2/klines': 1,
'v2/ticker/24hr': 1,
},
},
'marketcap': {
'get': {
'v1/assets': 1,
'v1/candles': 1,
'v1/orderbook': 1,
'v1/summary': 1,
'v1/ticker': 1,
'v1/token/assets': 1,
'v1/token/orderbook': 1,
'v1/token/summary': 1,
'v1/token/ticker': 1,
'v1/token/trades': 1,
'v1/token_crypto/OHLC': 1,
'v1/token_crypto/assets': 1,
'v1/token_crypto/orderbook': 1,
'v1/token_crypto/summary': 1,
'v1/token_crypto/ticker': 1,
'v1/token_crypto/trades': 1,
'v1/trades': 1,
},
},
'private': {
'get': {
'v1/account': 1,
'v1/currencies': 1,
'v1/deposits': 1,
'v1/depositAddress': 1,
'v1/ledger': 1,
'v1/leverageSettings': 1,
'v1/myTrades': 1,
'v1/openOrders': 1,
'v1/tradingPositions': 1,
'v1/tradingPositionsHistory': 1,
'v1/transactions': 1,
'v1/withdrawals': 1,
'v2/account': 1,
'v2/currencies': 1,
'v2/deposits': 1,
'v2/depositAddress': 1,
'v2/ledger': 1,
'v2/leverageSettings': 1,
'v2/myTrades': 1,
'v2/openOrders': 1,
'v2/tradingPositions': 1,
'v2/tradingPositionsHistory': 1,
'v2/transactions': 1,
'v2/withdrawals': 1,
},
'post': {
'v1/order': 1,
'v1/updateTradingPosition': 1,
'v1/updateTradingOrder': 1,
'v1/closeTradingPosition': 1,
'v2/order': 1,
'v2/updateTradingPosition': 1,
'v2/updateTradingOrder': 1,
'v2/closeTradingPosition': 1,
},
'delete': {
'v1/order': 1,
'v2/order': 1,
},
},
},
'fees': {
'trading': {
'feeSide': 'get',
'tierBased': false,
'percentage': true,
'taker': this.parseNumber ('0.002'),
'maker': this.parseNumber ('0.002'),
},
},
'precisionMode': TICK_SIZE,
// exchange-specific options
'options': {
'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel, 'FOK' = Fill Or Kill
'warnOnFetchOpenOrdersWithoutSymbol': true,
'recvWindow': 5 * 1000, // 5 sec, default
'timeDifference': 0, // the difference between system clock and Binance clock
'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation
'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision
'newOrderRespType': {
'market': 'FULL', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
'limit': 'RESULT', // we change it from 'ACK' by default to 'RESULT'
'stop': 'RESULT',
},
'leverage_markets_suffix': '_LEVERAGE',
'collateralCurrencies': [ 'USD', 'EUR', 'USDT' ],
},
'exceptions': {
'broad': {
'FIELD_VALIDATION_ERROR Cancel is available only for LIMIT order': InvalidOrder,
'API key does not exist': AuthenticationError,
'Order would trigger immediately.': InvalidOrder,
'Account has insufficient balance for requested action.': InsufficientFunds,
'Rest API trading is not enabled.': ExchangeNotAvailable,
'Combination of parameters invalid': BadRequest,
'Invalid limit price': BadRequest,
'Only leverage symbol allowed here:': BadSymbol, // when you fetchLeverage for non-leverage symbols, like 'BTC/USDT' instead of 'BTC/USDT_LEVERAGE': {"code":"-1128","msg":"Only leverage symbol allowed here: BTC/USDT"}
'market data service is not available': ExchangeNotAvailable, // {"code":"-1021","msg":"market data service is not available"}
'your time is ahead of server': InvalidNonce, // {"code":"-1021","msg":"your time is ahead of server"}
},
'exact': {
'-1000': ExchangeNotAvailable, // {"code":-1000,"msg":"An unknown error occured while processing the request."}
'-1013': InvalidOrder, // createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL
// '-1021': InvalidNonce, // {"code":"-1021","msg":"your time is ahead of server"} // see above in the broad section
'-1022': AuthenticationError, // {"code":-1022,"msg":"Signature for this request is not valid."}
'-1030': InvalidOrder, // {"code":"-1030","msg":"You mentioned an invalid value for the price parameter."}
'-1100': InvalidOrder, // createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price'
'-1104': ExchangeError, // Not all sent parameters were read, read 8 parameters but was sent 9
'-1025': AuthenticationError, // {"code":-1025,"msg":"Invalid API-key, IP, or permissions for action"}
'-1128': BadRequest, // {"code":-1128,"msg":"Combination of optional parameters invalid."} | {"code":"-1128","msg":"Combination of parameters invalid"} | {"code":"-1128","msg":"Invalid limit price"}
'-2010': ExchangeError, // generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc...
'-2011': OrderNotFound, // cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER'
'-2013': OrderNotFound, // fetchOrder (1, 'BTC/USDT') -> 'Order does not exist'
'-2014': AuthenticationError, // { "code":-2014, "msg": "API-key format invalid." }
'-2015': AuthenticationError, // "Invalid API-key, IP, or permissions for action."
},
},
'commonCurrencies': {
'ACN': 'Accenture',
'AMC': 'AMC Entertainment Holdings',
'BNS': 'Bank of Nova Scotia',
'CAR': 'Avis Budget Group Inc',
'CLR': 'Continental Resources',
'EDU': 'New Oriental Education & Technology Group Inc',
'ETN': 'Eaton',
'FOX': 'Fox Corporation',
'GM': 'General Motors Co',
'IQ': 'iQIYI',
'OSK': 'Oshkosh',
'PLAY': "Dave & Buster's Entertainment",
},
});
}
nonce () {
return this.milliseconds () - this.options['timeDifference'];
}
async fetchTime (params = {}) {
/**
* @method
* @name currencycom#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
const response = await this.publicGetV2Time (params);
//
// {
// "serverTime": 1590998366609
// }
//
return this.safeInteger (response, 'serverTime');
}
async fetchCurrencies (params = {}) {
/**
* @method
* @name currencycom#fetchCurrencies
* @description fetches all available currencies on an exchange
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} an associative dictionary of currencies
*/
// requires authentication
if (!this.checkRequiredCredentials (false)) {
return undefined;
}
const response = await this.privateGetV2Currencies (params);
//
// [
// {
// "name": "Euro",
// "displaySymbol": "EUR.cx",
// "precision": "2",
// "type": "FIAT",
// "minWithdrawal": "90.0",
// "maxWithdrawal": "1.0E+8",
// "commissionMin": "0.02", // some instruments do not have this property
// "commissionPercent": "1.5", // some instruments do not have this property
// "minDeposit": "90.0",
// },
// {
// name: "Bitcoin",
// displaySymbol: "BTC",
// precision: "8",
// type: "CRYPTO", // only a few major currencies have this value, others like USDT have a value of "TOKEN"
// minWithdrawal: "0.00020",
// commissionFixed: "0.00010",
// minDeposit: "0.00010",
// },
// ]
//
const result = {};
for (let i = 0; i < response.length; i++) {
const currency = response[i];
const id = this.safeString (currency, 'displaySymbol');
const code = this.safeCurrencyCode (id);
const fee = this.safeNumber (currency, 'commissionFixed');
result[code] = {
'id': id,
'code': code,
'address': this.safeString (currency, 'baseAddress'),
'type': this.safeStringLower (currency, 'type'),
'name': this.safeString (currency, 'name'),
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'fee': fee,
'precision': this.parseNumber (this.parsePrecision (this.safeString (currency, 'precision'))),
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': this.safeNumber (currency, 'minWithdrawal'),
'max': this.safeNumber (currency, 'maxWithdrawal'),
},
'deposit': {
'min': this.safeNumber (currency, 'minDeposit'),
'max': undefined,
},
},
'info': currency,
};
}
return result;
}
async fetchMarkets (params = {}) {
/**
* @method
* @name currencycom#fetchMarkets
* @description retrieves data on all markets for currencycom
* @param {dict} params extra parameters specific to the exchange api endpoint
* @returns {[dict]} an array of objects representing market data
*/
const response = await this.publicGetV2ExchangeInfo (params);
//
// {
// timezone: "UTC",
// serverTime: "1645186287261",
// rateLimits: [
// { rateLimitType: "REQUEST_WEIGHT", interval: "MINUTE", intervalNum: "1", limit: "1200" },
// { rateLimitType: "ORDERS", interval: "SECOND", intervalNum: "1", limit: "10" },
// { rateLimitType: "ORDERS", interval: "DAY", intervalNum: "1", limit: "864000" },
// ],
// exchangeFilters: [],
// symbols: [
// {
// symbol: "BTC/USDT", // BTC/USDT, BTC/USDT_LEVERAGE
// name: "Bitcoin / Tether",
// status: "TRADING", // TRADING, BREAK, HALT
// baseAsset: "BTC",
// baseAssetPrecision: "4",
// quoteAsset: "USDT",
// quoteAssetId: "USDT", // USDT, USDT_LEVERAGE
// quotePrecision: "4",
// orderTypes: [ "LIMIT", "MARKET" ], // LIMIT, MARKET, STOP
// filters: [
// { filterType: "LOT_SIZE", minQty: "0.0001", maxQty: "100", stepSize: "0.0001", },
// { filterType: "MIN_NOTIONAL", minNotional: "5", },
// ],
// marketModes: [ "REGULAR" ], // CLOSE_ONLY, LONG_ONLY, REGULAR
// marketType: "SPOT", // SPOT, LEVERAGE
// longRate: -0.0684932, // LEVERAGE only
// shortRate: -0.0684932, // LEVERAGE only
// swapChargeInterval: 1440, // LEVERAGE only
// country: "",
// sector: "",
// industry: "",
// tradingHours: "UTC; Mon - 22:00, 22:05 -; Tue - 22:00, 22:05 -; Wed - 22:00, 22:05 -; Thu - 22:00, 22:05 -; Fri - 22:00, 23:01 -; Sat - 22:00, 22:05 -; Sun - 21:00, 22:05 -",
// tickSize: "0.01",
// tickValue: "403.4405", // not available in BTC/USDT_LEVERAGE, but available in BTC/USD_LEVERAGE
// exchangeFee: "0.2", // SPOT only
// tradingFee: 0.075, // LEVERAGE only
// makerFee: -0.025, // LEVERAGE only
// takerFee: 0.06, // LEVERAGE only
// maxSLGap: 50, // LEVERAGE only
// minSLGap: 1, // LEVERAGE only
// maxTPGap: 50, // LEVERAGE only
// minTPGap: 0.5, // LEVERAGE only
// assetType: "CRYPTOCURRENCY",
// },
// ]
// }
//
if (this.options['adjustForTimeDifference']) {
await this.loadTimeDifference ();
}
const markets = this.safeValue (response, 'symbols', []);
const result = [];
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
const id = this.safeString (market, 'symbol');
const baseId = this.safeString (market, 'baseAsset');
const quoteId = this.safeString (market, 'quoteAsset');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
let symbol = base + '/' + quote;
const type = this.safeString (market, 'marketType');
const spot = (type === 'SPOT');
const futures = false;
const swap = (type === 'LEVERAGE');
const margin = swap; // as we decided to set
if (swap) {
symbol = symbol.replace (this.options['leverage_markets_suffix'], '');
symbol += ':' + quote;
}
const active = this.safeString (market, 'status') === 'TRADING';
// to set taker & maker fees, we use one from the below data - pairs either have 'exchangeFee' or 'tradingFee', if none of them (rare cases), then they should have 'takerFee & makerFee'
const exchangeFee = this.safeString2 (market, 'exchangeFee', 'tradingFee');
let makerFee = this.safeString (market, 'makerFee', exchangeFee);
let takerFee = this.safeString (market, 'takerFee', exchangeFee);
makerFee = Precise.stringDiv (makerFee, '100');
takerFee = Precise.stringDiv (takerFee, '100');
const filters = this.safeValue (market, 'filters', []);
const filtersByType = this.indexBy (filters, 'filterType');
let limitPriceMin = undefined;
let limitPriceMax = undefined;
let precisionPrice = this.safeNumber (market, 'tickSize');
if ('PRICE_FILTER' in filtersByType) {
const filter = this.safeValue (filtersByType, 'PRICE_FILTER', {});
precisionPrice = this.safeNumber (filter, 'tickSize');
// PRICE_FILTER reports zero values for maxPrice
// since they updated filter types in November 2018
// https://github.com/ccxt/ccxt/issues/4286
// therefore limits['price']['max'] doesn't have any meaningful value except undefined
limitPriceMin = this.safeNumber (filter, 'minPrice');
const maxPrice = this.safeNumber (filter, 'maxPrice');
if ((maxPrice !== undefined) && (maxPrice > 0)) {
limitPriceMax = maxPrice;
}
}
let precisionAmount = this.parseNumber (this.parsePrecision (this.safeString (market, 'baseAssetPrecision')));
let limitAmount = {
'min': undefined,
'max': undefined,
};
if ('LOT_SIZE' in filtersByType) {
const filter = this.safeValue (filtersByType, 'LOT_SIZE', {});
precisionAmount = this.safeNumber (filter, 'stepSize');
limitAmount = {
'min': this.safeNumber (filter, 'minQty'),
'max': this.safeNumber (filter, 'maxQty'),
};
}
let limitMarket = {
'min': undefined,
'max': undefined,
};
if ('MARKET_LOT_SIZE' in filtersByType) {
const filter = this.safeValue (filtersByType, 'MARKET_LOT_SIZE', {});
limitMarket = {
'min': this.safeNumber (filter, 'minQty'),
'max': this.safeNumber (filter, 'maxQty'),
};
}
let costMin = undefined;
if ('MIN_NOTIONAL' in filtersByType) {
const filter = this.safeValue (filtersByType, 'MIN_NOTIONAL', {});
costMin = this.safeNumber (filter, 'minNotional');
}
const isContract = swap || futures;
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': type,
'spot': spot,
'margin': margin,
'swap': swap,
'future': futures,
'option': false,
'active': active,
'contract': isContract,
'linear': isContract ? true : undefined,
'inverse': undefined,
'taker': this.parseNumber (takerFee),
'maker': this.parseNumber (makerFee),
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': precisionAmount,
'price': precisionPrice,
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': limitAmount,
'market': limitMarket,
'price': {
'min': limitPriceMin,
'max': limitPriceMax,
},
'cost': {
'min': costMin,
'max': undefined,
},
},
'info': market,
});
}
return result;
}
async fetchAccounts (params = {}) {
/**
* @method
* @name currencycom#fetchAccounts
* @description fetch all the accounts associated with a profile
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} a dictionary of [account structures]{@link https://docs.ccxt.com/en/latest/manual.html#account-structure} indexed by the account type
*/
const response = await this.privateGetV2Account (params);
//
// {
// "makerCommission": "0.20",
// "takerCommission": "0.20",
// "buyerCommission": "0.20",
// "sellerCommission": "0.20",
// "canTrade": true,
// "canWithdraw": true,
// "canDeposit": true,
// "updateTime": "1645266330",
// "userId": "644722",
// "balances": [
// {
// "accountId": "120702016179403605",
// "collateralCurrency": false,
// "asset": "CAKE",
// "free": "3.1",
// "locked": "0.0",
// "default": false,
// },
// {
// "accountId": "109698017713125316",
// "collateralCurrency": true,
// "asset": "USD",
// "free": "17.58632",
// "locked": "0.0",
// "default": true,
// }
// ]
// }
//
const accounts = this.safeValue (response, 'balances', []);
const result = [];
for (let i = 0; i < accounts.length; i++) {
const account = accounts[i];
const accountId = this.safeInteger (account, 'accountId');
const currencyId = this.safeString (account, 'asset');
const currencyCode = this.safeCurrencyCode (currencyId);
result.push ({
'id': accountId,
'type': undefined,
'currency': currencyCode,
'info': account,
});
}
return result;
}
async fetchTradingFees (params = {}) {
/**
* @method
* @name currencycom#fetchTradingFees
* @description fetch the trading fees for multiple markets
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} a dictionary of [fee structures]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure} indexed by market symbols
*/
await this.loadMarkets ();
const response = await this.privateGetV2Account (params);
//
// {
// makerCommission: '0.20',
// takerCommission: '0.20',
// buyerCommission: '0.20',
// sellerCommission: '0.20',
// canTrade: true,
// canWithdraw: true,
// canDeposit: true,
// updateTime: '1645738976',
// userId: '-1924114235',
// balances: []
// }
//
const makerFee = this.safeNumber (response, 'makerCommission');
const takerFee = this.safeNumber (response, 'takerCommission');
const result = {};
for (let i = 0; i < this.symbols.length; i++) {
const symbol = this.symbols[i];
result[symbol] = {
'info': response,
'symbol': symbol,
'maker': makerFee,
'taker': takerFee,
'percentage': true,
'tierBased': false,
};
}
return result;
}
parseBalance (response, type = undefined) {
//
// {
// "makerCommission":0.20,
// "takerCommission":0.20,
// "buyerCommission":0.20,
// "sellerCommission":0.20,
// "canTrade":true,
// "canWithdraw":true,
// "canDeposit":true,
// "updateTime":1591056268,
// "balances":[
// {
// "accountId":5470306579272368,
// "collateralCurrency":true,
// "asset":"ETH",
// "free":0.0,
// "locked":0.0,
// "default":false,
// },
// ]
// }
//
const result = { 'info': response };
const balances = this.safeValue (response, 'balances', []);
for (let i = 0; i < balances.length; i++) {
const balance = balances[i];
const currencyId = this.safeString (balance, 'asset');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeString (balance, 'free');
account['used'] = this.safeString (balance, 'locked');
result[code] = account;
}
return this.safeBalance (result);
}
async fetchBalance (params = {}) {
/**
* @method
* @name currencycom#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
*/
await this.loadMarkets ();
const response = await this.privateGetV2Account (params);
//
// {
// "makerCommission": "0.20",
// "takerCommission": "0.20",
// "buyerCommission": "0.20",
// "sellerCommission": "0.20",
// "canTrade": true,
// "canWithdraw": true,
// "canDeposit": true,
// "updateTime": "1645266330",
// "userId": "644722",
// "balances": [
// {
// "accountId": "120702016179403605",
// "collateralCurrency": false,
// "asset": "CAKE",
// "free": "1.784",
// "locked": "0.0",
// "default": false,
// },
// {
// "accountId": "109698017413175316",
// "collateralCurrency": true,
// "asset": "USD",
// "free": "7.58632",
// "locked": "0.0",
// "default": true,
// }
// ]
// }
//
return this.parseBalance (response);
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @param {str} symbol unified symbol of the market to fetch the order book for
* @param {int|undefined} limit the maximum amount of order book entries to return
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} A dictionary of [order book structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symbols
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit; // default 100, max 1000, valid limits 5, 10, 20, 50, 100, 500, 1000, 5000
}
const response = await this.publicGetV2Depth (this.extend (request, params));
//
// {
// "lastUpdateId":1590999849037,
// "asks":[
// [0.02495,60.0345],
// [0.02496,34.1],
// ...
// ],
// "bids":[
// [0.02487,72.4144854],
// [0.02486,24.043],
// ...
// ]
// }
//
const orderbook = this.parseOrderBook (response, symbol);
orderbook['nonce'] = this.safeInteger (response, 'lastUpdateId');
return orderbook;
}
parseTicker (ticker, market = undefined) {
//
// fetchTicker
//
// {
// "symbol":"ETH/BTC",
// "priceChange":"0.00030",
// "priceChangePercent":"1.21",
// "weightedAvgPrice":"0.02481",
// "prevClosePrice":"0.02447",
// "lastPrice":"0.02477",
// "lastQty":"60.0",
// "bidPrice":"0.02477",
// "askPrice":"0.02484",
// "openPrice":"0.02447",
// "highPrice":"0.02524",
// "lowPrice":"0.02438",
// "volume":"11.97",
// "quoteVolume":"0.298053",
// "openTime":1590969600000,
// "closeTime":1591000072693
// }
//
// fetchTickers
//
// {
// "symbol": "SHIB/USD_LEVERAGE",
// "weightedAvgPrice": "0.000027595",
// "lastPrice": "0.00002737",
// "lastQty": "1.11111111E8",
// "bidPrice": "0.00002737",
// "askPrice": "0.00002782",
// "highPrice": "0.00002896",
// "lowPrice": "0.00002738",
// "volume": "16472160000",
// "quoteVolume": "454796.3376",
// "openTime": "1645187472000",
// "closeTime": "1645273872000",
// }
//
// ws:marketData.subscribe
//
// {
// "symbolName":"TXN",
// "bid":139.85,
// "bidQty":2500,
// "ofr":139.92000000000002,
// "ofrQty":2500,
// "timestamp":1597850971558
// }
//
const timestamp = this.safeInteger2 (ticker, 'closeTime', 'timestamp');
const marketId = this.safeString2 (ticker, 'symbol', 'symbolName');
market = this.safeMarket (marketId, market, '/');
const last = this.safeString (ticker, 'lastPrice');
return this.safeTicker ({
'symbol': market['symbol'],
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeString (ticker, 'highPrice'),
'low': this.safeString (ticker, 'lowPrice'),
'bid': this.safeString2 (ticker, 'bidPrice', 'bid'),
'bidVolume': this.safeString (ticker, 'bidQty'),
'ask': this.safeString2 (ticker, 'askPrice', 'ofr'),
'askVolume': this.safeString (ticker, 'ofrQty'),
'vwap': this.safeString (ticker, 'weightedAvgPrice'),
'open': this.safeString (ticker, 'openPrice'),
'close': last,
'last': last,
'previousClose': this.safeString (ticker, 'prevClosePrice'), // previous day close
'change': this.safeString (ticker, 'priceChange'),
'percentage': this.safeString (ticker, 'priceChangePercent'),
'average': undefined,
'baseVolume': this.safeString (ticker, 'volume'),
'quoteVolume': this.safeString (ticker, 'quoteVolume'),
'info': ticker,
}, market);
}
async fetchTicker (symbol, params = {}) {
/**
* @method
* @name currencycom#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {str} symbol unified symbol of the market to fetch the ticker for
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const response = await this.publicGetV2Ticker24hr (this.extend (request, params));
//
// {
// "symbol":"ETH/BTC",
// "priceChange":"0.00030",
// "priceChangePercent":"1.21",
// "weightedAvgPrice":"0.02481",
// "prevClosePrice":"0.02447",
// "lastPrice":"0.02477",
// "lastQty":"60.0",
// "bidPrice":"0.02477",
// "askPrice":"0.02484",
// "openPrice":"0.02447",
// "highPrice":"0.02524",
// "lowPrice":"0.02438",
// "volume":"11.97",
// "quoteVolume":"0.298053",
// "openTime":1590969600000,
// "closeTime":1591000072693
// }
//
return this.parseTicker (response, market);
}
async fetchTickers (symbols = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchTickers
* @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
* @param {[str]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} an array of [ticker structures]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
await this.loadMarkets ();
const response = await this.publicGetV2Ticker24hr (params);
//
// [
// {
// "symbol": "SHIB/USD_LEVERAGE",
// "weightedAvgPrice": "0.000027595",
// "lastPrice": "0.00002737",
// "lastQty": "1.11111111E8",
// "bidPrice": "0.00002737",
// "askPrice": "0.00002782",
// "highPrice": "0.00002896",
// "lowPrice": "0.00002738",
// "volume": "16472160000",
// "quoteVolume": "454796.3376",
// "openTime": "1645187472000",
// "closeTime": "1645273872000",
// }
// ]
//
return this.parseTickers (response, symbols);
}
parseOHLCV (ohlcv, market = undefined) {
//
// [
// 1590971040000,
// "0.02454",
// "0.02456",
// "0.02452",
// "0.02456",
// 249
// ]
//
return [
this.safeInteger (ohlcv, 0),
this.safeNumber (ohlcv, 1),
this.safeNumber (ohlcv, 2),
this.safeNumber (ohlcv, 3),
this.safeNumber (ohlcv, 4),
this.safeNumber (ohlcv, 5),
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @param {str} symbol unified symbol of the market to fetch OHLCV data for
* @param {str} timeframe the length of time each candle represents
* @param {int|undefined} since timestamp in ms of the earliest candle to fetch
* @param {int|undefined} limit the maximum amount of candles to fetch
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
'interval': this.timeframes[timeframe],
};
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 1000
}
const response = await this.publicGetV2Klines (this.extend (request, params));
//
// [
// [1590971040000,"0.02454","0.02456","0.02452","0.02456",249],
// [1590971100000,"0.02455","0.02457","0.02452","0.02456",300],
// [1590971160000,"0.02455","0.02456","0.02453","0.02454",286],
// ]
//
return this.parseOHLCVs (response, market, timeframe, since, limit);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public aggregate trades)
//
// {
// "a":"1658318071", // Aggregate tradeId
// "p":"0.02476", // Price
// "q":"0.0", // Official doc says: "Quantity (should be ignored)"
// "T":"1591001423382", // Epoch timestamp in MS
// "m":false // Was the buyer the maker
// }
//
// createOrder fills (private)
//
// {
// "price": "9807.05",
// "qty": "0.01",
// "commission": "0",
// "commissionAsset": "dUSD"
// }
//
// fetchMyTrades
//
// {
// "symbol": "DOGE/USD",
// "id": "116046000",
// "orderId": "00000000-0000-0000-0000-000006dbb8ad",
// "price": "0.14094",
// "qty": "40.0",
// "commission": "0.01",
// "commissionAsset": "USD",
// "time": "1645283022351",
// "buyer": false,
// "maker": false,
// "isBuyer": false,
// "isMaker": false
// }
//
const timestamp = this.safeInteger2 (trade, 'T', 'time');
const priceString = this.safeString2 (trade, 'p', 'price');
const amountString = this.safeString2 (trade, 'q', 'qty');
const id = this.safeString2 (trade, 'a', 'id');
let side = undefined;
const orderId = this.safeString (trade, 'orderId');
let takerOrMaker = undefined;
if ('m' in trade) {
side = trade['m'] ? 'sell' : 'buy'; // this is reversed intentionally [TODO: needs reason to be mentioned]
takerOrMaker = 'taker'; // in public trades, it's always taker
} else if ('isBuyer' in trade) {
side = (trade['isBuyer']) ? 'buy' : 'sell'; // this is a true side
takerOrMaker = trade['isMaker'] ? 'maker' : 'taker';
}
let fee = undefined;
if ('commission' in trade) {
fee = {
'cost': this.safeString (trade, 'commission'),
'currency': this.safeCurrencyCode (this.safeString (trade, 'commissionAsset')),
};
}
const marketId = this.safeString (trade, 'symbol');
const symbol = this.safeSymbol (marketId, market);
return this.safeTrade ({
'id': id,
'order': orderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'type': undefined,
'takerOrMaker': takerOrMaker,
'side': side,
'price': priceString,
'amount': amountString,
'cost': undefined,
'fee': fee,
'info': trade,
}, market);
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @param {str} symbol unified symbol of the market to fetch trades for
* @param {int|undefined} since timestamp in ms of the earliest trade to fetch
* @param {int|undefined} limit the maximum amount of trades to fetch
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {[dict]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
// 'limit': 500, // default 500, max 1000
};
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 1000
}
if (since !== undefined) {
request['startTime'] = since;
}
const response = await this.publicGetV2AggTrades (this.extend (request, params));
//
// [
// {
// "a":"1658318071", // Aggregate tradeId
// "p":"0.02476", // Price
// "q":"0.0", // Official doc says: "Quantity (should be ignored)"
// "T":"1591001423382", // Epoch timestamp in MS
// "m":false // Was the buyer the maker
// },
// ]
//
return this.parseTrades (response, market, since, limit);
}
parseOrder (order, market = undefined) {
//
// createOrder
//
// limit
//
// {
// "symbol": "BTC/USD",
// "orderId": "00000000-0000-0000-0000-000006eacaa0",
// "transactTime": "1645281669295",
// "price": "30000.00000000",
// "origQty": "0.0002",
// "executedQty": "0.0", // positive for BUY, negative for SELL
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// }
//
// market
//
// {
// "symbol": "DOGE/USD",
// "orderId": "00000000-0000-0000-0000-000006eab2ad",
// "transactTime": "1645283022252",
// "price": "0.14066000",
// "origQty": "40",
// "executedQty": "40.0", // positive for BUY, negative for SELL
// "status": "FILLED",
// "timeInForce": "FOK",
// "type": "MARKET",
// "side": "SELL",
// "fills": [
// {
// "price": "0.14094",
// "qty": "40.0",
// "commission": "0",
// "commissionAsset": "dUSD",
// },
// ],
// }
//
// cancelOrder
//
// {
// "symbol": "DOGE/USD",
// "orderId": "00000000-0000-0003-0000-000006db714c",
// "price": "0.13",
// "origQty": "30.0",
// "executedQty": "0.0",
// "status": "CANCELED",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// }
//
// fetchOpenOrders
//
// {
// "symbol": "DOGE/USD",
// "orderId": "00000000-0000-0003-0000-000004bcc27a",
// "price": "0.13",
// "origQty": "39.0",
// "executedQty": "0.0",
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "time": "1645284216240",
// "updateTime": "1645284216240",
// "leverage": false, // whether it's swap or not
// "working": true,
// }
//
const marketId = this.safeString (order, 'symbol');
const symbol = this.safeSymbol (marketId, market, '/');
const id = this.safeString (order, 'orderId');
const price = this.safeString (order, 'price');
const amount = this.safeString (order, 'origQty');
const filledRaw = this.safeString (order, 'executedQty');
const filled = Precise.stringAbs (filledRaw);
const status = this.parseOrderStatus (this.safeString (order, 'status'));
const timeInForce = this.parseOrderTimeInForce (this.safeString (order, 'timeInForce'));
const type = this.parseOrderType (this.safeString (order, 'type'));
const side = this.parseOrderSide (this.safeString (order, 'side'));
const timestamp = this.safeInteger2 (order, 'time', 'transactTime');
const fills = this.safeValue (order, 'fills');
return this.safeOrder ({
'info': order,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'side': side,
'price': price,
'stopPrice': undefined,
'amount': amount,
'cost': undefined,
'average': undefined,
'filled': filled,
'remaining': undefined,
'status': status,
'fee': undefined,
'trades': fills,
}, market);
}
parseOrderStatus (status) {
const statuses = {
'NEW': 'open',
'PARTIALLY_FILLED': 'open',
'FILLED': 'closed',
'CANCELED': 'canceled',
'PENDING_CANCEL': 'canceling',
'REJECTED': 'rejected',
'EXPIRED': 'expired',
};
return this.safeString (statuses, status, status);
}
parseOrderType (status) {
const statuses = {
'MARKET': 'market',
'LIMIT': 'limit',
'STOP': 'stop',
// temporarily we remove custom mappings
// 'LIMIT_MAKER': '',
// 'STOP_LOSS': 'stop-loss',
// 'STOP_LOSS_LIMIT': 'stop-limit',
// 'TAKE_PROFIT': 'take-profit',
// 'TAKE_PROFIT_LIMIT': 'take-profit',
};
return this.safeString (statuses, status, status);
}
parseOrderTimeInForce (status) {
const statuses = {
'GTC': 'GTC',
'FOK': 'FOK',
'IOC': 'IOC',
};
return this.safeString (statuses, status, status);
}
parseOrderSide (status) {
const statuses = {
'BUY': 'buy',
'SELL': 'sell',
};
return this.safeString (statuses, status, status);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
/**
* @method
* @name currencycom#createOrder
* @description create a trade order
* @param {str} symbol unified symbol of the market to create an order in
* @param {str} type 'market' or 'limit'
* @param {str} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float|undefined} price the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} an [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
let accountId = undefined;
if (market['margin']) {
accountId = this.safeString (this.options, 'accountId');
accountId = this.safeString (params, 'accountId', accountId);
if (accountId === undefined) {
throw new ArgumentsRequired (this.id + " createOrder() requires an accountId parameter or an exchange.options['accountId'] option for " + market['type'] + ' markets');
}
}
const newOrderRespType = this.safeValue (this.options['newOrderRespType'], type, 'RESULT');
const request = {
'symbol': market['id'],
'quantity': this.amountToPrecision (symbol, amount),
'type': type.toUpperCase (),
'side': side.toUpperCase (),
'newOrderRespType': newOrderRespType, // 'RESULT' for full order or 'FULL' for order with fills
// 'leverage': 1,
// 'accountId': 5470306579272968, // required for leverage markets
// 'takeProfit': '123.45',
// 'stopLoss': '54.321',
// 'guaranteedStopLoss': '54.321',
};
if (type === 'limit') {
request['price'] = this.priceToPrecision (symbol, price);
request['timeInForce'] = this.options['defaultTimeInForce'];
} else {
if (type === 'stop') {
request['type'] = 'STOP';
request['price'] = this.priceToPrecision (symbol, price);
} else if (type === 'market') {
const stopPrice = this.safeNumber (params, 'stopPrice');
params = this.omit (params, 'stopPrice');
if (stopPrice !== undefined) {
request['type'] = 'STOP';
request['price'] = this.priceToPrecision (symbol, stopPrice);
}
}
}
const response = await this.privatePostV2Order (this.extend (request, params));
//
// limit
//
// {
// "symbol": "BTC/USD",
// "orderId": "00000000-0000-0000-0000-000006eaaaa0",
// "transactTime": "1645281669295",
// "price": "30000.00000000",
// "origQty": "0.0002",
// "executedQty": "0.0", // positive for BUY, negative for SELL
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// }
//
// market
//
// {
// "symbol": "DOGE/USD",
// "orderId": "00000000-0000-0000-0000-000006eab8ad",
// "transactTime": "1645283022252",
// "price": "0.14066000",
// "origQty": "40",
// "executedQty": "40.0", // positive for BUY, negative for SELL
// "status": "FILLED",
// "timeInForce": "FOK",
// "type": "MARKET",
// "side": "BUY",
// "fills": [
// {
// "price": "0.14094",
// "qty": "40.0",
// "commission": "0",
// "commissionAsset": "dUSD"
// }
// ]
// }
//
return this.parseOrder (response, market);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchOpenOrders
* @description fetch all unfilled currently open orders
* @param {str|undefined} symbol unified market symbol
* @param {int|undefined} since the earliest time in ms to fetch open orders for
* @param {int|undefined} limit the maximum number of open orders structures to retrieve
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {[dict]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
await this.loadMarkets ();
let market = undefined;
const request = {};
if (symbol !== undefined) {
market = this.market (symbol);
request['symbol'] = market['id'];
} else if (this.options['warnOnFetchOpenOrdersWithoutSymbol']) {
const symbols = this.symbols;
const numSymbols = symbols.length;
const fetchOpenOrdersRateLimit = parseInt (numSymbols / 2);
throw new ExchangeError (this.id + ' fetchOpenOrders() WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + fetchOpenOrdersRateLimit.toString () + ' seconds. Do not call this method frequently to avoid ban. Set ' + this.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = false to suppress this warning message.');
}
const response = await this.privateGetV2OpenOrders (this.extend (request, params));
//
// [
// {
// "symbol": "DOGE/USD",
// "orderId": "00000000-0000-0003-0000-000004bac57a",
// "price": "0.13",
// "origQty": "39.0",
// "executedQty": "0.0", // positive for BUY, negative for SELL
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "time": "1645284216240",
// "updateTime": "1645284216240",
// "leverage": false,
// "working": true
// },
// ]
//
return this.parseOrders (response, market, since, limit, params);
}
async cancelOrder (id, symbol = undefined, params = {}) {
/**
* @method
* @name currencycom#cancelOrder
* @description cancels an open order
* @param {str} id order id
* @param {str} symbol unified symbol of the market the order was made in
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const origClientOrderId = this.safeValue (params, 'origClientOrderId');
const request = {
'symbol': market['id'],
// 'orderId': parseInt (id),
// 'origClientOrderId': id,
};
if (origClientOrderId === undefined) {
request['orderId'] = id;
} else {
request['origClientOrderId'] = origClientOrderId;
}
const response = await this.privateDeleteV2Order (this.extend (request, params));
//
// {
// "symbol": "DOGE/USD",
// "orderId": "00000000-0000-0003-0000-000006db764c",
// "price": "0.13",
// "origQty": "30.0",
// "executedQty": "0.0", // positive for BUY, negative for SELL
// "status": "CANCELED",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// }
//
return this.parseOrder (response, market);
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchMyTrades
* @description fetch all trades made by the user
* @param {str} symbol unified market symbol
* @param {int|undefined} since the earliest time in ms to fetch trades for
* @param {int|undefined} limit the maximum number of trades structures to retrieve
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {[dict]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html#trade-structure}
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.privateGetV2MyTrades (this.extend (request, params));
//
// [
// {
// "symbol": "DOGE/USD",
// "id": "116046000",
// "orderId": "00000000-0000-0000-0000-000006dbb8ad",
// "price": "0.14094",
// "qty": "40.0",
// "commission": "0.01",
// "commissionAsset": "USD",
// "time": "1645283022351",
// "buyer": false,
// "maker": false,
// "isBuyer": false,
// "isMaker": false
// },
// ]
//
return this.parseTrades (response, market, since, limit);
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchDeposits
* @description fetch all deposits made to an account
* @param {str|undefined} code unified currency code
* @param {int|undefined} since the earliest time in ms to fetch deposits for
* @param {int|undefined} limit the maximum number of deposits structures to retrieve
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {[dict]} a list of [transaction structures]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
return await this.fetchTransactionsByMethod ('privateGetV2Deposits', code, since, limit, params);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchWithdrawals
* @description fetch all withdrawals made from an account
* @param {str|undefined} code unified currency code
* @param {int|undefined} since the earliest time in ms to fetch withdrawals for
* @param {int|undefined} limit the maximum number of withdrawals structures to retrieve
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {[dict]} a list of [transaction structures]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
return await this.fetchTransactionsByMethod ('privateGetV2Withdrawals', code, since, limit, params);
}
async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchTransactions
* @description fetch history of deposits and withdrawals
* @param {str|undefined} code unified currency code for the currency of the transactions, default is undefined
* @param {int|undefined} since timestamp in ms of the earliest transaction, default is undefined
* @param {int|undefined} limit max number of transactions to return, default is undefined
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} a list of [transaction structure]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
return await this.fetchTransactionsByMethod ('privateGetV2Transactions', code, since, limit, params);
}
async fetchTransactionsByMethod (method, code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
let currency = undefined;
if (code !== undefined) {
currency = this.currency (code);
}
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this[method] (this.extend (request, params));
//
// [
// {
// "id": "616769213",
// "balance": "2.088",
// "amount": "1.304", // negative for 'withdrawal'
// "currency": "CAKE",
// "type": "deposit",
// "timestamp": "1645282121023",
// "paymentMethod": "BLOCKCHAIN",
// "blockchainTransactionHash": "0x57c68c1f2ae74d5eda5a2a00516361d241a5c9e1ee95bf32573523857c38c112",
// "status": "PROCESSED",
// "commission": "0.14", // this property only exists in withdrawal
// },
// ]
//
return this.parseTransactions (response, currency, since, limit, params);
}
parseTransaction (transaction, currency = undefined) {
const id = this.safeString (transaction, 'id');
const txHash = this.safeString (transaction, 'blockchainTransactionHash');
const amount = this.safeNumber (transaction, 'amount');
const timestamp = this.safeInteger (transaction, 'timestamp');
const currencyId = this.safeString (transaction, 'currency');
const code = this.safeCurrencyCode (currencyId, currency);
const state = this.parseTransactionStatus (this.safeString (transaction, 'state'));
const type = this.parseTransactionType (this.safeString (transaction, 'type'));
const feeCost = this.safeString (transaction, 'commission');
let fee = undefined;
if (feeCost !== undefined) {
fee = { 'currency': code, 'cost': feeCost };
}
const result = {
'id': id,
'txid': txHash,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'network': undefined,
'addressFrom': undefined,
'address': undefined,
'addressTo': undefined,
'tagFrom': undefined,
'tag': undefined,
'tagTo': undefined,
'type': type,
'amount': amount,
'currency': code,
'status': state,
'updated': undefined,
'comment': undefined,
'fee': fee,
'info': transaction,
};
return result;
}
parseTransactionStatus (status) {
const statuses = {
'APPROVAL': 'pending',
'PROCESSED': 'ok',
};
return this.safeString (statuses, status, status);
}
parseTransactionType (type) {
const types = {
'deposit': 'deposit',
'withdrawal': 'withdrawal',
};
return this.safeString (types, type, type);
}
async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchLedger
* @description fetch the history of changes, actions done by the user or operations that altered balance of the user
* @param {str|undefined} code unified currency code, default is undefined
* @param {int|undefined} since timestamp in ms of the earliest ledger entry, default is undefined
* @param {int|undefined} limit max number of ledger entrys to return, default is undefined
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} a [ledger structure]{@link https://docs.ccxt.com/en/latest/manual.html#ledger-structure}
*/
await this.loadMarkets ();
const request = {};
let currency = undefined;
if (code !== undefined) {
currency = this.currency (code);
}
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.privateGetV2Ledger (this.extend (request, params));
// in the below example, first item expresses withdrawal/deposit type, second example expresses trade
//
// [
// {
// "id": "619031398",
// "balance": "0.0",
// "amount": "-1.088",
// "currency": "CAKE",
// "type": "withdrawal",
// "timestamp": "1645460496425",
// "commission": "0.13",
// "paymentMethod": "BLOCKCHAIN", // present in withdrawal/deposit
// "blockchainTransactionHash": "0x400ac905557c3d34638b1c60eba110b3ee0f97f4eb0f7318015ab76e7f16b7d6", // present in withdrawal/deposit
// "status": "PROCESSED"
// },
// {
// "id": "619031034",
// "balance": "8.17223588",
// "amount": "-0.01326294",
// "currency": "USD",
// "type": "exchange_commission",
// "timestamp": "1645460461235",
// "commission": "0.01326294",
// "status": "PROCESSED"
// },
// ]
//
return this.parseLedger (response, currency, since, limit);
}
parseLedgerEntry (item, currency = undefined) {
const id = this.safeString (item, 'id');
const amountString = this.safeString (item, 'amount');
const amount = Precise.stringAbs (amountString);
const timestamp = this.safeInteger (item, 'timestamp');
const currencyId = this.safeString (item, 'currency');
const code = this.safeCurrencyCode (currencyId, currency);
const feeCost = this.safeString (item, 'commission');
let fee = undefined;
if (feeCost !== undefined) {
fee = { 'currency': code, 'cost': feeCost };
}
const direction = Precise.stringLt (amountString, '0') ? 'out' : 'in';
const result = {
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'direction': direction,
'account': undefined,
'referenceId': this.safeString (item, 'blockchainTransactionHash'),
'referenceAccount': undefined,
'type': this.parseLedgerEntryType (this.safeString (item, 'type')),
'currency': code,
'amount': amount,
'before': undefined,
'after': this.safeString (item, 'balance'),
'status': this.parseLedgerEntryStatus (this.safeString (item, 'status')),
'fee': fee,
'info': item,
};
return result;
}
parseLedgerEntryStatus (status) {
const statuses = {
'APPROVAL': 'pending',
'PROCESSED': 'ok',
'CANCELLED': 'canceled',
};
return this.safeString (statuses, status, status);
}
parseLedgerEntryType (type) {
const types = {
'deposit': 'transaction',
'withdrawal': 'transaction',
'exchange_commission': 'fee',
};
return this.safeString (types, type, type);
}
async fetchLeverage (symbol, params = {}) {
/**
* @method
* @name currencycom#fetchLeverage
* @description fetch the set leverage for a market
* @param {str} symbol unified market symbol
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} a [leverage structure]{@link https://docs.ccxt.com/en/latest/manual.html#leverage-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const response = await this.privateGetV2LeverageSettings (this.extend (request, params));
//
// {
// "values": [ 1, 2, 5, 10, ],
// "value": "10",
// }
//
return this.safeNumber (response, 'value');
}
async fetchDepositAddress (code, params = {}) {
/**
* @method
* @name currencycom#fetchDepositAddress
* @description fetch the deposit address for a currency associated with this account
* @param {str} code unified currency code
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {dict} an [address structure]{@link https://docs.ccxt.com/en/latest/manual.html#address-structure}
*/
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'coin': currency['id'],
};
const response = await this.privateGetV2DepositAddress (this.extend (request, params));
//
// { "address":"0x97d64eb014ac779194991e7264f01c74c90327f0" }
//
return this.parseDepositAddress (response, currency);
}
parseDepositAddress (depositAddress, currency = undefined) {
const address = this.safeString (depositAddress, 'address');
this.checkAddress (address);
currency = this.safeCurrency (undefined, currency);
return {
'currency': currency['code'],
'address': address,
'tag': undefined,
'network': undefined,
'info': depositAddress,
};
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'][api] + '/' + path;
if (path === 'historicalTrades') {
headers = {
'X-MBX-APIKEY': this.apiKey,
};
}
if (api === 'private') {
this.checkRequiredCredentials ();
let query = this.urlencode (this.extend ({
'timestamp': this.nonce (),
'recvWindow': this.options['recvWindow'],
}, params));
const signature = this.hmac (this.encode (query), this.encode (this.secret));
query += '&' + 'signature=' + signature;
headers = {
'X-MBX-APIKEY': this.apiKey,
};
if ((method === 'GET') || (method === 'DELETE')) {
url += '?' + query;
} else {
body = query;
headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
} else {
if (Object.keys (params).length) {
url += '?' + this.urlencode (params);
}
}
url = this.implodeHostname (url);
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
async fetchPositions (symbols = undefined, params = {}) {
/**
* @method
* @name currencycom#fetchPositions
* @description fetch all open positions
* @param {[str]|undefined} symbols list of unified market symbols
* @param {dict} params extra parameters specific to the currencycom api endpoint
* @returns {[dict]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
*/
await this.loadMarkets ();
const response = await this.privateGetV2TradingPositions (params);
//
// {
// "positions": [
// {
// "accountId": "109698017416453793",
// "id": "00a18490-0079-54c4-0000-0000803e73d3",
// "instrumentId": "45463225268524228",
// "orderId": "00a18490-0079-54c4-0000-0000803e73d2",
// "openQuantity": "13.6",
// "openPrice": "0.75724",
// "closeQuantity": "0.0",
// "closePrice": "0",
// "rpl": "-0.007723848",
// "rplConverted": "0",
// "upl": "-0.006664",
// "uplConverted": "-0.006664",
// "swap": "0",
// "swapConverted": "0",
// "fee": "-0.007723848",
// "dividend": "0",
// "margin": "0.2",
// "state": "ACTIVE",
// "currency": "USD",
// "createdTimestamp": "1645473877236",
// "openTimestamp": "1645473877193",
// "type": "NET",
// "cost": "2.0583600",
// "symbol": "XRP/USD_LEVERAGE"
// }
// ]
// }
//
const data = this.safeValue (response, 'positions', []);
return this.parsePositions (data, symbols);
}
parsePosition (position, market = undefined) {
market = this.safeMarket (this.safeString (position, 'symbol'), market);
const symbol = market['symbol'];
const timestamp = this.safeNumber (position, 'createdTimestamp');
const quantityRaw = this.safeString (position, 'openQuantity');
const side = Precise.stringGt (quantityRaw, '0') ? 'long' : 'short';
const quantity = Precise.stringAbs (quantityRaw);
const entryPrice = this.safeNumber (position, 'openPrice');
const unrealizedProfit = this.safeNumber (position, 'upl');
const marginCoeff = this.safeString (position, 'margin');
const leverage = Precise.stringDiv ('1', marginCoeff);
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'contracts': this.parseNumber (quantity),
'contractSize': undefined,
'entryPrice': entryPrice,
'collateral': undefined,
'side': side,
// 'realizedProfit': this.safeNumber (position, 'rpl'),
'unrealizedProfit': unrealizedProfit,
'leverage': leverage,
'percentage': undefined,
'marginMode': undefined,
'notional': undefined,
'markPrice': undefined,
'liquidationPrice': undefined,
'initialMargin': undefined,
'initialMarginPercentage': undefined,
'maintenanceMargin': this.parseNumber (marginCoeff),
'maintenanceMarginPercentage': undefined,
'marginRatio': undefined,
'info': position,
};
}
handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if ((httpCode === 418) || (httpCode === 429)) {
throw new DDoSProtection (this.id + ' ' + httpCode.toString () + ' ' + reason + ' ' + body);
}
// error response in a form: { "code": -1013, "msg": "Invalid quantity." }
// following block cointains legacy checks against message patterns in "msg" property
// will switch "code" checks eventually, when we know all of them
if (httpCode >= 400) {
if (body.indexOf ('Price * QTY is zero or less') >= 0) {
throw new InvalidOrder (this.id + ' order cost = amount * price is zero or less ' + body);
}
if (body.indexOf ('LOT_SIZE') >= 0) {
throw new InvalidOrder (this.id + ' order amount should be evenly divisible by lot size ' + body);
}
if (body.indexOf ('PRICE_FILTER') >= 0) {
throw new InvalidOrder (this.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid float value in general, use this.priceToPrecision (symbol, amount) ' + body);
}
}
if (response === undefined) {
return; // fallback to default error handler
}
//
// {"code":-1128,"msg":"Combination of optional parameters invalid."}
//
const errorCode = this.safeString (response, 'code');
if ((errorCode !== undefined) && (errorCode !== '0')) {
const feedback = this.id + ' ' + this.json (response);
this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback);
const message = this.safeString (response, 'msg');
this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
throw new ExchangeError (feedback);
}
}
};