kamiswiss-ccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,234 lines (1,207 loc) • 65.5 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, BadRequest, AuthenticationError, InvalidOrder, InsufficientFunds } = require ('./base/errors');
// ---------------------------------------------------------------------------
module.exports = class mandala extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'mandala',
'name': 'Mandala',
'countries': [ 'MT' ],
'version': 'v1.1',
'rateLimit': 1500,
'certified': false,
// new metainfo interface
'has': {
'cancelAllOrders': true,
'CORS': true,
'createDepositAddress': true,
'createMarketOrder': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDepositAddresses': true,
'fetchDeposits': true,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrders': true,
'fetchOrderStatus': true,
'fetchTickers': true,
'fetchWithdrawals': true,
'withdraw': true,
},
'timeframes': {
'1m': '1',
'5m': '5',
'1h': '60',
'1d': '1440',
},
'comment': 'Modulus Exchange API ',
'hostname': 'mandalaex.com',
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/54686665-df629400-4b2a-11e9-84d3-d88856367dd7.jpg',
'api': 'https://zapi.{hostname}',
'www': 'https://mandalaex.com',
'doc': [
'https://apidocs.mandalaex.com',
],
'fees': [
'https://mandalaex.com/trading-rules/',
],
'referral': 'https://trade.mandalaex.com/?ref=564377',
},
'api': {
'settings': {
'get': [
'getCoinInfo', // FIX ME, this endpoint is documented, but broken: https://zapi.mandalaex.com/api/getCoinInfo
'GetSettings',
'CurrencySettings',
'Get_Withdrawal_Limits',
],
},
'token': {
'post': [
'token',
],
},
'public': {
'post': [
'AuthenticateUser',
'ForgotPassword',
'SignUp',
'check_Duplicate_Mobile',
'check_Duplicate_Email',
],
},
'api': {
'get': [
'GAuth_Check_Status',
'GAuth_Enable_Request',
'GetProfile',
'Loginhistory',
'ListAllAddresses',
'Get_User_Withdrawal_Limits',
'GetPendingOrders', // ?side=aLL&pair=ALL×tamp=1541240408&recvWindow=3600',
'TradeHistory', // ?side=ALL&pair=ALL×tamp=1550920234&recvWindow=10000&count=100&page=1',
'GOKYC_Get_Kyc_Form',
'language_list',
'language', // ?code=en&namespace=translation',
'get_page_n_content',
'GetExchangeTokenDiscountEnrollmentStatus',
'GetDiscountTiers',
'My_Affiliate',
'Affiliate_Summary',
'Affiliate_Commission',
'List_Fiat_Manual_Deposit_Requests',
'List_Fiat_BanksList/YCN/',
'Get_Fiat_PGs', // ?Currency=TRY',
'get_insta_pairs',
'hmac', // ?side=BUY&market=BTC&trade=ETH&type=STOPLIMIT&volume=0.025&rate=0.032&timeInForce=GTC&stop=2&',
],
'post': [
'GAuth_Set_Enable',
'GAuth_Disable_Request',
'VerifyAccount',
'SignUp_Resend_Email',
'AuthenticateUser_Resend_EmailOTP/{tempAuthToken}',
'Validate_BearerToken',
'RequestChangePasswordOT',
'ChangePassword',
'ResetPassword',
'GenerateAddress',
'GetBalance',
'GetDeposits',
'GetWithdrawals',
'RequestWithdraw',
'RequestWithdrawConfirmation',
'RequestTransfer_AeraPass',
'PlaceOrder',
'PlaceOrder_Priced',
'CancelOrder',
'KYC_GetSumAndSub_AccessToken',
'KYC_SaveSumAndSubstanceApplicationId',
'GOKYC_Submit_KYC_Form',
'SetExchangeTokenDiscountEnrollment',
'Dis_Enroll_ExchangeTokenDiscount',
'Webhook_BitGoDeposit',
'Add_Fiat_Manual_Deposit_Request',
'Add_Fiat_Manual_Withdrawal_Request',
'Add_Fiat_PG_Deposit_Request',
'ListApiKey',
'GenerateApiKey',
'DeleteApiKey',
'request_insta_trade',
'confirm_insta_trade',
'simplex_get_quote',
'simplex_payment',
'hmac',
'import_translations',
],
},
'market': {
'get': [
'get-market-summary',
'get-market-summary/{marketId}',
'get-trade-history/{marketId}',
'get-bid_ask-price/{marketId}',
'get-open-orders/{marketId}/{side}/{depth}',
'get-currency-price/{marketId}',
'get-currency-usd-rate/{currencyId}',
'depth', // ?symbol=BTC_ETH&limit=10
'get-chart-data', // ?baseCurrency=BTC"eCurrency=ETH&interval=60&limit=200×tamp=1541228704517
],
},
'order': {
'get': [
'my-order-history/{key}/{side}',
'my-order-history/{key}/{side}/{orderId}',
'my-order-status/{key}/{side}/{orderId}',
'my-trade-history', // ?side=BUY&pair=BTC_ETH&orderID=13165837&apiKey=d14b1eb4-fe1f-4bfc-896d-97285975989e
'hmac', // ?side=BUY&market=BTC&trade=ETH&type=STOPLIMIT&volume=0.025&rate=0.032&timeInForce=GTC&stop=2&'
],
'post': [
'PlaceOrder',
'cancel-my-order',
'cancel-all-my-orders',
'get-balance',
],
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': 0.005,
'taker': 0.005,
},
},
'exceptions': {
'exact': {
'Failure_General': ExchangeError, // {"Status":"Error","Message":"Failure_General","Data":"Cannot roll back TransBuyOrder. No transaction or savepoint of that name was found."}
'Exception_Insufficient_Funds': InsufficientFunds, // {"Status":"Error","Message":"Exception_Insufficient_Funds","Data":"Insufficient Funds."}
'Exception_TimeStamp': BadRequest, // {"status":"BadRequest","message":"Exception_TimeStamp","data":"Invalid timestamp."}
'Exception_HMAC_Validation': AuthenticationError, // {"status":"Error","message":"Exception_HMAC_Validation","data":"HMAC validation failed."}
'Exception_General': BadRequest, // {"status":"BadRequest","message":"Exception_General","data":"Our servers are experiencing some glitch, please try again later."}
'Must provide the orderID param.': BadRequest, // {"Status":"BadRequest","Message":"Must provide the orderID param.","Data":null}
'Invalid Market_Currency pair!': ExchangeError, // {"status":"Error","errorMessage":"Invalid Market_Currency pair!","data":null}
'Invalid volume parameter.': InvalidOrder, // {"Status":"BadRequest","Message":"Invalid volume parameter.","Data":null}
'Invalid rate parameter.': InvalidOrder, // {"Status":"BadRequest","Message":"Invalid rate parameter.","Data":null}
"Invalid parameter 'side', must be 'BUY' or 'SELL'.": InvalidOrder, // {"Status":"BadRequest","Message":"Invalid parameter 'side', must be 'BUY' or 'SELL'.","Data":null}
'Invalid Type': BadRequest, // on fetchOrders with a wrong type {"status":"Error","errorMessage":"Invalid Type","data":null}
'Exception_Invalid_CurrencyName': BadRequest, // {"status":"BadRequest","message":"Exception_Invalid_CurrencyName","data":"Invalid Currency name"}
'Exception_BadRequest': BadRequest, // {"status":"BadRequest","message":"Exception_BadRequest","data":"Invalid Payload"}
},
'broad': {
},
},
'options': {
'symbolSeparator': '_',
'api': {
'settings': 'api',
'public': 'api',
},
'fetchCurrencies': {
'expires': 5000,
},
// https://documenter.getpostman.com/view/5614390/RWguuvfd#a74ee943-3b7a-415e-9315-a7bf204db09d
// HMAC can be obtained using a Secret key. Thispre shared secret key ensures that the message is encrypted by a legitimate source. You can get a secret key issued for your sandbox enviroment by writing an email to support@modulus.io
// Secret-Key : 03c06dd7-4982-441a-910d-5fd2cbb3f1c6
'secret': '03c06dd7-4982-441a-910d-5fd2cbb3f1c6',
},
});
}
async signIn (params = {}) {
if (!this.login || !this.password) {
throw new AuthenticationError (this.id + ' signIn() requires this.login (email) and this.password credentials');
}
const authenticateRequest = {
'email': this.login,
'password': this.password,
};
const authenticateResponse = await this.publicPostAuthenticateUser (authenticateRequest);
//
// {
// status: 'Success',
// message: 'Success!',
// data: {
// tempAuthToken: 'e1b0603a-5996-4bac-9ec4-f097a02d9696',
// tokenExpiry: '2019-03-19T21:16:15.999201Z',
// twoFAMehtod: 'GAuth'
// }
// }
//
const data = this.safeValue (authenticateResponse, 'data', {});
const tempAuthToken = this.safeString (data, 'tempAuthToken');
let otp = undefined;
if (this.twofa !== undefined) {
otp = this.oath ();
}
otp = this.safeString (params, 'password', otp);
if (otp === undefined) {
throw new AuthenticationError (this.id + ' signIn() requires this.twofa credential or a one-time 2FA "password" parameter');
}
const tokenRequest = {
'grant_type': 'password',
'username': tempAuthToken,
'password': otp,
};
const tokenResponse = await this.tokenPostToken (this.extend (tokenRequest, params));
//
// {
// "access_token": "WWRNCO--bFjX3zKAixROAjy3dbU0csNoI91PXpT1oScTrik50mVrSIbr22HrsJV5ATXgN867vy66pxY7IzMQGzYtz-7KTxUnL6uPbQpiveBgPEGD5drpvh5KwhcCOzFelJ1-OxZa6g6trx82x2YqQI7Lny0VkAIEv-EBQT8B4C_UVYhoMVCzYumeQgcxtyXc9hoRolVUwwQ965--LrAYIybBby85LzRRIfh7Yg_CVSx6zehAcHFUeKh2tE4NwN9lYweeDEPb6z2kHn0UJb18nxYcC3-NjgiyublBiY1AI_U",
// "token_type": "bearer",
// "expires_in": 86399
// }
//
const expiresIn = this.safeInteger (tokenResponse, 'expires_in');
this.options['expires'] = this.sum (this.milliseconds (), expiresIn * 1000);
this.options['accessToken'] = this.safeString (tokenResponse, 'accessToken');
this.options['tokenType'] = this.safeString (tokenResponse, 'token_type');
// const accessToken = this.safeValue (tokenResponse, 'access_token');
// this.headers['Authorization'] = 'Bearer ' + accessToken;
return tokenResponse;
}
async fetchCurrenciesFromCache (params = {}) {
// this method is now redundant
// currencies are now fetched before markets
const options = this.safeValue (this.options, 'fetchCurrencies', {});
const timestamp = this.safeInteger (options, 'timestamp');
const expires = this.safeInteger (options, 'expires', 1000);
const now = this.milliseconds ();
if ((timestamp === undefined) || ((now - timestamp) > expires)) {
const response = await this.settingsGetCurrencySettings (params);
this.options['fetchCurrencies'] = this.extend (options, {
'response': response,
'timestamp': now,
});
}
return this.safeValue (this.options['fetchCurrencies'], 'response');
}
async fetchCurrencies (params = {}) {
const response = await this.fetchCurrenciesFromCache (params);
this.options['currencies'] = {
'timestamp': this.milliseconds (),
'response': response,
};
//
// {
// status: 'Success',
// message: 'Success!',
// data: [
// {
// shortName: 'BAT',
// fullName: 'Basic Attention Token',
// buyServiceCharge: 0.5,
// sellServiceCharge: 0.5,
// withdrawalServiceCharge: 0.25,
// withdrawalServiceChargeInBTC: 0,
// confirmationCount: 29,
// contractAddress: null,
// minWithdrawalLimit: 100,
// maxWithdrawalLimit: 2000000,
// decimalPrecision: 18,
// tradeEnabled: true,
// depositEnabled: true,
// withdrawalEnabled: true,
// secondaryWalletType: '',
// addressSeparator: '',
// walletType: 'BitGo',
// withdrawalServiceChargeType: 'Percentage',
// },
// {
// shortName: 'BCH',
// fullName: 'BitcoinCash',
// buyServiceCharge: 0.5,
// sellServiceCharge: 0.5,
// withdrawalServiceCharge: 0.25,
// withdrawalServiceChargeInBTC: 0.001,
// confirmationCount: 3,
// contractAddress: null,
// minWithdrawalLimit: 0.1,
// maxWithdrawalLimit: 300,
// decimalPrecision: 8,
// tradeEnabled: true,
// depositEnabled: true,
// withdrawalEnabled: true,
// secondaryWalletType: '',
// addressSeparator: '',
// walletType: 'BitGo',
// withdrawalServiceChargeType: 'Percentage',
// },
// ]
// }
//
const data = this.safeValue (response, 'data', []);
const result = {};
for (let i = 0; i < data.length; i++) {
const currency = data[i];
const id = this.safeString (currency, 'shortName');
const code = this.commonCurrencyCode (id);
const name = this.safeString (currency, 'fullName');
const precision = this.safeInteger (currency, 'decimalPrecision');
let active = true;
const canWithdraw = this.safeValue (currency, 'withdrawalEnabled');
const canDeposit = this.safeValue (currency, 'depositEnabled');
if (!canWithdraw || !canDeposit) {
active = false;
}
result[code] = {
'id': id,
'code': code,
'name': name,
'active': active,
'precision': precision,
'fee': this.safeFloat (currency, 'withdrawalServiceCharge') / 100,
'limits': {
'amount': {
'min': Math.pow (10, -precision),
'max': Math.pow (10, precision),
},
'price': {
'min': Math.pow (10, -precision),
'max': Math.pow (10, precision),
},
'cost': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': this.safeFloat (currency, 'minWithdrawalLimit'),
'max': this.safeFloat (currency, 'maxWithdrawalLimit'),
},
},
'info': currency,
};
}
return result;
}
async fetchMarkets (params = {}) {
const currenciesResponse = await this.fetchCurrenciesFromCache (params);
const currencies = this.safeValue (currenciesResponse, 'data', []);
const currenciesById = this.indexBy (currencies, 'shortName');
const response = await this.marketGetGetMarketSummary ();
//
// {
// status: 'Success',
// errorMessage: null,
// data: {
// BTC_BAT:
// Last: 0.00003431,
// LowestAsk: 0,
// HeighestBid: 0,
// PercentChange: 0,
// BaseVolume: 0,
// QuoteVolume: 0,
// High_24hr: 0,
// Low_24hr: 0,
// },
// ETH_ZRX: {
// Last: 0.00213827,
// LowestAsk: 0,
// HeighestBid: 0,
// PercentChange: 0,
// BaseVolume: 0,
// QuoteVolume: 0,
// High_24hr: 0,
// Low_24hr: 0,
// },
// },
// }
//
const result = [];
const data = this.safeValue (response, 'data', {});
const ids = Object.keys (data);
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
const market = data[id];
const [ quoteId, baseId ] = id.split ('_'); // they have base/quote reversed with some endpoints
const base = this.commonCurrencyCode (baseId);
const quote = this.commonCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const baseCurrency = this.safeValue (currenciesById, baseId, {});
const quoteCurrency = this.safeValue (currenciesById, quoteId, {});
const precision = {
'amount': this.safeInteger (baseCurrency, 'decimalPrecision', 8),
'price': this.safeInteger (quoteCurrency, 'decimalPrecision', 8),
};
const baseTradeEnabled = this.safeValue (baseCurrency, 'tradeEnabled', true);
const quoteTradeEnabled = this.safeValue (quoteCurrency, 'tradeEnabled', true);
const active = baseTradeEnabled && quoteTradeEnabled;
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': active,
'info': market,
'precision': precision,
'limits': {
'amount': {
'min': Math.pow (10, -precision['amount']),
'max': undefined,
},
'price': {
'min': Math.pow (10, -precision['price']),
'max': undefined,
},
},
});
}
return result;
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const request = {
'currency': 'ALL',
};
const response = await this.orderPostGetBalance (this.extend (request, params));
//
// {
// Status: 'Success',
// Message: null,
// Data: [
// { currency: 'BCH', balance: 0, balanceInTrade: 0 },
// { currency: 'BTC', balance: 0, balanceInTrade: 0 },
// ...,
// ],
// }
//
const data = this.safeValue (response, 'Data');
const result = { 'info': response };
for (let i = 0; i < data.length; i++) {
const balance = data[i];
const code = this.commonCurrencyCode (this.safeString (balance, 'currency'));
const account = this.account ();
const free = this.safeFloat (balance, 'balance', 0);
const used = this.safeFloat (balance, 'balanceInTrade', 0);
const total = this.sum (free, used);
account['free'] = free;
account['used'] = used;
account['total'] = total;
result[code] = account;
}
return this.parseBalance (result);
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
if (limit === undefined) {
limit = 10;
}
const request = {
'symbol': this.marketId (symbol),
'limit': limit,
};
const response = await this.marketGetDepth (this.extend (request, params));
// https://documenter.getpostman.com/view/6273708/RznBP1Hh#19469d73-45b5-4dd1-8464-c043efb62e00
//
// {
// status: 'Success',
// errorMessage: '',
// data: {
// lastUpdate: 1552825727108,
// bids: [
// [ "0.02880201", "0.05939008", []],
// [ "0.02880200", "0.30969842", []],
// ],
// 'asks': [
// [ "0.02877161", "0.00001779", []],
// [ "0.02881321", "0.47325696", []],
// ],
// },
// }
//
const data = this.safeValue (response, 'data', {});
const timestamp = this.safeInteger (data, 'lastUpdate');
return this.parseOrderBook (data, timestamp);
}
parseTicker (ticker, market = undefined) {
//
// fetchTicker, fetchTickers
// {
// Pair: 'ETH_MDX', // FIXME missing in fetchTickers
// Last: 0.000055,
// LowestAsk: 0.000049,
// HeighestBid: 0.00003,
// PercentChange: 12.47,
// BaseVolume: 34.60345,
// QuoteVolume: 629153.63636364,
// IsFrozen: false, // FIXME missing in fetchTickers
// High_24hr: 0,
// Low_24hr: 0
// }
//
let symbol = undefined;
const marketId = this.safeString (ticker, 'Pair');
if (marketId !== undefined) {
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
symbol = market['symbol'];
} else {
symbol = this.parseSymbol (marketId);
}
}
if (symbol === undefined) {
if (market !== undefined) {
symbol = market['symbol'];
}
}
const last = this.safeFloat (ticker, 'Last');
return {
'symbol': symbol,
'timestamp': undefined, // FIXME, no timestamp in tickers
'datetime': undefined,
'high': this.safeFloat (ticker, 'High_24hr'),
'low': this.safeFloat (ticker, 'Low_24hr'),
'bid': this.safeFloat (ticker, 'HeighestBid'),
'bidVolume': undefined,
'ask': this.safeFloat (ticker, 'LowestAsk'),
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': this.safeFloat (ticker, 'PercentChange'),
'average': undefined,
'baseVolume': this.safeFloat (ticker, 'QuoteVolume'),
'quoteVolume': this.safeFloat (ticker, 'BaseVolume'),
'info': ticker,
};
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.marketGetGetMarketSummary (params);
//
// {
// status: 'Success',
// errorMessage: null,
// data: {
// BTC_BAT: {
// Last: 0.00003431,
// LowestAsk: 0,
// HeighestBid: 0,
// PercentChange: 0,
// BaseVolume: 0,
// QuoteVolume: 0,
// High_24hr: 0,
// Low_24hr: 0,
// },
// ETH_ZRX: {
// Last: 0.00213827,
// LowestAsk: 0,
// HeighestBid: 0,
// PercentChange: 0,
// BaseVolume: 0,
// QuoteVolume: 0,
// High_24hr: 0,
// Low_24hr: 0,
// },
// },
// }
//
const data = this.safeValue (response, 'data', {});
const ids = Object.keys (data);
const result = {};
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
const ticker = data[id];
let market = undefined;
let symbol = id;
if (id in this.markets_by_id) {
market = this.markets_by_id[id];
symbol = market['symbol'];
} else {
symbol = this.parseSymbol (id);
}
result[symbol] = this.parseTicker (ticker, market);
}
return result;
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const request = {
'marketId': this.marketId (symbol),
};
const response = await this.marketGetGetMarketSummaryMarketId (this.extend (request, params));
//
// {
// status: 'Success',
// errorMessage: null,
// data: {
// Pair: 'ETH_MDX',
// Last: 0.000055,
// LowestAsk: 0.000049,
// HeighestBid: 0.00003,
// PercentChange: 12.47,
// BaseVolume: 34.60345,
// QuoteVolume: 629153.63636364,
// IsFrozen: false,
// High_24hr: 0,
// Low_24hr: 0
// }
// }
//
const data = this.safeValue (response, 'data');
return this.parseTicker (data);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
//
// {
// TradeID: 619255,
// Rate: 0.000055,
// Volume: 79163.63636364,
// Total: 4.354,
// Date: "2019-03-16T23:14:48.613",
// Type: "Buy"
// }
//
// fetchMyTrades (private)
//
// {
// orderId: 20000040,
// market: 'ETH',
// trade: 'MDX',
// volume: 1,
// rate: 2,
// amount: 2,
// serviceCharge: 0.003,
// side: 'SELL',
// date: '2019-03-20T01:47:09.14'
// }
//
const timestamp = this.parse8601 (this.safeString2 (trade, 'Date', 'date'));
let side = this.safeString2 (trade, 'Type', 'side');
if (side !== undefined) {
side = side.toLowerCase ();
}
const id = this.safeString (trade, 'TradeID');
let symbol = undefined;
const baseId = this.safeString (trade, 'trade');
const quoteId = this.safeString (trade, 'market');
const base = this.commonCurrencyCode (baseId);
const quote = this.commonCurrencyCode (quoteId);
if (base !== undefined && quote !== undefined) {
symbol = base + '/' + quote;
} else {
if (market !== undefined) {
symbol = market['symbol'];
}
}
const cost = this.safeFloat2 (trade, 'Total', 'amount');
const price = this.safeFloat2 (trade, 'Rate', 'rate');
const amount = this.safeFloat2 (trade, 'Volume', 'volume');
const orderId = this.safeString (trade, 'orderId');
const feeCost = this.safeValue (trade, 'serviceCharge');
let fee = undefined;
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': quote,
};
}
return {
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'id': id,
'order': orderId,
'type': undefined,
'takerOrMaker': undefined,
'side': side,
'price': price,
'amount': amount,
'cost': cost,
'fee': fee,
};
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'marketId': market['id'],
};
// this endpoint returns last 50 trades
const response = await this.marketGetGetTradeHistoryMarketId (this.extend (request, params));
//
// {
// status: "Success",
// errorMessage: null,
// data: [
// {
// TradeID: 619255,
// Rate: 0.000055,
// Volume: 79163.63636364,
// Total: 4.354,
// Date: "2019-03-16T23:14:48.613",
// Type: "Buy"
// },
// {
// TradeID: 619206,
// Rate: 0.000073,
// Volume: 7635.50136986,
// Total: 0.5573916,
// Date: "2019-02-13T16:49:54.02",
// Type: "Sell"
// }
// ]
// }
//
const data = this.safeValue (response, 'data');
return this.parseTrades (data, market, since, limit);
}
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
//
// {
// time: 1552830600000,
// open: 0.000055,
// close: 0.000055,
// high: 0.000055,
// low: 0.000055,
// volume: 0,
// }
//
return [
this.safeInteger (ohlcv, 'time'),
this.safeFloat (ohlcv, 'open'),
this.safeFloat (ohlcv, 'high'),
this.safeFloat (ohlcv, 'low'),
this.safeFloat (ohlcv, 'close'),
this.safeFloat (ohlcv, 'volume'),
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
if (limit === undefined) {
limit = 100; // default is 100
}
const offset = this.parseTimeframe (timeframe) * this.sum (limit, 1) * 1000;
if (since === undefined) {
since = this.milliseconds () - offset;
}
const request = {
'interval': this.timeframes[timeframe],
'baseCurrency': market['baseId'], // they have base/quote reversed with some endpoints
'quoteCurrency': market['quoteId'],
'limit': limit,
'timestamp': this.sum (since, offset),
};
const response = await this.marketGetGetChartData (this.extend (request, params));
//
// {
// status: 'Success',
// errorMessage: null,
// data: [
// {
// time: 1552830600000,
// open: 0.000055,
// close: 0.000055,
// high: 0.000055,
// low: 0.000055,
// volume: 0,
// },
// {
// time: 1552830540000,
// open: 0.000055,
// close: 0.000055,
// high: 0.000055,
// low: 0.000055,
// volume: 0,
// },
// ],
// }
//
const data = this.safeValue (response, 'data');
return this.parseOHLCVs (data, market, timeframe, since, limit);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
let orderPrice = price;
if (type === 'market') {
orderPrice = 0;
}
const request = {
'market': market['quoteId'],
'trade': market['baseId'],
'type': type.toUpperCase (), // MARKET, LIMIT, STOPLIMIT
'side': side.toUpperCase (), // BUY, SELL
// Here GTC should be default for LIMIT, MARKET & STOP LIMIT Orders.
// IOC,FOK, DO must be passed only with a LIMIT order.
// GTC (Good till cancelled), IOC (Immediate or cancel), FOK (Fill or Kill), Do (Day only)
'timeInForce': 'GTC',
'rate': this.priceToPrecision (symbol, orderPrice),
'volume': this.amountToPrecision (symbol, amount),
// the stop-price at which a stop-limit order
// triggers and becomes a limit order
'stop': 0, // stop is always zero for limit and market orders
// 'clientOrderId': this.uuid (),
};
const response = await this.orderPostPlaceOrder (this.extend (request, params));
//
// {
// Status: 'Success',
// Message: 'Success!',
// Data: {
// orderId: 20000031,
// },
// }
//
const data = this.safeValue (response, 'Data', {});
const order = this.parseOrder (data, market);
return this.extend (order, {
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'amount': amount,
'status': 'open',
});
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const side = this.safeString (params, 'side');
if (side === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder() requires an order `side` extra parameter');
}
params = this.omit (params, 'side');
id = id.toString ();
const request = {
'orderId': id,
'side': side.toUpperCase (),
};
const response = await this.orderPostCancelMyOrder (this.extend (request, params));
//
// {
// Status: 'Success',
// Message: 'Success_General',
// Data: 'Success!'
// }
//
return this.parseOrder (response, {
'id': id,
'symbol': symbol,
'side': side.toLowerCase (),
'status': 'canceled',
});
}
async cancelAllOrders (symbols = undefined, params = {}) {
const side = this.safeString (params, 'side');
if (side === undefined) {
throw new ArgumentsRequired (this.id + ' cancelAllOrders() requires an order `side` extra parameter');
}
params = this.omit (params, 'side');
if (symbols === undefined) {
throw new ArgumentsRequired (this.id + ' cancelAllOrders() requires a `symbols` argument (a list containing one symbol)');
} else {
const numSymbols = symbols.length;
if (numSymbols !== 1) {
throw new ArgumentsRequired (this.id + ' cancelAllOrders() requires a `symbols` argument (a list containing one symbol)');
}
}
const symbol = symbols[0];
const request = {
'side': side.toUpperCase (),
'pair': this.marketId (symbol),
};
return await this.orderPostCancelAllMyOrders (this.extend (request, params));
}
parseSymbol (id) {
let [ quote, base ] = id.split (this.options['symbolSeparator']);
base = this.commonCurrencyCode (base);
quote = this.commonCurrencyCode (quote);
return base + '/' + quote;
}
parseOrder (order, market = undefined) {
//
// fetchOrders
//
// {
// orderId: 20000038,
// market: 'BTC',
// trade: 'ETH',
// volume: 1,
// pendingVolume: 1,
// orderStatus: false,
// rate: 1,
// amount: 1,
// serviceCharge: 0,
// placementDate: '2019-03-19T18:28:43.553',
// completionDate: null
// }
//
// fetchOpenOrders
//
// {
// orderId: 20000038,
// market: 'BTC',
// trade: 'ETH',
// volume: 1,
// rate: 1,
// side: 'SELL',
// date: '2019-03-19T18:28:43.553',
// }
//
// fetchOrderStatus
//
// {
// "PendingVolume": 0.7368974,
// "Volume": 0.7368974,
// "Price": 0.22921771,
// "Status": true
// }
//
const id = this.safeString (order, 'orderId');
const baseId = this.safeString (order, 'trade');
const quoteId = this.safeString (order, 'market');
const base = this.commonCurrencyCode (baseId);
const quote = this.commonCurrencyCode (quoteId);
let symbol = undefined;
if (base !== undefined && quote !== undefined) {
symbol = base + '/' + quote;
}
const completionDate = this.parse8601 (this.safeString (order, 'completionDate'));
const timestamp = this.parse8601 (this.safeString2 (order, 'placementDate', 'date'));
let price = this.safeFloat2 (order, 'rate', 'Price');
const amount = this.safeFloat2 (order, 'volume', 'Volume');
let cost = this.safeFloat (order, 'amount');
const remaining = this.safeFloat2 (order, 'pendingVolume', 'PendingVolume');
let filled = undefined;
if (amount !== undefined && remaining !== undefined) {
filled = Math.max (amount - remaining, 0);
}
if (!cost) {
if (price && filled) {
cost = price * filled;
}
}
if (!price) {
if (cost && filled) {
price = cost / filled;
}
}
let status = this.safeValue2 (order, 'orderStatus', 'Status');
status = status ? 'closed' : 'open';
let lastTradeTimestamp = undefined;
if (filled > 0) {
lastTradeTimestamp = completionDate;
}
if ((filled !== undefined) && (amount !== undefined)) {
if ((filled < amount) && (status === 'closed')) {
status = 'canceled';
}
}
const feeCost = this.safeValue (order, 'serviceCharge');
let fee = undefined;
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': quote,
};
}
return {
'info': order,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
'symbol': symbol,
'type': 'limit',
'side': undefined,
'price': price,
'cost': cost,
'average': undefined,
'amount': amount,
'filled': filled,
'remaining': remaining,
'status': status,
'fee': fee,
};
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const side = this.safeString (params, 'side');
if (side === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrders() requires an order `side` extra parameter');
}
params = this.omit (params, 'side');
const request = {
'key': this.apiKey,
'side': side.toUpperCase (),
// 'orderId': id,
};
const response = await this.orderGetMyOrderHistoryKeySide (this.extend (request, params));
//
// {
// status: 'Success',
// errorMessage: null,
// data: [
// {
// orderId: 20000038,
// market: 'BTC',
// trade: 'ETH',
// volume: 1,
// pendingVolume: 1,
// orderStatus: false,
// rate: 1,
// amount: 1,
// serviceCharge: 0,
// placementDate: '2019-03-19T18:28:43.553',
// completionDate: null
// },
// {
// orderId: 20000037,
// market: 'BTC',
// trade: 'ETH',
// volume: 1,
// pendingVolume: 1,
// orderStatus: true,
// rate: 1,
// amount: 1,
// serviceCharge: 0,
// placementDate: '2019-03-19T18:27:51.087',
// completionDate: '2019-03-19T18:28:16.07'
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
const market = (symbol !== undefined) ? this.market (symbol) : undefined;
return this.parseOrders (data, market, since, limit, {
'side': side.toLowerCase (),
});
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const side = this.safeString (params, 'side', 'ALL');
params = this.omit (params, 'side');
let market = undefined;
let pair = 'ALL';
if (symbol !== undefined) {
market = this.market (symbol);
pair = market['baseId'] + '-' + market['quoteId'];
}
const request = {
'side': side.toUpperCase (),
'pair': pair,
};
const response = await this.apiGetGetPendingOrders (this.extend (request, params));
//
// {
// status: 'Success',
// message: 'Success!',
// data: [
// {
// orderId: 20000038,
// market: 'BTC',
// trade: 'ETH',
// volume: 1,
// rate: 1,
// side: 'SELL',
// date: '2019-03-19T18:28:43.553',
// },
// {
// orderId: 20000039,
// market: 'BTC',
// trade: 'ETH',
// volume: 1,
// rate: 2,
// side: 'SELL',
// date: '2019-03-19T18:48:12.033',
// }
// ]
// }
//
const data = this.safeValue (response, 'data');
return this.parseOrders (data, market, since, limit);
}
async fetchOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
let side = this.safeString (params, 'side');
if (side === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder() requires an order `side` extra parameter');
}
params = this.omit (params, 'side');
id = id.toString ();
const request = {
'key': this.apiKey,
'side': side.toUpperCase (),
'orderId': id,
};
const response = await this.orderGetMyOrderStatusKeySideOrderId (this.extend (request, params));
//
// {
// "status": "Success",
// "errorMessage": null,
// "data": {
// "PendingVolume": 0.7368974,
// "Volume": 0.7368974,
// "Price": 0.22921771,
// "Status": true
// }
// }
//
const data = this.safeValue (response, 'data');
return this.extend (this.parseOrder (data), {
'id': id,
'side': side.toLowerCase (),
});
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const side = this.safeString (params, 'side', 'ALL');
params = this.omit (params, 'side');
let market = undefined;
let pair = 'ALL';
if (symbol !== undefined) {
market = this.market (symbol);
pair = market['id'];
}
const request = {
'side': side.toUpperCase (),
'pair': pair,
'orderID': -1,
'apiKey': this.apiKey,
};
const response = await this.orderGetMyTradeHistory (this.extend (request, params));
//
// {
// Status: 'Success',
// Message: null,
// Data: [
// {
// orderId: 20000040,
// market: 'ETH',
// trade: 'MDX',
// volume: 1,
// rate: 2,
// amount: 2,
// serviceCharge: 0.003,
// side: 'SELL',
// date: '2019-03-20T01:47:09.14'
// },
// {
// orderId: 20000041,
// market: 'ETH',
// trade: 'MDX',
// volume: 0.5,
// rate: 3,
// amount: 1.5,
// serviceCharge: 0.00225,
// side: 'SELL',
// date: '2019-03-20T01:49:20.42'
// },
// {
// orderId: 20000041,
// market: 'ETH',
// trade: 'MDX',
// volume: 0.25,
// rate: 3,