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

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