consequunturatque
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,058 lines (1,031 loc) • 61.7 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, InvalidOrder, AuthenticationError, InsufficientFunds, BadSymbol, OrderNotFound, InvalidAddress, BadRequest } = require ('./base/errors');
const { TRUNCATE } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class gopax extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'gopax',
'name': 'GOPAX',
'countries': [ 'KR' ], // South Korea
'version': 'v1',
'rateLimit': 50,
'hostname': 'gopax.co.kr', // or 'gopax.com'
'certified': true,
'pro': true,
'has': {
'cancelOrder': true,
'createMarketOrder': true,
'createOrder': true,
'fetchBalance': true,
'fetchCurrencies': true,
'fetchDepositAddress': 'emulated',
'fetchDepositAddresses': true,
'fetchMarkets': true,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTransactions': true,
},
'timeframes': {
'1m': '1',
'5m': '5',
'30m': '30',
'1d': '1440',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/102897212-ae8a5e00-4478-11eb-9bab-91507c643900.jpg',
'api': {
'public': 'https://api.{hostname}', // or 'https://api.gopax.co.kr'
'private': 'https://api.{hostname}',
},
'www': 'https://www.gopax.co.kr',
'doc': 'https://gopax.github.io/API/index.en.html',
'fees': 'https://www.gopax.com/feeinfo',
},
'api': {
'public': {
'get': [
'notices',
'assets',
'price-tick-size',
'trading-pairs',
'trading-pairs/{tradingPair}/ticker',
'trading-pairs/{tradingPair}/book',
'trading-pairs/{tradingPair}/trades',
'trading-pairs/{tradingPair}/stats',
'trading-pairs/{tradingPair}/price-tick-size',
'trading-pairs/stats',
'trading-pairs/{tradingPair}/candles',
'time',
],
},
'private': {
'get': [
'balances',
'balances/{assetName}',
'orders',
'orders/{orderId}',
'orders/clientOrderId/{clientOrderId}',
'trades',
'deposit-withdrawal-status',
'crypto-deposit-addresses',
'crypto-withdrawal-addresses',
],
'post': [
'orders',
],
'delete': [
'orders/{orderId}',
'orders/clientOrderId/{clientOrderId}',
],
},
},
'fees': {
'trading': {
'percentage': true,
'tierBased': false,
'maker': 0.04 / 100,
'taker': 0.04 / 100,
},
},
'exceptions': {
'broad': {
'ERROR_INVALID_ORDER_TYPE': InvalidOrder,
'ERROR_INVALID_AMOUNT': InvalidOrder,
'ERROR_INVALID_TRADING_PAIR': BadSymbol, // Unlikely to be triggered, due to ccxt.gopax.js implementation
'No such order ID': OrderNotFound, // {"errorMessage":"No such order ID","errorCode":202,"errorData":"Order server error: 202"}
// 'Not enough amount': InsufficientFunds, // {"errorMessage":"Not enough amount, try increasing your order amount","errorCode":10212,"errorData":{}}
'Forbidden order type': InvalidOrder,
'the client order ID will be reusable which order has already been completed or canceled': InvalidOrder,
'ERROR_NO_SUCH_TRADING_PAIR': BadSymbol, // Unlikely to be triggered, due to ccxt.gopax.js implementation
'ERROR_INVALID_ORDER_SIDE': InvalidOrder,
'ERROR_NOT_HEDGE_TOKEN_USER': InvalidOrder,
'ORDER_EVENT_ERROR_NOT_ALLOWED_BID_ORDER': InvalidOrder, // Triggered only when the exchange is locked
'ORDER_EVENT_ERROR_INSUFFICIENT_BALANCE': InsufficientFunds,
'Invalid option combination': InvalidOrder,
'No such client order ID': OrderNotFound,
},
'exact': {
'100': BadSymbol, // Invalid asset name
'101': BadSymbol, // Invalid trading pair
'103': InvalidOrder, // Invalid order type
'104': BadSymbol, // Invalid trading pair
'105': BadSymbol, // Trading pair temporarily disabled
'106': BadSymbol, // Invalid asset name
'107': InvalidOrder, // Invalid order amount
'108': InvalidOrder, // Invalid order price
'111': InvalidOrder, // Invalid event type
'201': InsufficientFunds, // Not enough balance
'202': InvalidOrder, // Invalid order ID
'203': InvalidOrder, // Order amount X order price too large
'204': InvalidOrder, // Bid order temporarily unavailable
'205': InvalidOrder, // Invalid side
'206': InvalidOrder, // Invalid order option combination
'10004': AuthenticationError, // Not authorized
// '10004': ExchangeError, // API key not exist
// '10004': ExchangeError, // User KYC not approved
// '10004': ExchangeError, // User account is frozen
// '10004': ExchangeError, // User is under deactivation process
// '10004': ExchangeError, // 2FA is not enabled
// '10004': ExchangeError, // Invalid signature
'10041': BadRequest, // Invalid exchange
'10056': BadRequest, // No registered asset
'10057': BadSymbol, // No registered trading pair
'10059': BadSymbol, // Invalid trading pair
'10062': BadRequest, // Invalid chart interval
'10069': OrderNotFound, // {"errorMessage":"No such order ID: 73152094","errorCode":10069,"errorData":"73152094"}
'10155': AuthenticationError, // {"errorMessage":"Invalid API key","errorCode":10155}
'10166': BadRequest, // Invalid chart range
'10212': InvalidOrder, // {"errorMessage":"Not enough amount, try increasing your order amount","errorCode":10212,"errorData":{}}
'10221': OrderNotFound, // No such client order ID
'10222': InvalidOrder, // Client order ID being used
'10223': InvalidOrder, // Soon the client order ID will be reusable which order has already been completed or canceled
'10227': InvalidOrder, // Invalid client order ID format
'10319': BadRequest, // Pagination is required as you have too many orders
'10358': InvalidOrder, // Invalid order type
'10359': InvalidOrder, // Invalid order side
'10360': InvalidOrder, // Invalid order status
'10361': InvalidOrder, // Invalid order time in force
'10362': InvalidOrder, // Invalid order protection
'10363': InvalidOrder, // Invalid forced completion reason
},
},
'options': {
'createMarketBuyOrderRequiresPrice': true,
},
});
}
async fetchTime (params = {}) {
const response = await this.publicGetTime (params);
//
// {"serverTime":1608327726656}
//
return this.safeInteger (response, 'serverTime');
}
async fetchMarkets (params = {}) {
const response = await this.publicGetTradingPairs (params);
//
// [
// {
// "id":1,
// "name":"ETH-KRW",
// "baseAsset":"ETH",
// "quoteAsset":"KRW",
// "baseAssetScale":8,
// "quoteAssetScale":0,
// "priceMin":1,
// "restApiOrderAmountMin":{
// "limitAsk":{"amount":10000,"unit":"KRW"},
// "limitBid":{"amount":10000,"unit":"KRW"},
// "marketAsk":{"amount":0.001,"unit":"ETH"},
// "marketBid":{"amount":10000,"unit":"KRW"},
// },
// "makerFeePercent":0.2,
// "takerFeePercent":0.2,
// },
// ]
//
const result = [];
for (let i = 0; i < response.length; i++) {
const market = response[i];
const id = this.safeString (market, 'name');
const numericId = this.safeInteger (market, 'id');
const baseId = this.safeString (market, 'baseAsset');
const quoteId = this.safeString (market, 'quoteAsset');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const precision = {
'price': this.safeInteger (market, 'quoteAssetScale'),
'amount': this.safeInteger (market, 'baseAssetScale'),
};
const minimums = this.safeValue (market, 'restApiOrderAmountMin', {});
const marketAsk = this.safeValue (minimums, 'marketAsk', {});
const marketBid = this.safeValue (minimums, 'marketBid', {});
const takerFeePercentString = this.safeString (market, 'takerFeePercent');
const makerFeePercentString = this.safeString (market, 'makerFeePercent');
const taker = this.parseNumber (Precise.stringDiv (takerFeePercentString, '100'));
const maker = this.parseNumber (Precise.stringDiv (makerFeePercentString, '100'));
result.push ({
'id': id,
'info': market,
'numericId': numericId,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': this.safeString (market, 'baseAsset'),
'quoteId': this.safeString (market, 'quoteAsset'),
'active': true,
'taker': taker,
'maker': maker,
'precision': precision,
'limits': {
'amount': {
'min': this.safeNumber (marketAsk, 'amount'),
'max': undefined,
},
'price': {
'min': this.safeNumber (market, 'priceMin'),
'max': undefined,
},
'cost': {
'min': this.safeNumber (marketBid, 'amount'),
'max': undefined,
},
},
});
}
return result;
}
async fetchCurrencies (params = {}) {
const response = await this.publicGetAssets (params);
//
// [
// {
// "id":"KRW",
// "name":"대한민국 원",
// "scale":0,
// "withdrawalFee":1000,
// "withdrawalAmountMin":5000
// },
// {
// "id":"ETH",
// "name":"이더리움",
// "scale":8,
// "withdrawalFee":0.03,
// "withdrawalAmountMin":0.015
// },
// ]
//
const result = {};
for (let i = 0; i < response.length; i++) {
const currency = response[i];
const id = this.safeString (currency, 'id');
const code = this.safeCurrencyCode (id);
const name = this.safeString (currency, 'name');
const fee = this.safeNumber (currency, 'withdrawalFee');
const precision = this.safeNumber (currency, 'scale');
result[code] = {
'id': id,
'info': currency,
'code': code,
'name': name,
'active': true,
'fee': fee,
'precision': precision,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': this.safeNumber (currency, 'withdrawalAmountMin'),
'max': undefined,
},
},
};
}
return result;
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'tradingPair': market['id'],
// 'level': 3, // 1 best bidask, 2 top 50 bidasks, 3 all bidasks
};
const response = await this.publicGetTradingPairsTradingPairBook (this.extend (request, params));
//
// {
// "sequence":17691957,
// "bid":[
// ["17690499",25019000,0.00008904,"1608326468921"],
// ["17691894",25010000,0.4295,"1608326499940"],
// ["17691895",25009000,0.2359,"1608326499953"],
// ],
// "ask":[
// ["17689176",25024000,0.000098,"1608326442006"],
// ["17691351",25031000,0.206,"1608326490418"],
// ["17691571",25035000,0.3996,"1608326493742"],
// ]
// }
//
const nonce = this.safeInteger (response, 'sequence');
const result = this.parseOrderBook (response, symbol, undefined, 'bid', 'ask', 1, 2);
result['nonce'] = nonce;
return result;
}
parseTicker (ticker, market = undefined) {
//
// fetchTicker
//
// {
// "price":25087000,
// "ask":25107000,
// "askVolume":0.05837704,
// "bid":25087000,
// "bidVolume":0.00398628,
// "volume":350.09171591,
// "quoteVolume":8721016926.06529,
// "time":"2020-12-18T21:42:13.774Z",
// }
//
// fetchTickers
//
// {
// "name":"ETH-KRW",
// "open":690500,
// "high":719500,
// "low":681500,
// "close":709500,
// "volume":2784.6081544,
// "time":"2020-12-18T21:54:50.795Z"
// }
//
const marketId = this.safeString (ticker, 'name');
const symbol = this.safeSymbol (marketId, market, '-');
const timestamp = this.parse8601 (this.safeString (ticker, 'time'));
const open = this.safeNumber (ticker, 'open');
const last = this.safeNumber2 (ticker, 'price', 'close');
let change = undefined;
let percentage = undefined;
let average = undefined;
if ((last !== undefined) && (open !== undefined)) {
average = this.sum (last, open) / 2;
change = last - open;
if (open > 0) {
percentage = change / open * 100;
}
}
const baseVolume = this.safeNumber (ticker, 'volume');
const quoteVolume = this.safeNumber (ticker, 'quoteVolume');
const vwap = this.vwap (baseVolume, quoteVolume);
return {
'symbol': symbol,
'info': ticker,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeNumber (ticker, 'high'),
'low': this.safeNumber (ticker, 'low'),
'bid': this.safeNumber (ticker, 'bid'),
'bidVolume': this.safeNumber (ticker, 'bidVolume'),
'ask': this.safeNumber (ticker, 'ask'),
'askVolume': this.safeNumber (ticker, 'askVolume'),
'vwap': vwap,
'open': open,
'close': last,
'last': last,
'previousClose': undefined,
'change': change,
'percentage': percentage,
'average': average,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
};
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'tradingPair': market['id'],
};
const response = await this.publicGetTradingPairsTradingPairTicker (this.extend (request, params));
//
// {
// "price":25087000,
// "ask":25107000,
// "askVolume":0.05837704,
// "bid":25087000,
// "bidVolume":0.00398628,
// "volume":350.09171591,
// "quoteVolume":8721016926.06529,
// "time":"2020-12-18T21:42:13.774Z",
// }
//
return this.parseTicker (response, market);
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.publicGetTradingPairsStats (params);
//
// [
// {
// "name":"ETH-KRW",
// "open":690500,
// "high":719500,
// "low":681500,
// "close":709500,
// "volume":2784.6081544,
// "time":"2020-12-18T21:54:50.795Z"
// }
// ]
//
return this.parseTickers (response, symbols);
}
parsePublicTrade (trade, market = undefined) {
const timestamp = this.parse8601 (this.safeString (trade, 'time'));
const price = this.safeNumber (trade, 'price');
const amount = this.safeNumber (trade, 'amount');
let symbol = undefined;
if ('symbol' in market) {
symbol = this.safeString (market, 'symbol');
}
return {
'info': trade,
'id': this.safeString (trade, 'id'),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'order': undefined, // Not mandatory to specify
'type': undefined, // Not mandatory to specify
'side': this.safeString (trade, 'side'),
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': price * amount,
'fee': undefined,
};
}
parsePrivateTrade (trade, market = undefined) {
const timestamp = this.parse8601 (this.safeString (trade, 'timestamp'));
const symbol = this.safeString (trade, 'tradingPairName').replace ('-', '/');
const side = this.safeString (trade, 'side');
const price = this.safeNumber (trade, 'price');
const amount = this.safeNumber (trade, 'baseAmount');
let feeCurrency = symbol.slice (0, 3);
if (side === 'sell') {
feeCurrency = symbol.slice (4);
}
const fee = {
'cost': this.safeNumber (trade, 'fee'),
'currency': feeCurrency,
'rate': undefined,
};
return {
'info': trade,
'id': this.safeString (trade, 'id'),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'order': this.safeInteger (trade, 'orderId'),
'type': undefined,
'side': side,
'takerOrMaker': this.safeString (trade, 'position'),
'price': price,
'amount': amount,
'cost': price * amount,
'fee': fee,
};
}
parseTrade (trade, market = undefined) {
//
// public fetchTrades
//
// {
// "time":"2020-12-19T12:17:43.000Z",
// "date":1608380263,
// "id":23903608,
// "price":25155000,
// "amount":0.0505,
// "side":"sell",
// }
//
// private fetchMyTrades
//
// {
// "id": 73953, // trading event ID
// "orderId": 453324, // order ID
// "baseAmount": 3, // traded base asset amount
// "quoteAmount": 3000000, // traded quote asset amount
// "fee": 0.0012, // fee
// "price": 1000000, // price
// "timestamp": "2020-09-25T04:06:30.000Z", // trading time
// "side": "buy", // buy, sell
// "tradingPairName": "ZEC-KRW", // order book
// "position": "maker" // maker, taker
// }
//
// {
// "tradeId": 74072, // trade ID
// "orderId": 453529, // order ID
// "side": 2, // 1(bid), 2(ask)
// "type": 1, // 1(limit), 2(market)
// "baseAmount": 0.01, // filled base asset amount (in ZEC for this case)
// "quoteAmount": 1, // filled quote asset amount (in KRW for this case)
// "fee": 0.0004, // fee
// "price": 100, // price
// "isSelfTrade": false, // whether both of matching orders are yours
// "occurredAt": 1603932107, // trade occurrence time
// "tradingPairName": "ZEC-KRW" // order book
// }
//
const id = this.safeString2 (trade, 'id', 'tradeId');
const orderId = this.safeInteger (trade, 'orderId');
let timestamp = this.parse8601 (this.safeString2 (trade, 'time', 'timestamp'));
timestamp = this.safeTimestamp (trade, 'occuredAt', timestamp);
const marketId = this.safeString (trade, 'tradingPairName');
market = this.safeMarket (marketId, market, '-');
const symbol = market['symbol'];
let side = this.safeString (trade, 'side');
if (side === '1') {
side = 'buy';
} else if (side === '2') {
side = 'sell';
}
let type = this.safeString (trade, 'type');
if (type === '1') {
type = 'limit';
} else if (type === '2') {
type = 'market';
}
const priceString = this.safeString (trade, 'price');
const amountString = this.safeString2 (trade, 'amount', 'baseAmount');
const price = this.parseNumber (priceString);
const amount = this.parseNumber (amountString);
let cost = this.safeNumber (trade, 'quoteAmount');
if (cost === undefined) {
cost = this.parseNumber (Precise.stringMul (priceString, amountString));
}
const feeCost = this.safeNumber (trade, 'fee');
let fee = undefined;
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': market['base'],
};
}
const takerOrMaker = this.safeString (trade, 'position');
return {
'info': trade,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'order': orderId,
'type': undefined,
'side': side,
'takerOrMaker': takerOrMaker,
'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 = {
'tradingPair': market['id'],
// 'limit': limit,
// 'pastmax': id, // read data older than this ID
// 'latestmin': id, // read data newer than this ID
// 'after': parseInt (since / 1000),
// 'before': this.seconds (),
};
if (since !== undefined) {
request['after'] = parseInt (since / 1000);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetTradingPairsTradingPairTrades (this.extend (request, params));
//
// [
// {"time":"2020-12-19T12:17:43.000Z","date":1608380263,"id":23903608,"price":25155000,"amount":0.0505,"side":"sell"},
// {"time":"2020-12-19T12:17:13.000Z","date":1608380233,"id":23903604,"price":25140000,"amount":0.019,"side":"sell"},
// {"time":"2020-12-19T12:16:49.000Z","date":1608380209,"id":23903599,"price":25140000,"amount":0.0072,"side":"sell"},
// ]
//
return this.parseTrades (response, market, since, limit);
}
parseOHLCV (ohlcv, market = undefined) {
//
// [
// 1606780800000, // timestamp
// 21293000, // low
// 21300000, // high
// 21294000, // open
// 21300000, // close
// 1.019126, // volume
// ]
//
return [
this.safeInteger (ohlcv, 0),
this.safeNumber (ohlcv, 3),
this.safeNumber (ohlcv, 2),
this.safeNumber (ohlcv, 1),
this.safeNumber (ohlcv, 4),
this.safeNumber (ohlcv, 5),
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
limit = (limit === undefined) ? 1024 : limit; // default 1024
const request = {
'tradingPair': market['id'],
// 'start': since,
// 'end': this.milliseconds (),
'interval': this.timeframes[timeframe],
};
const duration = this.parseTimeframe (timeframe);
if (since === undefined) {
const end = this.milliseconds ();
request['end'] = end;
request['start'] = end - limit * duration * 1000;
} else {
request['start'] = since;
request['end'] = this.sum (since, limit * duration * 1000);
}
const response = await this.publicGetTradingPairsTradingPairCandles (this.extend (request, params));
//
// [
// [1606780800000,21293000,21300000,21294000,21300000,1.019126],
// [1606780860000,21237000,21293000,21293000,21263000,0.96800057],
// [1606780920000,21240000,21240000,21240000,21240000,0.11068715],
// ]
//
return this.parseOHLCVs (response, market, timeframe, since, limit);
}
parseBalanceResponse (response) {
const result = { 'info': response };
for (let i = 0; i < response.length; i++) {
const balance = response[i];
const currencyId = this.safeString2 (balance, 'asset', 'isoAlpha3');
const code = this.safeCurrencyCode (currencyId);
const hold = this.safeString (balance, 'hold');
const pendingWithdrawal = this.safeString (balance, 'pendingWithdrawal');
const account = this.account ();
account['free'] = this.safeString (balance, 'avail');
account['used'] = Precise.stringAdd (hold, pendingWithdrawal);
result[code] = account;
}
return this.parseBalance (result, false);
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privateGetBalances (params);
//
// [
// {
// "asset": "KRW", // asset name
// "avail": 1759466.76, // available amount to place order
// "hold": 16500, // outstanding amount on order books
// "pendingWithdrawal": 0, // amount being withdrawn
// "lastUpdatedAt": "1600684352032", // balance last update time
// },
// ]
//
return this.parseBalanceResponse (response);
}
parseOrderStatus (status) {
const statuses = {
'placed': 'open',
'cancelled': 'canceled',
'completed': 'closed',
'updated': 'open',
'reserved': 'open',
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
//
// cancelOrder
//
// {} // empty object
//
// fetchOrder, fetchOrders, fetchOpenOrders, createOrder
//
// {
// "id": "453324", // order ID
// "clientOrderId": "zeckrw23456", // client order ID (showed only when it exists)
// "status": "updated", // placed, cancelled, completed, updated, reserved
// "forcedCompletionReason": undefined, // the reason in case it was canceled in the middle (protection or timeInForce)
// "tradingPairName": "ZEC-KRW", // order book
// "side": "buy", // buy, sell
// "type": "limit", // limit, market
// "price": 1000000, // price
// "stopPrice": undefined, // stop price (showed only for stop orders)
// "amount": 4, // initial amount
// "remaining": 1, // outstanding amount
// "protection": "yes", // whether protection is activated (yes or no)
// "timeInForce": "gtc", // limit order's time in force (gtc/po/ioc/fok)
// "createdAt": "2020-09-25T04:06:20.000Z", // order placement time
// "updatedAt": "2020-09-25T04:06:29.000Z", // order last update time
// "balanceChange": {
// "baseGross": 3, // base asset balance's gross change (in ZEC for this case)
// "baseFee": {
// "taking": 0, // base asset fee imposed as taker
// "making": -0.0012 // base asset fee imposed as maker
// },
// "baseNet": 2.9988, // base asset balance's net change (in ZEC for this case)
// "quoteGross": -3000000, // quote asset balance's gross change (in KRW for
// "quoteFee": {
// "taking": 0, // quote asset fee imposed as taker
// "making": 0 // quote asset fee imposed as maker
// },
// "quoteNet": -3000000 // quote asset balance's net change (in KRW for this case)
// }
// }
//
const id = this.safeString (order, 'id');
const clientOrderId = this.safeString (order, 'clientOrderId');
const timestamp = this.parse8601 (this.safeString (order, 'createdAt'));
const type = this.safeString (order, 'type');
const side = this.safeString (order, 'side');
const timeInForce = this.safeStringUpper (order, 'timeInForce');
const price = this.safeNumber (order, 'price');
const amount = this.safeNumber (order, 'amount');
const stopPrice = this.safeNumber (order, 'stopPrice');
const remaining = this.safeNumber (order, 'remaining');
const marketId = this.safeString (order, 'tradingPairName');
market = this.safeMarket (marketId, market, '-');
const status = this.parseOrderStatus (this.safeString (order, 'status'));
const balanceChange = this.safeValue (order, 'balanceChange', {});
const filled = this.safeNumber (balanceChange, 'baseNet');
let cost = this.safeNumber (balanceChange, 'quoteNet');
if (cost !== undefined) {
cost = Math.abs (cost);
}
let updated = undefined;
if ((filled !== undefined) && (filled > 0)) {
updated = this.parse8601 (this.safeString (order, 'updatedAt'));
}
let fee = undefined;
if (side === 'buy') {
const baseFee = this.safeValue (balanceChange, 'baseFee', {});
const taking = this.safeNumber (baseFee, 'taking');
const making = this.safeNumber (baseFee, 'making');
fee = {
'currency': market['base'],
'cost': this.sum (taking, making),
};
} else {
const quoteFee = this.safeValue (balanceChange, 'quoteFee', {});
const taking = this.safeNumber (quoteFee, 'taking');
const making = this.safeNumber (quoteFee, 'making');
fee = {
'currency': market['quote'],
'cost': this.sum (taking, making),
};
}
let postOnly = undefined;
if (timeInForce !== undefined) {
postOnly = (timeInForce === 'PO');
}
return this.safeOrder ({
'id': id,
'clientOrderId': clientOrderId,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': updated,
'status': status,
'symbol': market['symbol'],
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
'average': undefined,
'amount': amount,
'filled': filled,
'remaining': remaining,
'cost': cost,
'trades': undefined,
'fee': fee,
'info': order,
});
}
async fetchOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
let method = undefined;
const clientOrderId = this.safeString (params, 'clientOrderId');
params = this.omit (params, 'clientOrderId');
const request = {};
if (clientOrderId === undefined) {
method = 'privateGetOrdersOrderId';
request['orderId'] = id;
} else {
method = 'privateGetOrdersClientOrderIdClientOrderId';
request['clientOrderId'] = clientOrderId;
}
const response = await this[method] (this.extend (request, params));
//
// {
// "id": "453324", // order ID
// "clientOrderId": "zeckrw23456", // client order ID (showed only when it exists)
// "status": "updated", // placed, cancelled, completed, updated, reserved
// "forcedCompletionReason": undefined, // the reason in case it was canceled in the middle (protection or timeInForce)
// "tradingPairName": "ZEC-KRW", // order book
// "side": "buy", // buy, sell
// "type": "limit", // limit, market
// "price": 1000000, // price
// "stopPrice": undefined, // stop price (showed only for stop orders)
// "amount": 4, // initial amount
// "remaining": 1, // outstanding amount
// "protection": "yes", // whether protection is activated (yes or no)
// "timeInForce": "gtc", // limit order's time in force (gtc/po/ioc/fok)
// "createdAt": "2020-09-25T04:06:20.000Z", // order placement time
// "updatedAt": "2020-09-25T04:06:29.000Z", // order last update time
// "balanceChange": {
// "baseGross": 3, // base asset balance's gross change (in ZEC for this case)
// "baseFee": {
// "taking": 0, // base asset fee imposed as taker
// "making": -0.0012 // base asset fee imposed as maker
// },
// "baseNet": 2.9988, // base asset balance's net change (in ZEC for this case)
// "quoteGross": -3000000, // quote asset balance's gross change (in KRW for
// "quoteFee": {
// "taking": 0, // quote asset fee imposed as taker
// "making": 0 // quote asset fee imposed as maker
// },
// "quoteNet": -3000000 // quote asset balance's net change (in KRW for this case)
// }
// }
//
return this.parseOrder (response);
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'includePast': 'true', // if true, completed and canceled orders are included as the result, they are accessible for one hour only from its completion or cancellation time
// 'pagination': 'false', // if the result is more than 3,000 orders, set this value as true to access 1000 orders at max per each page
};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
}
const response = await this.privateGetOrders (this.extend (request, params));
//
// [
// {
// "id": "453324", // order ID
// "clientOrderId": "zeckrw23456", // client order ID (showed only when it exists)
// "status": "updated", // placed, cancelled, completed, updated, reserved
// "forcedCompletionReason": undefined, // the reason in case it was canceled in the middle (protection or timeInForce)
// "tradingPairName": "ZEC-KRW", // order book
// "side": "buy", // buy, sell
// "type": "limit", // limit, market
// "price": 1000000, // price
// "stopPrice": undefined, // stop price (showed only for stop orders)
// "amount": 4, // initial amount
// "remaining": 1, // outstanding amount
// "protection": "yes", // whether protection is activated (yes or no)
// "timeInForce": "gtc", // limit order's time in force (gtc/po/ioc/fok)
// "createdAt": "2020-09-25T04:06:20.000Z", // order placement time
// "updatedAt": "2020-09-25T04:06:29.000Z", // order last update time
// "balanceChange": {
// "baseGross": 3, // base asset balance's gross change (in ZEC for this case)
// "baseFee": {
// "taking": 0, // base asset fee imposed as taker
// "making": -0.0012 // base asset fee imposed as maker
// },
// "baseNet": 2.9988, // base asset balance's net change (in ZEC for this case)
// "quoteGross": -3000000, // quote asset balance's gross change (in KRW for
// "quoteFee": {
// "taking": 0, // quote asset fee imposed as taker
// "making": 0 // quote asset fee imposed as maker
// },
// "quoteNet": -3000000 // quote asset balance's net change (in KRW for this case)
// }
// },
// ]
//
return this.parseOrders (response, market, since, limit);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
const request = {
'includePast': 'false',
};
return await this.fetchOrders (symbol, since, limit, this.extend (request, params));
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
// 'clientOrderId': 'test4321', // max 20 characters of [a-zA-Z0-9_-]
'tradingPairName': market['id'],
'side': side, // buy, sell
'type': type, // limit, market
// 'price': this.priceToPrecision (symbol, price),
// 'stopPrice': this.priceToPrecision (symbol, stopPrice), // optional, becomes a stop order if set
// 'amount': this.amountToPrecision (symbol, amount),
// 'protection': 'no', // whether protection is activated
// 'timeInForce': 'gtc', // gtc, po, ioc, fok
};
if (type === 'limit') {
request['price'] = this.priceToPrecision (symbol, price);
request['amount'] = this.amountToPrecision (symbol, amount);
} else if (type === 'market') {
// for market buy it requires the amount of quote currency to spend
if (side === 'buy') {
let total = amount;
const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true);
if (createMarketBuyOrderRequiresPrice) {
if (price === undefined) {
throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'amount' argument");
}
total = price * amount;
}
const precision = market['precision']['price'];
request['amount'] = this.decimalToPrecision (total, TRUNCATE, precision, this.precisionMode);
} else {
request['amount'] = this.amountToPrecision (symbol, amount);
}
}
const clientOrderId = this.safeString (params, 'clientOrderId');
if (clientOrderId !== undefined) {
request['clientOrderId'] = clientOrderId;
params = this.omit (params, 'clientOrderId');
}
const stopPrice = this.safeNumber (params, 'stopPrice');
if (stopPrice !== undefined) {
request['stopPrice'] = this.priceToPrecision (symbol, stopPrice);
params = this.omit (params, 'stopPrice');
}
const timeInForce = this.safeStringLower (params, 'timeInForce');
if (timeInForce !== undefined) {
request['timeInForce'] = timeInForce;
params = this.omit (params, 'timeInForce');
}
const response = await this.privatePostOrders (this.extend (request, params));
//
// {
// "id": "453327", // order ID
// "clientOrderId": "test4321", // client order ID (showed only when it exists)
// "status": "reserved", // placed, cancelled, completed, updated, reserved
// "forcedCompletionReason": undefined, // the reason in case it was canceled in the middle (protection or timeInForce)
// "tradingPairName": "BCH-KRW", // order book
// "side": "sell", // buy, sell
// "type": "limit", // limit, market
// "price": 11000000, // price
// "stopPrice": 12000000, // stop price (showed only for stop orders)
// "amount": 0.5, // initial amount
// "remaining": 0.5, // outstanding amount
// "protection": "no", // whether protection is activated (yes or no)
// "timeInForce": "gtc", // limit order's time in force (gtc/po/ioc/fok)
// "createdAt": "2020-09-25T04:51:31.000Z", // order placement time
// "balanceChange": {
// "baseGross": 0, // base asset balance's gross change (in BCH for this case)
// "baseFee": {
// "taking": 0, // base asset fee imposed as taker
// "making": 0 // base asset fee imposed as maker
// },
// "baseNet": 0, // base asset balance's net change (in BCH for this case)
// "quoteGross": 0, // quote asset balance's gross change (in KRW for
// "quoteFee": {
// "taking": 0, // quote asset fee imposed as taker
// "making": 0 // quote asset fee imposed as maker
// },
// "quoteNet": 0 // quote asset balance's net change (in KRW for this case)
// }
// }
//
return this.parseOrder (response, market);
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
const clientOrderId = this.safeString (params, 'clientOrderId');
let method = undefined;
if (clientOrderId === undefined) {
method = 'privateDeleteOrdersOrderId';
request['orderId'] = id;
} else {
method = 'privateDeleteOrdersClientOrderIdClientOrderId';
request['clientOrderId'] = clientOrderId;
params = this.omit (params, 'clientOrderId');
}
const response = await this[method] (this.extend (request, params));
//
// {}
//
const order = this.parseOrder (response);
return this.extend (order, { 'id': id });
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
// 'limit': limit, // max 100
// 'pastmax': id, // read data older than this id
// 'latestmin': id, // read data newer than this id
// 'after': parseInt (since / 1000), // Read data after this timestamp in seconds
// 'before': this.seconds (), // Read data before this timestamp in seconds
'deepSearch': 'true', // read data older than one month ago are inclusively looked up only when it is "true"
};
if (since !== undefined) {
request['after'] = parseInt (since / 1000);
}
if (li