@proton/ccxt
Version:
A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 130+ exchanges
588 lines (585 loc) • 24.7 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
'use strict';
// ---------------------------------------------------------------------------
import probitRest from '../probit.js';
import { NotSupported, ExchangeError } from '../base/errors.js';
import { ArrayCache, ArrayCacheBySymbolById } from '../base/ws/Cache.js';
// ---------------------------------------------------------------------------
export default class probit extends probitRest {
describe() {
return this.deepExtend(super.describe(), {
'has': {
'ws': true,
'watchBalance': true,
'watchTicker': true,
'watchTickers': false,
'watchTrades': true,
'watchMyTrades': true,
'watchOrders': true,
'watchOrderBook': true,
'watchOHLCV': false,
},
'urls': {
'api': {
'ws': 'wss://api.probit.com/api/exchange/v1/ws',
},
'test': {
'ws': 'wss://demo-api.probit.com/api/exchange/v1/ws',
},
},
'options': {
'watchOrderBook': {
'filter': 'order_books_l2',
'interval': 100, // or 500
},
'watchTrades': {
'filter': 'recent_trades',
},
'watchTicker': {
'filter': 'ticker',
},
'watchOrders': {
'channel': 'open_order',
},
},
'streaming': {},
'exceptions': {},
});
}
async watchBalance(params = {}) {
/**
* @method
* @name probit#watchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://docs-en.probit.com/reference/balance-1
* @param {object} params extra parameters specific to the probit api endpoint
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
*/
await this.authenticate(params);
const messageHash = 'balance';
const url = this.urls['api']['ws'];
const subscribe = {
'type': 'subscribe',
'channel': 'balance',
};
const request = this.extend(subscribe, params);
return await this.watch(url, messageHash, request, messageHash);
}
handleBalance(client, message) {
//
// {
// channel: 'balance',
// reset: false,
// data: {
// USDT: {
// available: '15',
// total: '15'
// }
// }
// }
//
const messageHash = 'balance';
this.parseWSBalance(message);
client.resolve(this.balance, messageHash);
}
parseWSBalance(message) {
//
// {
// channel: 'balance',
// reset: false,
// data: {
// USDT: {
// available: '15',
// total: '15'
// }
// }
// }
//
const reset = this.safeValue(message, 'reset', false);
const data = this.safeValue(message, 'data', {});
const currencyIds = Object.keys(data);
if (reset) {
this.balance = {};
}
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const entry = data[currencyId];
const code = this.safeCurrencyCode(currencyId);
const account = this.account();
account['free'] = this.safeString(entry, 'available');
account['total'] = this.safeString(entry, 'total');
this.balance[code] = account;
}
this.balance = this.safeBalance(this.balance);
}
async watchTicker(symbol, params = {}) {
/**
* @method
* @name probit#watchTicker
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://docs-en.probit.com/reference/marketdata
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} params extra parameters specific to the probit api endpoint
* @param {int|undefined} params.interval Unit time to synchronize market information (ms). Available units: 100, 500
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
let filter = undefined;
[filter, params] = this.handleOptionAndParams(params, 'watchTicker', 'filter', 'ticker');
return await this.subscribeOrderBook(symbol, 'ticker', filter, params);
}
handleTicker(client, message) {
//
// {
// channel: 'marketdata',
// market_id: 'BTC-USDT',
// status: 'ok',
// lag: 0,
// ticker: {
// time: '2022-07-21T14:18:04.000Z',
// last: '22591.3',
// low: '22500.1',
// high: '39790.7',
// change: '-1224',
// base_volume: '1002.32005445',
// quote_volume: '23304489.385351021'
// },
// reset: true
// }
//
const marketId = this.safeString(message, 'market_id');
const symbol = this.safeSymbol(marketId);
const ticker = this.safeValue(message, 'ticker', {});
const market = this.safeMarket(marketId);
const parsedTicker = this.parseTicker(ticker, market);
const messageHash = 'ticker:' + symbol;
this.tickers[symbol] = parsedTicker;
client.resolve(parsedTicker, messageHash);
}
async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name probit#watchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://docs-en.probit.com/reference/trade_history
* @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 probit api endpoint
* @param {int|undefined} params.interval Unit time to synchronize market information (ms). Available units: 100, 500
* @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades}
*/
let filter = undefined;
[filter, params] = this.handleOptionAndParams(params, 'watchTrades', 'filter', 'recent_trades');
const trades = await this.subscribeOrderBook(symbol, 'trades', filter, params);
if (this.newUpdates) {
limit = trades.getLimit(symbol, limit);
}
return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
}
handleTrades(client, message) {
//
// {
// channel: 'marketdata',
// market_id: 'BTC-USDT',
// status: 'ok',
// lag: 0,
// recent_trades: [
// {
// id: 'BTC-USDT:8010233',
// price: '22701.4',
// quantity: '0.011011',
// time: '2022-07-21T13:40:40.983Z',
// side: 'buy',
// tick_direction: 'up'
// }
// ...
// ]
// reset: true
// }
//
const marketId = this.safeString(message, 'market_id');
const symbol = this.safeSymbol(marketId);
const market = this.safeMarket(marketId);
const trades = this.safeValue(message, 'recent_trades', []);
const reset = this.safeValue(message, 'reset', false);
const messageHash = 'trades:' + symbol;
let stored = this.safeValue(this.trades, symbol);
if (stored === undefined || reset) {
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
stored = new ArrayCache(limit);
this.trades[symbol] = stored;
}
for (let i = 0; i < trades.length; i++) {
const trade = trades[i];
const parsed = this.parseTrade(trade, market);
stored.append(parsed);
}
this.trades[symbol] = stored;
client.resolve(this.trades[symbol], messageHash);
}
async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name probit#watchMyTrades
* @description get the list of trades associated with the user
* @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 probit api endpoint
* @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades}
*/
await this.loadMarkets();
await this.authenticate(params);
let messageHash = 'myTrades';
if (symbol !== undefined) {
const market = this.market(symbol);
symbol = market['symbol'];
messageHash = messageHash + ':' + symbol;
}
const url = this.urls['api']['ws'];
const channel = 'trade_history';
const message = {
'type': 'subscribe',
'channel': channel,
};
const request = this.extend(message, params);
const trades = await this.watch(url, messageHash, request, channel);
if (this.newUpdates) {
limit = trades.getLimit(symbol, limit);
}
return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
}
handleMyTrades(client, message) {
//
// {
// channel: 'trade_history',
// reset: false,
// data: [{
// id: 'BTC-USDT:8010722',
// order_id: '4124999207',
// side: 'buy',
// fee_amount: '0.0134999868096',
// fee_currency_id: 'USDT',
// status: 'settled',
// price: '23136.7',
// quantity: '0.00032416',
// cost: '7.499992672',
// time: '2022-07-21T17:09:33.056Z',
// market_id: 'BTC-USDT'
// }]
// }
//
const rawTrades = this.safeValue(message, 'data', []);
if (rawTrades.length === 0) {
return;
}
const reset = this.safeValue(message, 'reset', false);
const messageHash = 'myTrades';
let stored = this.myTrades;
if ((stored === undefined) || reset) {
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
stored = new ArrayCacheBySymbolById(limit);
this.myTrades = stored;
}
const trades = this.parseTrades(rawTrades);
const tradeSymbols = {};
for (let j = 0; j < trades.length; j++) {
const trade = trades[j];
tradeSymbols[trade['symbol']] = true;
stored.append(trade);
}
const unique = Object.keys(tradeSymbols);
for (let i = 0; i < unique.length; i++) {
const symbol = unique[i];
const symbolSpecificMessageHash = messageHash + ':' + symbol;
client.resolve(stored, symbolSpecificMessageHash);
}
client.resolve(stored, messageHash);
}
async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name probit#watchOrders
* @description watches information on an order made by the user
* @see https://docs-en.probit.com/reference/open_order
* @param {string|undefined} symbol unified symbol of the market the order was made in
* @param {int|undefined} since timestamp in ms of the earliest order to watch
* @param {int|undefined} limit the maximum amount of orders to watch
* @param {object} params extra parameters specific to the aax api endpoint
* @param {string|undefined} params.channel choose what channel to use. Can open_order or order_history.
* @returns {object} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
await this.authenticate(params);
const url = this.urls['api']['ws'];
let messageHash = 'orders';
if (symbol !== undefined) {
const market = this.market(symbol);
symbol = market['symbol'];
messageHash = messageHash + ':' + symbol;
}
let channel = undefined;
[channel, params] = this.handleOptionAndParams(params, 'watchOrders', 'channel', 'open_order');
const subscribe = {
'type': 'subscribe',
'channel': channel,
};
const request = this.extend(subscribe, params);
const orders = await this.watch(url, messageHash, request, channel);
if (this.newUpdates) {
limit = orders.getLimit(symbol, limit);
}
return this.filterBySymbolSinceLimit(orders, symbol, since, limit);
}
handleOrders(client, message) {
//
// {
// channel: 'order_history',
// reset: true,
// data: [{
// id: '4124999207',
// user_id: '633dc56a-621b-4680-8a4e-85a823499b6d',
// market_id: 'BTC-USDT',
// type: 'market',
// side: 'buy',
// limit_price: '0',
// time_in_force: 'ioc',
// filled_cost: '7.499992672',
// filled_quantity: '0.00032416',
// open_quantity: '0',
// status: 'filled',
// time: '2022-07-21T17:09:33.056Z',
// client_order_id: '',
// cost: '7.5'
// },
// ...
// ]
// }
//
const rawOrders = this.safeValue(message, 'data', []);
if (rawOrders.length === 0) {
return;
}
const messageHash = 'orders';
const reset = this.safeValue(message, 'reset', false);
let stored = this.orders;
if (stored === undefined || reset) {
const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
stored = new ArrayCacheBySymbolById(limit);
this.orders = stored;
}
const orderSymbols = {};
for (let i = 0; i < rawOrders.length; i++) {
const rawOrder = rawOrders[i];
const order = this.parseOrder(rawOrder);
orderSymbols[order['symbol']] = true;
stored.append(order);
}
const unique = Object.keys(orderSymbols);
for (let i = 0; i < unique.length; i++) {
const symbol = unique[i];
const symbolSpecificMessageHash = messageHash + ':' + symbol;
client.resolve(stored, symbolSpecificMessageHash);
}
client.resolve(stored, messageHash);
}
async watchOrderBook(symbol, limit = undefined, params = {}) {
/**
* @method
* @name probit#watchOrderBook
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://docs-en.probit.com/reference/marketdata
* @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 probit 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
*/
let filter = undefined;
[filter, params] = this.handleOptionAndParams(params, 'watchOrderBook', 'filter', 'order_books');
const orderbook = await this.subscribeOrderBook(symbol, 'orderbook', filter, params);
return orderbook.limit();
}
async subscribeOrderBook(symbol, messageHash, filter, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
const url = this.urls['api']['ws'];
const client = this.client(url);
let interval = undefined;
[interval, params] = this.handleOptionAndParams(params, 'watchOrderBook', 'interval', 100);
const subscriptionHash = 'marketdata:' + symbol;
messageHash = messageHash + ':' + symbol;
let filters = {};
if (subscriptionHash in client.subscriptions) {
// already subscribed
filters = client.subscriptions[subscriptionHash];
if (!(filter in filters)) {
// resubscribe
delete client.subscriptions[subscriptionHash];
}
}
filters[filter] = true;
const keys = Object.keys(filters);
const message = {
'channel': 'marketdata',
'interval': interval,
'market_id': market['id'],
'type': 'subscribe',
'filter': keys,
};
const request = this.extend(message, params);
return await this.watch(url, messageHash, request, messageHash, filters);
}
handleOrderBook(client, message, orderBook) {
//
// {
// channel: 'marketdata',
// market_id: 'BTC-USDT',
// status: 'ok',
// lag: 0,
// order_books: [
// { side: 'buy', price: '1420.7', quantity: '0.057' },
// ...
// ],
// reset: true
// }
//
const marketId = this.safeString(message, 'market_id');
const symbol = this.safeSymbol(marketId);
const dataBySide = this.groupBy(orderBook, 'side');
const messageHash = 'orderbook:' + symbol;
let storedOrderBook = this.safeValue(this.orderbooks, symbol);
if (storedOrderBook === undefined) {
storedOrderBook = this.orderBook({});
this.orderbooks[symbol] = storedOrderBook;
}
const reset = this.safeValue(message, 'reset', false);
if (reset) {
const snapshot = this.parseOrderBook(dataBySide, symbol, undefined, 'buy', 'sell', 'price', 'quantity');
storedOrderBook.reset(snapshot);
}
else {
this.handleDelta(storedOrderBook, dataBySide);
}
client.resolve(storedOrderBook, messageHash);
}
handleBidAsks(bookSide, bidAsks) {
for (let i = 0; i < bidAsks.length; i++) {
const bidAsk = bidAsks[i];
const parsed = this.parseBidAsk(bidAsk, 'price', 'quantity');
bookSide.storeArray(parsed);
}
}
handleDelta(orderbook, delta) {
const storedBids = orderbook['bids'];
const storedAsks = orderbook['asks'];
const asks = this.safeValue(delta, 'sell', []);
const bids = this.safeValue(delta, 'buy', []);
this.handleBidAsks(storedBids, bids);
this.handleBidAsks(storedAsks, asks);
}
handleErrorMessage(client, message) {
//
// {
// errorCode: 'INVALID_ARGUMENT',
// message: '',
// details: {
// interval: 'invalid'
// }
// }
//
const code = this.safeString(message, 'errorCode');
const errMessage = this.safeString(message, 'message', '');
const details = this.safeValue(message, 'details');
// todo - throw properly here
throw new ExchangeError(this.id + ' ' + code + ' ' + errMessage + ' ' + this.json(details));
}
handleAuthenticate(client, message) {
//
// { type: 'authorization', result: 'ok' }
//
const result = this.safeString(message, 'result');
const future = client.subscriptions['authenticated'];
if (result === 'ok') {
future.resolve(true);
}
else {
future.reject(message);
delete client.subscriptions['authenticated'];
}
}
handleMarketData(client, message) {
const ticker = this.safeValue(message, 'ticker');
if (ticker !== undefined) {
this.handleTicker(client, message);
}
const trades = this.safeValue(message, 'recent_trades', []);
if (trades.length > 0) {
this.handleTrades(client, message);
}
const orderBook = this.safeValueN(message, ['order_books', 'order_books_l1', 'order_books_l2', 'order_books_l3', 'order_books_l4'], []);
if (orderBook.length > 0) {
this.handleOrderBook(client, message, orderBook);
}
}
handleMessage(client, message) {
//
// {
// errorCode: 'INVALID_ARGUMENT',
// message: '',
// details: {
// interval: 'invalid'
// }
// }
//
const errorCode = this.safeString(message, 'errorCode');
if (errorCode !== undefined) {
return this.handleErrorMessage(client, message);
}
const type = this.safeString(message, 'type');
if (type === 'authorization') {
return this.handleAuthenticate(client, message);
}
const handlers = {
'marketdata': this.handleMarketData,
'balance': this.handleBalance,
'trade_history': this.handleMyTrades,
'open_order': this.handleOrders,
'order_history': this.handleOrders,
};
const channel = this.safeString(message, 'channel');
const handler = this.safeValue(handlers, channel);
if (handler !== undefined) {
return handler.call(this, client, message);
}
const error = new NotSupported(this.id + ' handleMessage: unknown message: ' + this.json(message));
client.reject(error);
}
async authenticate(params = {}) {
const url = this.urls['api']['ws'];
const client = this.client(url);
const messageHash = 'authenticated';
const expires = this.safeInteger(this.options, 'expires', 0);
let future = this.safeValue(client.subscriptions, messageHash);
if ((future === undefined) || (this.milliseconds() > expires)) {
const response = await this.signIn();
//
// {
// access_token: '0ttDv/2hTTn3bLi8GP1gKaneiEQ6+0hOBenPrxNQt2s=',
// token_type: 'bearer',
// expires_in: 900
// }
//
const accessToken = this.safeString(response, 'access_token');
const request = {
'type': 'authorization',
'token': accessToken,
};
future = this.watch(url, messageHash, this.extend(request, params));
client.subscriptions[messageHash] = future;
}
return await future;
}
}