UNPKG

sfccxt

Version:

A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges

306 lines (289 loc) 13 kB
'use strict'; // --------------------------------------------------------------------------- const ripioRest = require ('../ripio.js'); const { ArrayCache } = require ('./base/Cache'); // --------------------------------------------------------------------------- module.exports = class ripio extends ripioRest { describe () { return this.deepExtend (super.describe (), { 'has': { 'ws': true, 'watchOrderBook': true, 'watchTrades': true, 'watchTicker': true, }, 'urls': { 'api': { 'ws': 'wss://api.exchange.ripio.com/ws/v2/consumer/non-persistent/public/default/', }, }, 'options': { 'tradesLimit': 1000, 'uuid': this.uuid (), }, }); } async watchTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); symbol = market['symbol']; const name = 'trades'; const messageHash = name + '_' + market['id'].toLowerCase (); const url = this.urls['api']['ws'] + messageHash + '/' + this.options['uuid']; const subscription = { 'name': name, 'symbol': symbol, 'messageHash': messageHash, 'method': this.handleTrade, }; const trades = await this.watch (url, messageHash, undefined, messageHash, subscription); if (this.newUpdates) { limit = trades.getLimit (symbol, limit); } return this.filterBySinceLimit (trades, since, limit, 'timestamp', true); } handleTrade (client, message, subscription) { // // { // messageId: 'CAAQAA==', // payload: 'eyJjcmVhdGVkX2F0IjogMTYwMTczNjI0NywgImFtb3VudCI6ICIwLjAwMjAwIiwgInByaWNlIjogIjEwNTkzLjk5MDAwMCIsICJzaWRlIjogIkJVWSIsICJwYWlyIjogIkJUQ19VU0RDIiwgInRha2VyX2ZlZSI6ICIwIiwgInRha2VyX3NpZGUiOiAiQlVZIiwgIm1ha2VyX2ZlZSI6ICIwIiwgInRha2VyIjogMjYxODU2NCwgIm1ha2VyIjogMjYxODU1N30=', // properties: {}, // publishTime: '2020-10-03T14:44:09.881Z' // } // const payload = this.safeString (message, 'payload'); if (payload === undefined) { return message; } const data = JSON.parse (this.base64ToString (payload)); // // { // created_at: 1601736247, // amount: '0.00200', // price: '10593.990000', // side: 'BUY', // pair: 'BTC_USDC', // taker_fee: '0', // taker_side: 'BUY', // maker_fee: '0', // taker: 2618564, // maker: 2618557 // } // const symbol = this.safeString (subscription, 'symbol'); const messageHash = this.safeString (subscription, 'messageHash'); const market = this.market (symbol); const trade = this.parseTrade (data, market); let tradesArray = this.safeValue (this.trades, symbol); if (tradesArray === undefined) { const limit = this.safeInteger (this.options, 'tradesLimit', 1000); tradesArray = new ArrayCache (limit); this.trades[symbol] = tradesArray; } tradesArray.append (trade); client.resolve (tradesArray, messageHash); } async watchTicker (symbol, params = {}) { /** * @method * @name ripio#watchTicker * @description watches 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 ripio 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); symbol = market['symbol']; const name = 'rate'; const messageHash = name + '_' + market['id'].toLowerCase (); const url = this.urls['api']['ws'] + messageHash + '/' + this.options['uuid']; const subscription = { 'name': name, 'symbol': symbol, 'messageHash': messageHash, 'method': this.handleTicker, }; return await this.watch (url, messageHash, undefined, messageHash, subscription); } handleTicker (client, message, subscription) { // // { // messageId: 'CAAQAA==', // payload: 'eyJidXkiOiBbeyJhbW91bnQiOiAiMC4wOTMxMiIsICJ0b3RhbCI6ICI4MzguMDgiLCAicHJpY2UiOiAiOTAwMC4wMCJ9XSwgInNlbGwiOiBbeyJhbW91bnQiOiAiMC4wMDAwMCIsICJ0b3RhbCI6ICIwLjAwIiwgInByaWNlIjogIjkwMDAuMDAifV0sICJ1cGRhdGVkX2lkIjogMTI0NDA0fQ==', // properties: {}, // publishTime: '2020-10-03T10:05:09.445Z' // } // const payload = this.safeString (message, 'payload'); if (payload === undefined) { return message; } const data = JSON.parse (this.base64ToString (payload)); // // { // "pair": "BTC_BRL", // "last_price": "68558.59", // "low": "54736.11", // "high": "70034.68", // "variation": "8.75", // "volume": "10.10537" // } // const ticker = this.parseTicker (data); const timestamp = this.parse8601 (this.safeString (message, 'publishTime')); ticker['timestamp'] = timestamp; ticker['datetime'] = this.iso8601 (timestamp); const symbol = ticker['symbol']; this.tickers[symbol] = ticker; const messageHash = this.safeString (subscription, 'messageHash'); if (messageHash !== undefined) { client.resolve (ticker, messageHash); } return message; } async watchOrderBook (symbol, limit = undefined, params = {}) { /** * @method * @name ripio#watchOrderBook * @description watches 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 ripio 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); symbol = market['symbol']; const name = 'orderbook'; const messageHash = name + '_' + market['id'].toLowerCase (); const url = this.urls['api']['ws'] + messageHash + '/' + this.options['uuid']; const client = this.client (url); const subscription = { 'name': name, 'symbol': symbol, 'messageHash': messageHash, 'method': this.handleOrderBook, }; if (!(messageHash in client.subscriptions)) { this.orderbooks[symbol] = this.orderBook ({}); client.subscriptions[messageHash] = subscription; const options = this.safeValue (this.options, 'fetchOrderBookSnapshot', {}); const delay = this.safeInteger (options, 'delay', this.rateLimit); // fetch the snapshot in a separate async call after a warmup delay this.delay (delay, this.fetchOrderBookSnapshot, client, subscription); } const orderbook = await this.watch (url, messageHash, undefined, messageHash, subscription); return orderbook.limit (); } async fetchOrderBookSnapshot (client, subscription) { const symbol = this.safeString (subscription, 'symbol'); const messageHash = this.safeString (subscription, 'messageHash'); try { // todo: this is a synch blocking call in ccxt.php - make it async const snapshot = await this.fetchOrderBook (symbol); const orderbook = this.orderbooks[symbol]; const messages = orderbook.cache; orderbook.reset (snapshot); // unroll the accumulated deltas for (let i = 0; i < messages.length; i++) { const message = messages[i]; this.handleOrderBookMessage (client, message, orderbook); } this.orderbooks[symbol] = orderbook; client.resolve (orderbook, messageHash); } catch (e) { client.reject (e, messageHash); } } handleOrderBook (client, message, subscription) { const messageHash = this.safeString (subscription, 'messageHash'); const symbol = this.safeString (subscription, 'symbol'); const orderbook = this.safeValue (this.orderbooks, symbol); if (orderbook === undefined) { return message; } if (orderbook['nonce'] === undefined) { orderbook.cache.push (message); } else { this.handleOrderBookMessage (client, message, orderbook); client.resolve (orderbook, messageHash); } return message; } handleOrderBookMessage (client, message, orderbook) { // // { // messageId: 'CAAQAA==', // payload: 'eyJidXkiOiBbeyJhbW91bnQiOiAiMC4wOTMxMiIsICJ0b3RhbCI6ICI4MzguMDgiLCAicHJpY2UiOiAiOTAwMC4wMCJ9XSwgInNlbGwiOiBbeyJhbW91bnQiOiAiMC4wMDAwMCIsICJ0b3RhbCI6ICIwLjAwIiwgInByaWNlIjogIjkwMDAuMDAifV0sICJ1cGRhdGVkX2lkIjogMTI0NDA0fQ==', // properties: {}, // publishTime: '2020-10-03T10:05:09.445Z' // } // const payload = this.safeString (message, 'payload'); if (payload === undefined) { return message; } const data = JSON.parse (this.base64ToString (payload)); // // { // "buy": [ // {"amount": "0.05000", "total": "532.77", "price": "10655.41"} // ], // "sell": [ // {"amount": "0.00000", "total": "0.00", "price": "10655.41"} // ], // "updated_id": 99740 // } // const nonce = this.safeInteger (data, 'updated_id'); if (nonce > orderbook['nonce']) { const asks = this.safeValue (data, 'sell', []); const bids = this.safeValue (data, 'buy', []); this.handleDeltas (orderbook['asks'], asks, orderbook['nonce']); this.handleDeltas (orderbook['bids'], bids, orderbook['nonce']); orderbook['nonce'] = nonce; const timestamp = this.parse8601 (this.safeString (message, 'publishTime')); orderbook['timestamp'] = timestamp; orderbook['datetime'] = this.iso8601 (timestamp); } return orderbook; } handleDelta (bookside, delta) { const price = this.safeFloat (delta, 'price'); const amount = this.safeFloat (delta, 'amount'); bookside.store (price, amount); } handleDeltas (bookside, deltas) { for (let i = 0; i < deltas.length; i++) { this.handleDelta (bookside, deltas[i]); } } async ack (client, messageId) { // the exchange requires acknowledging each received message await client.send ({ 'messageId': messageId }); } handleMessage (client, message) { // // { // messageId: 'CAAQAA==', // payload: 'eyJidXkiOiBbeyJhbW91bnQiOiAiMC4wNTAwMCIsICJ0b3RhbCI6ICI1MzIuNzciLCAicHJpY2UiOiAiMTA2NTUuNDEifV0sICJzZWxsIjogW3siYW1vdW50IjogIjAuMDAwMDAiLCAidG90YWwiOiAiMC4wMCIsICJwcmljZSI6ICIxMDY1NS40MSJ9XSwgInVwZGF0ZWRfaWQiOiA5OTc0MH0=', // properties: {}, // publishTime: '2020-09-30T17:35:27.851Z' // } // const messageId = this.safeString (message, 'messageId'); if (messageId !== undefined) { // the exchange requires acknowledging each received message this.spawn (this.ack, client, messageId); } const keys = Object.keys (client.subscriptions); const firstKey = this.safeString (keys, 0); const subscription = this.safeValue (client.subscriptions, firstKey, {}); const method = this.safeValue (subscription, 'method'); if (method !== undefined) { return method.call (this, client, message, subscription); } return message; } };