ccxt 4.4.64__py2.py3-none-any.whl → 4.4.67__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. ccxt/__init__.py +5 -3
  2. ccxt/abstract/cryptomus.py +20 -0
  3. ccxt/abstract/derive.py +117 -0
  4. ccxt/abstract/tradeogre.py +1 -0
  5. ccxt/abstract/whitebit.py +16 -0
  6. ccxt/async_support/__init__.py +5 -3
  7. ccxt/async_support/base/exchange.py +1 -1
  8. ccxt/async_support/binance.py +5 -3
  9. ccxt/async_support/bitget.py +22 -12
  10. ccxt/async_support/bitrue.py +5 -2
  11. ccxt/async_support/cryptocom.py +2 -0
  12. ccxt/async_support/cryptomus.py +1041 -0
  13. ccxt/async_support/derive.py +2530 -0
  14. ccxt/async_support/gate.py +5 -1
  15. ccxt/async_support/htx.py +19 -5
  16. ccxt/async_support/hyperliquid.py +108 -68
  17. ccxt/async_support/paradex.py +51 -12
  18. ccxt/async_support/tradeogre.py +131 -13
  19. ccxt/async_support/whitebit.py +276 -2
  20. ccxt/base/errors.py +0 -6
  21. ccxt/base/exchange.py +8 -1
  22. ccxt/binance.py +5 -3
  23. ccxt/bitget.py +22 -12
  24. ccxt/bitrue.py +5 -2
  25. ccxt/cryptocom.py +2 -0
  26. ccxt/cryptomus.py +1041 -0
  27. ccxt/derive.py +2529 -0
  28. ccxt/gate.py +5 -1
  29. ccxt/htx.py +19 -5
  30. ccxt/hyperliquid.py +108 -68
  31. ccxt/paradex.py +51 -12
  32. ccxt/pro/__init__.py +3 -3
  33. ccxt/pro/bybit.py +3 -2
  34. ccxt/pro/derive.py +704 -0
  35. ccxt/pro/gate.py +5 -2
  36. ccxt/pro/hyperliquid.py +3 -3
  37. ccxt/test/tests_async.py +36 -3
  38. ccxt/test/tests_sync.py +36 -3
  39. ccxt/tradeogre.py +131 -13
  40. ccxt/whitebit.py +276 -2
  41. {ccxt-4.4.64.dist-info → ccxt-4.4.67.dist-info}/METADATA +14 -10
  42. {ccxt-4.4.64.dist-info → ccxt-4.4.67.dist-info}/RECORD +45 -42
  43. ccxt/abstract/currencycom.py +0 -68
  44. ccxt/async_support/currencycom.py +0 -2070
  45. ccxt/currencycom.py +0 -2070
  46. ccxt/pro/currencycom.py +0 -536
  47. {ccxt-4.4.64.dist-info → ccxt-4.4.67.dist-info}/LICENSE.txt +0 -0
  48. {ccxt-4.4.64.dist-info → ccxt-4.4.67.dist-info}/WHEEL +0 -0
  49. {ccxt-4.4.64.dist-info → ccxt-4.4.67.dist-info}/top_level.txt +0 -0
