ccxt 4.4.64__py2.py3-none-any.whl → 4.4.68__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccxt/__init__.py +5 -3
- ccxt/abstract/cryptomus.py +20 -0
- ccxt/abstract/derive.py +117 -0
- ccxt/abstract/tradeogre.py +1 -0
- ccxt/abstract/whitebit.py +16 -0
- ccxt/async_support/__init__.py +5 -3
- ccxt/async_support/base/exchange.py +6 -5
- ccxt/async_support/binance.py +5 -3
- ccxt/async_support/bitget.py +22 -12
- ccxt/async_support/bitrue.py +6 -3
- ccxt/async_support/bybit.py +1 -1
- ccxt/async_support/coinbase.py +73 -2
- ccxt/async_support/cryptocom.py +2 -0
- ccxt/async_support/cryptomus.py +1041 -0
- ccxt/async_support/derive.py +2530 -0
- ccxt/async_support/gate.py +5 -1
- ccxt/async_support/htx.py +19 -5
- ccxt/async_support/hyperliquid.py +108 -68
- ccxt/async_support/luno.py +113 -1
- ccxt/async_support/paradex.py +51 -12
- ccxt/async_support/tradeogre.py +132 -13
- ccxt/async_support/whitebit.py +276 -2
- ccxt/base/errors.py +0 -6
- ccxt/base/exchange.py +13 -4
- ccxt/binance.py +5 -3
- ccxt/bitget.py +22 -12
- ccxt/bitrue.py +6 -3
- ccxt/bybit.py +1 -1
- ccxt/coinbase.py +73 -2
- ccxt/cryptocom.py +2 -0
- ccxt/cryptomus.py +1041 -0
- ccxt/derive.py +2529 -0
- ccxt/gate.py +5 -1
- ccxt/htx.py +19 -5
- ccxt/hyperliquid.py +108 -68
- ccxt/luno.py +113 -1
- ccxt/paradex.py +51 -12
- ccxt/pro/__init__.py +3 -3
- ccxt/pro/bybit.py +3 -2
- ccxt/pro/derive.py +704 -0
- ccxt/pro/gate.py +5 -2
- ccxt/pro/hyperliquid.py +3 -3
- ccxt/test/tests_async.py +36 -3
- ccxt/test/tests_sync.py +36 -3
- ccxt/tradeogre.py +132 -13
- ccxt/whitebit.py +276 -2
- {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/METADATA +16 -12
- {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/RECORD +51 -48
- ccxt/abstract/currencycom.py +0 -68
- ccxt/async_support/currencycom.py +0 -2070
- ccxt/currencycom.py +0 -2070
- ccxt/pro/currencycom.py +0 -536
- {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/WHEEL +0 -0
- {ccxt-4.4.64.dist-info → ccxt-4.4.68.dist-info}/top_level.txt +0 -0
@@ -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}
|