UNPKG

ccxt

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

594 lines (591 loc) • 24.8 kB
// ---------------------------------------------------------------------------- // 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 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, 'watchTradesForSymbols': false, '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 }, }, 'streaming': {}, }); } /** * @method * @name probit#watchBalance * @description watch 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 exchange API endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ async watchBalance(params = {}) { await this.authenticate(params); const messageHash = 'balance'; return await this.subscribePrivate(messageHash, 'balance', params); } 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.safeBool(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); } /** * @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 exchange API endpoint * @param {int} [params.interval] Unit time to synchronize market information (ms). Available units: 100, 500 * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async watchTicker(symbol, params = {}) { const channel = 'ticker'; return await this.subscribePublic('watchTicker', symbol, 'ticker', channel, 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); } /** * @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} [since] timestamp in ms of the earliest trade to fetch * @param {int} [limit] the maximum amount of trades to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [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/#/?id=public-trades} */ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) { const channel = 'recent_trades'; symbol = this.safeSymbol(symbol); const trades = await this.subscribePublic('watchTrades', symbol, 'trades', channel, 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', []); if (this.safeBool(message, 'reset', false)) { return; // see comment in handleMessage } const messageHash = 'trades:' + symbol; let stored = this.safeValue(this.trades, symbol); if (stored === undefined) { 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); } /** * @method * @name probit#watchMyTrades * @description get the list of trades associated with the user * @see https://docs-en.probit.com/reference/trade_history * @param {string} symbol unified symbol of the market to fetch trades for * @param {int} [since] timestamp in ms of the earliest trade to fetch * @param {int} [limit] the maximum amount of trades to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} */ async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); await this.authenticate(params); let messageHash = 'trades'; if (symbol !== undefined) { symbol = this.safeSymbol(symbol); messageHash = messageHash + ':' + symbol; } const trades = await this.subscribePrivate(messageHash, 'trade_history', params); 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', []); const length = rawTrades.length; if (length === 0) { return; } if (this.safeBool(message, 'reset', false)) { return; // see comment in handleMessage } const messageHash = 'trades'; let stored = this.myTrades; if (stored === undefined) { 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]; // don't include 'executed' state, because it's just blanket state of the trade, emited before actual trade event if (this.safeString(trade['info'], 'status') === 'executed') { continue; } tradeSymbols[trade['symbol']] = true; stored.append(trade); } const unique = Object.keys(tradeSymbols); const uniqueLength = unique.length; if (uniqueLength === 0) { return; } for (let i = 0; i < unique.length; i++) { const symbol = unique[i]; const symbolSpecificMessageHash = messageHash + ':' + symbol; client.resolve(stored, symbolSpecificMessageHash); } client.resolve(stored, messageHash); } /** * @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} symbol unified symbol of the market the order was made in * @param {int} [since] timestamp in ms of the earliest order to watch * @param {int} [limit] the maximum amount of orders to watch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.channel] choose what channel to use. Can open_order or order_history. * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.authenticate(params); let messageHash = 'orders'; if (symbol !== undefined) { symbol = this.safeSymbol(symbol); messageHash = messageHash + ':' + symbol; } const orders = await this.subscribePrivate(messageHash, 'open_order', params); if (this.newUpdates) { limit = orders.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true); } 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', []); const length = rawOrders.length; if (length === 0) { return; } const messageHash = 'orders'; const reset = this.safeBool(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); } /** * @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} [limit] the maximum amount of order book entries to return * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols */ async watchOrderBook(symbol, limit = undefined, params = {}) { let channel = undefined; [channel, params] = this.handleOptionAndParams(params, 'watchOrderBook', 'filter', 'order_books'); const orderbook = await this.subscribePublic('watchOrderBook', symbol, 'orderbook', channel, params); return orderbook.limit(); } async subscribePrivate(messageHash, channel, params) { const url = this.urls['api']['ws']; const subscribe = { 'type': 'subscribe', 'channel': channel, }; const request = this.extend(subscribe, params); const subscribeHash = messageHash; return await this.watch(url, messageHash, request, subscribeHash); } async subscribePublic(methodName, symbol, dataType, filter, params = {}) { await this.loadMarkets(); const market = this.market(symbol); symbol = market['symbol']; const url = this.urls['api']['ws']; const client = this.client(url); const subscribeHash = 'marketdata:' + symbol; const messageHash = dataType + ':' + symbol; let filters = {}; if (subscribeHash in client.subscriptions) { // already subscribed filters = client.subscriptions[subscribeHash]; if (!(filter in filters)) { // resubscribe delete client.subscriptions[subscribeHash]; } } filters[filter] = true; const keys = Object.keys(filters); let interval = undefined; [interval, params] = this.handleOptionAndParams(params, methodName, 'interval', 100); let request = { 'type': 'subscribe', 'channel': 'marketdata', 'market_id': market['id'], 'filter': keys, 'interval': interval, }; request = this.extend(request, params); return await this.watch(url, messageHash, request, subscribeHash, 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 orderbook = this.safeValue (this.orderbooks, symbol); if (!(symbol in this.orderbooks)) { this.orderbooks[symbol] = this.orderBook({}); } const orderbook = this.orderbooks[symbol]; const reset = this.safeBool(message, 'reset', false); if (reset) { const snapshot = this.parseOrderBook(dataBySide, symbol, undefined, 'buy', 'sell', 'price', 'quantity'); orderbook.reset(snapshot); } else { this.handleDelta(orderbook, dataBySide); } client.resolve(orderbook, 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'); const feedback = this.id + ' ' + code + ' ' + errMessage + ' ' + this.json(details); if ('exact' in this.exceptions) { this.throwExactlyMatchedException(this.exceptions['exact'], code, feedback); } if ('broad' in this.exceptions) { this.throwBroadlyMatchedException(this.exceptions['broad'], errMessage, feedback); } throw new ExchangeError(feedback); } handleAuthenticate(client, message) { // // { type: "authorization", result: "ok" } // const result = this.safeString(message, 'result'); const future = client.subscriptions['authenticated']; if (result === 'ok') { const messageHash = 'authenticated'; client.resolve(message, messageHash); } 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', []); const tradesLength = trades.length; if (tradesLength) { this.handleTrades(client, message); } const orderBook = this.safeValueN(message, ['order_books', 'order_books_l1', 'order_books_l2', 'order_books_l3', 'order_books_l4'], []); const orderBookLength = orderBook.length; if (orderBookLength) { this.handleOrderBook(client, message, orderBook); } } handleMessage(client, message) { // // { // "errorCode": "INVALID_ARGUMENT", // "message": '', // "details": { // "interval": "invalid" // } // } // // Note about 'reset' field // 'reset': true field - it happens once after initial subscription, which just returns old items by the moment of subscription (like "fetchMyTrades" does) // const errorCode = this.safeString(message, 'errorCode'); if (errorCode !== undefined) { this.handleErrorMessage(client, message); return; } const type = this.safeString(message, 'type'); if (type === 'authorization') { this.handleAuthenticate(client, message); return; } 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) { handler.call(this, client, message); return; } 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 = await this.watch(url, messageHash, this.extend(request, params), messageHash); client.subscriptions[messageHash] = future; } return future; } }