ccxt/pro/derive.py ADDED
@@ -0,0 +1,704 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+
6
+ import ccxt.async_support
7
+ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById
8
+ from ccxt.base.types import Any, Int, Order, OrderBook, Str, Ticker, Trade
9
+ from ccxt.async_support.base.ws.client import Client
10
+ from typing import List
11
+ from ccxt.base.errors import ExchangeError
12
+ from ccxt.base.errors import AuthenticationError
13
+ from ccxt.base.errors import UnsubscribeError
14
+
15
+
16
+ class derive(ccxt.async_support.derive):
17
+
18
+ def describe(self) -> Any:
19
+ return self.deep_extend(super(derive, self).describe(), {
20
+ 'has': {
21
+ 'ws': False,
22
+ 'watchBalance': False,
23
+ 'watchMyTrades': True,
24
+ 'watchOHLCV': False,
25
+ 'watchOrderBook': True,
26
+ 'watchOrders': True,
27
+ 'watchTicker': True,
28
+ 'watchTickers': False,
29
+ 'watchBidsAsks': False,
30
+ 'watchTrades': True,
31
+ 'watchTradesForSymbols': False,
32
+ 'watchPositions': False,
33
+ },
34
+ 'urls': {
35
+ 'api': {
36
+ 'ws': 'wss://api.lyra.finance/ws',
37
+ },
38
+ 'test': {
39
+ 'ws': 'wss://api-demo.lyra.finance/ws',
40
+ },
41
+ },
42
+ 'options': {
43
+ 'tradesLimit': 1000,
44
+ 'ordersLimit': 1000,
45
+ 'requestId': {},
46
+ },
47
+ 'streaming': {
48
+ 'keepAlive': 9000,
49
+ },
50
+ 'exceptions': {
51
+ 'ws': {
52
+ 'exact': {},
53
+ },
54
+ },
55
+ })
56
+
57
+ def request_id(self, url):
58
+ options = self.safe_value(self.options, 'requestId', {})
59
+ previousValue = self.safe_integer(options, url, 0)
60
+ newValue = self.sum(previousValue, 1)
61
+ self.options['requestId'][url] = newValue
62
+ return newValue
63
+
64
+ async def watch_public(self, messageHash, message, subscription):
65
+ url = self.urls['api']['ws']
66
+ requestId = self.request_id(url)
67
+ request = self.extend(message, {
68
+ 'id': requestId,
69
+ })
70
+ subscription = self.extend(subscription, {
71
+ 'id': requestId,
72
+ 'method': 'subscribe',
73
+ })
74
+ return await self.watch(url, messageHash, request, messageHash, subscription)
75
+
76
+ async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
77
+ """
78
+
79
+ https://docs.derive.xyz/reference/orderbook-instrument_name-group-depth
80
+
81
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
82
+ :param str symbol: unified symbol of the market to fetch the order book for
83
+ :param int [limit]: the maximum amount of order book entries to return.
84
+ :param dict [params]: extra parameters specific to the exchange API endpoint
85
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
86
+ """
87
+ await self.load_markets()
88
+ if limit is None:
89
+ limit = 10
90
+ market = self.market(symbol)
91
+ topic = 'orderbook.' + market['id'] + '.10.' + self.number_to_string(limit)
92
+ request: dict = {
93
+ 'method': 'subscribe',
94
+ 'params': {
95
+ 'channels': [
96
+ topic,
97
+ ],
98
+ },
99
+ }
100
+ subscription: dict = {
101
+ 'name': topic,
102
+ 'symbol': symbol,
103
+ 'limit': limit,
104
+ 'params': params,
105
+ }
106
+ orderbook = await self.watch_public(topic, request, subscription)
107
+ return orderbook.limit()
108
+
109
+ def handle_order_book(self, client: Client, message):
110
+ #
111
+ # {
112
+ # method: 'subscription',
113
+ # params: {
114
+ # channel: 'orderbook.BTC-PERP.10.1',
115
+ # data: {
116
+ # timestamp: 1738331231506,
117
+ # instrument_name: 'BTC-PERP',
118
+ # publish_id: 628419,
119
+ # bids: [['104669', '40']],
120
+ # asks: [['104736', '40']]
121
+ # }
122
+ # }
123
+ # }
124
+ #
125
+ params = self.safe_dict(message, 'params')
126
+ data = self.safe_dict(params, 'data')
127
+ marketId = self.safe_string(data, 'instrument_name')
128
+ market = self.safe_market(marketId)
129
+ symbol = market['symbol']
130
+ topic = self.safe_string(params, 'channel')
131
+ if not (symbol in self.orderbooks):
132
+ defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000)
133
+ subscription = client.subscriptions[topic]
134
+ limit = self.safe_integer(subscription, 'limit', defaultLimit)
135
+ self.orderbooks[symbol] = self.order_book({}, limit)
136
+ orderbook = self.orderbooks[symbol]
137
+ timestamp = self.safe_integer(data, 'timestamp')
138
+ snapshot = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks')
139
+ orderbook.reset(snapshot)
140
+ client.resolve(orderbook, topic)
141
+
142
+ async def watch_ticker(self, symbol: str, params={}) -> Ticker:
143
+ """
144
+
145
+ https://docs.derive.xyz/reference/ticker-instrument_name-interval
146
+
147
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
148
+ :param str symbol: unified symbol of the market to fetch the ticker for
149
+ :param dict [params]: extra parameters specific to the exchange API endpoint
150
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
151
+ """
152
+ await self.load_markets()
153
+ market = self.market(symbol)
154
+ topic = 'ticker.' + market['id'] + '.100'
155
+ request: dict = {
156
+ 'method': 'subscribe',
157
+ 'params': {
158
+ 'channels': [
159
+ topic,
160
+ ],
161
+ },
162
+ }
163
+ subscription: dict = {
164
+ 'name': topic,
165
+ 'symbol': symbol,
166
+ 'params': params,
167
+ }
168
+ return await self.watch_public(topic, request, subscription)
169
+
170
+ def handle_ticker(self, client: Client, message):
171
+ #
172
+ # {
173
+ # method: 'subscription',
174
+ # params: {
175
+ # channel: 'ticker.BTC-PERP.100',
176
+ # data: {
177
+ # timestamp: 1738485104439,
178
+ # instrument_ticker: {
179
+ # instrument_type: 'perp',
180
+ # instrument_name: 'BTC-PERP',
181
+ # scheduled_activation: 1701840228,
182
+ # scheduled_deactivation: '9223372036854775807',
183
+ # is_active: True,
184
+ # tick_size: '0.1',
185
+ # minimum_amount: '0.01',
186
+ # maximum_amount: '10000',
187
+ # amount_step: '0.001',
188
+ # mark_price_fee_rate_cap: '0',
189
+ # maker_fee_rate: '0.0001',
190
+ # taker_fee_rate: '0.0003',
191
+ # base_fee: '0.1',
192
+ # base_currency: 'BTC',
193
+ # quote_currency: 'USD',
194
+ # option_details: null,
195
+ # perp_details: {
196
+ # index: 'BTC-USD',
197
+ # max_rate_per_hour: '0.004',
198
+ # min_rate_per_hour: '-0.004',
199
+ # static_interest_rate: '0.0000125',
200
+ # aggregate_funding: '10581.779418721074588722',
201
+ # funding_rate: '0.000024792239208858'
202
+ # },
203
+ # erc20_details: null,
204
+ # base_asset_address: '0xDBa83C0C654DB1cd914FA2710bA743e925B53086',
205
+ # base_asset_sub_id: '0',
206
+ # pro_rata_fraction: '0',
207
+ # fifo_min_allocation: '0',
208
+ # pro_rata_amount_step: '0.1',
209
+ # best_ask_amount: '0.131',
210
+ # best_ask_price: '99898.6',
211
+ # best_bid_amount: '0.056',
212
+ # best_bid_price: '99889.1',
213
+ # five_percent_bid_depth: '11.817',
214
+ # five_percent_ask_depth: '9.116',
215
+ # option_pricing: null,
216
+ # index_price: '99883.8',
217
+ # mark_price: '99897.52408421244763303548098',
218
+ # stats: {
219
+ # contract_volume: '92.395',
220
+ # num_trades: '2924',
221
+ # open_interest: '33.743468027373780786',
222
+ # high: '102320.4',
223
+ # low: '99064.3',
224
+ # percent_change: '-0.021356',
225
+ # usd_change: '-2178'
226
+ # },
227
+ # timestamp: 1738485165881,
228
+ # min_price: '97939.1',
229
+ # max_price: '101895.2'
230
+ # }
231
+ # }
232
+ # }
233
+ # }
234
+ #
235
+ params = self.safe_dict(message, 'params')
236
+ rawData = self.safe_dict(params, 'data')
237
+ data = self.safe_dict(rawData, 'instrument_ticker')
238
+ topic = self.safe_value(params, 'channel')
239
+ ticker = self.parse_ticker(data)
240
+ self.tickers[ticker['symbol']] = ticker
241
+ client.resolve(ticker, topic)
242
+ return message
243
+
244
+ async def un_watch_order_book(self, symbol: str, params={}) -> Any:
245
+ """
246
+ unsubscribe from the orderbook channel
247
+ :param str symbol: unified symbol of the market to fetch the order book for
248
+ :param dict [params]: extra parameters specific to the exchange API endpoint
249
+ :param int [params.limit]: orderbook limit, default is None
250
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
251
+ """
252
+ await self.load_markets()
253
+ limit = self.safe_integer(params, 'limit')
254
+ if limit is None:
255
+ limit = 10
256
+ market = self.market(symbol)
257
+ topic = 'orderbook.' + market['id'] + '.10.' + self.number_to_string(limit)
258
+ messageHash = 'unwatch' + topic
259
+ request: dict = {
260
+ 'method': 'unsubscribe',
261
+ 'params': {
262
+ 'channels': [
263
+ topic,
264
+ ],
265
+ },
266
+ }
267
+ subscription: dict = {
268
+ 'name': topic,
269
+ }
270
+ return await self.un_watch_public(messageHash, request, subscription)
271
+
272
+ async def un_watch_trades(self, symbol: str, params={}) -> Any:
273
+ """
274
+ unsubscribe from the trades channel
275
+ :param str symbol: unified symbol of the market to unwatch the trades for
276
+ :param dict [params]: extra parameters specific to the exchange API endpoint
277
+ :returns any: status of the unwatch request
278
+ """
279
+ await self.load_markets()
280
+ market = self.market(symbol)
281
+ topic = 'trades.' + market['id']
282
+ messageHah = 'unwatch' + topic
283
+ request: dict = {
284
+ 'method': 'unsubscribe',
285
+ 'params': {
286
+ 'channels': [
287
+ topic,
288
+ ],
289
+ },
290
+ }
291
+ subscription: dict = {
292
+ 'name': topic,
293
+ }
294
+ return await self.un_watch_public(messageHah, request, subscription)
295
+
296
+ async def un_watch_public(self, messageHash, message, subscription):
297
+ url = self.urls['api']['ws']
298
+ requestId = self.request_id(url)
299
+ request = self.extend(message, {
300
+ 'id': requestId,
301
+ })
302
+ subscription = self.extend(subscription, {
303
+ 'id': requestId,
304
+ 'method': 'unsubscribe',
305
+ })
306
+ return await self.watch(url, messageHash, request, messageHash, subscription)
307
+
308
+ def handle_order_book_un_subscription(self, client: Client, topic):
309
+ parsedTopic = topic.split('.')
310
+ marketId = self.safe_string(parsedTopic, 1)
311
+ market = self.safe_market(marketId)
312
+ symbol = market['symbol']
313
+ if symbol in self.orderbooks:
314
+ del self.orderbooks[symbol]
315
+ if topic in client.subscriptions:
316
+ del client.subscriptions[topic]
317
+ error = UnsubscribeError(self.id + ' orderbook ' + symbol)
318
+ client.reject(error, topic)
319
+ client.resolve(error, 'unwatch' + topic)
320
+
321
+ def handle_trades_un_subscription(self, client: Client, topic):
322
+ parsedTopic = topic.split('.')
323
+ marketId = self.safe_string(parsedTopic, 1)
324
+ market = self.safe_market(marketId)
325
+ symbol = market['symbol']
326
+ if symbol in self.orderbooks:
327
+ del self.trades[symbol]
328
+ if topic in client.subscriptions:
329
+ del client.subscriptions[topic]
330
+ error = UnsubscribeError(self.id + ' trades ' + symbol)
331
+ client.reject(error, topic)
332
+ client.resolve(error, 'unwatch' + topic)
333
+
334
+ def handle_un_subscribe(self, client: Client, message):
335
+ #
336
+ # {
337
+ # id: 1,
338
+ # result: {
339
+ # status: {'orderbook.BTC-PERP.10.10': 'ok'},
340
+ # remaining_subscriptions: []
341
+ # }
342
+ # }
343
+ #
344
+ result = self.safe_dict(message, 'result')
345
+ status = self.safe_dict(result, 'status')
346
+ if status is not None:
347
+ topics = list(status.keys())
348
+ for i in range(0, len(topics)):
349
+ topic = topics[i]
350
+ if topic.find('orderbook') >= 0:
351
+ self.handle_order_book_un_subscription(client, topic)
352
+ elif topic.find('trades') >= 0:
353
+ self.handle_trades_un_subscription(client, topic)
354
+ return message
355
+
356
+ async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
357
+ """
358
+ watches information on multiple trades made in a market
359
+
360
+ https://docs.derive.xyz/reference/trades-instrument_name
361
+
362
+ :param str symbol: unified market symbol of the market trades were made in
363
+ :param int [since]: the earliest time in ms to fetch trades for
364
+ :param int [limit]: the maximum number of trade structures to retrieve
365
+ :param dict [params]: extra parameters specific to the exchange API endpoint
366
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
367
+ """
368
+ await self.load_markets()
369
+ market = self.market(symbol)
370
+ topic = 'trades.' + market['id']
371
+ request: dict = {
372
+ 'method': 'subscribe',
373
+ 'params': {
374
+ 'channels': [
375
+ topic,
376
+ ],
377
+ },
378
+ }
379
+ subscription: dict = {
380
+ 'name': topic,
381
+ 'symbol': symbol,
382
+ 'params': params,
383
+ }
384
+ trades = await self.watch_public(topic, request, subscription)
385
+ if self.newUpdates:
386
+ limit = trades.getLimit(market['symbol'], limit)
387
+ return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
388
+
389
+ def handle_trade(self, client: Client, message):
390
+ #
391
+ #
392
+ params = self.safe_dict(message, 'params')
393
+ data = self.safe_dict(params, 'data')
394
+ topic = self.safe_value(params, 'channel')
395
+ parsedTopic = topic.split('.')
396
+ marketId = self.safe_string(parsedTopic, 1)
397
+ market = self.safe_market(marketId)
398
+ symbol = market['symbol']
399
+ tradesArray = self.safe_value(self.trades, symbol)
400
+ if tradesArray is None:
401
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
402
+ tradesArray = ArrayCache(limit)
403
+ for i in range(0, len(data)):
404
+ trade = self.parse_trade(data[i])
405
+ tradesArray.append(trade)
406
+ self.trades[symbol] = tradesArray
407
+ client.resolve(tradesArray, topic)
408
+
409
+ async def authenticate(self, params={}):
410
+ self.check_required_credentials()
411
+ url = self.urls['api']['ws']
412
+ client = self.client(url)
413
+ messageHash = 'authenticated'
414
+ future = client.future(messageHash)
415
+ authenticated = self.safe_value(client.subscriptions, messageHash)
416
+ if authenticated is None:
417
+ requestId = self.request_id(url)
418
+ now = str(self.milliseconds())
419
+ signature = self.signMessage(now, self.privateKey)
420
+ contractWalletAddress = self.safe_string(self.options, 'contractWalletAddress')
421
+ request: dict = {
422
+ 'id': requestId,
423
+ 'method': 'public/login',
424
+ 'params': {
425
+ 'wallet': contractWalletAddress,
426
+ 'timestamp': now,
427
+ 'signature': signature,
428
+ },
429
+ }
430
+ # subscription: Dict = {
431
+ # 'name': topic,
432
+ # 'symbol': symbol,
433
+ # 'params': params,
434
+ # }
435
+ message = self.extend(request, params)
436
+ self.watch(url, messageHash, message, messageHash, message)
437
+ return await future
438
+
439
+ async def watch_private(self, messageHash, message, subscription):
440
+ await self.authenticate()
441
+ url = self.urls['api']['ws']
442
+ requestId = self.request_id(url)
443
+ request = self.extend(message, {
444
+ 'id': requestId,
445
+ })
446
+ subscription = self.extend(subscription, {
447
+ 'id': requestId,
448
+ 'method': 'subscribe',
449
+ })
450
+ return await self.watch(url, messageHash, request, messageHash, subscription)
451
+
452
+ async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
453
+ """
454
+
455
+ https://docs.derive.xyz/reference/subaccount_id-orders
456
+
457
+ watches information on multiple orders made by the user
458
+ :param str symbol: unified market symbol of the market orders were made in
459
+ :param int [since]: the earliest time in ms to fetch orders for
460
+ :param int [limit]: the maximum number of order structures to retrieve
461
+ :param dict [params]: extra parameters specific to the exchange API endpoint
462
+ :param str [params.subaccount_id]: *required* the subaccount id
463
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
464
+ """
465
+ await self.load_markets()
466
+ subaccountId = None
467
+ subaccountId, params = self.handleDeriveSubaccountId('watchOrders', params)
468
+ topic = self.number_to_string(subaccountId) + '.orders'
469
+ messageHash = topic
470
+ if symbol is not None:
471
+ market = self.market(symbol)
472
+ symbol = market['symbol']
473
+ messageHash += ':' + symbol
474
+ request: dict = {
475
+ 'method': 'subscribe',
476
+ 'params': {
477
+ 'channels': [
478
+ topic,
479
+ ],
480
+ },
481
+ }
482
+ subscription: dict = {
483
+ 'name': topic,
484
+ 'params': params,
485
+ }
486
+ message = self.extend(request, params)
487
+ orders = await self.watch_private(messageHash, message, subscription)
488
+ if self.newUpdates:
489
+ limit = orders.getLimit(symbol, limit)
490
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True)
491
+
492
+ def handle_order(self, client: Client, message):
493
+ #
494
+ # {
495
+ # method: 'subscription',
496
+ # params: {
497
+ # channel: '130837.orders',
498
+ # data: [
499
+ # {
500
+ # subaccount_id: 130837,
501
+ # order_id: '1f44c564-5658-4b69-b8c4-4019924207d5',
502
+ # instrument_name: 'BTC-PERP',
503
+ # direction: 'buy',
504
+ # label: 'test1234',
505
+ # quote_id: null,
506
+ # creation_timestamp: 1738578974146,
507
+ # last_update_timestamp: 1738578974146,
508
+ # limit_price: '10000',
509
+ # amount: '0.01',
510
+ # filled_amount: '0',
511
+ # average_price: '0',
512
+ # order_fee: '0',
513
+ # order_type: 'limit',
514
+ # time_in_force: 'post_only',
515
+ # order_status: 'untriggered',
516
+ # max_fee: '219',
517
+ # signature_expiry_sec: 1746354973,
518
+ # nonce: 1738578973570,
519
+ # signer: '0x30CB7B06AdD6749BbE146A6827502B8f2a79269A',
520
+ # signature: '0xc6927095f74a0d3b1aeef8c0579d120056530479f806e9d2e6616df742a8934c69046361beae833b32b25c0145e318438d7d1624bb835add956f63aa37192f571c',
521
+ # cancel_reason: '',
522
+ # mmp: False,
523
+ # is_transfer: False,
524
+ # replaced_order_id: null,
525
+ # trigger_type: 'stoploss',
526
+ # trigger_price_type: 'mark',
527
+ # trigger_price: '102800',
528
+ # trigger_reject_message: null
529
+ # }
530
+ # ]
531
+ # }
532
+ # }
533
+ #
534
+ params = self.safe_dict(message, 'params')
535
+ topic = self.safe_string(params, 'channel')
536
+ rawOrders = self.safe_list(params, 'data')
537
+ for i in range(0, len(rawOrders)):
538
+ data = rawOrders[i]
539
+ parsed = self.parse_order(data)
540
+ symbol = self.safe_string(parsed, 'symbol')
541
+ orderId = self.safe_string(parsed, 'id')
542
+ if symbol is not None:
543
+ if self.orders is None:
544
+ limit = self.safe_integer(self.options, 'ordersLimit', 1000)
545
+ self.orders = ArrayCacheBySymbolById(limit)
546
+ cachedOrders = self.orders
547
+ orders = self.safe_value(cachedOrders.hashmap, symbol, {})
548
+ order = self.safe_value(orders, orderId)
549
+ if order is not None:
550
+ fee = self.safe_value(order, 'fee')
551
+ if fee is not None:
552
+ parsed['fee'] = fee
553
+ fees = self.safe_value(order, 'fees')
554
+ if fees is not None:
555
+ parsed['fees'] = fees
556
+ parsed['trades'] = self.safe_value(order, 'trades')
557
+ parsed['timestamp'] = self.safe_integer(order, 'timestamp')
558
+ parsed['datetime'] = self.safe_string(order, 'datetime')
559
+ cachedOrders.append(parsed)
560
+ messageHashSymbol = topic + ':' + symbol
561
+ client.resolve(self.orders, messageHashSymbol)
562
+ client.resolve(self.orders, topic)
563
+
564
+ async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
565
+ """
566
+
567
+ https://docs.derive.xyz/reference/subaccount_id-trades
568
+
569
+ watches information on multiple trades made by the user
570
+ :param str symbol: unified market symbol of the market orders were made in
571
+ :param int [since]: the earliest time in ms to fetch orders for
572
+ :param int [limit]: the maximum number of order structures to retrieve
573
+ :param dict [params]: extra parameters specific to the exchange API endpoint
574
+ :param str [params.subaccount_id]: *required* the subaccount id
575
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
576
+ """
577
+ await self.load_markets()
578
+ subaccountId = None
579
+ subaccountId, params = self.handleDeriveSubaccountId('watchMyTrades', params)
580
+ topic = self.number_to_string(subaccountId) + '.trades'
581
+ messageHash = topic
582
+ if symbol is not None:
583
+ market = self.market(symbol)
584
+ symbol = market['symbol']
585
+ messageHash += ':' + symbol
586
+ request: dict = {
587
+ 'method': 'subscribe',
588
+ 'params': {
589
+ 'channels': [
590
+ topic,
591
+ ],
592
+ },
593
+ }
594
+ subscription: dict = {
595
+ 'name': topic,
596
+ 'params': params,
597
+ }
598
+ message = self.extend(request, params)
599
+ trades = await self.watch_private(messageHash, message, subscription)
600
+ if self.newUpdates:
601
+ limit = trades.getLimit(symbol, limit)
602
+ return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
603
+
604
+ def handle_my_trade(self, client: Client, message):
605
+ #
606
+ #
607
+ myTrades = self.myTrades
608
+ if myTrades is None:
609
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
610
+ myTrades = ArrayCacheBySymbolById(limit)
611
+ params = self.safe_dict(message, 'params')
612
+ topic = self.safe_string(params, 'channel')
613
+ rawTrades = self.safe_list(params, 'data')
614
+ for i in range(0, len(rawTrades)):
615
+ trade = self.parse_trade(message)
616
+ myTrades.append(trade)
617
+ client.resolve(myTrades, topic)
618
+ messageHash = topic + trade['symbol']
619
+ client.resolve(myTrades, messageHash)
620
+
621
+ def handle_error_message(self, client: Client, message):
622
+ #
623
+ # {
624
+ # id: '690c6276-0fc6-4121-aafa-f28bf5adedcb',
625
+ # error: {code: -32600, message: 'Invalid Request'}
626
+ # }
627
+ #
628
+ if not ('error' in message):
629
+ return False
630
+ errorMessage = self.safe_dict(message, 'error')
631
+ errorCode = self.safe_string(errorMessage, 'code')
632
+ try:
633
+ if errorCode is not None:
634
+ feedback = self.id + ' ' + self.json(message)
635
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
636
+ raise ExchangeError(feedback)
637
+ return False
638
+ except Exception as error:
639
+ if isinstance(error, AuthenticationError):
640
+ messageHash = 'authenticated'
641
+ client.reject(error, messageHash)
642
+ if messageHash in client.subscriptions:
643
+ del client.subscriptions[messageHash]
644
+ else:
645
+ client.reject(error)
646
+ return True
647
+
648
+ def handle_message(self, client: Client, message):
649
+ if self.handle_error_message(client, message):
650
+ return
651
+ methods: dict = {
652
+ 'orderbook': self.handle_order_book,
653
+ 'ticker': self.handle_ticker,
654
+ 'trades': self.handle_trade,
655
+ 'orders': self.handle_order,
656
+ 'mytrades': self.handle_my_trade,
657
+ }
658
+ event = None
659
+ params = self.safe_dict(message, 'params')
660
+ if params is not None:
661
+ channel = self.safe_string(params, 'channel')
662
+ if channel is not None:
663
+ parsedChannel = channel.split('.')
664
+ if (channel.find('orders') >= 0) or channel.find('trades') > 0:
665
+ event = self.safe_string(parsedChannel, 1)
666
+ # {subaccounr_id}.trades
667
+ if event == 'trades':
668
+ event = 'mytrades'
669
+ else:
670
+ event = self.safe_string(parsedChannel, 0)
671
+ method = self.safe_value(methods, event)
672
+ if method is not None:
673
+ method(client, message)
674
+ return
675
+ if 'id' in message:
676
+ id = self.safe_string(message, 'id')
677
+ subscriptionsById = self.index_by(client.subscriptions, 'id')
678
+ subscription = self.safe_value(subscriptionsById, id, {})
679
+ if 'method' in subscription:
680
+ if subscription['method'] == 'public/login':
681
+ self.handle_auth(client, message)
682
+ elif subscription['method'] == 'unsubscribe':
683
+ self.handle_un_subscribe(client, message)
684
+ # could handleSubscribe
685
+
686
+ def handle_auth(self, client: Client, message):
687
+ #
688
+ # {
689
+ # id: 1,
690
+ # result: [130837]
691
+ # }
692
+ #
693
+ messageHash = 'authenticated'
694
+ ids = self.safe_list(message, 'result')
695
+ if len(ids) > 0:
696
+ # client.resolve(message, messageHash)
697
+ future = self.safe_value(client.futures, 'authenticated')
698
+ future.resolve(True)
699
+ else:
700
+ error = AuthenticationError(self.json(message))
701
+ client.reject(error, messageHash)
702
+ # allows further authentication attempts
703
+ if messageHash in client.subscriptions:
704
+ del client.subscriptions['authenticated']