sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,218 lines (1,195 loc) • 105 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, InvalidAddress, DuplicateOrderId, ArgumentsRequired, InsufficientFunds, InvalidOrder, InvalidNonce, AuthenticationError, RateLimitExceeded, PermissionDenied, BadRequest, BadSymbol } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class lbank2 extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'lbank2',
'name': 'LBank',
'countries': [ 'CN' ],
'version': 'v2',
// 50 per second for making and cancelling orders 1000ms / 50 = 20
// 20 per second for all other requests, cost = 50 / 20 = 2.5
'rateLimit': 20,
'has': {
'CORS': false,
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'addMargin': false,
'cancelAllOrders': true,
'cancelOrder': true,
'createOrder': true,
'createReduceOnlyOrder': false,
'createStopLimitOrder': false,
'createStopMarketOrder': false,
'createStopOrder': false,
'fetchBalance': true,
'fetchBorrowRate': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchBorrowRates': false,
'fetchBorrowRatesPerSymbol': false,
'fetchClosedOrders': false,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchIsolatedPositions': false,
'fetchLeverage': false,
'fetchLeverageTiers': false,
'fetchMarginMode': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchPosition': false,
'fetchPositionMode': false,
'fetchPositions': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTrades': true,
'fetchTradingFees': true,
'fetchTransactionFees': true,
'reduceMargin': false,
'setLeverage': false,
'setMarginMode': false,
'setPositionMode': false,
'withdraw': false,
},
'timeframes': {
'1m': 'minute1',
'5m': 'minute5',
'15m': 'minute15',
'30m': 'minute30',
'1h': 'hour1',
'2h': 'hour2',
'4h': 'hour4',
'6h': 'hour6',
'8h': 'hour8',
'12h': 'hour12',
'1d': 'day1',
'1w': 'week1',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/38063602-9605e28a-3302-11e8-81be-64b1e53c4cfb.jpg',
'api': {
'rest': 'https://api.lbank.info',
},
'api2': 'https://api.lbkex.com',
'www': 'https://www.lbank.info',
'doc': 'https://www.lbank.info/en-US/docs/index.html',
'fees': 'https://lbankinfo.zendesk.com/hc/en-gb/articles/360012072873-Trading-Fees',
'referral': 'https://www.lbank.info/invitevip?icode=7QCY',
},
'api': {
'public': {
'get': {
'currencyPairs': 2.5,
'accuracy': 2.5,
'usdToCny': 2.5,
'withdrawConfigs': 2.5,
'timestamp': 2.5,
'ticker/24hr': 2.5,
'ticker': 2.5,
'depth': 2.5,
'incrDepth': 2.5,
'trades': 2.5,
'kline': 2.5,
// new quote endpoints
'supplement/system_ping': 2.5,
'supplement/incrDepth': 2.5,
'supplement/trades': 2.5,
'supplement/ticker/price': 2.5,
'supplement/ticker/bookTicker': 2.5,
},
'post': {
'supplement/system_status': 2.5,
},
},
'private': {
'post': {
// account
'user_info': 2.5,
'subscribe/get_key': 2.5,
'subscribe/refresh_key': 2.5,
'subscribe/destroy_key': 2.5,
'get_deposit_address': 2.5,
'deposit_history': 2.5,
// order
'create_order': 1,
'batch_create_order': 1,
'cancel_order': 1,
'cancel_clientOrders': 1,
'orders_info': 2.5,
'orders_info_history': 2.5,
'order_transaction_detail': 2.5,
'transaction_history': 2.5,
'orders_info_no_deal': 2.5,
// withdraw
'withdraw': 2.5,
'withdrawCancel': 2.5,
'withdraws': 2.5,
'supplement/user_info': 2.5,
'supplement/withdraw': 2.5,
'supplement/deposit_history': 2.5,
'supplement/withdraws': 2.5,
'supplement/get_deposit_address': 2.5,
'supplement/asset_detail': 2.5,
'supplement/customer_trade_fee': 2.5,
'supplement/api_Restrictions': 2.5,
// new quote endpoints
'supplement/system_ping': 2.5,
// new order endpoints
'supplement/create_order_test': 1,
'supplement/create_order': 1,
'supplement/cancel_order': 1,
'supplement/cancel_order_by_symbol': 1,
'supplement/orders_info': 2.5,
'supplement/orders_info_no_deal': 2.5,
'supplement/orders_info_history': 2.5,
'supplement/user_info_account': 2.5,
'supplement/transaction_history': 2.5,
},
},
},
'fees': {
'trading': {
'maker': this.parseNumber ('0.001'),
'taker': this.parseNumber ('0.001'),
},
'funding': {
'withdraw': {},
},
},
'commonCurrencies': {
'VET_ERC20': 'VEN',
'PNT': 'Penta',
},
'precisionMode': TICK_SIZE,
'options': {
'cacheSecretAsPem': true,
'createMarketBuyOrderRequiresPrice': true,
'fetchTrades': {
'method': 'publicGetTrades', // or 'publicGetTradesSupplement'
},
'fetchTransactionFees': { // DEPRECATED, please use fetchDepositWithdrawFees
'method': 'fetchPrivateTransactionFees', // or 'fetchPublicTransactionFees'
},
'fetchDepositWithdrawFees': {
'method': 'fetchPrivateDepositWithdrawFees', // or 'fetchPublicDepositWithdrawFees'
},
'fetchDepositAddress': {
'method': 'fetchDepositAddressDefault', // or fetchDepositAddressSupplement
},
'createOrder': {
'method': 'privatePostSupplementCreateOrder', // or privatePostCreateOrder
},
'fetchOrder': {
'method': 'fetchOrderSupplement', // or fetchOrderDefault
},
'fetchBalance': {
'method': 'privatePostSupplementUserInfo', // or privatePostSupplementUserInfoAccount or privatePostUserInfo
},
'networks': {
'ERC20': 'erc20',
'ETH': 'erc20',
'TRC20': 'trc20',
'TRX': 'trc20',
'OMNI': 'omni',
'ASA': 'asa',
'BEP20': 'bep20(bsc)',
'BSC': 'bep20(bsc)',
'HT': 'heco',
'BNB': 'bep2',
'BTC': 'btc',
'DOGE': 'dogecoin',
'MATIC': 'matic',
'POLYGON': 'matic',
'OEC': 'oec',
'BTCTRON': 'btctron',
'XRP': 'xrp',
// other unusual chains with number of listed currencies supported
// 'avax c-chain': 1,
// klay: 12,
// bta: 1,
// fantom: 1,
// celo: 1,
// sol: 2,
// zenith: 1,
// ftm: 5,
// bep20: 1, (single token with mis-named chain) SSS
// bitci: 1,
// sgb: 1,
// moonbeam: 1,
// ekta: 1,
// etl: 1,
// arbitrum: 1,
// tpc: 1,
// ptx: 1
// }
},
'inverse-networks': {
'erc20': 'ERC20',
'trc20': 'TRC20',
'omni': 'OMNI',
'asa': 'ASA',
'bep20(bsc)': 'BSC',
'bep20': 'BSC',
'heco': 'HT',
'bep2': 'BNB',
'btc': 'BTC',
'dogecoin': 'DOGE',
'matic': 'MATIC',
'oec': 'OEC',
'btctron': 'BTCTRON',
'xrp': 'XRP',
},
'defaultNetworks': {
'USDT': 'TRC20',
},
},
});
}
async fetchMarkets (params = {}) {
/**
* @method
* @name lbank2#fetchMarkets
* @description retrieves data on all markets for lbank2
* @param {object} params extra parameters specific to the exchange api endpoint
* @returns {[object]} an array of objects representing market data
*/
// needs to return a list of unified market structures
const response = await this.publicGetAccuracy ();
const data = this.safeValue (response, 'data');
// [
// {
// symbol: 'snx3s_usdt',
// quantityAccuracy: '2',
// minTranQua: '0.01',
// priceAccuracy: '6'
// }
// ]
const result = [];
for (let i = 0; i < data.length; i++) {
const market = data[i];
const marketId = this.safeString (market, 'symbol');
const parts = marketId.split ('_');
const baseId = parts[0];
const quoteId = parts[1];
const base = baseId.toUpperCase ();
const quote = quoteId.toUpperCase ();
let symbol = base + '/' + quote;
const productTypes = {
'3l': true,
'5l': true,
'3s': true,
'5s': true,
};
const ending = baseId.slice (-2);
const isLeveragedProduct = this.safeValue (productTypes, ending, false);
if (isLeveragedProduct) {
symbol += ':' + quote;
}
let linear = undefined;
if (isLeveragedProduct === true) {
linear = true;
}
result.push ({
'id': marketId,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': false,
'swap': isLeveragedProduct,
'future': false,
'option': false,
'active': true,
'contract': isLeveragedProduct,
'linear': linear, // all leveraged ETF products are in USDT
'inverse': undefined,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'quantityAccuracy'))),
'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'priceAccuracy'))),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber (market, 'minTranQua'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': market,
});
}
return result;
}
parseTicker (ticker, market = undefined) {
//
// {
// "symbol":"btc_usdt",
// "ticker": {
// "high":40200.88,
// "vol":7508.3096,
// "low":38239.38,
// "change":0.75,
// "turnover":292962771.34,
// "latest":39577.95
// },
// "timestamp":1647005189792
// }
//
const marketId = this.safeString (ticker, 'symbol');
const symbol = this.safeSymbol (marketId, market);
const timestamp = this.safeInteger (ticker, 'timestamp');
const tickerData = this.safeValue (ticker, 'ticker');
return this.safeTicker ({
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeString (tickerData, 'high'),
'low': this.safeString (tickerData, 'low'),
'bid': undefined,
'bidVolume': undefined,
'ask': undefined,
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'close': undefined,
'last': this.safeString (tickerData, 'latest'),
'previousClose': undefined,
'change': undefined,
'percentage': this.safeString (tickerData, 'change'),
'average': undefined,
'baseVolume': this.safeString (tickerData, 'vol'),
'quoteVolume': this.safeString (tickerData, 'turnover'),
'info': ticker,
}, market);
}
async fetchTicker (symbol, params = {}) {
/**
* @method
* @name lbank2#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} params extra parameters specific to the lbank2 api endpoint
* @returns {object} 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.publicGetTicker24hr (this.extend (request, params));
//
// {
// "result":"true",
// "data": [
// {
// "symbol":"btc_usdt",
// "ticker":{
// "high":40200.88,
// "vol":7508.3096,
// "low":38239.38,
// "change":0.75,
// "turnover":292962771.34,
// "latest":39577.95
// },
// "timestamp":1647005189792
// }
// ],
// "error_code":0,"ts":1647005190755
// }
//
const data = this.safeValue (response, 'data', []);
const first = this.safeValue (data, 0, {});
return this.parseTicker (first, market);
}
async fetchTickers (symbols = undefined, params = {}) {
/**
* @method
* @name lbank2#fetchTickers
* @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
* @param {[string]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {object} params extra parameters specific to the lbank api endpoint
* @returns {object} an array of [ticker structures]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
await this.loadMarkets ();
const request = {
'symbol': 'all',
};
const response = await this.publicGetTicker24hr (this.extend (request, params));
const data = this.safeValue (response, 'data', []);
return this.parseTickers (data, symbols);
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
/**
* @method
* @name lbank2#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @param {string} 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 {object} params extra parameters specific to the lbank2 api endpoint
* @returns {object} 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);
if (limit === undefined) {
limit = 60;
}
const request = {
'symbol': market['id'],
'size': limit,
};
const response = await this.publicGetDepth (this.extend (request, params));
const orderbook = response['data'];
const timestamp = this.milliseconds ();
return this.parseOrderBook (orderbook, market['symbol'], timestamp);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (old) publicGetTrades
//
// {
// "date_ms":1647021989789,
// "amount":0.0028,
// "price":38804.2,
// "type":"buy",
// "tid":"52d5616ee35c43019edddebe59b3e094"
// }
//
//
// fetchTrades (new) publicGetTradesSupplement
//
// {
// "quoteQty":1675.048485,
// "price":0.127545,
// "qty":13133,
// "id":"3589541dc22e4357b227283650f714e2",
// "time":1648058297110,
// "isBuyerMaker":false
// }
//
// fetchMyTrades (private)
//
// {
// "orderUuid":"38b4e7a4-14f6-45fd-aba1-1a37024124a0",
// "tradeFeeRate":0.0010000000,
// "dealTime":1648500944496,
// "dealQuantity":30.00000000000000000000,
// "tradeFee":0.00453300000000000000,
// "txUuid":"11f3850cc6214ea3b495adad3a032794",
// "dealPrice":0.15111300000000000000,
// "dealVolumePrice":4.53339000000000000000,
// "tradeType":"sell_market"
// }
//
let timestamp = this.safeInteger2 (trade, 'date_ms', 'time');
if (timestamp === undefined) {
timestamp = this.safeInteger (trade, 'dealTime');
}
let amountString = this.safeString2 (trade, 'amount', 'qty');
if (amountString === undefined) {
amountString = this.safeString (trade, 'dealQuantity');
}
let priceString = this.safeString (trade, 'price');
if (priceString === undefined) {
priceString = this.safeString (trade, 'dealPrice');
}
let costString = this.safeString (trade, 'quoteQty');
if (costString === undefined) {
costString = this.safeString (trade, 'dealVolumePrice');
}
let side = this.safeString2 (trade, 'tradeType', 'type');
let type = undefined;
let takerOrMaker = undefined;
if (side !== undefined) {
const parts = side.split ('_');
side = this.safeString (parts, 0);
const typePart = this.safeString (parts, 1);
type = 'limit';
takerOrMaker = 'taker';
if (typePart !== undefined) {
if (typePart === 'market') {
type = 'market';
} else if (typePart === 'maker') {
takerOrMaker = 'maker';
}
}
}
let id = this.safeString2 (trade, 'tid', 'id');
if (id === undefined) {
id = this.safeString (trade, 'txUuid');
}
const order = this.safeString (trade, 'orderUuid');
const symbol = this.safeSymbol (undefined, market);
let fee = undefined;
const feeCost = this.safeString (trade, 'tradeFee');
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': undefined,
'rate': this.safeString (trade, 'tradeFeeRate'),
};
}
return this.safeTrade ({
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'id': id,
'order': order,
'type': type,
'takerOrMaker': takerOrMaker,
'side': side,
'price': priceString,
'amount': amountString,
'cost': costString,
'fee': fee,
'info': trade,
}, market);
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name lbank2#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @param {string} 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 {object} params extra parameters specific to the lbank2 api endpoint
* @returns {[object]} 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'],
};
if (since !== undefined) {
request['time'] = since;
}
if (limit !== undefined) {
request['size'] = limit;
} else {
request['size'] = 600; // max
}
let method = this.safeString (params, 'method');
params = this.omit (params, 'method');
if (method === undefined) {
const options = this.safeValue (this.options, 'fetchTrades', {});
method = this.safeString (options, 'method', 'publicGetTrades');
}
const response = await this[method] (this.extend (request, params));
//
// {
// "result":"true",
// "data": [
// {
// "date_ms":1647021989789,
// "amount":0.0028,
// "price":38804.2,
// "type":"buy",
// "tid":"52d5616ee35c43019edddebe59b3e094"
// }
// ],
// "error_code":0,
// "ts":1647021999308
// }
const trades = this.safeValue (response, 'data', []);
return this.parseTrades (trades, market, since, limit);
}
parseOHLCV (ohlcv, market = undefined) {
//
// [
// 1482311500, // timestamp
// 5423.23, // open
// 5472.80, // high
// 5516.09, // low
// 5462, // close
// 234.3250 // volume
// ],
//
return [
this.safeTimestamp (ohlcv, 0), // timestamp
this.safeNumber (ohlcv, 1), // open
this.safeNumber (ohlcv, 2), // high
this.safeNumber (ohlcv, 3), // low
this.safeNumber (ohlcv, 4), // close
this.safeNumber (ohlcv, 5), // volume
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name lbank2#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int|undefined} since timestamp in ms of the earliest candle to fetch
* @param {int|undefined} limit the maximum amount of candles to fetch
* @param {object} params extra parameters specific to the lbank2 api endpoint
* @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
// endpoint doesnt work
await this.loadMarkets ();
const market = this.market (symbol);
if (limit === undefined) {
limit = 100;
}
if (since === undefined) {
const duration = this.parseTimeframe (timeframe);
since = this.milliseconds () - duration * 1000 * limit;
}
const request = {
'symbol': market['id'],
'type': this.timeframes[timeframe],
'time': parseInt (since / 1000),
'size': limit, // max 2000
};
const response = await this.publicGetKline (this.extend (request, params));
const ohlcvs = this.safeValue (response, 'data', []);
//
//
// [
// [
// 1482311500,
// 5423.23,
// 5472.80,
// 5516.09,
// 5462,
// 234.3250
// ],
// [
// 1482311400,
// 5432.52,
// 5459.87,
// 5414.30,
// 5428.23,
// 213.7329
// ]
// ]
//
return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit);
}
parseBalance (response) {
//
// privatePostUserInfo
//
// {
// "toBtc": {
// "egc:": "0",
// "iog": "0",
// "ksm": "0",
// },
// "freeze": {
// "egc": "0",
// "iog": "0",
// "ksm": "0" ,
// },
// "asset": {
// "egc": "0",
// "iog": "0",
// "ksm": "0",
// },
// "free": {
// "egc": "0",
// "iog": "0",
// "ksm": "0",
// }
// }
//
// privatePostSupplementUserInfoAccount
//
// {
// "balances":[
// {
// "asset":"lbk",
// "free":"0",
// "locked":"0"
// }, ...
// ]
// }
//
// privatePostSupplementUserInfo
//
// [
// {
// "usableAmt":"31.45130723",
// "assetAmt":"31.45130723",
// "networkList":[
// {
// "isDefault":true,
// "withdrawFeeRate":"",
// "name":"bep20(bsc)",
// "withdrawMin":30,
// "minLimit":0.0001,
// "minDeposit":0.0001,
// "feeAssetCode":"doge",
// "withdrawFee":"30",
// "type":1,
// "coin":"doge",
// "network":"bsc"
// },
// {
// "isDefault":false,
// "withdrawFeeRate":"",
// "name":"dogecoin",
// "withdrawMin":10,
// "minLimit":0.0001,
// "minDeposit":10,
// "feeAssetCode":"doge",
// "withdrawFee":"10",
// "type":1,
// "coin":"doge",
// "network":"dogecoin"
// }
// ],
// "freezeAmt":"0",
// "coin":"doge"
// }, ...
// ]
//
const timestamp = this.safeInteger (response, 'ts');
const result = {
'info': response,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
};
const data = this.safeValue (response, 'data');
// from privatePostUserInfo
const toBtc = this.safeValue (data, 'toBtc');
if (toBtc !== undefined) {
const used = this.safeValue (data, 'freeze', {});
const free = this.safeValue (data, 'free', {});
const currencies = Object.keys (free);
for (let i = 0; i < currencies.length; i++) {
const currencyId = currencies[i];
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['used'] = this.safeString (used, currencyId);
account['free'] = this.safeString (free, currencyId);
result[code] = account;
}
return this.safeBalance (result);
}
// from privatePostSupplementUserInfoAccount
const balances = this.safeValue (data, 'balances');
if (balances !== undefined) {
for (let i = 0; i < balances.length; i++) {
const item = balances[i];
const currencyId = this.safeString (item, 'asset');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeString (item, 'free');
account['used'] = this.safeString (item, 'locked');
result[code] = account;
}
return this.safeBalance (result);
}
// from privatePostSupplementUserInfo
const isArray = Array.isArray (data);
if (isArray === true) {
for (let i = 0; i < data.length; i++) {
const item = data[i];
const currencyId = this.safeString (item, 'coin');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeString (item, 'usableAmt');
account['used'] = this.safeString (item, 'freezeAmt');
result[code] = account;
}
return this.safeBalance (result);
}
}
async fetchBalance (params = {}) {
/**
* @method
* @name lbank2#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @param {object} params extra parameters specific to the lbank2 api endpoint
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
*/
await this.loadMarkets ();
let method = this.safeString (params, 'method');
if (method === undefined) {
const options = this.safeValue (this.options, 'fetchBalance', {});
method = this.safeString (options, 'method', 'privatePostSupplementUserInfo');
}
const response = await this[method] ();
//
// {
// "result": "true",
// "data": [
// {
// "usableAmt": "14.36",
// "assetAmt": "14.36",
// "networkList": [
// {
// "isDefault": false,
// "withdrawFeeRate": "",
// "name": "erc20",
// "withdrawMin": 30,
// "minLimit": 0.0001,
// "minDeposit": 20,
// "feeAssetCode": "usdt",
// "withdrawFee": "30",
// "type": 1,
// "coin": "usdt",
// "network": "eth"
// },
// ...
// ],
// "freezeAmt": "0",
// "coin": "ada"
// }
// ],
// "code": 0
// }
//
return this.parseBalance (response);
}
parseTradingFee (fee, market = undefined) {
//
// {
// "symbol":"skt_usdt",
// "makerCommission":"0.10",
// "takerCommission":"0.10"
// }
//
const marketId = this.safeString (fee, 'symbol');
const symbol = this.safeSymbol (marketId);
return {
'info': fee,
'symbol': symbol,
'maker': this.safeNumber (fee, 'makerCommission'),
'taker': this.safeNumber (fee, 'takerCommission'),
};
}
async fetchTradingFee (symbol, params = {}) {
/**
* @method
* @name lbank2#fetchTradingFee
* @description fetch the trading fees for a market
* @param {string} symbol unified market symbol
* @param {object} params extra parameters specific to the lbank2 api endpoint
* @returns {object} a [fee structure]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure}
*/
const market = this.market (symbol);
const result = await this.fetchTradingFees (this.extend (params, { 'category': market['id'] }));
return result;
}
async fetchTradingFees (params = {}) {
/**
* @method
* @name lbank2#fetchTradingFees
* @description fetch the trading fees for multiple markets
* @param {object} params extra parameters specific to the lbank2 api endpoint
* @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure} indexed by market symbols
*/
await this.loadMarkets ();
const request = {};
const response = await this.privatePostSupplementCustomerTradeFee (this.extend (request, params));
const fees = this.safeValue (response, 'data', []);
const result = {};
for (let i = 0; i < fees.length; i++) {
const fee = this.parseTradingFee (fees[i]);
const symbol = fee['symbol'];
result[symbol] = fee;
}
return result;
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
/**
* @method
* @name lbank2#createOrder
* @description create a trade order
* @param {string} symbol unified symbol of the market to create an order in
* @param {string} type 'market' or 'limit'
* @param {string} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float|undefined} price the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
* @param {object} params extra parameters specific to the lbank2 api endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const clientOrderId = this.safeString2 (params, 'custom_id', 'clientOrderId');
const postOnly = this.safeValue (params, 'postOnly', false);
const timeInForce = this.safeStringUpper (params, 'timeInForce');
params = this.omit (params, [ 'custom_id', 'clientOrderId', 'timeInForce', 'postOnly' ]);
const request = {
'symbol': market['id'],
};
const ioc = (timeInForce === 'IOC');
const fok = (timeInForce === 'FOK');
const maker = (postOnly || (timeInForce === 'PO'));
if ((type === 'market') && (ioc || fok || maker)) {
throw new InvalidOrder (this.id + ' createOrder () does not allow market FOK, IOC, or postOnly orders. Only limit IOC, FOK, and postOnly orders are allowed');
}
if (type === 'limit') {
request['type'] = side;
request['price'] = this.priceToPrecision (symbol, price);
request['amount'] = this.amountToPrecision (symbol, amount);
if (ioc) {
request['type'] = side + '_' + 'ioc';
} else if (fok) {
request['type'] = side + '_' + 'fok';
} else if (maker) {
request['type'] = side + '_' + 'maker';
}
} else if (type === 'market') {
if (side === 'sell') {
request['type'] = side + '_' + 'market';
request['amount'] = this.amountToPrecision (symbol, amount);
} else if (side === 'buy') {
request['type'] = side + '_' + 'market';
if (this.options['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 the 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 to supply the cost in the amount argument (the exchange-specific behaviour)");
} else {
const amountString = this.numberToString (amount);
const priceString = this.numberToString (price);
const quoteAmount = Precise.stringMul (amountString, priceString);
const cost = this.parseNumber (quoteAmount);
request['price'] = this.priceToPrecision (symbol, cost);
}
} else {
request['price'] = amount;
}
}
}
if (clientOrderId !== undefined) {
request['custom_id'] = clientOrderId;
}
let method = undefined;
method = this.safeString (params, 'method');
params = this.omit (params, 'method');
if (method === undefined) {
const options = this.safeValue (this.options, 'createOrder', {});
method = this.safeString (options, 'method', 'privatePostSupplementCreateOrder');
}
const response = await this[method] (this.extend (request, params));
//
// {
// "result":true,
// "data":{
// "symbol":"doge_usdt",
// "order_id":"0cf8a3de-4597-4296-af45-be7abaa06b07"
// },
// "error_code":0,
// "ts":1648162321043
// }
//
const result = this.safeValue (response, 'data', {});
return {
'id': this.safeString (result, 'order_id'),
'info': result,
};
}
parseOrderStatus (status) {
const statuses = {
'-1': 'canceled', // canceled
'0': 'open', // not traded
'1': 'open', // partial deal
'2': 'closed', // complete deal
'3': 'canceled', // filled partially and cancelled
'4': 'closed', // disposal processing
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
//
// fetchOrderSupplement (private)
//
// {
// "cummulativeQuoteQty":0,
// "symbol":"doge_usdt",
// "executedQty":0,
// "orderId":"53d2d53e-70fb-4398-b722-f48571a5f61e",
// "origQty":1E+2,
// "price":0.05,
// "clientOrderId":null,
// "origQuoteOrderQty":5,
// "updateTime":1648163406000,
// "time":1648163139387,
// "type":"buy_maker",
// "status":-1
// }
//
//
// fetchOrderDefault (private)
//
// {
// "symbol":"shib_usdt",
// "amount":1,
// "create_time":1649367863356,
// "price":0.0000246103,
// "avg_price":0.00002466180000000104,
// "type":"buy_market",
// "order_id":"abe8b92d-86d9-4d6d-b71e-d14f5fb53ddf",
// "custom_id": "007", // field only present if user creates it at order time
// "deal_amount":40548.54065802,
// "status":2
// }
//
// fetchOpenOrders (private)
//
// {
// "cummulativeQuoteQty":0,
// "symbol":"doge_usdt",
// "executedQty":0,
// "orderId":"73878edf-008d-4e4c-8041-df1f1b2cd8bb",
// "origQty":100,
// "price":0.05,
// "origQuoteOrderQty":5,
// "updateTime":1648501762000,
// "time":1648501762353,
// "type":"buy",
// "status":0
// }
//
// fetchOrders (private)
//
// {
// "cummulativeQuoteQty":0,
// "symbol":"doge_usdt",
// "executedQty":0,
// "orderId":"2cadc7cc-b5f6-486b-a5b4-d6ac49a9c186",
// "origQty":100,
// "price":0.05,
// "origQuoteOrderQty":5,
// "updateTime":1648501384000,
// "time":1648501363889,
// "type":"buy",
// "status":-1
// }
//
const id = this.safeString2 (order, 'orderId', 'order_id');
const clientOrderId = this.safeString2 (order, 'clientOrderId', 'custom_id');
const timestamp = this.safeInteger2 (order, 'time', 'create_time');
const rawStatus = this.safeString (order, 'status');
const marketId = this.safeString (order, 'symbol');
market = this.safeMarket (marketId, market);
let timeInForce = undefined;
let postOnly = false;
let type = 'limit';
const rawType = this.safeString (order, 'type'); // buy, sell, buy_market, sell_market, buy_maker,sell_maker,buy_ioc,sell_ioc, buy_fok, sell_fok
const parts = rawType.split ('_');
const side = this.safeString (parts, 0);
const typePart = this.safeString (parts, 1); // market, maker, ioc, fok or undefined (limit)
if (typePart === 'market') {
type = 'market';
}
if (typePart === 'maker') {
postOnly = true;
timeInForce = 'PO';
}
if (typePart === 'ioc') {
timeInForce = 'IOC';
}
if (typePart === 'fok') {
timeInForce = 'FOK';
}
const price = this.safeString (order, 'price');
const costString = this.safeString (order, 'cummulativeQuoteQty');
let amountString = undefined;
if (rawType !== 'buy_market') {
amountString = this.safeString2 (order, 'origQty', 'amount');
}
const filledString = this.safeString2 (order, 'executedQty', 'deal_amount');
return this.safeOrder ({
'id': id,
'clientOrderId': clientOrderId,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
'status': this.parseOrderStatus (rawStatus),
'symbol': market['symbol'],
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': undefined,
'cost': costString,
'amount': amountString,
'filled': filledString,
'remaining': undefined,
'trades': undefined,
'fee': undefined,
'info': order,
'average': undefined,
}, market);
}
async fetchOrder (id, symbol = undefined, params = {}) {
/**
* @method
* @name lbank2#fetchOrder
* @description fetches information on an order made by the user
* @param {string|undefined} symbol unified symbol of the market the order was made in
* @param {object} params extra parameters specific to the lbank2 api endpoint
* @returns {object} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
await this.loadMarkets ();
let method = this.safeString (params, 'method');
if (method === undefined) {
const options = this.safeValue (this.options, 'fetchOrder', {});
method = this.safeString (options, 'method', 'fetchOrderSupplement');
}
const result = await this[method] (id, symbol, params);
return result;
}
async fetchOrderSupplement (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder () requires a symbol argument');
}
const market = this.market (symbol);
const request = {
'symbol': market['id'],
'orderId': id,
};
const response = await this.privatePostSupplementOrdersInfo (this.extend (request, params));
//
// {
// "result":true,
// "data":{
// "cummulativeQuoteQty":0,
// "symbol":"doge_usdt",
//