ccxt 4.4.63__py2.py3-none-any.whl → 4.4.68__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 (60) hide show
  1. ccxt/__init__.py +5 -3
  2. ccxt/abstract/binance.py +1 -0
  3. ccxt/abstract/binancecoinm.py +1 -0
  4. ccxt/abstract/binanceus.py +1 -0
  5. ccxt/abstract/binanceusdm.py +1 -0
  6. ccxt/abstract/cryptomus.py +20 -0
  7. ccxt/abstract/derive.py +117 -0
  8. ccxt/abstract/tradeogre.py +1 -0
  9. ccxt/abstract/whitebit.py +16 -0
  10. ccxt/async_support/__init__.py +5 -3
  11. ccxt/async_support/base/exchange.py +6 -5
  12. ccxt/async_support/binance.py +8 -6
  13. ccxt/async_support/bitget.py +22 -12
  14. ccxt/async_support/bitrue.py +6 -3
  15. ccxt/async_support/bybit.py +1 -1
  16. ccxt/async_support/coinbase.py +73 -2
  17. ccxt/async_support/cryptocom.py +2 -0
  18. ccxt/async_support/cryptomus.py +1041 -0
  19. ccxt/async_support/derive.py +2530 -0
  20. ccxt/async_support/gate.py +5 -1
  21. ccxt/async_support/htx.py +19 -5
  22. ccxt/async_support/hyperliquid.py +108 -68
  23. ccxt/async_support/luno.py +113 -1
  24. ccxt/async_support/paradex.py +51 -12
  25. ccxt/async_support/tradeogre.py +132 -13
  26. ccxt/async_support/whitebit.py +276 -2
  27. ccxt/base/exchange.py +13 -4
  28. ccxt/binance.py +8 -6
  29. ccxt/bitget.py +22 -12
  30. ccxt/bitrue.py +6 -3
  31. ccxt/bybit.py +1 -1
  32. ccxt/coinbase.py +73 -2
  33. ccxt/cryptocom.py +2 -0
  34. ccxt/cryptomus.py +1041 -0
  35. ccxt/derive.py +2529 -0
  36. ccxt/gate.py +5 -1
  37. ccxt/htx.py +19 -5
  38. ccxt/hyperliquid.py +108 -68
  39. ccxt/luno.py +113 -1
  40. ccxt/paradex.py +51 -12
  41. ccxt/pro/__init__.py +3 -3
  42. ccxt/pro/bitopro.py +1 -1
  43. ccxt/pro/bybit.py +3 -2
  44. ccxt/pro/derive.py +704 -0
  45. ccxt/pro/gate.py +8 -1
  46. ccxt/pro/hyperliquid.py +3 -3
  47. ccxt/pro/vertex.py +5 -0
  48. ccxt/test/tests_async.py +36 -3
  49. ccxt/test/tests_sync.py +36 -3
  50. ccxt/tradeogre.py +132 -13
  51. ccxt/whitebit.py +276 -2
  52. {ccxt-4.4.63.dist-info → ccxt-4.4.68.dist-info}/METADATA +16 -12
  53. {ccxt-4.4.63.dist-info → ccxt-4.4.68.dist-info}/RECORD +56 -53
  54. ccxt/abstract/currencycom.py +0 -68
  55. ccxt/async_support/currencycom.py +0 -2070
  56. ccxt/currencycom.py +0 -2070
  57. ccxt/pro/currencycom.py +0 -536
  58. {ccxt-4.4.63.dist-info → ccxt-4.4.68.dist-info}/LICENSE.txt +0 -0
  59. {ccxt-4.4.63.dist-info → ccxt-4.4.68.dist-info}/WHEEL +0 -0
  60. {ccxt-4.4.63.dist-info → ccxt-4.4.68.dist-info}/top_level.txt +0 -0
