UNPKG

ccxt

Version:

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

754 lines (751 loc) • 30.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 deriveRest from '../derive.js'; import { ExchangeError, AuthenticationError, UnsubscribeError } from '../base/errors.js'; import { ArrayCacheBySymbolById, ArrayCache } from '../base/ws/Cache.js'; // ---------------------------------------------------------------------------- export default class derive extends deriveRest { describe() { return this.deepExtend(super.describe(), { 'has': { 'ws': false, 'watchBalance': false, 'watchMyTrades': true, 'watchOHLCV': false, 'watchOrderBook': true, 'watchOrders': true, 'watchTicker': true, 'watchTickers': false, 'watchBidsAsks': false, 'watchTrades': true, 'watchTradesForSymbols': false, 'watchPositions': false, }, 'urls': { 'api': { 'ws': 'wss://api.lyra.finance/ws', }, 'test': { 'ws': 'wss://api-demo.lyra.finance/ws', }, }, 'options': { 'tradesLimit': 1000, 'ordersLimit': 1000, 'requestId': {}, }, 'streaming': { 'keepAlive': 9000, }, 'exceptions': { 'ws': { 'exact': {}, }, }, }); } requestId(url) { const options = this.safeValue(this.options, 'requestId', {}); const previousValue = this.safeInteger(options, url, 0); const newValue = this.sum(previousValue, 1); this.options['requestId'][url] = newValue; return newValue; } async watchPublic(messageHash, message, subscription) { const url = this.urls['api']['ws']; const requestId = this.requestId(url); const request = this.extend(message, { 'id': requestId, }); subscription = this.extend(subscription, { 'id': requestId, 'method': 'subscribe', }); return await this.watch(url, messageHash, request, messageHash, subscription); } /** * @method * @name derive#watchOrderBook * @see https://docs.derive.xyz/reference/orderbook-instrument_name-group-depth * @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} [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 = {}) { await this.loadMarkets(); if (limit === undefined) { limit = 10; } const market = this.market(symbol); const topic = 'orderbook.' + market['id'] + '.10.' + this.numberToString(limit); const request = { 'method': 'subscribe', 'params': { 'channels': [ topic, ], }, }; const subscription = { 'name': topic, 'symbol': symbol, 'limit': limit, 'params': params, }; const orderbook = await this.watchPublic(topic, request, subscription); return orderbook.limit(); } handleOrderBook(client, message) { // // { // method: 'subscription', // params: { // channel: 'orderbook.BTC-PERP.10.1', // data: { // timestamp: 1738331231506, // instrument_name: 'BTC-PERP', // publish_id: 628419, // bids: [ [ '104669', '40' ] ], // asks: [ [ '104736', '40' ] ] // } // } // } // const params = this.safeDict(message, 'params'); const data = this.safeDict(params, 'data'); const marketId = this.safeString(data, 'instrument_name'); const market = this.safeMarket(marketId); const symbol = market['symbol']; const topic = this.safeString(params, 'channel'); if (!(symbol in this.orderbooks)) { const defaultLimit = this.safeInteger(this.options, 'watchOrderBookLimit', 1000); const subscription = client.subscriptions[topic]; const limit = this.safeInteger(subscription, 'limit', defaultLimit); this.orderbooks[symbol] = this.orderBook({}, limit); } const orderbook = this.orderbooks[symbol]; const timestamp = this.safeInteger(data, 'timestamp'); const snapshot = this.parseOrderBook(data, symbol, timestamp, 'bids', 'asks'); orderbook.reset(snapshot); client.resolve(orderbook, topic); } /** * @method * @name derive#watchTicker * @see https://docs.derive.xyz/reference/ticker-instrument_name-interval * @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 exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async watchTicker(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const topic = 'ticker.' + market['id'] + '.100'; const request = { 'method': 'subscribe', 'params': { 'channels': [ topic, ], }, }; const subscription = { 'name': topic, 'symbol': symbol, 'params': params, }; return await this.watchPublic(topic, request, subscription); } handleTicker(client, message) { // // { // method: 'subscription', // params: { // channel: 'ticker.BTC-PERP.100', // data: { // timestamp: 1738485104439, // instrument_ticker: { // instrument_type: 'perp', // instrument_name: 'BTC-PERP', // scheduled_activation: 1701840228, // scheduled_deactivation: '9223372036854775807', // is_active: true, // tick_size: '0.1', // minimum_amount: '0.01', // maximum_amount: '10000', // amount_step: '0.001', // mark_price_fee_rate_cap: '0', // maker_fee_rate: '0.0001', // taker_fee_rate: '0.0003', // base_fee: '0.1', // base_currency: 'BTC', // quote_currency: 'USD', // option_details: null, // perp_details: { // index: 'BTC-USD', // max_rate_per_hour: '0.004', // min_rate_per_hour: '-0.004', // static_interest_rate: '0.0000125', // aggregate_funding: '10581.779418721074588722', // funding_rate: '0.000024792239208858' // }, // erc20_details: null, // base_asset_address: '0xDBa83C0C654DB1cd914FA2710bA743e925B53086', // base_asset_sub_id: '0', // pro_rata_fraction: '0', // fifo_min_allocation: '0', // pro_rata_amount_step: '0.1', // best_ask_amount: '0.131', // best_ask_price: '99898.6', // best_bid_amount: '0.056', // best_bid_price: '99889.1', // five_percent_bid_depth: '11.817', // five_percent_ask_depth: '9.116', // option_pricing: null, // index_price: '99883.8', // mark_price: '99897.52408421244763303548098', // stats: { // contract_volume: '92.395', // num_trades: '2924', // open_interest: '33.743468027373780786', // high: '102320.4', // low: '99064.3', // percent_change: '-0.021356', // usd_change: '-2178' // }, // timestamp: 1738485165881, // min_price: '97939.1', // max_price: '101895.2' // } // } // } // } // const params = this.safeDict(message, 'params'); const rawData = this.safeDict(params, 'data'); const data = this.safeDict(rawData, 'instrument_ticker'); const topic = this.safeValue(params, 'channel'); const ticker = this.parseTicker(data); this.tickers[ticker['symbol']] = ticker; client.resolve(ticker, topic); return message; } /** * @method * @name derive#unWatchOrderBook * @description unsubscribe from the orderbook channel * @param {string} symbol unified symbol of the market to fetch the order book for * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.limit] orderbook limit, default is undefined * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols */ async unWatchOrderBook(symbol, params = {}) { await this.loadMarkets(); let limit = this.safeInteger(params, 'limit'); if (limit === undefined) { limit = 10; } const market = this.market(symbol); const topic = 'orderbook.' + market['id'] + '.10.' + this.numberToString(limit); const messageHash = 'unwatch' + topic; const request = { 'method': 'unsubscribe', 'params': { 'channels': [ topic, ], }, }; const subscription = { 'name': topic, }; return await this.unWatchPublic(messageHash, request, subscription); } /** * @method * @name derive#unWatchTrades * @description unsubscribe from the trades channel * @param {string} symbol unified symbol of the market to unwatch the trades for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {any} status of the unwatch request */ async unWatchTrades(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const topic = 'trades.' + market['id']; const messageHah = 'unwatch' + topic; const request = { 'method': 'unsubscribe', 'params': { 'channels': [ topic, ], }, }; const subscription = { 'name': topic, }; return await this.unWatchPublic(messageHah, request, subscription); } async unWatchPublic(messageHash, message, subscription) { const url = this.urls['api']['ws']; const requestId = this.requestId(url); const request = this.extend(message, { 'id': requestId, }); subscription = this.extend(subscription, { 'id': requestId, 'method': 'unsubscribe', }); return await this.watch(url, messageHash, request, messageHash, subscription); } handleOrderBookUnSubscription(client, topic) { const parsedTopic = topic.split('.'); const marketId = this.safeString(parsedTopic, 1); const market = this.safeMarket(marketId); const symbol = market['symbol']; if (symbol in this.orderbooks) { delete this.orderbooks[symbol]; } if (topic in client.subscriptions) { delete client.subscriptions[topic]; } const error = new UnsubscribeError(this.id + ' orderbook ' + symbol); client.reject(error, topic); client.resolve(error, 'unwatch' + topic); } handleTradesUnSubscription(client, topic) { const parsedTopic = topic.split('.'); const marketId = this.safeString(parsedTopic, 1); const market = this.safeMarket(marketId); const symbol = market['symbol']; if (symbol in this.orderbooks) { delete this.trades[symbol]; } if (topic in client.subscriptions) { delete client.subscriptions[topic]; } const error = new UnsubscribeError(this.id + ' trades ' + symbol); client.reject(error, topic); client.resolve(error, 'unwatch' + topic); } handleUnSubscribe(client, message) { // // { // id: 1, // result: { // status: { 'orderbook.BTC-PERP.10.10': 'ok' }, // remaining_subscriptions: [] // } // } // const result = this.safeDict(message, 'result'); const status = this.safeDict(result, 'status'); if (status !== undefined) { const topics = Object.keys(status); for (let i = 0; i < topics.length; i++) { const topic = topics[i]; if (topic.indexOf('orderbook') >= 0) { this.handleOrderBookUnSubscription(client, topic); } else if (topic.indexOf('trades') >= 0) { this.handleTradesUnSubscription(client, topic); } } } return message; } /** * @method * @name derive#watchTrades * @description watches information on multiple trades made in a market * @see https://docs.derive.xyz/reference/trades-instrument_name * @param {string} symbol unified market symbol of the market trades were made in * @param {int} [since] the earliest time in ms to fetch trades for * @param {int} [limit] the maximum number of trade structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} */ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const topic = 'trades.' + market['id']; const request = { 'method': 'subscribe', 'params': { 'channels': [ topic, ], }, }; const subscription = { 'name': topic, 'symbol': symbol, 'params': params, }; const trades = await this.watchPublic(topic, request, subscription); if (this.newUpdates) { limit = trades.getLimit(market['symbol'], limit); } return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true); } handleTrade(client, message) { // // const params = this.safeDict(message, 'params'); const data = this.safeDict(params, 'data'); const topic = this.safeValue(params, 'channel'); const parsedTopic = topic.split('.'); const marketId = this.safeString(parsedTopic, 1); const market = this.safeMarket(marketId); const symbol = market['symbol']; let tradesArray = this.safeValue(this.trades, symbol); if (tradesArray === undefined) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); tradesArray = new ArrayCache(limit); } for (let i = 0; i < data.length; i++) { const trade = this.parseTrade(data[i]); tradesArray.append(trade); } this.trades[symbol] = tradesArray; client.resolve(tradesArray, topic); } async authenticate(params = {}) { this.checkRequiredCredentials(); const url = this.urls['api']['ws']; const client = this.client(url); const messageHash = 'authenticated'; const future = client.future(messageHash); const authenticated = this.safeValue(client.subscriptions, messageHash); if (authenticated === undefined) { const requestId = this.requestId(url); const now = this.milliseconds().toString(); const signature = this.signMessage(now, this.privateKey); const deriveWalletAddress = this.safeString(this.options, 'deriveWalletAddress'); const request = { 'id': requestId, 'method': 'public/login', 'params': { 'wallet': deriveWalletAddress, 'timestamp': now, 'signature': signature, }, }; // const subscription: Dict = { // 'name': topic, // 'symbol': symbol, // 'params': params, // }; const message = this.extend(request, params); this.watch(url, messageHash, message, messageHash, message); } return await future; } async watchPrivate(messageHash, message, subscription) { await this.authenticate(); const url = this.urls['api']['ws']; const requestId = this.requestId(url); const request = this.extend(message, { 'id': requestId, }); subscription = this.extend(subscription, { 'id': requestId, 'method': 'subscribe', }); return await this.watch(url, messageHash, request, messageHash, subscription); } /** * @method * @name derive#watchOrders * @see https://docs.derive.xyz/reference/subaccount_id-orders * @description watches information on multiple orders made by the user * @param {string} symbol unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.subaccount_id] *required* the subaccount id * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let subaccountId = undefined; [subaccountId, params] = this.handleDeriveSubaccountId('watchOrders', params); const topic = this.numberToString(subaccountId) + '.orders'; let messageHash = topic; if (symbol !== undefined) { const market = this.market(symbol); symbol = market['symbol']; messageHash += ':' + symbol; } const request = { 'method': 'subscribe', 'params': { 'channels': [ topic, ], }, }; const subscription = { 'name': topic, 'params': params, }; const message = this.extend(request, params); const orders = await this.watchPrivate(messageHash, message, subscription); if (this.newUpdates) { limit = orders.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true); } handleOrder(client, message) { // // { // method: 'subscription', // params: { // channel: '130837.orders', // data: [ // { // subaccount_id: 130837, // order_id: '1f44c564-5658-4b69-b8c4-4019924207d5', // instrument_name: 'BTC-PERP', // direction: 'buy', // label: 'test1234', // quote_id: null, // creation_timestamp: 1738578974146, // last_update_timestamp: 1738578974146, // limit_price: '10000', // amount: '0.01', // filled_amount: '0', // average_price: '0', // order_fee: '0', // order_type: 'limit', // time_in_force: 'post_only', // order_status: 'untriggered', // max_fee: '219', // signature_expiry_sec: 1746354973, // nonce: 1738578973570, // signer: '0x30CB7B06AdD6749BbE146A6827502B8f2a79269A', // signature: '0xc6927095f74a0d3b1aeef8c0579d120056530479f806e9d2e6616df742a8934c69046361beae833b32b25c0145e318438d7d1624bb835add956f63aa37192f571c', // cancel_reason: '', // mmp: false, // is_transfer: false, // replaced_order_id: null, // trigger_type: 'stoploss', // trigger_price_type: 'mark', // trigger_price: '102800', // trigger_reject_message: null // } // ] // } // } // const params = this.safeDict(message, 'params'); const topic = this.safeString(params, 'channel'); const rawOrders = this.safeList(params, 'data'); for (let i = 0; i < rawOrders.length; i++) { const data = rawOrders[i]; const parsed = this.parseOrder(data); const symbol = this.safeString(parsed, 'symbol'); const orderId = this.safeString(parsed, 'id'); if (symbol !== undefined) { if (this.orders === undefined) { const limit = this.safeInteger(this.options, 'ordersLimit', 1000); this.orders = new ArrayCacheBySymbolById(limit); } const cachedOrders = this.orders; const orders = this.safeValue(cachedOrders.hashmap, symbol, {}); const order = this.safeValue(orders, orderId); if (order !== undefined) { const fee = this.safeValue(order, 'fee'); if (fee !== undefined) { parsed['fee'] = fee; } const fees = this.safeValue(order, 'fees'); if (fees !== undefined) { parsed['fees'] = fees; } parsed['trades'] = this.safeValue(order, 'trades'); parsed['timestamp'] = this.safeInteger(order, 'timestamp'); parsed['datetime'] = this.safeString(order, 'datetime'); } cachedOrders.append(parsed); const messageHashSymbol = topic + ':' + symbol; client.resolve(this.orders, messageHashSymbol); } } client.resolve(this.orders, topic); } /** * @method * @name derive#watchMyTrades * @see https://docs.derive.xyz/reference/subaccount_id-trades * @description watches information on multiple trades made by the user * @param {string} symbol unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.subaccount_id] *required* the subaccount id * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} */ async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let subaccountId = undefined; [subaccountId, params] = this.handleDeriveSubaccountId('watchMyTrades', params); const topic = this.numberToString(subaccountId) + '.trades'; let messageHash = topic; if (symbol !== undefined) { const market = this.market(symbol); symbol = market['symbol']; messageHash += ':' + symbol; } const request = { 'method': 'subscribe', 'params': { 'channels': [ topic, ], }, }; const subscription = { 'name': topic, 'params': params, }; const message = this.extend(request, params); const trades = await this.watchPrivate(messageHash, message, subscription); if (this.newUpdates) { limit = trades.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true); } handleMyTrade(client, message) { // // let myTrades = this.myTrades; if (myTrades === undefined) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); myTrades = new ArrayCacheBySymbolById(limit); } const params = this.safeDict(message, 'params'); const topic = this.safeString(params, 'channel'); const rawTrades = this.safeList(params, 'data'); for (let i = 0; i < rawTrades.length; i++) { const trade = this.parseTrade(message); myTrades.append(trade); client.resolve(myTrades, topic); const messageHash = topic + trade['symbol']; client.resolve(myTrades, messageHash); } } handleErrorMessage(client, message) { // // { // id: '690c6276-0fc6-4121-aafa-f28bf5adedcb', // error: { code: -32600, message: 'Invalid Request' } // } // if (!('error' in message)) { return false; } const errorMessage = this.safeDict(message, 'error'); const errorCode = this.safeString(errorMessage, 'code'); try { if (errorCode !== undefined) { const feedback = this.id + ' ' + this.json(message); this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback); throw new ExchangeError(feedback); } return false; } catch (error) { if (error instanceof AuthenticationError) { const messageHash = 'authenticated'; client.reject(error, messageHash); if (messageHash in client.subscriptions) { delete client.subscriptions[messageHash]; } } else { client.reject(error); } return true; } } handleMessage(client, message) { if (this.handleErrorMessage(client, message)) { return; } const methods = { 'orderbook': this.handleOrderBook, 'ticker': this.handleTicker, 'trades': this.handleTrade, 'orders': this.handleOrder, 'mytrades': this.handleMyTrade, }; let event = undefined; const params = this.safeDict(message, 'params'); if (params !== undefined) { const channel = this.safeString(params, 'channel'); if (channel !== undefined) { const parsedChannel = channel.split('.'); if ((channel.indexOf('orders') >= 0) || channel.indexOf('trades') > 0) { event = this.safeString(parsedChannel, 1); // {subaccounr_id}.trades if (event === 'trades') { event = 'mytrades'; } } else { event = this.safeString(parsedChannel, 0); } } } const method = this.safeValue(methods, event); if (method !== undefined) { method.call(this, client, message); return; } if ('id' in message) { const id = this.safeString(message, 'id'); const subscriptionsById = this.indexBy(client.subscriptions, 'id'); const subscription = this.safeValue(subscriptionsById, id, {}); if ('method' in subscription) { if (subscription['method'] === 'public/login') { this.handleAuth(client, message); } else if (subscription['method'] === 'unsubscribe') { this.handleUnSubscribe(client, message); } // could handleSubscribe } } } handleAuth(client, message) { // // { // id: 1, // result: [ 130837 ] // } // const messageHash = 'authenticated'; const ids = this.safeList(message, 'result'); if (ids.length > 0) { // client.resolve (message, messageHash); const future = this.safeValue(client.futures, 'authenticated'); future.resolve(true); } else { const error = new AuthenticationError(this.json(message)); client.reject(error, messageHash); // allows further authentication attempts if (messageHash in client.subscriptions) { delete client.subscriptions['authenticated']; } } } }