@proton/ccxt
Version:
A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 130+ exchanges
468 lines (465 loc) • 17.2 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import bitrueRest from '../bitrue.js';
import { ArrayCacheBySymbolById } from '../base/ws/Cache.js';
import { ArgumentsRequired } from '../base/errors.js';
// ---------------------------------------------------------------------------
export default class bitrue extends bitrueRest {
describe() {
return this.deepExtend(super.describe(), {
'has': {
'ws': true,
'watchBalance': true,
'watchTicker': false,
'watchTickers': false,
'watchTrades': false,
'watchMyTrades': false,
'watchOrders': true,
'watchOrderBook': true,
'watchOHLCV': false,
},
'urls': {
'api': {
'open': 'https://open.bitrue.com',
'ws': {
'public': 'wss://ws.bitrue.com/market/ws',
'private': 'wss://wsapi.bitrue.com',
},
},
},
'api': {
'open': {
'private': {
'post': {
'poseidon/api/v1/listenKey': 1,
},
'put': {
'poseidon/api/v1/listenKey/{listenKey}': 1,
},
'delete': {
'poseidon/api/v1/listenKey/{listenKey}': 1,
},
},
},
},
'options': {
'listenKeyRefreshRate': 1800000,
'ws': {
'gunzip': true,
},
},
});
}
async watchBalance(params = {}) {
/**
* @method
* @name bitrue#watchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#balance-update
* @param {object} params extra parameters specific to the bitrue api endpoint
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
*/
const url = await this.authenticate();
const messageHash = 'balance';
const message = {
'event': 'sub',
'params': {
'channel': 'user_balance_update',
},
};
const request = this.deepExtend(message, params);
return await this.watch(url, messageHash, request, messageHash);
}
handleBalance(client, message) {
//
// {
// e: 'BALANCE',
// x: 'OutboundAccountPositionTradeEvent',
// E: 1657799510175,
// I: '302274978401288200',
// i: 1657799510175,
// B: [{
// a: 'btc',
// F: '0.0006000000000000',
// T: 1657799510000,
// f: '0.0006000000000000',
// t: 0
// },
// {
// a: 'usdt',
// T: 0,
// L: '0.0000000000000000',
// l: '-11.8705317318000000',
// t: 1657799510000
// }
// ],
// u: 1814396
// }
//
// {
// e: 'BALANCE',
// x: 'OutboundAccountPositionOrderEvent',
// E: 1670051332478,
// I: '353662845694083072',
// i: 1670051332478,
// B: [
// {
// a: 'eth',
// F: '0.0400000000000000',
// T: 1670051332000,
// f: '-0.0100000000000000',
// L: '0.0100000000000000',
// l: '0.0100000000000000',
// t: 1670051332000
// }
// ],
// u: 2285311
// }
//
const balances = this.safeValue(message, 'B', []);
this.parseWSBalances(balances);
const messageHash = 'balance';
client.resolve(this.balance, messageHash);
}
parseWSBalances(balances) {
//
// [{
// a: 'btc',
// F: '0.0006000000000000',
// T: 1657799510000,
// f: '0.0006000000000000',
// t: 0
// },
// {
// a: 'usdt',
// T: 0,
// L: '0.0000000000000000',
// l: '-11.8705317318000000',
// t: 1657799510000
// }]
//
this.balance['info'] = balances;
for (let i = 0; i < balances.length; i++) {
const balance = balances[i];
const currencyId = this.safeString(balance, 'a');
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
const free = this.safeString(balance, 'F');
const used = this.safeString(balance, 'L');
const balanceUpdateTime = this.safeInteger(balance, 'T', 0);
const lockBalanceUpdateTime = this.safeInteger(balance, 't', 0);
const updateFree = balanceUpdateTime !== 0;
const updateUsed = lockBalanceUpdateTime !== 0;
if (updateFree || updateUsed) {
if (updateFree) {
account['free'] = free;
}
if (updateUsed) {
account['used'] = used;
}
this.balance[code] = account;
}
}
this.balance = this.safeBalance(this.balance);
}
async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name bitrue#watchOrders
* @description watches information on user orders
* @see https://github.com/Bitrue-exchange/Spot-official-api-docs#order-update
* @param {[string]} symbols unified symbols of the market to watch the orders for
* @param {int|undefined} since timestamp in ms of the earliest order
* @param {int|undefined} limit the maximum amount of orders to return
* @param {object} params extra parameters specific to the bitrue api endpoint
* @returns {object} A dictionary of [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} indexed by market symbols
*/
await this.loadMarkets();
if (symbol !== undefined) {
const market = this.market(symbol);
symbol = market['symbol'];
}
const url = await this.authenticate();
const messageHash = 'orders';
const message = {
'event': 'sub',
'params': {
'channel': 'user_order_update',
},
};
const request = this.deepExtend(message, params);
const orders = await this.watch(url, messageHash, request, messageHash);
if (this.newUpdates) {
limit = orders.getLimit(symbol, limit);
}
return this.filterBySymbolSinceLimit(orders, symbol, since, limit);
}
handleOrder(client, message) {
//
// {
// e: 'ORDER',
// i: 16122802798,
// E: 1657882521876,
// I: '302623154710888464',
// u: 1814396,
// s: 'btcusdt',
// S: 2,
// o: 1,
// q: '0.0005',
// p: '60000',
// X: 0,
// x: 1,
// z: '0',
// n: '0',
// N: 'usdt',
// O: 1657882521876,
// L: '0',
// l: '0',
// Y: '0'
// }
//
const parsed = this.parseWsOrder(message);
if (this.orders === undefined) {
const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
this.orders = new ArrayCacheBySymbolById(limit);
}
const orders = this.orders;
orders.append(parsed);
const messageHash = 'orders';
client.resolve(this.orders, messageHash);
}
parseWsOrder(order, market = undefined) {
//
// {
// e: 'ORDER',
// i: 16122802798,
// E: 1657882521876,
// I: '302623154710888464',
// u: 1814396,
// s: 'btcusdt',
// S: 2,
// o: 1,
// q: '0.0005',
// p: '60000',
// X: 0,
// x: 1,
// z: '0',
// n: '0',
// N: 'usdt',
// O: 1657882521876,
// L: '0',
// l: '0',
// Y: '0'
// }
//
const timestamp = this.safeInteger(order, 'E');
const marketId = this.safeStringUpper(order, 's');
const typeId = this.safeString(order, 'o');
const sideId = this.safeInteger(order, 'S');
// 1: buy
// 2: sell
const side = (sideId === 1) ? 'buy' : 'sell';
const statusId = this.safeString(order, 'X');
const feeCurrencyId = this.safeString(order, 'N');
return this.safeOrder({
'info': order,
'id': this.safeString(order, 'i'),
'clientOrderId': this.safeString(order, 'c'),
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'lastTradeTimestamp': this.safeInteger(order, 'T'),
'symbol': this.safeSymbol(marketId, market),
'type': this.parseWsOrderType(typeId),
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': this.safeString(order, 'p'),
'triggerPrice': undefined,
'amount': this.safeString(order, 'q'),
'cost': this.safeString(order, 'Y'),
'average': undefined,
'filled': this.safeString(order, 'z'),
'remaining': undefined,
'status': this.parseWsOrderStatus(statusId),
'fee': {
'currency': this.safeCurrencyCode(feeCurrencyId),
'cost': this.safeNumber(order, 'n'),
},
}, market);
}
async watchOrderBook(symbol, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired(this.id + ' watchOrderBook() requires a symbol argument');
}
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
const messageHash = 'orderbook:' + symbol;
const marketIdLowercase = market['id'].toLowerCase();
const channel = 'market_' + marketIdLowercase + '_simple_depth_step0';
const url = this.urls['api']['ws']['public'];
const message = {
'event': 'sub',
'params': {
'cb_id': marketIdLowercase,
'channel': channel,
},
};
const request = this.deepExtend(message, params);
return await this.watch(url, messageHash, request, messageHash);
}
handleOrderBook(client, message) {
//
// {
// "channel": "market_ethbtc_simple_depth_step0",
// "ts": 1670056708670,
// "tick": {
// "buys": [
// [
// "0.075170",
// "67.153"
// ],
// [
// "0.075169",
// "17.195"
// ],
// [
// "0.075166",
// "29.788"
// ],
// ]
// "asks": [
// [
// "0.075171",
// "0.256"
// ],
// [
// "0.075172",
// "0.160"
// ],
// ]
// }
// }
//
const channel = this.safeString(message, 'channel');
const parts = channel.split('_');
const marketId = this.safeStringUpper(parts, 1);
const market = this.safeMarket(marketId);
const symbol = market['symbol'];
const timestamp = this.safeInteger(message, 'ts');
const tick = this.safeValue(message, 'tick', {});
const orderbook = this.parseOrderBook(tick, symbol, timestamp, 'buys', 'asks');
this.orderbooks[symbol] = orderbook;
const messageHash = 'orderbook:' + symbol;
client.resolve(orderbook, messageHash);
}
parseWsOrderType(typeId) {
const types = {
'1': 'limit',
'2': 'market',
'3': 'limit',
};
return this.safeString(types, typeId, typeId);
}
parseWsOrderStatus(status) {
const statuses = {
'0': 'open',
'1': 'open',
'2': 'closed',
'3': 'open',
'4': 'canceled',
'7': 'open', // Stop order placed.
};
return this.safeString(statuses, status, status);
}
handlePing(client, message) {
this.spawn(this.pong, client, message);
}
async pong(client, message) {
//
// {
// "ping": 1670057540627
// }
//
const time = this.safeInteger(message, 'ping');
const pong = {
'pong': time,
};
await client.send(pong);
}
handleMessage(client, message) {
if ('channel' in message) {
this.handleOrderBook(client, message);
}
else if ('ping' in message) {
this.handlePing(client, message);
}
else {
const event = this.safeString(message, 'e');
const handlers = {
'BALANCE': this.handleBalance,
'ORDER': this.handleOrder,
};
const handler = this.safeValue(handlers, event);
if (handler !== undefined) {
handler.call(this, client, message);
}
}
}
async authenticate(params = {}) {
const listenKey = this.safeValue(this.options, 'listenKey');
if (listenKey === undefined) {
let response = undefined;
try {
response = await this.openPrivatePostPoseidonApiV1ListenKey(params);
}
catch (error) {
this.options['listenKey'] = undefined;
this.options['listenKeyUrl'] = undefined;
return;
}
//
// {
// "msg": "succ",
// "code": 200,
// "data": {
// "listenKey": "7d1ec51340f499d85bb33b00a96ef680bda28869d5c3374a444c5ca4847d1bf0"
// }
// }
//
const data = this.safeValue(response, 'data', {});
const key = this.safeString(data, 'listenKey');
this.options['listenKey'] = key;
this.options['listenKeyUrl'] = this.urls['api']['ws']['private'] + '/stream?listenKey=' + key;
const refreshTimeout = this.safeInteger(this.options, 'listenKeyRefreshRate', 1800000);
this.delay(refreshTimeout, this.keepAliveListenKey);
}
return this.options['listenKeyUrl'];
}
async keepAliveListenKey(params = {}) {
const listenKey = this.safeString(this.options, 'listenKey');
const request = {
'listenKey': listenKey,
};
try {
await this.openPrivatePutPoseidonApiV1ListenKeyListenKey(this.extend(request, params));
//
// ಠ_ಠ
// {
// "msg": "succ",
// "code": "200"
// }
//
}
catch (error) {
this.options['listenKey'] = undefined;
this.options['listenKeyUrl'] = undefined;
return;
}
const refreshTimeout = this.safeInteger(this.options, 'listenKeyRefreshRate', 1800000);
this.delay(refreshTimeout, this.keepAliveListenKey);
}
}