ccxt/derive.py ADDED
@@ -0,0 +1,2529 @@
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
+ from ccxt.base.exchange import Exchange
7
+ from ccxt.abstract.derive import ImplicitAPI
8
+ from ccxt.base.types import Any, Balances, Bool, Currencies, Currency, Int, Market, MarketType, Num, Order, OrderSide, OrderType, Str, Strings, Ticker, FundingRate, Trade, Transaction
9
+ from typing import List
10
+ from ccxt.base.errors import ExchangeError
11
+ from ccxt.base.errors import AuthenticationError
12
+ from ccxt.base.errors import ArgumentsRequired
13
+ from ccxt.base.errors import BadRequest
14
+ from ccxt.base.errors import InsufficientFunds
15
+ from ccxt.base.errors import InvalidOrder
16
+ from ccxt.base.errors import OrderNotFound
17
+ from ccxt.base.errors import RateLimitExceeded
18
+ from ccxt.base.decimal_to_precision import TICK_SIZE
19
+ from ccxt.base.precise import Precise
20
+
21
+
22
+ class derive(Exchange, ImplicitAPI):
23
+
24
+ def describe(self) -> Any:
25
+ return self.deep_extend(super(derive, self).describe(), {
26
+ 'id': 'derive',
27
+ 'name': 'derive',
28
+ 'countries': [],
29
+ 'version': 'v1',
30
+ 'rateLimit': 50,
31
+ 'certified': False,
32
+ 'pro': True,
33
+ 'dex': True,
34
+ 'has': {
35
+ 'CORS': None,
36
+ 'spot': False,
37
+ 'margin': False,
38
+ 'swap': False,
39
+ 'future': False,
40
+ 'option': False,
41
+ 'addMargin': False,
42
+ 'borrowCrossMargin': False,
43
+ 'borrowIsolatedMargin': False,
44
+ 'cancelAllOrders': True,
45
+ 'cancelAllOrdersAfter': False,
46
+ 'cancelOrder': True,
47
+ 'cancelOrders': False,
48
+ 'cancelOrdersForSymbols': False,
49
+ 'closeAllPositions': False,
50
+ 'closePosition': False,
51
+ 'createMarketBuyOrderWithCost': False,
52
+ 'createMarketOrderWithCost': False,
53
+ 'createMarketSellOrderWithCost': False,
54
+ 'createOrder': True,
55
+ 'createOrders': False,
56
+ 'createReduceOnlyOrder': False,
57
+ 'createStopOrder': False,
58
+ 'createTriggerOrder': False,
59
+ 'editOrder': True,
60
+ 'fetchAccounts': False,
61
+ 'fetchBalance': True,
62
+ 'fetchBorrowInterest': False,
63
+ 'fetchBorrowRateHistories': False,
64
+ 'fetchBorrowRateHistory': False,
65
+ 'fetchCanceledAndClosedOrders': False,
66
+ 'fetchCanceledOrders': True,
67
+ 'fetchClosedOrders': True,
68
+ 'fetchCrossBorrowRate': False,
69
+ 'fetchCrossBorrowRates': False,
70
+ 'fetchCurrencies': True,
71
+ 'fetchDepositAddress': False,
72
+ 'fetchDepositAddresses': False,
73
+ 'fetchDeposits': True,
74
+ 'fetchDepositWithdrawFee': 'emulated',
75
+ 'fetchDepositWithdrawFees': False,
76
+ 'fetchFundingHistory': True,
77
+ 'fetchFundingRate': True,
78
+ 'fetchFundingRateHistory': True,
79
+ 'fetchFundingRates': False,
80
+ 'fetchIndexOHLCV': False,
81
+ 'fetchIsolatedBorrowRate': False,
82
+ 'fetchIsolatedBorrowRates': False,
83
+ 'fetchLedger': True,
84
+ 'fetchLeverage': False,
85
+ 'fetchLeverageTiers': False,
86
+ 'fetchLiquidations': False,
87
+ 'fetchMarginMode': None,
88
+ 'fetchMarketLeverageTiers': False,
89
+ 'fetchMarkets': True,
90
+ 'fetchMarkOHLCV': False,
91
+ 'fetchMyLiquidations': False,
92
+ 'fetchMyTrades': True,
93
+ 'fetchOHLCV': False,
94
+ 'fetchOpenInterest': False,
95
+ 'fetchOpenInterestHistory': False,
96
+ 'fetchOpenInterests': False,
97
+ 'fetchOpenOrders': True,
98
+ 'fetchOrder': False,
99
+ 'fetchOrderBook': False,
100
+ 'fetchOrders': True,
101
+ 'fetchOrderTrades': True,
102
+ 'fetchPosition': False,
103
+ 'fetchPositionMode': False,
104
+ 'fetchPositions': True,
105
+ 'fetchPositionsRisk': False,
106
+ 'fetchPremiumIndexOHLCV': False,
107
+ 'fetchTicker': True,
108
+ 'fetchTickers': False,
109
+ 'fetchTime': True,
110
+ 'fetchTrades': True,
111
+ 'fetchTradingFee': False,
112
+ 'fetchTradingFees': False,
113
+ 'fetchTransfer': False,
114
+ 'fetchTransfers': False,
115
+ 'fetchWithdrawal': False,
116
+ 'fetchWithdrawals': True,
117
+ 'reduceMargin': False,
118
+ 'repayCrossMargin': False,
119
+ 'repayIsolatedMargin': False,
120
+ 'sandbox': True,
121
+ 'setLeverage': False,
122
+ 'setMarginMode': False,
123
+ 'setPositionMode': False,
124
+ 'transfer': False,
125
+ 'withdraw': False,
126
+ },
127
+ 'timeframes': {
128
+ '1m': '1m',
129
+ '3m': '3m',
130
+ '5m': '5m',
131
+ '15m': '15m',
132
+ '30m': '30m',
133
+ '1h': '1h',
134
+ '2h': '2h',
135
+ '4h': '4h',
136
+ '8h': '8h',
137
+ '12h': '12h',
138
+ '1d': '1d',
139
+ '3d': '3d',
140
+ '1w': '1w',
141
+ '1M': '1M',
142
+ },
143
+ 'hostname': 'derive.xyz',
144
+ 'urls': {
145
+ 'logo': 'https://github.com/user-attachments/assets/f835b95f-033a-43dd-b6bb-24e698fc498c',
146
+ 'api': {
147
+ 'public': 'https://api.lyra.finance/public',
148
+ 'private': 'https://api.lyra.finance/private',
149
+ },
150
+ 'test': {
151
+ 'public': 'https://api-demo.lyra.finance/public',
152
+ 'private': 'https://api-demo.lyra.finance/private',
153
+ },
154
+ 'www': 'https://www.derive.xyz/',
155
+ 'doc': 'https://docs.derive.xyz/docs/',
156
+ 'fees': 'https://docs.derive.xyz/reference/fees-1/',
157
+ 'referral': 'https://www.derive.xyz/invite/3VB0B',
158
+ },
159
+ 'api': {
160
+ 'public': {
161
+ 'get': [
162
+ 'get_all_currencies',
163
+ ],
164
+ 'post': [
165
+ 'build_register_session_key_tx',
166
+ 'register_session_key',
167
+ 'deregister_session_key',
168
+ 'login',
169
+ 'statistics',
170
+ 'get_all_currencies',
171
+ 'get_currency',
172
+ 'get_instrument',
173
+ 'get_all_instruments',
174
+ 'get_instruments',
175
+ 'get_ticker',
176
+ 'get_latest_signed_feeds',
177
+ 'get_option_settlement_prices',
178
+ 'get_spot_feed_history',
179
+ 'get_spot_feed_history_candles',
180
+ 'get_funding_rate_history',
181
+ 'get_trade_history',
182
+ 'get_option_settlement_history',
183
+ 'get_liquidation_history',
184
+ 'get_interest_rate_history',
185
+ 'get_transaction',
186
+ 'get_margin',
187
+ 'margin_watch',
188
+ 'validate_invite_code',
189
+ 'get_points',
190
+ 'get_all_points',
191
+ 'get_points_leaderboard',
192
+ 'get_descendant_tree',
193
+ 'get_tree_roots',
194
+ 'get_swell_percent_points',
195
+ 'get_vault_assets',
196
+ 'get_etherfi_effective_balances',
197
+ 'get_kelp_effective_balances',
198
+ 'get_bridge_balances',
199
+ 'get_ethena_participants',
200
+ 'get_vault_share',
201
+ 'get_vault_statistics',
202
+ 'get_vault_balances',
203
+ 'estimate_integrator_points',
204
+ 'create_subaccount_debug',
205
+ 'deposit_debug',
206
+ 'withdraw_debug',
207
+ 'send_quote_debug',
208
+ 'execute_quote_debug',
209
+ 'get_invite_code',
210
+ 'register_invite',
211
+ 'get_time',
212
+ 'get_live_incidents',
213
+ 'get_maker_programs',
214
+ 'get_maker_program_scores',
215
+ ],
216
+ },
217
+ 'private': {
218
+ 'post': [
219
+ 'get_account',
220
+ 'create_subaccount',
221
+ 'get_subaccount',
222
+ 'get_subaccounts',
223
+ 'get_all_portfolios',
224
+ 'change_subaccount_label',
225
+ 'get_notificationsv',
226
+ 'update_notifications',
227
+ 'deposit',
228
+ 'withdraw',
229
+ 'transfer_erc20',
230
+ 'transfer_position',
231
+ 'transfer_positions',
232
+ 'order',
233
+ 'replace',
234
+ 'order_debug',
235
+ 'get_order',
236
+ 'get_orders',
237
+ 'get_open_orders',
238
+ 'cancel',
239
+ 'cancel_by_label',
240
+ 'cancel_by_nonce',
241
+ 'cancel_by_instrument',
242
+ 'cancel_all',
243
+ 'cancel_trigger_order',
244
+ 'get_order_history',
245
+ 'get_trade_history',
246
+ 'get_deposit_history',
247
+ 'get_withdrawal_history',
248
+ 'send_rfq',
249
+ 'cancel_rfq',
250
+ 'cancel_batch_rfqs',
251
+ 'get_rfqs',
252
+ 'poll_rfqs',
253
+ 'send_quote',
254
+ 'cancel_quote',
255
+ 'cancel_batch_quotes',
256
+ 'get_quotes',
257
+ 'poll_quotes',
258
+ 'execute_quote',
259
+ 'rfq_get_best_quote',
260
+ 'get_margin',
261
+ 'get_collaterals',
262
+ 'get_positions',
263
+ 'get_option_settlement_history',
264
+ 'get_subaccount_value_history',
265
+ 'expired_and_cancelled_history',
266
+ 'get_funding_history',
267
+ 'get_interest_history',
268
+ 'get_erc20_transfer_history',
269
+ 'get_liquidation_history',
270
+ 'liquidate',
271
+ 'get_liquidator_history',
272
+ 'session_keys',
273
+ 'edit_session_key',
274
+ 'register_scoped_session_key',
275
+ 'get_mmp_config',
276
+ 'set_mmp_config',
277
+ 'reset_mmp',
278
+ 'set_cancel_on_disconnect',
279
+ 'get_invite_code',
280
+ 'register_invite',
281
+ ],
282
+ },
283
+ },
284
+ 'fees': {
285
+ },
286
+ 'requiredCredentials': {
287
+ 'apiKey': False,
288
+ 'secret': False,
289
+ 'walletAddress': True,
290
+ 'privateKey': True,
291
+ },
292
+ 'exceptions': {
293
+ 'exact': {
294
+ '-32000': RateLimitExceeded, # Rate limit exceeded
295
+ '-32100': RateLimitExceeded, # Number of concurrent websocket clients limit exceeded
296
+ '-32700': BadRequest, # Parse error
297
+ '-32600': BadRequest, # Invalid Request
298
+ '-32601': BadRequest, # Method not found
299
+ '-32602': InvalidOrder, # {"id":"55e66a3d-6a4e-4a36-a23d-5cf8a91ef478","error":{"code":"","message":"Invalid params"}}
300
+ '-32603': InvalidOrder, # {"code":"-32603","message":"Internal error","data":"SubAccount matching query does not exist."}
301
+ '9000': InvalidOrder, # Order confirmation timeout
302
+ '10000': BadRequest, # Manager not found
303
+ '10001': BadRequest, # Asset is not an ERC20 token
304
+ '10002': BadRequest, # Sender and recipient wallet do not match
305
+ '10003': BadRequest, # Sender and recipient subaccount IDs are the same
306
+ '10004': InvalidOrder, # Multiple currencies not supported
307
+ '10005': BadRequest, # Maximum number of subaccounts per wallet reached
308
+ '10006': BadRequest, # Maximum number of session keys per wallet reached
309
+ '10007': BadRequest, # Maximum number of assets per subaccount reached
310
+ '10008': BadRequest, # Maximum number of expiries per subaccount reached
311
+ '10009': BadRequest, # Recipient subaccount ID of the transfer cannot be 0
312
+ '10010': InvalidOrder, # PMRM only supports USDC asset collateral. Cannot trade spot markets.
313
+ '10011': InsufficientFunds, # ERC20 allowance is insufficient
314
+ '10012': InsufficientFunds, # ERC20 balance is less than transfer amount
315
+ '10013': ExchangeError, # There is a pending deposit for self asset
316
+ '10014': ExchangeError, # There is a pending withdrawal for self asset
317
+ '11000': InsufficientFunds, # Insufficient funds
318
+ '11002': InvalidOrder, # Order rejected from queue
319
+ '11003': InvalidOrder, # Already cancelled
320
+ '11004': InvalidOrder, # Already filled
321
+ '11005': InvalidOrder, # Already expired
322
+ '11006': OrderNotFound, # {"code":"11006","message":"Does not exist","data":"Open order with id: 804018f3-b092-40a3-a933-b29574fa1ff8 does not exist."}
323
+ '11007': InvalidOrder, # Self-crossing disallowed
324
+ '11008': InvalidOrder, # Post-only reject
325
+ '11009': InvalidOrder, # Zero liquidity for market or IOC/FOK order
326
+ '11010': InvalidOrder, # Post-only invalid order type
327
+ '11011': InvalidOrder, # {"code":11011,"message":"Invalid signature expiry","data":"Order must expire in 300 sec or more"}
328
+ '11012': InvalidOrder, # {"code":"11012","message":"Invalid amount","data":"Amount must be a multiple of 0.01"}
329
+ '11013': InvalidOrder, # {"code":"11013","message":"Invalid limit price","data":{"limit":"10000","bandwidth":"92530"}}
330
+ '11014': InvalidOrder, # Fill-or-kill not filled
331
+ '11015': InvalidOrder, # MMP frozen
332
+ '11016': InvalidOrder, # Already consumed
333
+ '11017': InvalidOrder, # Non unique nonce
334
+ '11018': InvalidOrder, # Invalid nonce date
335
+ '11019': InvalidOrder, # Open orders limit exceeded
336
+ '11020': InsufficientFunds, # Negative ERC20 balance
337
+ '11021': InvalidOrder, # Instrument is not live
338
+ '11022': InvalidOrder, # Reject timestamp exceeded
339
+ '11023': InvalidOrder, # {"code":"11023","message":"Max fee order param is too low","data":"signed max_fee must be >= 194.420835871999983091712000000000000000"}
340
+ '11024': InvalidOrder, # {"code":11024,"message":"Reduce only not supported with self time in force"}
341
+ '11025': InvalidOrder, # Reduce only reject
342
+ '11026': BadRequest, # Transfer reject
343
+ '11027': InvalidOrder, # Subaccount undergoing liquidation
344
+ '11028': InvalidOrder, # Replaced order filled amount does not match expected state.
345
+ '11050': InvalidOrder, # Trigger order was cancelled between the time worker sent order and engine processed order
346
+ '11051': InvalidOrder, # {"code":"11051","message":"Trigger price must be higher than the current price for stop orders and vice versa for take orders","data":"Trigger price 9000.0 must be < or > current price 102671.2 depending on trigger type and direction."}
347
+ '11052': InvalidOrder, # Trigger order limit exceeded(separate limit from regular orders)
348
+ '11053': InvalidOrder, # Index and last-trade trigger price types not supported yet
349
+ '11054': InvalidOrder, # {"code":"11054","message":"Trigger orders cannot replace or be replaced"}
350
+ '11055': InvalidOrder, # Market order limit_price is unfillable at the given trigger price
351
+ '11100': InvalidOrder, # Leg instruments are not unique
352
+ '11101': InvalidOrder, # RFQ not found
353
+ '11102': InvalidOrder, # Quote not found
354
+ '11103': InvalidOrder, # Quote leg does not match RFQ leg
355
+ '11104': InvalidOrder, # Requested quote or RFQ is not open
356
+ '11105': InvalidOrder, # Requested quote ID references a different RFQ ID
357
+ '11106': InvalidOrder, # Invalid RFQ counterparty
358
+ '11107': InvalidOrder, # Quote maker total cost too high
359
+ '11200': InvalidOrder, # Auction not ongoing
360
+ '11201': InvalidOrder, # Open orders not allowed
361
+ '11202': InvalidOrder, # Price limit exceeded
362
+ '11203': InvalidOrder, # Last trade ID mismatch
363
+ '12000': InvalidOrder, # Asset not found
364
+ '12001': InvalidOrder, # Instrument not found
365
+ '12002': BadRequest, # Currency not found
366
+ '12003': BadRequest, # USDC does not have asset caps per manager
367
+ '13000': BadRequest, # Invalid channels
368
+ '14000': BadRequest, # {"code": 14000, "message": "Account not found"}
369
+ '14001': InvalidOrder, # {"code": 14001, "message": "Subaccount not found"}
370
+ '14002': BadRequest, # Subaccount was withdrawn
371
+ '14008': BadRequest, # Cannot reduce expiry using registerSessionKey RPC route
372
+ '14009': BadRequest, # Session key expiry must be > utc_now + 10 min
373
+ '14010': BadRequest, # Session key already registered for self account
374
+ '14011': BadRequest, # Session key already registered with another account
375
+ '14012': BadRequest, # Address must be checksummed
376
+ '14013': BadRequest, # str is not a valid ethereum address
377
+ '14014': InvalidOrder, # {"code":"14014","message":"Signature invalid for message or transaction","data":"Signature does not match data"}
378
+ '14015': BadRequest, # Transaction count for given wallet does not match provided nonce
379
+ '14016': BadRequest, # The provided signed raw transaction contains function name that does not match the expected function name
380
+ '14017': BadRequest, # The provided signed raw transaction contains contract address that does not match the expected contract address
381
+ '14018': BadRequest, # The provided signed raw transaction contains function params that do not match any expected function params
382
+ '14019': BadRequest, # The provided signed raw transaction contains function param values that do not match the expected values
383
+ '14020': BadRequest, # The X-LyraWallet header does not match the requested subaccount_id or wallet
384
+ '14021': BadRequest, # The X-LyraWallet header not provided
385
+ '14022': AuthenticationError, # Subscription to a private channel failed
386
+ '14023': InvalidOrder, # {"code":"14023","message":"Signer in on-chain related request is not wallet owner or registered session key","data":"Session key does not belong to wallet"}
387
+ '14024': BadRequest, # Chain ID must match the current roll up chain id
388
+ '14025': BadRequest, # The private request is missing a wallet or subaccount_id param
389
+ '14026': BadRequest, # Session key not found
390
+ '14027': AuthenticationError, # Unauthorized maker
391
+ '14028': BadRequest, # Cross currency RFQ not supported
392
+ '14029': AuthenticationError, # Session key IP not whitelisted
393
+ '14030': BadRequest, # Session key expired
394
+ '14031': AuthenticationError, # Unauthorized key scope
395
+ '14032': BadRequest, # Scope should not be changed
396
+ '16000': AuthenticationError, # You are in a restricted region that violates our terms of service.
397
+ '16001': AuthenticationError, # Account is disabled due to compliance violations, please contact support to enable it.
398
+ '16100': AuthenticationError, # Sentinel authorization is invalid
399
+ '17000': BadRequest, # This accoount does not have a shareable invite code
400
+ '17001': BadRequest, # Invalid invite code
401
+ '17002': BadRequest, # Invite code already registered for self account
402
+ '17003': BadRequest, # Invite code has no remaining uses
403
+ '17004': BadRequest, # Requirement for successful invite registration not met
404
+ '17005': BadRequest, # Account must register with a valid invite code to be elligible for points
405
+ '17006': BadRequest, # Point program does not exist
406
+ '17007': BadRequest, # Invalid leaderboard page number
407
+ '18000': BadRequest, # Invalid block number
408
+ '18001': BadRequest, # Failed to estimate block number. Please try again later.
409
+ '18002': BadRequest, # The provided smart contract owner does not match the wallet in LightAccountFactory.getAddress()
410
+ '18003': BadRequest, # Vault ERC20 asset does not exist
411
+ '18004': BadRequest, # Vault ERC20 pool does not exist
412
+ '18005': BadRequest, # Must add asset to pool before getting balances
413
+ '18006': BadRequest, # Invalid Swell season. Swell seasons are in the form 'swell_season_X'.
414
+ '18007': BadRequest, # Vault not found
415
+ '19000': BadRequest, # Maker program not found
416
+ },
417
+ 'broad': {
418
+ },
419
+ },
420
+ 'precisionMode': TICK_SIZE,
421
+ 'commonCurrencies': {
422
+ },
423
+ 'options': {
424
+ 'deriveWalletAddress': '', # a derive wallet address "0x"-prefixed hexstring
425
+ 'id': '0x0ad42b8e602c2d3d475ae52d678cf63d84ab2749',
426
+ },
427
+ })
428
+
429
+ def set_sandbox_mode(self, enable: bool):
430
+ super(derive, self).set_sandbox_mode(enable)
431
+ self.options['sandboxMode'] = enable
432
+
433
+ def fetch_time(self, params={}):
434
+ """
435
+ fetches the current integer timestamp in milliseconds from the exchange server
436
+
437
+ https://docs.derive.xyz/reference/post_public-get-time
438
+
439
+ :param dict [params]: extra parameters specific to the exchange API endpoint
440
+ :returns int: the current integer timestamp in milliseconds from the exchange server
441
+ """
442
+ response = self.publicPostGetTime(params)
443
+ #
444
+ # {
445
+ # "result": 1735846536758,
446
+ # "id": "f1c03d21-f886-4c5a-9a9d-33dd06f180f0"
447
+ # }
448
+ #
449
+ return self.safe_integer(response, 'result')
450
+
451
+ def fetch_currencies(self, params={}) -> Currencies:
452
+ """
453
+ fetches all available currencies on an exchange
454
+
455
+ https://docs.derive.xyz/reference/post_public-get-all-currencies
456
+
457
+ :param dict [params]: extra parameters specific to the exchange API endpoint
458
+ :returns dict: an associative dictionary of currencies
459
+ """
460
+ result: dict = {}
461
+ tokenResponse = self.publicGetGetAllCurrencies(params)
462
+ #
463
+ # {
464
+ # "result": [
465
+ # {
466
+ # "currency": "USDC",
467
+ # "spot_price": "1.000066413299999872",
468
+ # "spot_price_24h": "1.000327785299999872"
469
+ # }
470
+ # ],
471
+ # "id": "7e07fe1d-0ab4-4d2b-9e22-b65ce9e232dc"
472
+ # }
473
+ #
474
+ currencies = self.safe_list(tokenResponse, 'result', [])
475
+ for i in range(0, len(currencies)):
476
+ currency = currencies[i]
477
+ currencyId = self.safe_string(currency, 'currency')
478
+ code = self.safe_currency_code(currencyId)
479
+ result[code] = {
480
+ 'id': currencyId,
481
+ 'name': None,
482
+ 'code': code,
483
+ 'precision': None,
484
+ 'active': None,
485
+ 'fee': None,
486
+ 'networks': None,
487
+ 'deposit': None,
488
+ 'withdraw': None,
489
+ 'limits': {
490
+ 'deposit': {
491
+ 'min': None,
492
+ 'max': None,
493
+ },
494
+ 'withdraw': {
495
+ 'min': None,
496
+ 'max': None,
497
+ },
498
+ },
499
+ 'info': currency,
500
+ }
501
+ return result
502
+
503
+ def fetch_markets(self, params={}) -> List[Market]:
504
+ """
505
+ retrieves data on all markets for bybit
506
+
507
+ https://docs.derive.xyz/reference/post_public-get-all-instruments
508
+
509
+ :param dict [params]: extra parameters specific to the exchange API endpoint
510
+ :returns dict[]: an array of objects representing market data
511
+ """
512
+ spotMarketsPromise = self.fetch_spot_markets(params)
513
+ swapMarketsPromise = self.fetch_swap_markets(params)
514
+ optionMarketsPromise = self.fetch_option_markets(params)
515
+ spotMarkets, swapMarkets, optionMarkets = [spotMarketsPromise, swapMarketsPromise, optionMarketsPromise]
516
+ #
517
+ # {
518
+ # "result": {
519
+ # "instruments": [
520
+ # {
521
+ # "instrument_type": "perp",
522
+ # "instrument_name": "BTC-PERP",
523
+ # "scheduled_activation": 1701840228,
524
+ # "scheduled_deactivation": 9223372036854776000,
525
+ # "is_active": True,
526
+ # "tick_size": "0.1",
527
+ # "minimum_amount": "0.01",
528
+ # "maximum_amount": "10000",
529
+ # "amount_step": "0.001",
530
+ # "mark_price_fee_rate_cap": "0",
531
+ # "maker_fee_rate": "0.00005",
532
+ # "taker_fee_rate": "0.0003",
533
+ # "base_fee": "0.1",
534
+ # "base_currency": "BTC",
535
+ # "quote_currency": "USD",
536
+ # "option_details": null,
537
+ # "perp_details": {
538
+ # "index": "BTC-USD",
539
+ # "max_rate_per_hour": "0.004",
540
+ # "min_rate_per_hour": "-0.004",
541
+ # "static_interest_rate": "0.0000125",
542
+ # "aggregate_funding": "10538.574363381759146829",
543
+ # "funding_rate": "0.0000125"
544
+ # },
545
+ # "erc20_details": null,
546
+ # "base_asset_address": "0xDBa83C0C654DB1cd914FA2710bA743e925B53086",
547
+ # "base_asset_sub_id": "0",
548
+ # "pro_rata_fraction": "0",
549
+ # "fifo_min_allocation": "0",
550
+ # "pro_rata_amount_step": "0.1"
551
+ # }
552
+ # ],
553
+ # "pagination": {
554
+ # "num_pages": 1,
555
+ # "count": 1
556
+ # }
557
+ # },
558
+ # "id": "a06bc0b2-8e78-4536-a21f-f785f225b5a5"
559
+ # }
560
+ #
561
+ result = self.array_concat(spotMarkets, swapMarkets)
562
+ result = self.array_concat(result, optionMarkets)
563
+ return result
564
+
565
+ def fetch_spot_markets(self, params={}) -> List[Market]:
566
+ request: dict = {
567
+ 'expired': False,
568
+ 'instrument_type': 'erc20',
569
+ }
570
+ response = self.publicPostGetAllInstruments(self.extend(request, params))
571
+ result = self.safe_dict(response, 'result', {})
572
+ data = self.safe_list(result, 'instruments', [])
573
+ return self.parse_markets(data)
574
+
575
+ def fetch_swap_markets(self, params={}) -> List[Market]:
576
+ request: dict = {
577
+ 'expired': False,
578
+ 'instrument_type': 'perp',
579
+ }
580
+ response = self.publicPostGetAllInstruments(self.extend(request, params))
581
+ result = self.safe_dict(response, 'result', {})
582
+ data = self.safe_list(result, 'instruments', [])
583
+ return self.parse_markets(data)
584
+
585
+ def fetch_option_markets(self, params={}) -> List[Market]:
586
+ request: dict = {
587
+ 'expired': False,
588
+ 'instrument_type': 'option',
589
+ }
590
+ response = self.publicPostGetAllInstruments(self.extend(request, params))
591
+ result = self.safe_dict(response, 'result', {})
592
+ data = self.safe_list(result, 'instruments', [])
593
+ return self.parse_markets(data)
594
+
595
+ def parse_market(self, market: dict) -> Market:
596
+ type = self.safe_string(market, 'instrument_type')
597
+ marketType: MarketType
598
+ spot = False
599
+ margin = True
600
+ swap = False
601
+ option = False
602
+ linear: Bool = None
603
+ baseId = self.safe_string(market, 'base_currency')
604
+ quoteId = self.safe_string(market, 'quote_currency')
605
+ base = self.safe_currency_code(baseId)
606
+ quote = self.safe_currency_code(quoteId)
607
+ marketId = self.safe_string(market, 'instrument_name')
608
+ symbol = base + '/' + quote
609
+ settleId: Str = None
610
+ settle: Str = None
611
+ expiry: Num = None
612
+ strike: Num = None
613
+ optionType: Str = None
614
+ optionLetter: Str = None
615
+ if type == 'erc20':
616
+ spot = True
617
+ marketType = 'spot'
618
+ elif type == 'perp':
619
+ margin = False
620
+ settleId = 'USDC'
621
+ settle = self.safe_currency_code(settleId)
622
+ symbol = base + '/' + quote + ':' + settle
623
+ swap = True
624
+ linear = True
625
+ marketType = 'swap'
626
+ elif type == 'option':
627
+ settleId = 'USDC'
628
+ settle = self.safe_currency_code(settleId)
629
+ margin = False
630
+ option = True
631
+ marketType = 'option'
632
+ optionDetails = self.safe_dict(market, 'option_details')
633
+ expiry = self.safe_timestamp(optionDetails, 'expiry')
634
+ strike = self.safe_integer(optionDetails, 'strike')
635
+ optionLetter = self.safe_string(optionDetails, 'option_type')
636
+ symbol = base + '/' + quote + ':' + settle + '-' + self.yymmdd(expiry) + '-' + self.number_to_string(strike) + '-' + optionLetter
637
+ if optionLetter == 'P':
638
+ optionType = 'put'
639
+ else:
640
+ optionType = 'call'
641
+ return self.safe_market_structure({
642
+ 'id': marketId,
643
+ 'symbol': symbol,
644
+ 'base': base,
645
+ 'quote': quote,
646
+ 'settle': settle,
647
+ 'baseId': baseId,
648
+ 'quoteId': quoteId,
649
+ 'settleId': settleId,
650
+ 'type': marketType,
651
+ 'spot': spot,
652
+ 'margin': margin,
653
+ 'swap': swap,
654
+ 'future': False,
655
+ 'option': option,
656
+ 'active': self.safe_bool(market, 'is_active'),
657
+ 'contract': (swap or option),
658
+ 'linear': linear,
659
+ 'inverse': None,
660
+ 'contractSize': None if (spot) else 1,
661
+ 'expiry': expiry,
662
+ 'expiryDatetime': self.iso8601(expiry),
663
+ 'taker': self.safe_number(market, 'taker_fee_rate'),
664
+ 'maker': self.safe_number(market, 'maker_fee_rate'),
665
+ 'strike': strike,
666
+ 'optionType': optionType,
667
+ 'precision': {
668
+ 'amount': self.safe_number(market, 'amount_step'),
669
+ 'price': self.safe_number(market, 'tick_size'),
670
+ },
671
+ 'limits': {
672
+ 'leverage': {
673
+ 'min': None,
674
+ 'max': None,
675
+ },
676
+ 'amount': {
677
+ 'min': self.safe_number(market, 'minimum_amount'),
678
+ 'max': self.safe_number(market, 'maximum_amount'),
679
+ },
680
+ 'price': {
681
+ 'min': None,
682
+ 'max': None,
683
+ },
684
+ 'cost': {
685
+ 'min': None,
686
+ 'max': None,
687
+ },
688
+ },
689
+ 'created': None,
690
+ 'info': market,
691
+ })
692
+
693
+ def fetch_ticker(self, symbol: str, params={}) -> Ticker:
694
+ """
695
+ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
696
+
697
+ https://docs.derive.xyz/reference/post_public-get-ticker
698
+
699
+ :param str symbol: unified symbol of the market to fetch the ticker for
700
+ :param dict [params]: extra parameters specific to the exchange API endpoint
701
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
702
+ """
703
+ self.load_markets()
704
+ market = self.market(symbol)
705
+ request: dict = {
706
+ 'instrument_name': market['id'],
707
+ }
708
+ response = self.publicPostGetTicker(self.extend(request, params))
709
+ #
710
+ # spot
711
+ #
712
+ # {
713
+ # "result": {
714
+ # "instrument_type": "perp",
715
+ # "instrument_name": "BTC-PERP",
716
+ # "scheduled_activation": 1701840228,
717
+ # "scheduled_deactivation": 9223372036854776000,
718
+ # "is_active": True,
719
+ # "tick_size": "0.1",
720
+ # "minimum_amount": "0.01",
721
+ # "maximum_amount": "10000",
722
+ # "amount_step": "0.001",
723
+ # "mark_price_fee_rate_cap": "0",
724
+ # "maker_fee_rate": "0.00005",
725
+ # "taker_fee_rate": "0.0003",
726
+ # "base_fee": "0.1",
727
+ # "base_currency": "BTC",
728
+ # "quote_currency": "USD",
729
+ # "option_details": null,
730
+ # "perp_details": {
731
+ # "index": "BTC-USD",
732
+ # "max_rate_per_hour": "0.004",
733
+ # "min_rate_per_hour": "-0.004",
734
+ # "static_interest_rate": "0.0000125",
735
+ # "aggregate_funding": "10512.580833189805742522",
736
+ # "funding_rate": "-0.000022223906766867"
737
+ # },
738
+ # "erc20_details": null,
739
+ # "base_asset_address": "0xDBa83C0C654DB1cd914FA2710bA743e925B53086",
740
+ # "base_asset_sub_id": "0",
741
+ # "pro_rata_fraction": "0",
742
+ # "fifo_min_allocation": "0",
743
+ # "pro_rata_amount_step": "0.1",
744
+ # "best_ask_amount": "0.012",
745
+ # "best_ask_price": "99567.9",
746
+ # "best_bid_amount": "0.129",
747
+ # "best_bid_price": "99554.5",
748
+ # "five_percent_bid_depth": "11.208",
749
+ # "five_percent_ask_depth": "11.42",
750
+ # "option_pricing": null,
751
+ # "index_price": "99577.2",
752
+ # "mark_price": "99543.642926357933902181684970855712890625",
753
+ # "stats": {
754
+ # "contract_volume": "464.712",
755
+ # "num_trades": "10681",
756
+ # "open_interest": "72.804739389481989861",
757
+ # "high": "99519.1",
758
+ # "low": "97254.1",
759
+ # "percent_change": "0.0128",
760
+ # "usd_change": "1258.1"
761
+ # },
762
+ # "timestamp": 1736140984000,
763
+ # "min_price": "97591.2",
764
+ # "max_price": "101535.1"
765
+ # },
766
+ # "id": "bbd7c271-c2be-48f7-b93a-26cf6d4cb79f"
767
+ # }
768
+ #
769
+ data = self.safe_dict(response, 'result', {})
770
+ return self.parse_ticker(data, market)
771
+
772
+ def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
773
+ #
774
+ # {
775
+ # "instrument_type": "perp",
776
+ # "instrument_name": "BTC-PERP",
777
+ # "scheduled_activation": 1701840228,
778
+ # "scheduled_deactivation": 9223372036854776000,
779
+ # "is_active": True,
780
+ # "tick_size": "0.1",
781
+ # "minimum_amount": "0.01",
782
+ # "maximum_amount": "10000",
783
+ # "amount_step": "0.001",
784
+ # "mark_price_fee_rate_cap": "0",
785
+ # "maker_fee_rate": "0.00005",
786
+ # "taker_fee_rate": "0.0003",
787
+ # "base_fee": "0.1",
788
+ # "base_currency": "BTC",
789
+ # "quote_currency": "USD",
790
+ # "option_details": null,
791
+ # "perp_details": {
792
+ # "index": "BTC-USD",
793
+ # "max_rate_per_hour": "0.004",
794
+ # "min_rate_per_hour": "-0.004",
795
+ # "static_interest_rate": "0.0000125",
796
+ # "aggregate_funding": "10512.580833189805742522",
797
+ # "funding_rate": "-0.000022223906766867"
798
+ # },
799
+ # "erc20_details": null,
800
+ # "base_asset_address": "0xDBa83C0C654DB1cd914FA2710bA743e925B53086",
801
+ # "base_asset_sub_id": "0",
802
+ # "pro_rata_fraction": "0",
803
+ # "fifo_min_allocation": "0",
804
+ # "pro_rata_amount_step": "0.1",
805
+ # "best_ask_amount": "0.012",
806
+ # "best_ask_price": "99567.9",
807
+ # "best_bid_amount": "0.129",
808
+ # "best_bid_price": "99554.5",
809
+ # "five_percent_bid_depth": "11.208",
810
+ # "five_percent_ask_depth": "11.42",
811
+ # "option_pricing": null,
812
+ # "index_price": "99577.2",
813
+ # "mark_price": "99543.642926357933902181684970855712890625",
814
+ # "stats": {
815
+ # "contract_volume": "464.712",
816
+ # "num_trades": "10681",
817
+ # "open_interest": "72.804739389481989861",
818
+ # "high": "99519.1",
819
+ # "low": "97254.1",
820
+ # "percent_change": "0.0128",
821
+ # "usd_change": "1258.1"
822
+ # },
823
+ # "timestamp": 1736140984000,
824
+ # "min_price": "97591.2",
825
+ # "max_price": "101535.1"
826
+ # }
827
+ #
828
+ marketId = self.safe_string(ticker, 'instrument_name')
829
+ timestamp = self.safe_integer_omit_zero(ticker, 'timestamp')
830
+ symbol = self.safe_symbol(marketId, market)
831
+ stats = self.safe_dict(ticker, 'stats')
832
+ change = self.safe_string(stats, 'percent_change')
833
+ return self.safe_ticker({
834
+ 'symbol': symbol,
835
+ 'timestamp': timestamp,
836
+ 'datetime': self.iso8601(timestamp),
837
+ 'high': self.safe_string(stats, 'high'),
838
+ 'low': self.safe_string(stats, 'low'),
839
+ 'bid': self.safe_string(ticker, 'best_bid_price'),
840
+ 'bidVolume': self.safe_string(ticker, 'best_bid_amount'),
841
+ 'ask': self.safe_string(ticker, 'best_ask_price'),
842
+ 'askVolume': self.safe_string(ticker, 'best_ask_amount'),
843
+ 'vwap': None,
844
+ 'open': None,
845
+ 'close': None,
846
+ 'last': None,
847
+ 'previousClose': None,
848
+ 'change': change,
849
+ 'percentage': Precise.string_mul(change, '100'),
850
+ 'average': None,
851
+ 'baseVolume': None,
852
+ 'quoteVolume': None,
853
+ 'indexPrice': self.safe_string(ticker, 'index_price'),
854
+ 'markPrice': self.safe_string(ticker, 'mark_price'),
855
+ 'info': ticker,
856
+ }, market)
857
+
858
+ def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
859
+ """
860
+ get the list of most recent trades for a particular symbol
861
+
862
+ https://docs.derive.xyz/reference/post_public-get-trade-history
863
+
864
+ :param str symbol: unified symbol of the market to fetch trades for
865
+ :param int [since]: timestamp in ms of the earliest trade to fetch
866
+ :param int [limit]: the maximum amount of trades to fetch
867
+ :param dict [params]: extra parameters specific to the exchange API endpoint
868
+ :param int [params.until]: the latest time in ms to fetch trades for
869
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
870
+ """
871
+ self.load_markets()
872
+ request: dict = {}
873
+ market = None
874
+ if symbol is not None:
875
+ market = self.market(symbol)
876
+ request['instrument_name'] = market['id']
877
+ if limit is not None:
878
+ if limit > 1000:
879
+ limit = 1000
880
+ request['page_size'] = limit # default 100, max 1000
881
+ if since is not None:
882
+ request['from_timestamp'] = since
883
+ until = self.safe_integer(params, 'until')
884
+ params = self.omit(params, ['until'])
885
+ if until is not None:
886
+ request['to_timestamp'] = until
887
+ response = self.publicPostGetTradeHistory(self.extend(request, params))
888
+ #
889
+ # {
890
+ # "result": {
891
+ # "trades": [
892
+ # {
893
+ # "trade_id": "9dbc88b0-f0c4-4439-9cc1-4e6409d4eafb",
894
+ # "instrument_name": "BTC-PERP",
895
+ # "timestamp": 1736153910930,
896
+ # "trade_price": "98995.3",
897
+ # "trade_amount": "0.033",
898
+ # "mark_price": "98990.875914388161618263",
899
+ # "index_price": "99038.050611100001501184",
900
+ # "direction": "sell",
901
+ # "quote_id": null,
902
+ # "wallet": "0x88B6BB87fbFac92a34F8155aaA35c87B5b166fA9",
903
+ # "subaccount_id": 8250,
904
+ # "tx_status": "settled",
905
+ # "tx_hash": "0x020bd735b312f867f17f8cc254946d87cfe9f2c8ff3605035d8129082eb73723",
906
+ # "trade_fee": "0.980476701049890015",
907
+ # "liquidity_role": "taker",
908
+ # "realized_pnl": "-2.92952402688793509",
909
+ # "realized_pnl_excl_fees": "-1.949047325838045075"
910
+ # }
911
+ # ],
912
+ # "pagination": {
913
+ # "num_pages": 598196,
914
+ # "count": 598196
915
+ # }
916
+ # },
917
+ # "id": "b8539544-6975-4497-8163-5e51a38e4aa7"
918
+ # }
919
+ #
920
+ result = self.safe_dict(response, 'result', {})
921
+ data = self.safe_list(result, 'trades', [])
922
+ return self.parse_trades(data, market, since, limit)
923
+
924
+ def parse_trade(self, trade: dict, market: Market = None) -> Trade:
925
+ #
926
+ # {
927
+ # "subaccount_id": 130837,
928
+ # "order_id": "30c48194-8d48-43ac-ad00-0d5ba29eddc9",
929
+ # "instrument_name": "BTC-PERP",
930
+ # "direction": "sell",
931
+ # "label": "test1234",
932
+ # "quote_id": null,
933
+ # "trade_id": "f8a30740-488c-4c2d-905d-e17057bafde1",
934
+ # "timestamp": 1738065303708,
935
+ # "mark_price": "102740.137375457314192317",
936
+ # "index_price": "102741.553409299981533184",
937
+ # "trade_price": "102700.6",
938
+ # "trade_amount": "0.01",
939
+ # "liquidity_role": "taker",
940
+ # "realized_pnl": "0",
941
+ # "realized_pnl_excl_fees": "0",
942
+ # "is_transfer": False,
943
+ # "tx_status": "settled",
944
+ # "trade_fee": "1.127415534092999815",
945
+ # "tx_hash": "0xc55df1f07330faf86579bd8a6385391fbe9e73089301149d8550e9d29c9ead74",
946
+ # "transaction_id": "e18b9426-3fa5-41bb-99d3-8b54fb4d51bb"
947
+ # }
948
+ #
949
+ marketId = self.safe_string(trade, 'instrument_name')
950
+ symbol = self.safe_symbol(marketId, market)
951
+ timestamp = self.safe_integer(trade, 'timestamp')
952
+ fee = {
953
+ 'currency': 'USDC',
954
+ 'cost': self.safe_string(trade, 'trade_fee'),
955
+ }
956
+ return self.safe_trade({
957
+ 'info': trade,
958
+ 'id': self.safe_string(trade, 'trade_id'),
959
+ 'order': self.safe_string(trade, 'order_id'),
960
+ 'symbol': symbol,
961
+ 'side': self.safe_string_lower(trade, 'direction'),
962
+ 'type': None,
963
+ 'takerOrMaker': self.safe_string(trade, 'liquidity_role'),
964
+ 'price': self.safe_string(trade, 'trade_price'),
965
+ 'amount': self.safe_string(trade, 'trade_amount'),
966
+ 'cost': None,
967
+ 'timestamp': timestamp,
968
+ 'datetime': self.iso8601(timestamp),
969
+ 'fee': fee,
970
+ }, market)
971
+
972
+ def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
973
+ """
974
+ fetches historical funding rate prices
975
+
976
+ https://docs.derive.xyz/reference/post_public-get-funding-rate-history
977
+
978
+ :param str symbol: unified symbol of the market to fetch the funding rate history for
979
+ :param int [since]: timestamp in ms of the earliest funding rate to fetch
980
+ :param int [limit]: the maximum amount of funding rate structures to fetch
981
+ :param dict [params]: extra parameters specific to the exchange API endpoint
982
+ :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
983
+ """
984
+ self.load_markets()
985
+ market = self.market(symbol)
986
+ request: dict = {
987
+ 'instrument_name': market['id'],
988
+ }
989
+ if since is not None:
990
+ request['start_timestamp'] = since
991
+ until = self.safe_integer(params, 'until')
992
+ params = self.omit(params, ['until'])
993
+ if until is not None:
994
+ request['to_timestamp'] = until
995
+ response = self.publicPostGetFundingRateHistory(self.extend(request, params))
996
+ #
997
+ # {
998
+ # "result": {
999
+ # "funding_rate_history": [
1000
+ # {
1001
+ # "timestamp": 1736215200000,
1002
+ # "funding_rate": "-0.000020014"
1003
+ # }
1004
+ # ]
1005
+ # },
1006
+ # "id": "3200ab8d-0080-42f0-8517-c13e3d9201d8"
1007
+ # }
1008
+ #
1009
+ result = self.safe_dict(response, 'result', {})
1010
+ data = self.safe_list(result, 'funding_rate_history', [])
1011
+ rates = []
1012
+ for i in range(0, len(data)):
1013
+ entry = data[i]
1014
+ timestamp = self.safe_integer(entry, 'timestamp')
1015
+ rates.append({
1016
+ 'info': entry,
1017
+ 'symbol': market['symbol'],
1018
+ 'fundingRate': self.safe_number(entry, 'funding_rate'),
1019
+ 'timestamp': timestamp,
1020
+ 'datetime': self.iso8601(timestamp),
1021
+ })
1022
+ sorted = self.sort_by(rates, 'timestamp')
1023
+ return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)
1024
+
1025
+ def fetch_funding_rate(self, symbol: str, params={}) -> FundingRate:
1026
+ """
1027
+ fetch the current funding rate
1028
+
1029
+ https://docs.derive.xyz/reference/post_public-get-funding-rate-history
1030
+
1031
+ :param str symbol: unified market symbol
1032
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1033
+ :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
1034
+ """
1035
+ response = self.fetch_funding_rate_history(symbol, None, 1, params)
1036
+ #
1037
+ # [
1038
+ # {
1039
+ # "info": {
1040
+ # "timestamp": 1736157600000,
1041
+ # "funding_rate": "-0.000008872"
1042
+ # },
1043
+ # "symbol": "BTC/USD:USDC",
1044
+ # "fundingRate": -0.000008872,
1045
+ # "timestamp": 1736157600000,
1046
+ # "datetime": "2025-01-06T10:00:00.000Z"
1047
+ # }
1048
+ # ]
1049
+ #
1050
+ data = self.safe_dict(response, 0)
1051
+ return self.parse_funding_rate(data)
1052
+
1053
+ def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
1054
+ symbol = self.safe_string(contract, 'symbol')
1055
+ fundingTimestamp = self.safe_integer(contract, 'timestamp')
1056
+ return {
1057
+ 'info': contract,
1058
+ 'symbol': symbol,
1059
+ 'markPrice': None,
1060
+ 'indexPrice': None,
1061
+ 'interestRate': None,
1062
+ 'estimatedSettlePrice': None,
1063
+ 'timestamp': None,
1064
+ 'datetime': None,
1065
+ 'fundingRate': self.safe_number(contract, 'fundingRate'),
1066
+ 'fundingTimestamp': fundingTimestamp,
1067
+ 'fundingDatetime': self.iso8601(fundingTimestamp),
1068
+ 'nextFundingRate': None,
1069
+ 'nextFundingTimestamp': None,
1070
+ 'nextFundingDatetime': None,
1071
+ 'previousFundingRate': None,
1072
+ 'previousFundingTimestamp': None,
1073
+ 'previousFundingDatetime': None,
1074
+ 'interval': None,
1075
+ }
1076
+
1077
+ def hash_order_message(self, order):
1078
+ accountHash = self.hash(self.eth_abi_encode([
1079
+ 'bytes32', 'uint256', 'uint256', 'address', 'bytes32', 'uint256', 'address', 'address',
1080
+ ], order), 'keccak', 'binary')
1081
+ sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1082
+ DOMAIN_SEPARATOR = '9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105' if (sandboxMode) else 'd96e5f90797da7ec8dc4e276260c7f3f87fedf68775fbe1ef116e996fc60441b'
1083
+ binaryDomainSeparator = self.base16_to_binary(DOMAIN_SEPARATOR)
1084
+ prefix = self.base16_to_binary('1901')
1085
+ return self.hash(self.binary_concat(prefix, binaryDomainSeparator, accountHash), 'keccak', 'hex')
1086
+
1087
+ def sign_order(self, order, privateKey):
1088
+ hashOrder = self.hash_order_message(order)
1089
+ return self.sign_hash(hashOrder[-64:], privateKey[-64:])
1090
+
1091
+ def hash_message(self, message):
1092
+ binaryMessage = self.encode(message)
1093
+ binaryMessageLength = self.binary_length(binaryMessage)
1094
+ x19 = self.base16_to_binary('19')
1095
+ newline = self.base16_to_binary('0a')
1096
+ prefix = self.binary_concat(x19, self.encode('Ethereum Signed Message:'), newline, self.encode(self.number_to_string(binaryMessageLength)))
1097
+ return '0x' + self.hash(self.binary_concat(prefix, binaryMessage), 'keccak', 'hex')
1098
+
1099
+ def sign_hash(self, hash, privateKey):
1100
+ self.check_required_credentials()
1101
+ signature = self.ecdsa(hash[-64:], privateKey[-64:], 'secp256k1', None)
1102
+ r = signature['r']
1103
+ s = signature['s']
1104
+ v = self.int_to_base16(self.sum(27, signature['v']))
1105
+ return '0x' + r.rjust(64, '0') + s.rjust(64, '0') + v
1106
+
1107
+ def sign_message(self, message, privateKey):
1108
+ return self.sign_hash(self.hash_message(message), privateKey[-64:])
1109
+
1110
+ def parse_units(self, num: str, dec='1000000000000000000'):
1111
+ return Precise.string_mul(num, dec)
1112
+
1113
+ def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1114
+ """
1115
+ create a trade order
1116
+
1117
+ https://docs.derive.xyz/reference/post_private-order
1118
+
1119
+ :param str symbol: unified symbol of the market to create an order in
1120
+ :param str type: 'market' or 'limit'
1121
+ :param str side: 'buy' or 'sell'
1122
+ :param float amount: how much of currency you want to trade in units of base currency
1123
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1124
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1125
+ :param str [params.subaccount_id]: *required* the subaccount id
1126
+ :param float [params.triggerPrice]: The price a trigger order is triggered at
1127
+ :param dict [params.takeProfit]: *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered(perpetual swap markets only)
1128
+ :param float [params.takeProfit.triggerPrice]: take profit trigger price
1129
+ :param dict [params.stopLoss]: *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered(perpetual swap markets only)
1130
+ :param float [params.stopLoss.triggerPrice]: stop loss trigger price
1131
+ :param float [params.max_fee]: *required* the maximum fee you are willing to pay for the order
1132
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1133
+ """
1134
+ self.load_markets()
1135
+ market = self.market(symbol)
1136
+ if price is None:
1137
+ raise ArgumentsRequired(self.id + ' createOrder() requires a price argument')
1138
+ subaccountId = None
1139
+ subaccountId, params = self.handle_derive_subaccount_id('createOrder', params)
1140
+ test = self.safe_bool(params, 'test', False)
1141
+ reduceOnly = self.safe_bool_2(params, 'reduceOnly', 'reduce_only')
1142
+ timeInForce = self.safe_string_lower_2(params, 'timeInForce', 'time_in_force')
1143
+ postOnly = self.safe_bool(params, 'postOnly')
1144
+ orderType = type.lower()
1145
+ orderSide = side.lower()
1146
+ nonce = self.milliseconds()
1147
+ # Order signature expiry must be between 2592000 and 7776000 sec from now
1148
+ signatureExpiry = self.safe_integer(params, 'signature_expiry_sec', self.seconds() + 7776000)
1149
+ ACTION_TYPEHASH = self.base16_to_binary('4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17')
1150
+ sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1151
+ TRADE_MODULE_ADDRESS = '0x87F2863866D85E3192a35A73b388BD625D83f2be' if (sandboxMode) else '0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b'
1152
+ priceString = self.number_to_string(price)
1153
+ maxFee = None
1154
+ maxFee, params = self.handle_option_and_params(params, 'createOrder', 'max_fee')
1155
+ if maxFee is None:
1156
+ raise ArgumentsRequired(self.id + ' createOrder() requires a max_fee argument in params')
1157
+ maxFeeString = self.number_to_string(maxFee)
1158
+ amountString = self.number_to_string(amount)
1159
+ tradeModuleDataHash = self.hash(self.eth_abi_encode([
1160
+ 'address', 'uint', 'int', 'int', 'uint', 'uint', 'bool',
1161
+ ], [
1162
+ market['info']['base_asset_address'],
1163
+ self.parse_to_numeric(market['info']['base_asset_sub_id']),
1164
+ self.convert_to_big_int(self.parse_units(priceString)),
1165
+ self.convert_to_big_int(self.parse_units(self.amount_to_precision(symbol, amountString))),
1166
+ self.convert_to_big_int(self.parse_units(maxFeeString)),
1167
+ subaccountId,
1168
+ orderSide == 'buy',
1169
+ ]), 'keccak', 'binary')
1170
+ deriveWalletAddress = None
1171
+ deriveWalletAddress, params = self.handle_derive_wallet_address('createOrder', params)
1172
+ signature = self.sign_order([
1173
+ ACTION_TYPEHASH,
1174
+ subaccountId,
1175
+ nonce,
1176
+ TRADE_MODULE_ADDRESS,
1177
+ tradeModuleDataHash,
1178
+ signatureExpiry,
1179
+ deriveWalletAddress,
1180
+ self.walletAddress,
1181
+ ], self.privateKey)
1182
+ request: dict = {
1183
+ 'instrument_name': market['id'],
1184
+ 'direction': orderSide,
1185
+ 'order_type': orderType,
1186
+ 'nonce': nonce,
1187
+ 'amount': amountString,
1188
+ 'limit_price': priceString,
1189
+ 'max_fee': maxFeeString,
1190
+ 'subaccount_id': subaccountId,
1191
+ 'signature_expiry_sec': signatureExpiry,
1192
+ 'referral_code': self.safe_string(self.options, 'id', '0x0ad42b8e602c2d3d475ae52d678cf63d84ab2749'),
1193
+ 'signer': self.walletAddress,
1194
+ }
1195
+ if reduceOnly is not None:
1196
+ request['reduce_only'] = reduceOnly
1197
+ if reduceOnly and postOnly:
1198
+ raise InvalidOrder(self.id + ' cannot use reduce only with post only time in force')
1199
+ if postOnly is not None:
1200
+ request['time_in_force'] = 'post_only'
1201
+ elif timeInForce is not None:
1202
+ request['time_in_force'] = timeInForce
1203
+ stopLoss = self.safe_value(params, 'stopLoss')
1204
+ takeProfit = self.safe_value(params, 'takeProfit')
1205
+ triggerPriceType = self.safe_string(params, 'trigger_price_type', 'mark')
1206
+ if stopLoss is not None:
1207
+ stopLossPrice = self.safe_string(stopLoss, 'triggerPrice', stopLoss)
1208
+ request['trigger_price'] = stopLossPrice
1209
+ request['trigger_type'] = 'stoploss'
1210
+ request['trigger_price_type'] = triggerPriceType
1211
+ elif takeProfit is not None:
1212
+ takeProfitPrice = self.safe_string(takeProfit, 'triggerPrice', takeProfit)
1213
+ request['trigger_price'] = takeProfitPrice
1214
+ request['trigger_type'] = 'takeprofit'
1215
+ request['trigger_price_type'] = triggerPriceType
1216
+ clientOrderId = self.safe_string(params, 'clientOrderId')
1217
+ if clientOrderId is not None:
1218
+ request['label'] = clientOrderId
1219
+ request['signature'] = signature
1220
+ params = self.omit(params, ['reduceOnly', 'reduce_only', 'timeInForce', 'time_in_force', 'postOnly', 'test', 'clientOrderId', 'stopPrice', 'triggerPrice', 'trigger_price', 'stopLoss', 'takeProfit', 'trigger_price_type'])
1221
+ response = None
1222
+ if test:
1223
+ response = self.privatePostOrderDebug(self.extend(request, params))
1224
+ else:
1225
+ response = self.privatePostOrder(self.extend(request, params))
1226
+ #
1227
+ # {
1228
+ # "result": {
1229
+ # "raw_data": {
1230
+ # "subaccount_id": 130837,
1231
+ # "nonce": 1736923517552,
1232
+ # "module": "0x87F2863866D85E3192a35A73b388BD625D83f2be",
1233
+ # "expiry": 86400,
1234
+ # "owner": "0x108b9aF9279a525b8A8AeAbE7AC2bA925Bc50075",
1235
+ # "signer": "0x108b9aF9279a525b8A8AeAbE7AC2bA925Bc50075",
1236
+ # "signature": "0xaa4f42b2f3da33c668fa703ea872d4c3a6b55aca66025b5119e3bebb6679fe2e2794638db51dcace21fc39a498047835994f07eb59f311bb956ce057e66793d1c",
1237
+ # "data": {
1238
+ # "asset": "0xAFB6Bb95cd70D5367e2C39e9dbEb422B9815339D",
1239
+ # "sub_id": 0,
1240
+ # "limit_price": "10000",
1241
+ # "desired_amount": "0.001",
1242
+ # "worst_fee": "0",
1243
+ # "recipient_id": 130837,
1244
+ # "is_bid": True,
1245
+ # "trade_id": ""
1246
+ # }
1247
+ # },
1248
+ # "encoded_data": "0x000000000000000000000000afb6bb95cd70d5367e2c39e9dbeb422b9815339d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff150000000000000000000000000000000000000000000000000000000000000001",
1249
+ # "encoded_data_hashed": "0xe88fb416bc54dba2d288988f1a82fee40fd792ed555b3471b5f6b4b810d279b4",
1250
+ # "action_hash": "0x273a0befb3751fa991edc7ed73582456c3b50ae964d458c8f472e932fb6a0069",
1251
+ # "typed_data_hash": "0x123e2d2f3d5b2473b4e260f51c6459d6bf904e5db8f042a3ea63be8d55329ce9"
1252
+ # },
1253
+ # "id": "f851c8c4-dddf-4b77-93cf-aeddd0966f29"
1254
+ # }
1255
+ # {
1256
+ # "result": {
1257
+ # "order": {
1258
+ # "subaccount_id": 130837,
1259
+ # "order_id": "96349ebb-7d46-43ae-81c7-7ab390444293",
1260
+ # "instrument_name": "BTC-PERP",
1261
+ # "direction": "buy",
1262
+ # "label": "",
1263
+ # "quote_id": null,
1264
+ # "creation_timestamp": 1737467576257,
1265
+ # "last_update_timestamp": 1737467576257,
1266
+ # "limit_price": "10000",
1267
+ # "amount": "0.01",
1268
+ # "filled_amount": "0",
1269
+ # "average_price": "0",
1270
+ # "order_fee": "0",
1271
+ # "order_type": "limit",
1272
+ # "time_in_force": "gtc",
1273
+ # "order_status": "open",
1274
+ # "max_fee": "210",
1275
+ # "signature_expiry_sec": 1737468175989,
1276
+ # "nonce": 1737467575989,
1277
+ # "signer": "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
1278
+ # "signature": "0xd1ca49df1fa06bd805bb59b132ff6c0de29bf973a3e01705abe0a01cc956e4945ed9eb99ab68f3df4c037908113cac5a5bfc3a954a0b7103cdab285962fa6a51c",
1279
+ # "cancel_reason": "",
1280
+ # "mmp": False,
1281
+ # "is_transfer": False,
1282
+ # "replaced_order_id": null,
1283
+ # "trigger_type": null,
1284
+ # "trigger_price_type": null,
1285
+ # "trigger_price": null,
1286
+ # "trigger_reject_message": null
1287
+ # },
1288
+ # "trades": []
1289
+ # },
1290
+ # "id": "397087fa-0125-42af-bfc3-f66166f9fb55"
1291
+ # }
1292
+ #
1293
+ result = self.safe_dict(response, 'result')
1294
+ rawOrder = self.safe_dict(result, 'raw_data')
1295
+ if rawOrder is None:
1296
+ rawOrder = self.safe_dict(result, 'order')
1297
+ order = self.parse_order(rawOrder, market)
1298
+ order['type'] = type
1299
+ return order
1300
+
1301
+ def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
1302
+ """
1303
+ edit a trade order
1304
+
1305
+ https://docs.derive.xyz/reference/post_private-replace
1306
+
1307
+ :param str id: order id
1308
+ :param str symbol: unified symbol of the market to create an order in
1309
+ :param str type: 'market' or 'limit'
1310
+ :param str side: 'buy' or 'sell'
1311
+ :param float amount: how much of currency you want to trade in units of base currency
1312
+ :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1313
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1314
+ :param str [params.subaccount_id]: *required* the subaccount id
1315
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1316
+ """
1317
+ self.load_markets()
1318
+ market = self.market(symbol)
1319
+ subaccountId = None
1320
+ subaccountId, params = self.handle_derive_subaccount_id('editOrder', params)
1321
+ reduceOnly = self.safe_bool_2(params, 'reduceOnly', 'reduce_only')
1322
+ timeInForce = self.safe_string_lower_2(params, 'timeInForce', 'time_in_force')
1323
+ postOnly = self.safe_bool(params, 'postOnly')
1324
+ orderType = type.lower()
1325
+ orderSide = side.lower()
1326
+ nonce = self.milliseconds()
1327
+ signatureExpiry = self.safe_number(params, 'signature_expiry_sec', self.seconds() + 7776000)
1328
+ # TODO: subaccount id / trade module address
1329
+ ACTION_TYPEHASH = self.base16_to_binary('4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17')
1330
+ sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
1331
+ TRADE_MODULE_ADDRESS = '0x87F2863866D85E3192a35A73b388BD625D83f2be' if (sandboxMode) else '0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b'
1332
+ priceString = self.number_to_string(price)
1333
+ maxFeeString = self.safe_string(params, 'max_fee', '0')
1334
+ amountString = self.number_to_string(amount)
1335
+ tradeModuleDataHash = self.hash(self.eth_abi_encode([
1336
+ 'address', 'uint', 'int', 'int', 'uint', 'uint', 'bool',
1337
+ ], [
1338
+ market['info']['base_asset_address'],
1339
+ self.parse_to_numeric(market['info']['base_asset_sub_id']),
1340
+ self.convert_to_big_int(self.parse_units(priceString)),
1341
+ self.convert_to_big_int(self.parse_units(self.amount_to_precision(symbol, amountString))),
1342
+ self.convert_to_big_int(self.parse_units(maxFeeString)),
1343
+ subaccountId,
1344
+ orderSide == 'buy',
1345
+ ]), 'keccak', 'binary')
1346
+ deriveWalletAddress = None
1347
+ deriveWalletAddress, params = self.handle_derive_wallet_address('editOrder', params)
1348
+ signature = self.sign_order([
1349
+ ACTION_TYPEHASH,
1350
+ subaccountId,
1351
+ nonce,
1352
+ TRADE_MODULE_ADDRESS,
1353
+ tradeModuleDataHash,
1354
+ signatureExpiry,
1355
+ deriveWalletAddress,
1356
+ self.walletAddress,
1357
+ ], self.privateKey)
1358
+ request: dict = {
1359
+ 'instrument_name': market['id'],
1360
+ 'order_id_to_cancel': id,
1361
+ 'direction': orderSide,
1362
+ 'order_type': orderType,
1363
+ 'nonce': nonce,
1364
+ 'amount': amountString,
1365
+ 'limit_price': priceString,
1366
+ 'max_fee': maxFeeString,
1367
+ 'subaccount_id': subaccountId,
1368
+ 'signature_expiry_sec': signatureExpiry,
1369
+ 'signer': self.walletAddress,
1370
+ }
1371
+ if reduceOnly is not None:
1372
+ request['reduce_only'] = reduceOnly
1373
+ if reduceOnly and postOnly:
1374
+ raise InvalidOrder(self.id + ' cannot use reduce only with post only time in force')
1375
+ if postOnly is not None:
1376
+ request['time_in_force'] = 'post_only'
1377
+ elif timeInForce is not None:
1378
+ request['time_in_force'] = timeInForce
1379
+ clientOrderId = self.safe_string(params, 'clientOrderId')
1380
+ if clientOrderId is not None:
1381
+ request['label'] = clientOrderId
1382
+ request['signature'] = signature
1383
+ params = self.omit(params, ['reduceOnly', 'reduce_only', 'timeInForce', 'time_in_force', 'postOnly', 'clientOrderId'])
1384
+ response = self.privatePostReplace(self.extend(request, params))
1385
+ #
1386
+ # {
1387
+ # "result":
1388
+ # {
1389
+ # "cancelled_order":
1390
+ # {
1391
+ # "subaccount_id": 130837,
1392
+ # "order_id": "c2337704-f1af-437d-91c8-dddb9d6bac59",
1393
+ # "instrument_name": "BTC-PERP",
1394
+ # "direction": "buy",
1395
+ # "label": "test1234",
1396
+ # "quote_id": null,
1397
+ # "creation_timestamp": 1737539743959,
1398
+ # "last_update_timestamp": 1737539764234,
1399
+ # "limit_price": "10000",
1400
+ # "amount": "0.01",
1401
+ # "filled_amount": "0",
1402
+ # "average_price": "0",
1403
+ # "order_fee": "0",
1404
+ # "order_type": "limit",
1405
+ # "time_in_force": "post_only",
1406
+ # "order_status": "cancelled",
1407
+ # "max_fee": "211",
1408
+ # "signature_expiry_sec": 1737540343631,
1409
+ # "nonce": 1737539743631,
1410
+ # "signer": "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
1411
+ # "signature": "0xdb669e18f407a3efa816b79c0dd3bac1c651d4dbf3caad4db67678ce9b81c76378d787a08143a30707eb0827ce4626640767c9f174358df1b90611bd6d1391711b",
1412
+ # "cancel_reason": "user_request",
1413
+ # "mmp": False,
1414
+ # "is_transfer": False,
1415
+ # "replaced_order_id": null,
1416
+ # "trigger_type": null,
1417
+ # "trigger_price_type": null,
1418
+ # "trigger_price": null,
1419
+ # "trigger_reject_message": null,
1420
+ # },
1421
+ # "order":
1422
+ # {
1423
+ # "subaccount_id": 130837,
1424
+ # "order_id": "97af0902-813f-4892-a54b-797e5689db05",
1425
+ # "instrument_name": "BTC-PERP",
1426
+ # "direction": "buy",
1427
+ # "label": "test1234",
1428
+ # "quote_id": null,
1429
+ # "creation_timestamp": 1737539764154,
1430
+ # "last_update_timestamp": 1737539764154,
1431
+ # "limit_price": "10000",
1432
+ # "amount": "0.01",
1433
+ # "filled_amount": "0",
1434
+ # "average_price": "0",
1435
+ # "order_fee": "0",
1436
+ # "order_type": "limit",
1437
+ # "time_in_force": "post_only",
1438
+ # "order_status": "open",
1439
+ # "max_fee": "211",
1440
+ # "signature_expiry_sec": 1737540363890,
1441
+ # "nonce": 1737539763890,
1442
+ # "signer": "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
1443
+ # "signature": "0xef2c459ab4797cbbd7d97b47678ff172542af009bac912bf53e7879cf92eb1aa6b1a6cf40bf0928684f5394942fb424cc2db71eac0eaf7226a72480034332f291c",
1444
+ # "cancel_reason": "",
1445
+ # "mmp": False,
1446
+ # "is_transfer": False,
1447
+ # "replaced_order_id": "c2337704-f1af-437d-91c8-dddb9d6bac59",
1448
+ # "trigger_type": null,
1449
+ # "trigger_price_type": null,
1450
+ # "trigger_price": null,
1451
+ # "trigger_reject_message": null,
1452
+ # },
1453
+ # "trades": [],
1454
+ # "create_order_error": null,
1455
+ # },
1456
+ # "id": "fb19e991-15f6-4c80-a20c-917e762a1a38",
1457
+ # }
1458
+ #
1459
+ result = self.safe_dict(response, 'result')
1460
+ rawOrder = self.safe_dict(result, 'order')
1461
+ order = self.parse_order(rawOrder, market)
1462
+ return order
1463
+
1464
+ def cancel_order(self, id: str, symbol: Str = None, params={}):
1465
+ """
1466
+
1467
+ https://docs.derive.xyz/reference/post_private-cancel
1468
+
1469
+ cancels an open order
1470
+ :param str id: order id
1471
+ :param str symbol: unified symbol of the market the order was made in
1472
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1473
+ :param boolean [params.trigger]: whether the order is a trigger/algo order
1474
+ :param str [params.subaccount_id]: *required* the subaccount id
1475
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1476
+ """
1477
+ if symbol is None:
1478
+ raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
1479
+ self.load_markets()
1480
+ market: Market = self.market(symbol)
1481
+ isTrigger = self.safe_bool_2(params, 'trigger', 'stop', False)
1482
+ subaccountId = None
1483
+ subaccountId, params = self.handle_derive_subaccount_id('cancelOrder', params)
1484
+ params = self.omit(params, ['trigger', 'stop'])
1485
+ request: dict = {
1486
+ 'instrument_name': market['id'],
1487
+ 'subaccount_id': subaccountId,
1488
+ }
1489
+ clientOrderIdUnified = self.safe_string(params, 'clientOrderId')
1490
+ clientOrderIdExchangeSpecific = self.safe_string(params, 'label', clientOrderIdUnified)
1491
+ isByClientOrder = clientOrderIdExchangeSpecific is not None
1492
+ response = None
1493
+ if isByClientOrder:
1494
+ request['label'] = clientOrderIdExchangeSpecific
1495
+ params = self.omit(params, ['clientOrderId', 'label'])
1496
+ response = self.privatePostCancelByLabel(self.extend(request, params))
1497
+ else:
1498
+ request['order_id'] = id
1499
+ if isTrigger:
1500
+ response = self.privatePostCancelTriggerOrder(self.extend(request, params))
1501
+ else:
1502
+ response = self.privatePostCancel(self.extend(request, params))
1503
+ #
1504
+ # {
1505
+ # "result": {
1506
+ # "subaccount_id": 130837,
1507
+ # "order_id": "de4f30b6-0dcb-4df6-9222-c1a27f1ad80d",
1508
+ # "instrument_name": "BTC-PERP",
1509
+ # "direction": "buy",
1510
+ # "label": "test1234",
1511
+ # "quote_id": null,
1512
+ # "creation_timestamp": 1737540100989,
1513
+ # "last_update_timestamp": 1737540574696,
1514
+ # "limit_price": "10000",
1515
+ # "amount": "0.01",
1516
+ # "filled_amount": "0",
1517
+ # "average_price": "0",
1518
+ # "order_fee": "0",
1519
+ # "order_type": "limit",
1520
+ # "time_in_force": "post_only",
1521
+ # "order_status": "cancelled",
1522
+ # "max_fee": "211",
1523
+ # "signature_expiry_sec": 1737540700726,
1524
+ # "nonce": 1737540100726,
1525
+ # "signer": "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
1526
+ # "signature": "0x9cd1a6e32a0699929e4e090c08c548366b1353701ec56e02d5cdf37fc89bd19b7b29e00e57e8383bb6336d73019027a7e2a4364f40859e7a949115024c7f199a1b",
1527
+ # "cancel_reason": "user_request",
1528
+ # "mmp": False,
1529
+ # "is_transfer": False,
1530
+ # "replaced_order_id": "4ccc89ba-3c3d-4047-8900-0aa5fb4ef706",
1531
+ # "trigger_type": null,
1532
+ # "trigger_price_type": null,
1533
+ # "trigger_price": null,
1534
+ # "trigger_reject_message": null
1535
+ # },
1536
+ # "id": "cef61e2a-cb13-4779-8e6b-535361981fad"
1537
+ # }
1538
+ #
1539
+ # {
1540
+ # "result": {
1541
+ # "cancelled_orders": 1
1542
+ # },
1543
+ # "id": "674e075e-1e8a-4a47-99ff-75efbdd2370f"
1544
+ # }
1545
+ #
1546
+ extendParams: dict = {'symbol': symbol}
1547
+ order = self.safe_dict(response, 'result')
1548
+ if isByClientOrder:
1549
+ extendParams['client_order_id'] = clientOrderIdExchangeSpecific
1550
+ return self.extend(self.parse_order(order, market), extendParams)
1551
+
1552
+ def cancel_all_orders(self, symbol: Str = None, params={}):
1553
+ """
1554
+
1555
+ https://docs.derive.xyz/reference/post_private-cancel-by-instrument
1556
+ https://docs.derive.xyz/reference/post_private-cancel-all
1557
+
1558
+ cancel all open orders in a market
1559
+ :param str symbol: unified market symbol
1560
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1561
+ :param str [params.subaccount_id]: *required* the subaccount id
1562
+ :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1563
+ """
1564
+ self.load_markets()
1565
+ market: Market = None
1566
+ if symbol is not None:
1567
+ market = self.market(symbol)
1568
+ subaccountId = None
1569
+ subaccountId, params = self.handle_derive_subaccount_id('cancelAllOrders', params)
1570
+ request: dict = {
1571
+ 'subaccount_id': subaccountId,
1572
+ }
1573
+ response = None
1574
+ if market is not None:
1575
+ request['instrument_name'] = market['id']
1576
+ response = self.privatePostCancelByInstrument(self.extend(request, params))
1577
+ else:
1578
+ response = self.privatePostCancelAll(self.extend(request, params))
1579
+ #
1580
+ # {
1581
+ # "result": {
1582
+ # "cancelled_orders": 0
1583
+ # },
1584
+ # "id": "9d633799-2098-4559-b547-605bb6f4d8f4"
1585
+ # }
1586
+ #
1587
+ # {
1588
+ # "id": "45548646-c74f-4ca2-9de4-551e6de49afa",
1589
+ # "result": "ok"
1590
+ # }
1591
+ #
1592
+ return response
1593
+
1594
+ def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1595
+ """
1596
+ fetches information on multiple orders made by the user
1597
+
1598
+ https://docs.derive.xyz/reference/post_private-get-orders
1599
+
1600
+ :param str symbol: unified market symbol of the market orders were made in
1601
+ :param int [since]: the earliest time in ms to fetch orders for
1602
+ :param int [limit]: the maximum number of order structures to retrieve
1603
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1604
+ :param boolean [params.paginate]: set to True if you want to fetch orders with pagination
1605
+ :param boolean [params.trigger]: whether the order is a trigger/algo order
1606
+ :param str [params.subaccount_id]: *required* the subaccount id
1607
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1608
+ """
1609
+ self.load_markets()
1610
+ paginate = False
1611
+ paginate, params = self.handle_option_and_params(params, 'fetchOrders', 'paginate')
1612
+ if paginate:
1613
+ return self.fetch_paginated_call_incremental('fetchOrders', symbol, since, limit, params, 'page', 500)
1614
+ isTrigger = self.safe_bool_2(params, 'trigger', 'stop', False)
1615
+ params = self.omit(params, ['trigger', 'stop'])
1616
+ subaccountId = None
1617
+ subaccountId, params = self.handle_derive_subaccount_id('fetchOrders', params)
1618
+ request: dict = {
1619
+ 'subaccount_id': subaccountId,
1620
+ }
1621
+ market: Market = None
1622
+ if symbol is not None:
1623
+ market = self.market(symbol)
1624
+ request['instrument_name'] = market['id']
1625
+ if limit is not None:
1626
+ request['page_size'] = limit
1627
+ else:
1628
+ request['page_size'] = 500
1629
+ if isTrigger:
1630
+ request['status'] = 'untriggered'
1631
+ response = self.privatePostGetOrders(self.extend(request, params))
1632
+ #
1633
+ # {
1634
+ # "result": {
1635
+ # "subaccount_id": 130837,
1636
+ # "orders": [
1637
+ # {
1638
+ # "subaccount_id": 130837,
1639
+ # "order_id": "63a80cb8-387b-472b-a838-71cd9513c365",
1640
+ # "instrument_name": "BTC-PERP",
1641
+ # "direction": "buy",
1642
+ # "label": "test1234",
1643
+ # "quote_id": null,
1644
+ # "creation_timestamp": 1737551053207,
1645
+ # "last_update_timestamp": 1737551053207,
1646
+ # "limit_price": "10000",
1647
+ # "amount": "0.01",
1648
+ # "filled_amount": "0",
1649
+ # "average_price": "0",
1650
+ # "order_fee": "0",
1651
+ # "order_type": "limit",
1652
+ # "time_in_force": "post_only",
1653
+ # "order_status": "open",
1654
+ # "max_fee": "211",
1655
+ # "signature_expiry_sec": 1737551652765,
1656
+ # "nonce": 1737551052765,
1657
+ # "signer": "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
1658
+ # "signature": "0x35535ccb1bcad509ecc435c79e966174db6403fc9aeee1e237d08a941014c57b59279dfe4be39e081f9921a53eaad59cb2a151d9f52f2d05fc47e6280254952e1c",
1659
+ # "cancel_reason": "",
1660
+ # "mmp": False,
1661
+ # "is_transfer": False,
1662
+ # "replaced_order_id": null,
1663
+ # "trigger_type": null,
1664
+ # "trigger_price_type": null,
1665
+ # "trigger_price": null,
1666
+ # "trigger_reject_message": null
1667
+ # }
1668
+ # ],
1669
+ # "pagination": {
1670
+ # "num_pages": 1,
1671
+ # "count": 1
1672
+ # }
1673
+ # },
1674
+ # "id": "e5a88d4f-7ac7-40cd-aec9-e0e8152b8b92"
1675
+ # }
1676
+ #
1677
+ data = self.safe_value(response, 'result')
1678
+ page = self.safe_integer(params, 'page')
1679
+ if page is not None:
1680
+ pagination = self.safe_dict(data, 'pagination')
1681
+ currentPage = self.safe_integer(pagination, 'num_pages')
1682
+ if page > currentPage:
1683
+ return []
1684
+ orders = self.safe_list(data, 'orders')
1685
+ return self.parse_orders(orders, market, since, limit)
1686
+
1687
+ def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1688
+ """
1689
+ fetches information on multiple orders made by the user
1690
+
1691
+ https://docs.derive.xyz/reference/post_private-get-orders
1692
+
1693
+ :param str symbol: unified market symbol of the market orders were made in
1694
+ :param int [since]: the earliest time in ms to fetch orders for
1695
+ :param int [limit]: the maximum number of order structures to retrieve
1696
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1697
+ :param boolean [params.paginate]: set to True if you want to fetch orders with pagination
1698
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1699
+ """
1700
+ self.load_markets()
1701
+ extendedParams = self.extend(params, {'status': 'open'})
1702
+ return self.fetch_orders(symbol, since, limit, extendedParams)
1703
+
1704
+ def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1705
+ """
1706
+ fetches information on multiple orders made by the user
1707
+
1708
+ https://docs.derive.xyz/reference/post_private-get-orders
1709
+
1710
+ :param str symbol: unified market symbol of the market orders were made in
1711
+ :param int [since]: the earliest time in ms to fetch orders for
1712
+ :param int [limit]: the maximum number of order structures to retrieve
1713
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1714
+ :param boolean [params.paginate]: set to True if you want to fetch orders with pagination
1715
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1716
+ """
1717
+ self.load_markets()
1718
+ extendedParams = self.extend(params, {'status': 'filled'})
1719
+ return self.fetch_orders(symbol, since, limit, extendedParams)
1720
+
1721
+ def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1722
+ """
1723
+ fetches information on multiple canceled orders made by the user
1724
+
1725
+ https://docs.derive.xyz/reference/post_private-get-orders
1726
+
1727
+ :param str symbol: unified market symbol of the market the orders were made in
1728
+ :param int [since]: the earliest time in ms to fetch orders for
1729
+ :param int [limit]: the maximum number of order structures to retrieve
1730
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1731
+ :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
1732
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1733
+ """
1734
+ self.load_markets()
1735
+ extendedParams = self.extend(params, {'status': 'cancelled'})
1736
+ return self.fetch_orders(symbol, since, limit, extendedParams)
1737
+
1738
+ def parse_time_in_force(self, timeInForce: Str):
1739
+ timeInForces: dict = {
1740
+ 'ioc': 'IOC',
1741
+ 'fok': 'FOK',
1742
+ 'gtc': 'GTC',
1743
+ 'post_only': 'PO',
1744
+ }
1745
+ return self.safe_string(timeInForces, timeInForce, None)
1746
+
1747
+ def parse_order_status(self, status: Str):
1748
+ if status is not None:
1749
+ statuses: dict = {
1750
+ 'open': 'open',
1751
+ 'untriggered': 'open',
1752
+ 'filled': 'closed',
1753
+ 'cancelled': 'canceled',
1754
+ 'expired': 'rejected',
1755
+ }
1756
+ return self.safe_string(statuses, status, status)
1757
+ return status
1758
+
1759
+ def parse_order(self, rawOrder: dict, market: Market = None) -> Order:
1760
+ #
1761
+ # {
1762
+ # "subaccount_id": 130837,
1763
+ # "nonce": 1736923517552,
1764
+ # "module": "0x87F2863866D85E3192a35A73b388BD625D83f2be",
1765
+ # "expiry": 86400,
1766
+ # "owner": "0x108b9aF9279a525b8A8AeAbE7AC2bA925Bc50075",
1767
+ # "signer": "0x108b9aF9279a525b8A8AeAbE7AC2bA925Bc50075",
1768
+ # "signature": "0xaa4f42b2f3da33c668fa703ea872d4c3a6b55aca66025b5119e3bebb6679fe2e2794638db51dcace21fc39a498047835994f07eb59f311bb956ce057e66793d1c",
1769
+ # "data": {
1770
+ # "asset": "0xAFB6Bb95cd70D5367e2C39e9dbEb422B9815339D",
1771
+ # "sub_id": 0,
1772
+ # "limit_price": "10000",
1773
+ # "desired_amount": "0.001",
1774
+ # "worst_fee": "0",
1775
+ # "recipient_id": 130837,
1776
+ # "is_bid": True,
1777
+ # "trade_id": ""
1778
+ # }
1779
+ # }
1780
+ # {
1781
+ # "subaccount_id": 130837,
1782
+ # "order_id": "96349ebb-7d46-43ae-81c7-7ab390444293",
1783
+ # "instrument_name": "BTC-PERP",
1784
+ # "direction": "buy",
1785
+ # "label": "",
1786
+ # "quote_id": null,
1787
+ # "creation_timestamp": 1737467576257,
1788
+ # "last_update_timestamp": 1737467576257,
1789
+ # "limit_price": "10000",
1790
+ # "amount": "0.01",
1791
+ # "filled_amount": "0",
1792
+ # "average_price": "0",
1793
+ # "order_fee": "0",
1794
+ # "order_type": "limit",
1795
+ # "time_in_force": "gtc",
1796
+ # "order_status": "open",
1797
+ # "max_fee": "210",
1798
+ # "signature_expiry_sec": 1737468175989,
1799
+ # "nonce": 1737467575989,
1800
+ # "signer": "0x30CB7B06AdD6749BbE146A6827502B8f2a79269A",
1801
+ # "signature": "0xd1ca49df1fa06bd805bb59b132ff6c0de29bf973a3e01705abe0a01cc956e4945ed9eb99ab68f3df4c037908113cac5a5bfc3a954a0b7103cdab285962fa6a51c",
1802
+ # "cancel_reason": "",
1803
+ # "mmp": False,
1804
+ # "is_transfer": False,
1805
+ # "replaced_order_id": null,
1806
+ # "trigger_type": null,
1807
+ # "trigger_price_type": null,
1808
+ # "trigger_price": null,
1809
+ # "trigger_reject_message": null
1810
+ # }
1811
+ order = self.safe_dict(rawOrder, 'data')
1812
+ if order is None:
1813
+ order = rawOrder
1814
+ timestamp = self.safe_integer(rawOrder, 'nonce')
1815
+ orderId = self.safe_string(order, 'order_id')
1816
+ marketId = self.safe_string(order, 'instrument_name')
1817
+ if marketId is not None:
1818
+ market = self.safe_market(marketId, market)
1819
+ symbol = market['symbol']
1820
+ price = self.safe_string(order, 'limit_price')
1821
+ average = self.safe_string(order, 'average_price')
1822
+ amount = self.safe_string(order, 'desired_amount')
1823
+ filled = self.safe_string(order, 'filled_amount')
1824
+ fee = self.safe_string(order, 'order_fee')
1825
+ orderType = self.safe_string_lower(order, 'order_type')
1826
+ isBid = self.safe_bool(order, 'is_bid')
1827
+ side = self.safe_string(order, 'direction')
1828
+ if side is None:
1829
+ if isBid:
1830
+ side = 'buy'
1831
+ else:
1832
+ side = 'sell'
1833
+ triggerType = self.safe_string(order, 'trigger_type')
1834
+ stopLossPrice = None
1835
+ takeProfitPrice = None
1836
+ triggerPrice = None
1837
+ if triggerType is not None:
1838
+ triggerPrice = self.safe_string(order, 'trigger_price')
1839
+ if triggerType == 'stoploss':
1840
+ stopLossPrice = triggerPrice
1841
+ else:
1842
+ takeProfitPrice = triggerPrice
1843
+ lastUpdateTimestamp = self.safe_integer(rawOrder, 'last_update_timestamp')
1844
+ status = self.safe_string(order, 'order_status')
1845
+ timeInForce = self.safe_string(order, 'time_in_force')
1846
+ return self.safe_order({
1847
+ 'id': orderId,
1848
+ 'clientOrderId': self.safe_string(order, 'label'),
1849
+ 'timestamp': timestamp,
1850
+ 'datetime': self.iso8601(timestamp),
1851
+ 'lastTradeTimestamp': None,
1852
+ 'lastUpdateTimestamp': lastUpdateTimestamp,
1853
+ 'status': self.parse_order_status(status),
1854
+ 'symbol': symbol,
1855
+ 'type': orderType,
1856
+ 'timeInForce': self.parse_time_in_force(timeInForce),
1857
+ 'postOnly': None, # handled in safeOrder
1858
+ 'reduceOnly': self.safe_bool(order, 'reduce_only'),
1859
+ 'side': side,
1860
+ 'price': price,
1861
+ 'triggerPrice': triggerPrice,
1862
+ 'takeProfitPrice': takeProfitPrice,
1863
+ 'stopLossPrice': stopLossPrice,
1864
+ 'average': average,
1865
+ 'amount': amount,
1866
+ 'filled': filled,
1867
+ 'remaining': None,
1868
+ 'cost': None,
1869
+ 'trades': None,
1870
+ 'fee': {
1871
+ 'cost': fee,
1872
+ 'currency': 'USDC',
1873
+ },
1874
+ 'info': order,
1875
+ }, market)
1876
+
1877
+ def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1878
+ """
1879
+ fetch all the trades made from a single order
1880
+
1881
+ https://docs.derive.xyz/reference/post_private-get-trade-history
1882
+
1883
+ :param str id: order id
1884
+ :param str symbol: unified market symbol
1885
+ :param int [since]: the earliest time in ms to fetch trades for
1886
+ :param int [limit]: the maximum number of trades to retrieve
1887
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1888
+ :param str [params.subaccount_id]: *required* the subaccount id
1889
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1890
+ """
1891
+ self.load_markets()
1892
+ subaccountId = None
1893
+ subaccountId, params = self.handle_derive_subaccount_id('fetchOrderTrades', params)
1894
+ request: dict = {
1895
+ 'order_id': id,
1896
+ 'subaccount_id': subaccountId,
1897
+ }
1898
+ market: Market = None
1899
+ if symbol is not None:
1900
+ market = self.market(symbol)
1901
+ request['instrument_name'] = market['id']
1902
+ if limit is not None:
1903
+ request['page_size'] = limit
1904
+ if since is not None:
1905
+ request['from_timestamp'] = since
1906
+ response = self.privatePostGetTradeHistory(self.extend(request, params))
1907
+ #
1908
+ # {
1909
+ # "result": {
1910
+ # "subaccount_id": 130837,
1911
+ # "trades": [
1912
+ # {
1913
+ # "subaccount_id": 130837,
1914
+ # "order_id": "30c48194-8d48-43ac-ad00-0d5ba29eddc9",
1915
+ # "instrument_name": "BTC-PERP",
1916
+ # "direction": "sell",
1917
+ # "label": "test1234",
1918
+ # "quote_id": null,
1919
+ # "trade_id": "f8a30740-488c-4c2d-905d-e17057bafde1",
1920
+ # "timestamp": 1738065303708,
1921
+ # "mark_price": "102740.137375457314192317",
1922
+ # "index_price": "102741.553409299981533184",
1923
+ # "trade_price": "102700.6",
1924
+ # "trade_amount": "0.01",
1925
+ # "liquidity_role": "taker",
1926
+ # "realized_pnl": "0",
1927
+ # "realized_pnl_excl_fees": "0",
1928
+ # "is_transfer": False,
1929
+ # "tx_status": "settled",
1930
+ # "trade_fee": "1.127415534092999815",
1931
+ # "tx_hash": "0xc55df1f07330faf86579bd8a6385391fbe9e73089301149d8550e9d29c9ead74",
1932
+ # "transaction_id": "e18b9426-3fa5-41bb-99d3-8b54fb4d51bb"
1933
+ # }
1934
+ # ],
1935
+ # "pagination": {
1936
+ # "num_pages": 1,
1937
+ # "count": 1
1938
+ # }
1939
+ # },
1940
+ # "id": "a16f798c-a121-44e2-b77e-c38a063f8a99"
1941
+ # }
1942
+ #
1943
+ result = self.safe_dict(response, 'result', {})
1944
+ trades = self.safe_list(result, 'trades', [])
1945
+ return self.parse_trades(trades, market, since, limit, params)
1946
+
1947
+ def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1948
+ """
1949
+ fetch all trades made by the user
1950
+
1951
+ https://docs.derive.xyz/reference/post_private-get-trade-history
1952
+
1953
+ :param str symbol: unified market symbol
1954
+ :param int [since]: the earliest time in ms to fetch trades for
1955
+ :param int [limit]: the maximum number of trades structures to retrieve
1956
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1957
+ :param boolean [params.paginate]: set to True if you want to fetch trades with pagination
1958
+ :param str [params.subaccount_id]: *required* the subaccount id
1959
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
1960
+ """
1961
+ self.load_markets()
1962
+ paginate = False
1963
+ paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
1964
+ if paginate:
1965
+ return self.fetch_paginated_call_incremental('fetchMyTrades', symbol, since, limit, params, 'page', 500)
1966
+ subaccountId = None
1967
+ subaccountId, params = self.handle_derive_subaccount_id('fetchMyTrades', params)
1968
+ request: dict = {
1969
+ 'subaccount_id': subaccountId,
1970
+ }
1971
+ market: Market = None
1972
+ if symbol is not None:
1973
+ market = self.market(symbol)
1974
+ request['instrument_name'] = market['id']
1975
+ if limit is not None:
1976
+ request['page_size'] = limit
1977
+ if since is not None:
1978
+ request['from_timestamp'] = since
1979
+ response = self.privatePostGetTradeHistory(self.extend(request, params))
1980
+ #
1981
+ # {
1982
+ # "result": {
1983
+ # "subaccount_id": 130837,
1984
+ # "trades": [
1985
+ # {
1986
+ # "subaccount_id": 130837,
1987
+ # "order_id": "30c48194-8d48-43ac-ad00-0d5ba29eddc9",
1988
+ # "instrument_name": "BTC-PERP",
1989
+ # "direction": "sell",
1990
+ # "label": "test1234",
1991
+ # "quote_id": null,
1992
+ # "trade_id": "f8a30740-488c-4c2d-905d-e17057bafde1",
1993
+ # "timestamp": 1738065303708,
1994
+ # "mark_price": "102740.137375457314192317",
1995
+ # "index_price": "102741.553409299981533184",
1996
+ # "trade_price": "102700.6",
1997
+ # "trade_amount": "0.01",
1998
+ # "liquidity_role": "taker",
1999
+ # "realized_pnl": "0",
2000
+ # "realized_pnl_excl_fees": "0",
2001
+ # "is_transfer": False,
2002
+ # "tx_status": "settled",
2003
+ # "trade_fee": "1.127415534092999815",
2004
+ # "tx_hash": "0xc55df1f07330faf86579bd8a6385391fbe9e73089301149d8550e9d29c9ead74",
2005
+ # "transaction_id": "e18b9426-3fa5-41bb-99d3-8b54fb4d51bb"
2006
+ # }
2007
+ # ],
2008
+ # "pagination": {
2009
+ # "num_pages": 1,
2010
+ # "count": 1
2011
+ # }
2012
+ # },
2013
+ # "id": "a16f798c-a121-44e2-b77e-c38a063f8a99"
2014
+ # }
2015
+ #
2016
+ result = self.safe_dict(response, 'result', {})
2017
+ page = self.safe_integer(params, 'page')
2018
+ if page is not None:
2019
+ pagination = self.safe_dict(result, 'pagination')
2020
+ currentPage = self.safe_integer(pagination, 'num_pages')
2021
+ if page > currentPage:
2022
+ return []
2023
+ trades = self.safe_list(result, 'trades', [])
2024
+ return self.parse_trades(trades, market, since, limit, params)
2025
+
2026
+ def fetch_positions(self, symbols: Strings = None, params={}):
2027
+ """
2028
+ fetch all open positions
2029
+
2030
+ https://docs.derive.xyz/reference/post_private-get-positions
2031
+
2032
+ :param str[] [symbols]: not used by kraken fetchPositions()
2033
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2034
+ :param str [params.subaccount_id]: *required* the subaccount id
2035
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
2036
+ """
2037
+ self.load_markets()
2038
+ subaccountId = None
2039
+ subaccountId, params = self.handle_derive_subaccount_id('fetchPositions', params)
2040
+ request: dict = {
2041
+ 'subaccount_id': subaccountId,
2042
+ }
2043
+ params = self.omit(params, ['subaccount_id'])
2044
+ response = self.privatePostGetPositions(self.extend(request, params))
2045
+ #
2046
+ # {
2047
+ # "result": {
2048
+ # "subaccount_id": 130837,
2049
+ # "positions": [
2050
+ # {
2051
+ # "instrument_type": "perp",
2052
+ # "instrument_name": "BTC-PERP",
2053
+ # "amount": "-0.02",
2054
+ # "average_price": "102632.9105389869500088",
2055
+ # "realized_pnl": "0",
2056
+ # "unrealized_pnl": "-2.6455959784245548835819950103759765625",
2057
+ # "total_fees": "2.255789220260999824",
2058
+ # "average_price_excl_fees": "102745.7",
2059
+ # "realized_pnl_excl_fees": "0",
2060
+ # "unrealized_pnl_excl_fees": "-0.3898067581635550595819950103759765625",
2061
+ # "net_settlements": "-4.032902047219498639",
2062
+ # "cumulative_funding": "-0.004677736347850093",
2063
+ # "pending_funding": "0",
2064
+ # "mark_price": "102765.190337908177752979099750518798828125",
2065
+ # "index_price": "102767.657193800017641472",
2066
+ # "delta": "1",
2067
+ # "gamma": "0",
2068
+ # "vega": "0",
2069
+ # "theta": "0",
2070
+ # "mark_value": "1.38730606879471451975405216217041015625",
2071
+ # "maintenance_margin": "-101.37788426911356509663164615631103515625",
2072
+ # "initial_margin": "-132.2074413704858670826070010662078857421875",
2073
+ # "open_orders_margin": "264.116085900726830004714429378509521484375",
2074
+ # "leverage": "8.6954476205089299495699106539379941746377322586618",
2075
+ # "liquidation_price": "109125.705451984322280623018741607666015625",
2076
+ # "creation_timestamp": 1738065303840
2077
+ # }
2078
+ # ]
2079
+ # },
2080
+ # "id": "167350f1-d9fc-41d4-9797-1c78f83fda8e"
2081
+ # }
2082
+ #
2083
+ result = self.safe_dict(response, 'result', {})
2084
+ positions = self.safe_list(result, 'positions', [])
2085
+ return self.parse_positions(positions, symbols)
2086
+
2087
+ def parse_position(self, position: dict, market: Market = None):
2088
+ #
2089
+ # {
2090
+ # "instrument_type": "perp",
2091
+ # "instrument_name": "BTC-PERP",
2092
+ # "amount": "-0.02",
2093
+ # "average_price": "102632.9105389869500088",
2094
+ # "realized_pnl": "0",
2095
+ # "unrealized_pnl": "-2.6455959784245548835819950103759765625",
2096
+ # "total_fees": "2.255789220260999824",
2097
+ # "average_price_excl_fees": "102745.7",
2098
+ # "realized_pnl_excl_fees": "0",
2099
+ # "unrealized_pnl_excl_fees": "-0.3898067581635550595819950103759765625",
2100
+ # "net_settlements": "-4.032902047219498639",
2101
+ # "cumulative_funding": "-0.004677736347850093",
2102
+ # "pending_funding": "0",
2103
+ # "mark_price": "102765.190337908177752979099750518798828125",
2104
+ # "index_price": "102767.657193800017641472",
2105
+ # "delta": "1",
2106
+ # "gamma": "0",
2107
+ # "vega": "0",
2108
+ # "theta": "0",
2109
+ # "mark_value": "1.38730606879471451975405216217041015625",
2110
+ # "maintenance_margin": "-101.37788426911356509663164615631103515625",
2111
+ # "initial_margin": "-132.2074413704858670826070010662078857421875",
2112
+ # "open_orders_margin": "264.116085900726830004714429378509521484375",
2113
+ # "leverage": "8.6954476205089299495699106539379941746377322586618",
2114
+ # "liquidation_price": "109125.705451984322280623018741607666015625",
2115
+ # "creation_timestamp": 1738065303840
2116
+ # }
2117
+ #
2118
+ contract = self.safe_string(position, 'instrument_name')
2119
+ market = self.safe_market(contract, market)
2120
+ size = self.safe_string(position, 'amount')
2121
+ side: Str = None
2122
+ if Precise.string_gt(size, '0'):
2123
+ side = 'long'
2124
+ else:
2125
+ side = 'short'
2126
+ contractSize = self.safe_string(market, 'contractSize')
2127
+ markPrice = self.safe_string(position, 'mark_price')
2128
+ timestamp = self.safe_integer(position, 'creation_timestamp')
2129
+ unrealisedPnl = self.safe_string(position, 'unrealized_pnl')
2130
+ size = Precise.string_abs(size)
2131
+ notional = Precise.string_mul(size, markPrice)
2132
+ return self.safe_position({
2133
+ 'info': position,
2134
+ 'id': None,
2135
+ 'symbol': self.safe_string(market, 'symbol'),
2136
+ 'timestamp': timestamp,
2137
+ 'datetime': self.iso8601(timestamp),
2138
+ 'lastUpdateTimestamp': None,
2139
+ 'initialMargin': self.safe_string(position, 'initial_margin'),
2140
+ 'initialMarginPercentage': None,
2141
+ 'maintenanceMargin': self.safe_string(position, 'maintenance_margin'),
2142
+ 'maintenanceMarginPercentage': None,
2143
+ 'entryPrice': None,
2144
+ 'notional': self.parse_number(notional),
2145
+ 'leverage': self.safe_number(position, 'leverage'),
2146
+ 'unrealizedPnl': self.parse_number(unrealisedPnl),
2147
+ 'contracts': self.parse_number(size),
2148
+ 'contractSize': self.parse_number(contractSize),
2149
+ 'marginRatio': None,
2150
+ 'liquidationPrice': self.safe_number(position, 'liquidation_price'),
2151
+ 'markPrice': self.parse_number(markPrice),
2152
+ 'lastPrice': None,
2153
+ 'collateral': None,
2154
+ 'marginMode': None,
2155
+ 'side': side,
2156
+ 'percentage': None,
2157
+ 'hedged': None,
2158
+ 'stopLossPrice': None,
2159
+ 'takeProfitPrice': None,
2160
+ })
2161
+
2162
+ def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2163
+ """
2164
+ fetch the history of funding payments paid and received on self account
2165
+
2166
+ https://docs.derive.xyz/reference/post_private-get-funding-history
2167
+
2168
+ :param str [symbol]: unified market symbol
2169
+ :param int [since]: the earliest time in ms to fetch funding history for
2170
+ :param int [limit]: the maximum number of funding history structures to retrieve
2171
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2172
+ :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
2173
+ :returns dict: a `funding history structure <https://docs.ccxt.com/#/?id=funding-history-structure>`
2174
+ """
2175
+ self.load_markets()
2176
+ paginate = False
2177
+ paginate, params = self.handle_option_and_params(params, 'fetchFundingHistory', 'paginate')
2178
+ if paginate:
2179
+ return self.fetch_paginated_call_incremental('fetchFundingHistory', symbol, since, limit, params, 'page', 500)
2180
+ subaccountId = None
2181
+ subaccountId, params = self.handle_derive_subaccount_id('fetchFundingHistory', params)
2182
+ request: dict = {
2183
+ 'subaccount_id': subaccountId,
2184
+ }
2185
+ market: Market = None
2186
+ if symbol is not None:
2187
+ market = self.market(symbol)
2188
+ request['instrument_name'] = market['id']
2189
+ if since is not None:
2190
+ request['start_timestamp'] = since
2191
+ if limit is not None:
2192
+ request['page_size'] = limit
2193
+ response = self.privatePostGetFundingHistory(self.extend(request, params))
2194
+ #
2195
+ # {
2196
+ # "result": {
2197
+ # "events": [
2198
+ # {
2199
+ # "instrument_name": "BTC-PERP",
2200
+ # "timestamp": 1738066618272,
2201
+ # "funding": "-0.004677736347850093",
2202
+ # "pnl": "-0.944081615774632967"
2203
+ # },
2204
+ # {
2205
+ # "instrument_name": "BTC-PERP",
2206
+ # "timestamp": 1738066617964,
2207
+ # "funding": "0",
2208
+ # "pnl": "-0.437556413479249408"
2209
+ # },
2210
+ # {
2211
+ # "instrument_name": "BTC-PERP",
2212
+ # "timestamp": 1738065307565,
2213
+ # "funding": "0",
2214
+ # "pnl": "-0.39547479770461644"
2215
+ # }
2216
+ # ],
2217
+ # "pagination": {
2218
+ # "num_pages": 1,
2219
+ # "count": 3
2220
+ # }
2221
+ # },
2222
+ # "id": "524b817f-2108-467f-8795-511066f4acec"
2223
+ # }
2224
+ #
2225
+ result = self.safe_dict(response, 'result', {})
2226
+ page = self.safe_integer(params, 'page')
2227
+ if page is not None:
2228
+ pagination = self.safe_dict(result, 'pagination')
2229
+ currentPage = self.safe_integer(pagination, 'num_pages')
2230
+ if page > currentPage:
2231
+ return []
2232
+ events = self.safe_list(result, 'events', [])
2233
+ return self.parse_incomes(events, market, since, limit)
2234
+
2235
+ def parse_income(self, income, market: Market = None):
2236
+ #
2237
+ # {
2238
+ # "instrument_name": "BTC-PERP",
2239
+ # "timestamp": 1738065307565,
2240
+ # "funding": "0",
2241
+ # "pnl": "-0.39547479770461644"
2242
+ # }
2243
+ #
2244
+ marketId = self.safe_string(income, 'instrument_name')
2245
+ symbol = self.safe_symbol(marketId, market)
2246
+ rate = self.safe_string(income, 'funding')
2247
+ code = self.safe_currency_code('USDC')
2248
+ timestamp = self.safe_integer(income, 'timestamp')
2249
+ return {
2250
+ 'info': income,
2251
+ 'symbol': symbol,
2252
+ 'code': code,
2253
+ 'timestamp': timestamp,
2254
+ 'datetime': self.iso8601(timestamp),
2255
+ 'id': None,
2256
+ 'amount': None,
2257
+ 'rate': rate,
2258
+ }
2259
+
2260
+ def fetch_balance(self, params={}) -> Balances:
2261
+ """
2262
+ query for balance and get the amount of funds available for trading or funds locked in orders
2263
+
2264
+ https://docs.derive.xyz/reference/post_private-get-all-portfolios
2265
+
2266
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2267
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
2268
+ """
2269
+ self.load_markets()
2270
+ deriveWalletAddress = None
2271
+ deriveWalletAddress, params = self.handle_derive_wallet_address('fetchBalance', params)
2272
+ request = {
2273
+ 'wallet': deriveWalletAddress,
2274
+ }
2275
+ response = self.privatePostGetAllPortfolios(self.extend(request, params))
2276
+ #
2277
+ # {
2278
+ # "result": [{
2279
+ # "subaccount_id": 130837,
2280
+ # "label": "",
2281
+ # "currency": "all",
2282
+ # "margin_type": "SM",
2283
+ # "is_under_liquidation": False,
2284
+ # "positions_value": "0",
2285
+ # "collaterals_value": "318.0760325000001103035174310207366943359375",
2286
+ # "subaccount_value": "318.0760325000001103035174310207366943359375",
2287
+ # "positions_maintenance_margin": "0",
2288
+ # "positions_initial_margin": "0",
2289
+ # "collaterals_maintenance_margin": "238.557024375000082727638073265552520751953125",
2290
+ # "collaterals_initial_margin": "190.845619500000083235136116854846477508544921875",
2291
+ # "maintenance_margin": "238.557024375000082727638073265552520751953125",
2292
+ # "initial_margin": "190.845619500000083235136116854846477508544921875",
2293
+ # "open_orders_margin": "0",
2294
+ # "projected_margin_change": "0",
2295
+ # "open_orders": [],
2296
+ # "positions": [],
2297
+ # "collaterals": [
2298
+ # {
2299
+ # "asset_type": "erc20",
2300
+ # "asset_name": "ETH",
2301
+ # "currency": "ETH",
2302
+ # "amount": "0.1",
2303
+ # "mark_price": "3180.760325000000438272",
2304
+ # "mark_value": "318.0760325000001103035174310207366943359375",
2305
+ # "cumulative_interest": "0",
2306
+ # "pending_interest": "0",
2307
+ # "initial_margin": "190.845619500000083235136116854846477508544921875",
2308
+ # "maintenance_margin": "238.557024375000082727638073265552520751953125",
2309
+ # "realized_pnl": "0",
2310
+ # "average_price": "3184.891931",
2311
+ # "unrealized_pnl": "-0.413161",
2312
+ # "total_fees": "0",
2313
+ # "average_price_excl_fees": "3184.891931",
2314
+ # "realized_pnl_excl_fees": "0",
2315
+ # "unrealized_pnl_excl_fees": "-0.413161",
2316
+ # "open_orders_margin": "0",
2317
+ # "creation_timestamp": 1736860533493
2318
+ # }
2319
+ # ]
2320
+ # }],
2321
+ # "id": "27b9a64e-3379-4ce6-a126-9fb941c4a970"
2322
+ # }
2323
+ #
2324
+ result = self.safe_list(response, 'result')
2325
+ return self.parse_balance(result)
2326
+
2327
+ def parse_balance(self, response) -> Balances:
2328
+ result: dict = {
2329
+ 'info': response,
2330
+ }
2331
+ # TODO:
2332
+ # checked multiple subaccounts
2333
+ # checked balance after open orders / positions
2334
+ for i in range(0, len(response)):
2335
+ subaccount = response[i]
2336
+ collaterals = self.safe_list(subaccount, 'collaterals', [])
2337
+ for j in range(0, len(collaterals)):
2338
+ balance = collaterals[j]
2339
+ code = self.safe_currency_code(self.safe_string(balance, 'currency'))
2340
+ account = self.account()
2341
+ account['total'] = self.safe_string(balance, 'amount')
2342
+ result[code] = account
2343
+ return self.safe_balance(result)
2344
+
2345
+ def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
2346
+ """
2347
+ fetch all deposits made to an account
2348
+
2349
+ https://docs.derive.xyz/reference/post_private-get-deposit-history
2350
+
2351
+ :param str code: unified currency code
2352
+ :param int [since]: the earliest time in ms to fetch deposits for
2353
+ :param int [limit]: the maximum number of deposits structures to retrieve
2354
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2355
+ :param str [params.subaccount_id]: *required* the subaccount id
2356
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
2357
+ """
2358
+ self.load_markets()
2359
+ subaccountId = None
2360
+ subaccountId, params = self.handle_derive_subaccount_id('fetchDeposits', params)
2361
+ request: dict = {
2362
+ 'subaccount_id': subaccountId,
2363
+ }
2364
+ if since is not None:
2365
+ request['start_timestamp'] = since
2366
+ response = self.privatePostGetDepositHistory(self.extend(request, params))
2367
+ #
2368
+ # {
2369
+ # "result": {
2370
+ # "events": [
2371
+ # {
2372
+ # "timestamp": 1736860533599,
2373
+ # "transaction_id": "f2069395-ec00-49f5-925a-87202a5d240f",
2374
+ # "asset": "ETH",
2375
+ # "amount": "0.1",
2376
+ # "tx_status": "settled",
2377
+ # "tx_hash": "0xeda21a315c59302a19c42049b4cef05a10b685302b6cc3edbaf49102d91166d4",
2378
+ # "error_log": {}
2379
+ # }
2380
+ # ]
2381
+ # },
2382
+ # "id": "ceebc730-22ab-40cd-9941-33ceb2a74389"
2383
+ # }
2384
+ #
2385
+ currency = self.safe_currency(code)
2386
+ result = self.safe_dict(response, 'result', {})
2387
+ events = self.safe_list(result, 'events')
2388
+ return self.parse_transactions(events, currency, since, limit, params)
2389
+
2390
+ def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
2391
+ """
2392
+ fetch all withdrawals made from an account
2393
+
2394
+ https://docs.derive.xyz/reference/post_private-get-withdrawal-history
2395
+
2396
+ :param str code: unified currency code
2397
+ :param int [since]: the earliest time in ms to fetch withdrawals for
2398
+ :param int [limit]: the maximum number of withdrawals structures to retrieve
2399
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2400
+ :param str [params.subaccount_id]: *required* the subaccount id
2401
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
2402
+ """
2403
+ self.load_markets()
2404
+ subaccountId = None
2405
+ subaccountId, params = self.handle_derive_subaccount_id('fetchWithdrawals', params)
2406
+ request: dict = {
2407
+ 'subaccount_id': subaccountId,
2408
+ }
2409
+ if since is not None:
2410
+ request['start_timestamp'] = since
2411
+ response = self.privatePostGetWithdrawalHistory(self.extend(request, params))
2412
+ #
2413
+ # {
2414
+ # "result": {
2415
+ # "events": [
2416
+ # {
2417
+ # "timestamp": 1736860533599,
2418
+ # "transaction_id": "f2069395-ec00-49f5-925a-87202a5d240f",
2419
+ # "asset": "ETH",
2420
+ # "amount": "0.1",
2421
+ # "tx_status": "settled",
2422
+ # "tx_hash": "0xeda21a315c59302a19c42049b4cef05a10b685302b6cc3edbaf49102d91166d4",
2423
+ # "error_log": {}
2424
+ # }
2425
+ # ]
2426
+ # },
2427
+ # "id": "ceebc730-22ab-40cd-9941-33ceb2a74389"
2428
+ # }
2429
+ #
2430
+ currency = self.safe_currency(code)
2431
+ result = self.safe_dict(response, 'result', {})
2432
+ events = self.safe_list(result, 'events')
2433
+ return self.parse_transactions(events, currency, since, limit, params)
2434
+
2435
+ def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
2436
+ #
2437
+ # {
2438
+ # "timestamp": 1736860533599,
2439
+ # "transaction_id": "f2069395-ec00-49f5-925a-87202a5d240f",
2440
+ # "asset": "ETH",
2441
+ # "amount": "0.1",
2442
+ # "tx_status": "settled",
2443
+ # "tx_hash": "0xeda21a315c59302a19c42049b4cef05a10b685302b6cc3edbaf49102d91166d4",
2444
+ # "error_log": {}
2445
+ # }
2446
+ #
2447
+ code = self.safe_string(transaction, 'asset')
2448
+ timestamp = self.safe_integer(transaction, 'timestamp')
2449
+ txId = self.safe_string(transaction, 'tx_hash')
2450
+ if txId == '0x0':
2451
+ txId = None
2452
+ return {
2453
+ 'info': transaction,
2454
+ 'id': None,
2455
+ 'txid': txId,
2456
+ 'timestamp': timestamp,
2457
+ 'datetime': self.iso8601(timestamp),
2458
+ 'address': None,
2459
+ 'addressFrom': None,
2460
+ 'addressTo': None,
2461
+ 'tag': None,
2462
+ 'tagFrom': None,
2463
+ 'tagTo': None,
2464
+ 'type': None,
2465
+ 'amount': self.safe_number(transaction, 'amount'),
2466
+ 'currency': code,
2467
+ 'status': self.parse_transaction_status(self.safe_string(transaction, 'tx_status')),
2468
+ 'updated': None,
2469
+ 'comment': None,
2470
+ 'internal': None,
2471
+ 'fee': None,
2472
+ 'network': None,
2473
+ }
2474
+
2475
+ def parse_transaction_status(self, status: Str):
2476
+ statuses: dict = {
2477
+ 'settled': 'ok',
2478
+ 'reverted': 'failed',
2479
+ }
2480
+ return self.safe_string(statuses, status, status)
2481
+
2482
+ def handle_derive_subaccount_id(self, methodName: str, params: dict):
2483
+ derivesubAccountId = None
2484
+ derivesubAccountId, params = self.handle_option_and_params(params, methodName, 'subaccount_id')
2485
+ if (derivesubAccountId is not None) and (derivesubAccountId != ''):
2486
+ self.options['subaccount_id'] = derivesubAccountId # saving in options
2487
+ return [derivesubAccountId, params]
2488
+ optionsWallet = self.safe_string(self.options, 'subaccount_id')
2489
+ if optionsWallet is not None:
2490
+ return [optionsWallet, params]
2491
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a subaccount_id parameter inside \'params\' or exchange.options[\'subaccount_id\']=ID.')
2492
+
2493
+ def handle_derive_wallet_address(self, methodName: str, params: dict):
2494
+ deriveWalletAddress = None
2495
+ deriveWalletAddress, params = self.handle_option_and_params(params, methodName, 'deriveWalletAddress')
2496
+ if (deriveWalletAddress is not None) and (deriveWalletAddress != ''):
2497
+ self.options['deriveWalletAddress'] = deriveWalletAddress # saving in options
2498
+ return [deriveWalletAddress, params]
2499
+ optionsWallet = self.safe_string(self.options, 'deriveWalletAddress')
2500
+ if optionsWallet is not None:
2501
+ return [optionsWallet, params]
2502
+ raise ArgumentsRequired(self.id + ' ' + methodName + '() requires a deriveWalletAddress parameter inside \'params\' or exchange.options[\'deriveWalletAddress\'] = ADDRESS, the address can find in HOME => Developers tab.')
2503
+
2504
+ def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
2505
+ if not response:
2506
+ return None # fallback to default error handler
2507
+ error = self.safe_dict(response, 'error')
2508
+ if error is not None:
2509
+ errorCode = self.safe_string(error, 'code')
2510
+ feedback = self.id + ' ' + self.json(response)
2511
+ self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
2512
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
2513
+ raise ExchangeError(feedback)
2514
+ return None
2515
+
2516
+ def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
2517
+ url = self.urls['api'][api] + '/' + path
2518
+ if method == 'POST':
2519
+ headers = {
2520
+ 'Content-Type': 'application/json',
2521
+ }
2522
+ if api == 'private':
2523
+ now = str(self.milliseconds())
2524
+ signature = self.sign_message(now, self.privateKey)
2525
+ headers['X-LyraWallet'] = self.safe_string(self.options, 'deriveWalletAddress')
2526
+ headers['X-LyraTimestamp'] = now
2527
+ headers['X-LyraSignature'] = signature
2528
+ body = self.json(params)
2529
+ return {'url': url, 'method': method, 'body': body, 'headers': headers}