ccxt 4.5.22 → 4.5.23
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.
- package/README.md +6 -5
- package/dist/ccxt.browser.min.js +2 -2
- package/dist/cjs/ccxt.js +6 -1
- package/dist/cjs/src/abstract/bullish.js +11 -0
- package/dist/cjs/src/base/Exchange.js +3 -2
- package/dist/cjs/src/base/ws/WsClient.js +15 -0
- package/dist/cjs/src/binance.js +159 -36
- package/dist/cjs/src/bingx.js +2 -1
- package/dist/cjs/src/bitmart.js +1 -0
- package/dist/cjs/src/bullish.js +2919 -0
- package/dist/cjs/src/bybit.js +34 -37
- package/dist/cjs/src/gate.js +2 -2
- package/dist/cjs/src/htx.js +4 -1
- package/dist/cjs/src/hyperliquid.js +115 -12
- package/dist/cjs/src/kucoin.js +22 -3
- package/dist/cjs/src/mexc.js +7 -0
- package/dist/cjs/src/okx.js +117 -63
- package/dist/cjs/src/paradex.js +78 -3
- package/dist/cjs/src/pro/binance.js +131 -29
- package/dist/cjs/src/pro/bullish.js +781 -0
- package/dist/cjs/src/pro/coinbase.js +2 -2
- package/dist/cjs/src/pro/hyperliquid.js +75 -15
- package/dist/cjs/src/pro/upbit.js +28 -82
- package/js/ccxt.d.ts +8 -2
- package/js/ccxt.js +6 -2
- package/js/src/abstract/binance.d.ts +1 -0
- package/js/src/abstract/binancecoinm.d.ts +1 -0
- package/js/src/abstract/binanceus.d.ts +1 -0
- package/js/src/abstract/binanceusdm.d.ts +1 -0
- package/js/src/abstract/bingx.d.ts +1 -0
- package/js/src/abstract/bullish.d.ts +65 -0
- package/js/src/abstract/bullish.js +5 -0
- package/js/src/abstract/kucoin.d.ts +15 -0
- package/js/src/abstract/kucoinfutures.d.ts +15 -0
- package/js/src/abstract/mexc.d.ts +7 -0
- package/js/src/abstract/myokx.d.ts +90 -39
- package/js/src/abstract/okx.d.ts +90 -39
- package/js/src/abstract/okxus.d.ts +90 -39
- package/js/src/base/Exchange.d.ts +1 -1
- package/js/src/base/Exchange.js +3 -2
- package/js/src/base/ws/Client.d.ts +1 -0
- package/js/src/base/ws/WsClient.js +15 -0
- package/js/src/binance.d.ts +14 -5
- package/js/src/binance.js +159 -36
- package/js/src/bingx.js +2 -1
- package/js/src/bitmart.js +1 -0
- package/js/src/bullish.d.ts +446 -0
- package/js/src/bullish.js +2912 -0
- package/js/src/bybit.js +34 -37
- package/js/src/gate.js +2 -2
- package/js/src/htx.js +4 -1
- package/js/src/hyperliquid.d.ts +24 -0
- package/js/src/hyperliquid.js +115 -12
- package/js/src/kucoin.js +22 -3
- package/js/src/mexc.js +7 -0
- package/js/src/okx.js +117 -63
- package/js/src/paradex.d.ts +15 -1
- package/js/src/paradex.js +78 -3
- package/js/src/pro/binance.d.ts +7 -0
- package/js/src/pro/binance.js +131 -29
- package/js/src/pro/bullish.d.ts +108 -0
- package/js/src/pro/bullish.js +774 -0
- package/js/src/pro/coinbase.js +2 -2
- package/js/src/pro/hyperliquid.d.ts +13 -1
- package/js/src/pro/hyperliquid.js +75 -15
- package/js/src/pro/upbit.d.ts +0 -1
- package/js/src/pro/upbit.js +28 -82
- package/package.json +2 -2
|
@@ -0,0 +1,2912 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
import Exchange from './abstract/bullish.js';
|
|
3
|
+
import { AuthenticationError, ArgumentsRequired, BadRequest, BadSymbol, DuplicateOrderId, ExchangeError, InvalidAddress, InvalidNonce, InvalidOrder, InsufficientFunds, MarketClosed, NotSupported, OperationRejected, OrderNotFillable, OrderNotFound, PermissionDenied, RateLimitExceeded } from './base/errors.js';
|
|
4
|
+
import { TICK_SIZE } from './base/functions/number.js';
|
|
5
|
+
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/**
|
|
8
|
+
* @class bullish
|
|
9
|
+
* @augments Exchange
|
|
10
|
+
*/
|
|
11
|
+
export default class bullish extends Exchange {
|
|
12
|
+
describe() {
|
|
13
|
+
return this.deepExtend(super.describe(), {
|
|
14
|
+
'id': 'bullish',
|
|
15
|
+
'name': 'Bullish',
|
|
16
|
+
'countries': ['DE'],
|
|
17
|
+
'version': 'v3',
|
|
18
|
+
'rateLimit': 20,
|
|
19
|
+
'pro': true,
|
|
20
|
+
'has': {
|
|
21
|
+
'CORS': undefined,
|
|
22
|
+
'spot': true,
|
|
23
|
+
'margin': false,
|
|
24
|
+
'swap': false,
|
|
25
|
+
'future': false,
|
|
26
|
+
'option': false,
|
|
27
|
+
'addMargin': false,
|
|
28
|
+
'borrowMargin': false,
|
|
29
|
+
'cancelAllOrders': true,
|
|
30
|
+
'cancelOrder': true,
|
|
31
|
+
'cancelOrders': false,
|
|
32
|
+
'createDepositAddress': false,
|
|
33
|
+
'createLimitBuyOrder': true,
|
|
34
|
+
'createLimitOrder': true,
|
|
35
|
+
'createLimitSellOrder': true,
|
|
36
|
+
'createMarketBuyOrder': true,
|
|
37
|
+
'createMarketOrder': true,
|
|
38
|
+
'createMarketSellOrder': true,
|
|
39
|
+
'createOrder': true,
|
|
40
|
+
'createPostOnlyOrder': true,
|
|
41
|
+
'createTriggerOrder': true,
|
|
42
|
+
'deposit': false,
|
|
43
|
+
'editOrder': true,
|
|
44
|
+
'fetchAccounts': true,
|
|
45
|
+
'fetchBalance': true,
|
|
46
|
+
'fetchBidsAsks': false,
|
|
47
|
+
'fetchBorrowInterest': false,
|
|
48
|
+
'fetchBorrowRateHistories': false,
|
|
49
|
+
'fetchBorrowRateHistory': true,
|
|
50
|
+
'fetchCanceledAndClosedOrders': true,
|
|
51
|
+
'fetchCanceledOrders': true,
|
|
52
|
+
'fetchClosedOrder': false,
|
|
53
|
+
'fetchClosedOrders': true,
|
|
54
|
+
'fetchCrossBorrowRate': false,
|
|
55
|
+
'fetchCrossBorrowRates': false,
|
|
56
|
+
'fetchCurrencies': true,
|
|
57
|
+
'fetchDeposit': false,
|
|
58
|
+
'fetchDepositAddress': true,
|
|
59
|
+
'fetchDepositAddresses': false,
|
|
60
|
+
'fetchDepositAddressesByNetwork': false,
|
|
61
|
+
'fetchDeposits': false,
|
|
62
|
+
'fetchDepositsWithdrawals': true,
|
|
63
|
+
'fetchDepositWithdrawFee': false,
|
|
64
|
+
'fetchDepositWithdrawFees': false,
|
|
65
|
+
'fetchFundingHistory': false,
|
|
66
|
+
'fetchFundingRate': false,
|
|
67
|
+
'fetchFundingRateHistory': true,
|
|
68
|
+
'fetchFundingRates': false,
|
|
69
|
+
'fetchIndexOHLCV': false,
|
|
70
|
+
'fetchIsolatedBorrowRate': false,
|
|
71
|
+
'fetchIsolatedBorrowRates': false,
|
|
72
|
+
'fetchL3OrderBook': false,
|
|
73
|
+
'fetchLedger': false,
|
|
74
|
+
'fetchLeverage': false,
|
|
75
|
+
'fetchLeverageTiers': false,
|
|
76
|
+
'fetchMarketLeverageTiers': false,
|
|
77
|
+
'fetchMarkets': true,
|
|
78
|
+
'fetchMarkOHLCV': false,
|
|
79
|
+
'fetchMyTrades': true,
|
|
80
|
+
'fetchOHLCV': true,
|
|
81
|
+
'fetchOpenInterestHistory': false,
|
|
82
|
+
'fetchOpenOrder': false,
|
|
83
|
+
'fetchOpenOrders': true,
|
|
84
|
+
'fetchOrder': true,
|
|
85
|
+
'fetchOrderBook': true,
|
|
86
|
+
'fetchOrderBooks': false,
|
|
87
|
+
'fetchOrders': true,
|
|
88
|
+
'fetchOrderTrades': true,
|
|
89
|
+
'fetchPosition': false,
|
|
90
|
+
'fetchPositionHistory': false,
|
|
91
|
+
'fetchPositionMode': false,
|
|
92
|
+
'fetchPositions': true,
|
|
93
|
+
'fetchPositionsForSymbol': false,
|
|
94
|
+
'fetchPositionsHistory': false,
|
|
95
|
+
'fetchPositionsRisk': false,
|
|
96
|
+
'fetchPremiumIndexOHLCV': false,
|
|
97
|
+
'fetchStatus': false,
|
|
98
|
+
'fetchTicker': true,
|
|
99
|
+
'fetchTickers': false,
|
|
100
|
+
'fetchTime': true,
|
|
101
|
+
'fetchTrades': true,
|
|
102
|
+
'fetchTradingFee': false,
|
|
103
|
+
'fetchTradingFees': false,
|
|
104
|
+
'fetchTradingLimits': false,
|
|
105
|
+
'fetchTransactionFee': false,
|
|
106
|
+
'fetchTransactionFees': false,
|
|
107
|
+
'fetchTransactions': false,
|
|
108
|
+
'fetchTransfers': true,
|
|
109
|
+
'fetchWithdrawal': false,
|
|
110
|
+
'fetchWithdrawals': false,
|
|
111
|
+
'fetchWithdrawalWhitelist': false,
|
|
112
|
+
'reduceMargin': false,
|
|
113
|
+
'repayMargin': false,
|
|
114
|
+
'setLeverage': false,
|
|
115
|
+
'setMargin': false,
|
|
116
|
+
'setMarginMode': false,
|
|
117
|
+
'setPositionMode': false,
|
|
118
|
+
'signIn': true,
|
|
119
|
+
'transfer': true,
|
|
120
|
+
'withdraw': true,
|
|
121
|
+
'ws': true,
|
|
122
|
+
},
|
|
123
|
+
'timeframes': {
|
|
124
|
+
'1m': '1m',
|
|
125
|
+
'5m': '5m',
|
|
126
|
+
'30m': '30m',
|
|
127
|
+
'1h': '1h',
|
|
128
|
+
'6h': '6h',
|
|
129
|
+
'12h': '12h',
|
|
130
|
+
'1d': '1d',
|
|
131
|
+
},
|
|
132
|
+
'urls': {
|
|
133
|
+
'logo': 'https://github.com/user-attachments/assets/68f0686b-84f0-4da9-a751-f7089af3a9ed',
|
|
134
|
+
'api': {
|
|
135
|
+
'public': 'https://api.exchange.bullish.com/trading-api',
|
|
136
|
+
'private': 'https://api.exchange.bullish.com/trading-api',
|
|
137
|
+
},
|
|
138
|
+
'test': {
|
|
139
|
+
'public': 'https://api.simnext.bullish-test.com/trading-api',
|
|
140
|
+
'private': 'https://api.simnext.bullish-test.com/trading-api',
|
|
141
|
+
},
|
|
142
|
+
'www': 'https://bullish.com/',
|
|
143
|
+
'referral': '',
|
|
144
|
+
'doc': [
|
|
145
|
+
'https://api.exchange.bullish.com/docs/api/rest/',
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
'api': {
|
|
149
|
+
'public': {
|
|
150
|
+
'get': {
|
|
151
|
+
'v1/nonce': 1,
|
|
152
|
+
'v1/time': 1,
|
|
153
|
+
'v1/assets': 1,
|
|
154
|
+
'v1/assets/{symbol}': 1,
|
|
155
|
+
'v1/markets': 1,
|
|
156
|
+
'v1/markets/{symbol}': 1,
|
|
157
|
+
'v1/history/markets/{symbol}': 1,
|
|
158
|
+
'v1/markets/{symbol}/orderbook/hybrid': 1,
|
|
159
|
+
'v1/markets/{symbol}/trades': 1,
|
|
160
|
+
'v1/markets/{symbol}/tick': 1,
|
|
161
|
+
'v1/markets/{symbol}/candle': 1,
|
|
162
|
+
'v1/history/markets/{symbol}/trades': 1,
|
|
163
|
+
'v1/history/markets/{symbol}/funding-rate': 1,
|
|
164
|
+
'v1/index-prices': 1,
|
|
165
|
+
'v1/index-prices/{assetSymbol}': 1,
|
|
166
|
+
'v1/expiry-prices/{symbol}': 1,
|
|
167
|
+
'v1/option-ladder': 1,
|
|
168
|
+
'v1/option-ladder/{symbol}': 1,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
'private': {
|
|
172
|
+
'get': {
|
|
173
|
+
'v2/orders': 1,
|
|
174
|
+
'v2/history/orders': 1,
|
|
175
|
+
'v2/orders/{orderId}': 1,
|
|
176
|
+
'v2/amm-instructions': 1,
|
|
177
|
+
'v2/amm-instructions/{instructionId}': 1,
|
|
178
|
+
'v1/wallets/transactions': 1,
|
|
179
|
+
'v1/wallets/limits/{symbol}': 1,
|
|
180
|
+
'v1/wallets/deposit-instructions/crypto/{symbol}': 1,
|
|
181
|
+
'v1/wallets/withdrawal-instructions/crypto/{symbol}': 1,
|
|
182
|
+
'v1/wallets/deposit-instructions/fiat/{symbol}': 1,
|
|
183
|
+
'v1/wallets/withdrawal-instructions/fiat/{symbol}': 1,
|
|
184
|
+
'v1/wallets/self-hosted/verification-attempts': 1,
|
|
185
|
+
'v1/trades': 5,
|
|
186
|
+
'v1/history/trades': 5,
|
|
187
|
+
'v1/trades/{tradeId}': 5,
|
|
188
|
+
'v1/trades/client-order-id/{clientOrderId}': 1,
|
|
189
|
+
'v1/accounts/asset': 1,
|
|
190
|
+
'v1/accounts/asset/{symbol}': 1,
|
|
191
|
+
'v1/users/logout': 1,
|
|
192
|
+
'v1/users/hmac/login': 1,
|
|
193
|
+
'v1/accounts/trading-accounts': 1,
|
|
194
|
+
'v1/accounts/trading-accounts/{tradingAccountId}': 1,
|
|
195
|
+
'v1/derivatives-positions': 1,
|
|
196
|
+
'v1/history/derivatives-settlement': 1,
|
|
197
|
+
'v1/history/transfer': 1,
|
|
198
|
+
'v1/history/borrow-interest': 1,
|
|
199
|
+
'v2/mmp-configuration': 1,
|
|
200
|
+
'v2/otc-trades': 1,
|
|
201
|
+
'v2/otc-trades/{otcTradeId}': 1,
|
|
202
|
+
'v2/otc-trades/unconfirmed-trade': 1,
|
|
203
|
+
},
|
|
204
|
+
'post': {
|
|
205
|
+
'v2/orders': 5,
|
|
206
|
+
'v2/command': 5,
|
|
207
|
+
'v2/amm-instructions': 1,
|
|
208
|
+
'v1/wallets/withdrawal': 1,
|
|
209
|
+
'v2/users/login': 1,
|
|
210
|
+
'v1/simulate-portfolio-margin': 1,
|
|
211
|
+
'v1/wallets/self-hosted/initiate': 1,
|
|
212
|
+
'v2/mmp-configuration': 1,
|
|
213
|
+
'v2/otc-trades': 1,
|
|
214
|
+
'v2/otc-command': 1,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
'fees': {
|
|
219
|
+
'trading': {
|
|
220
|
+
'tierBased': false,
|
|
221
|
+
'percentage': true,
|
|
222
|
+
// todo check fees
|
|
223
|
+
'taker': this.parseNumber('0.001'),
|
|
224
|
+
'maker': this.parseNumber('0.001'),
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
'precisionMode': TICK_SIZE,
|
|
228
|
+
// exchange-specific options
|
|
229
|
+
'options': {
|
|
230
|
+
'timeDifference': 0,
|
|
231
|
+
'adjustForTimeDifference': false,
|
|
232
|
+
'networks': {
|
|
233
|
+
'BTC': 'BTC',
|
|
234
|
+
'EOS': 'EOS',
|
|
235
|
+
'ERC20': 'ETH',
|
|
236
|
+
},
|
|
237
|
+
'defaultNetwork': 'ERC20',
|
|
238
|
+
'defaultNetworks': {
|
|
239
|
+
'USDC': 'ERC20',
|
|
240
|
+
},
|
|
241
|
+
'tradingAccountId': undefined,
|
|
242
|
+
},
|
|
243
|
+
'features': {
|
|
244
|
+
'default': {
|
|
245
|
+
'sandbox': true,
|
|
246
|
+
'createOrder': {
|
|
247
|
+
'marginMode': false,
|
|
248
|
+
'triggerPrice': true,
|
|
249
|
+
'triggerPriceType': undefined,
|
|
250
|
+
'triggerDirection': false,
|
|
251
|
+
'stopLossPrice': false,
|
|
252
|
+
'takeProfitPrice': false,
|
|
253
|
+
'attachedStopLossTakeProfit': undefined,
|
|
254
|
+
'timeInForce': {
|
|
255
|
+
'IOC': true,
|
|
256
|
+
'FOK': true,
|
|
257
|
+
'PO': true,
|
|
258
|
+
'GTD': false,
|
|
259
|
+
},
|
|
260
|
+
'hedged': false,
|
|
261
|
+
'trailing': false,
|
|
262
|
+
'leverage': false,
|
|
263
|
+
'marketBuyByCost': false,
|
|
264
|
+
'marketBuyRequiresPrice': false,
|
|
265
|
+
'selfTradePrevention': false,
|
|
266
|
+
'iceberg': false,
|
|
267
|
+
},
|
|
268
|
+
'createOrders': undefined,
|
|
269
|
+
'fetchMyTrades': {
|
|
270
|
+
'marginMode': false,
|
|
271
|
+
'limit': 100,
|
|
272
|
+
'daysBack': 90,
|
|
273
|
+
'symbolRequired': false,
|
|
274
|
+
'untilDays': 90,
|
|
275
|
+
},
|
|
276
|
+
'fetchOrder': {
|
|
277
|
+
'marginMode': false,
|
|
278
|
+
'trigger': false,
|
|
279
|
+
'trailing': false,
|
|
280
|
+
'symbolRequired': false,
|
|
281
|
+
},
|
|
282
|
+
'fetchOrders': {
|
|
283
|
+
'marginMode': false,
|
|
284
|
+
'limit': 100,
|
|
285
|
+
'daysBack': 90,
|
|
286
|
+
'untilDays': 90,
|
|
287
|
+
'trigger': false,
|
|
288
|
+
'trailing': false,
|
|
289
|
+
'symbolRequired': false,
|
|
290
|
+
},
|
|
291
|
+
'fetchOpenOrders': {
|
|
292
|
+
'marginMode': false,
|
|
293
|
+
'limit': 100,
|
|
294
|
+
'daysBack': 90,
|
|
295
|
+
'untilDays': 90,
|
|
296
|
+
'trigger': false,
|
|
297
|
+
'trailing': false,
|
|
298
|
+
'symbolRequired': false,
|
|
299
|
+
},
|
|
300
|
+
'fetchCanceledAndClosedOrders': {
|
|
301
|
+
'marginMode': false,
|
|
302
|
+
'limit': 100,
|
|
303
|
+
'daysBack': 90,
|
|
304
|
+
'untilDays': 90,
|
|
305
|
+
'trigger': false,
|
|
306
|
+
'trailing': false,
|
|
307
|
+
'symbolRequired': false,
|
|
308
|
+
},
|
|
309
|
+
'fetchClosedOrders': {
|
|
310
|
+
'marginMode': false,
|
|
311
|
+
'limit': 100,
|
|
312
|
+
'daysBack': 1,
|
|
313
|
+
'daysBackCanceled': 1,
|
|
314
|
+
'untilDays': 1,
|
|
315
|
+
'trigger': false,
|
|
316
|
+
'trailing': false,
|
|
317
|
+
'symbolRequired': false,
|
|
318
|
+
},
|
|
319
|
+
'fetchCanceledOrders': {
|
|
320
|
+
'marginMode': false,
|
|
321
|
+
'limit': 100,
|
|
322
|
+
'daysBack': 1,
|
|
323
|
+
'untilDays': 1,
|
|
324
|
+
'trigger': false,
|
|
325
|
+
'trailing': false,
|
|
326
|
+
'symbolRequired': false,
|
|
327
|
+
},
|
|
328
|
+
'fetchOHLCV': {
|
|
329
|
+
'limit': 1000,
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
'spot': {
|
|
333
|
+
'extends': 'default',
|
|
334
|
+
},
|
|
335
|
+
'swap': {
|
|
336
|
+
'linear': {
|
|
337
|
+
'extends': 'default',
|
|
338
|
+
},
|
|
339
|
+
'inverse': undefined,
|
|
340
|
+
},
|
|
341
|
+
'future': {
|
|
342
|
+
'linear': {
|
|
343
|
+
'extends': 'default',
|
|
344
|
+
},
|
|
345
|
+
'inverse': undefined,
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
'exceptions': {
|
|
349
|
+
'exact': {
|
|
350
|
+
'1': BadRequest,
|
|
351
|
+
'5': InvalidOrder,
|
|
352
|
+
'6': DuplicateOrderId,
|
|
353
|
+
'13': BadRequest,
|
|
354
|
+
'15': BadRequest,
|
|
355
|
+
'18': BadRequest,
|
|
356
|
+
'1002': BadRequest,
|
|
357
|
+
'2001': BadRequest,
|
|
358
|
+
'2002': BadRequest,
|
|
359
|
+
'2003': BadRequest,
|
|
360
|
+
'2004': BadRequest,
|
|
361
|
+
'2005': ExchangeError,
|
|
362
|
+
'2006': BadRequest,
|
|
363
|
+
'2007': BadRequest,
|
|
364
|
+
'2008': BadRequest,
|
|
365
|
+
'2009': BadSymbol,
|
|
366
|
+
'2010': AuthenticationError,
|
|
367
|
+
'2011': AuthenticationError,
|
|
368
|
+
'2012': BadRequest,
|
|
369
|
+
'2013': InvalidOrder,
|
|
370
|
+
'2015': OperationRejected,
|
|
371
|
+
'2016': BadRequest,
|
|
372
|
+
'2017': BadRequest,
|
|
373
|
+
'2018': BadRequest,
|
|
374
|
+
'2020': PermissionDenied,
|
|
375
|
+
'2021': OperationRejected,
|
|
376
|
+
'2029': InvalidNonce,
|
|
377
|
+
'2035': InvalidNonce,
|
|
378
|
+
'3001': InsufficientFunds,
|
|
379
|
+
'3002': OrderNotFound,
|
|
380
|
+
'3003': PermissionDenied,
|
|
381
|
+
'3004': InsufficientFunds,
|
|
382
|
+
'3005': InsufficientFunds,
|
|
383
|
+
'3006': InsufficientFunds,
|
|
384
|
+
'3007': DuplicateOrderId,
|
|
385
|
+
'3031': BadRequest,
|
|
386
|
+
'3032': BadRequest,
|
|
387
|
+
'3033': PermissionDenied,
|
|
388
|
+
'3034': RateLimitExceeded,
|
|
389
|
+
'3035': RateLimitExceeded,
|
|
390
|
+
'3047': OperationRejected,
|
|
391
|
+
'3048': OperationRejected,
|
|
392
|
+
'3049': OperationRejected,
|
|
393
|
+
'3051': InsufficientFunds,
|
|
394
|
+
'3052': InsufficientFunds,
|
|
395
|
+
'3063': BadRequest,
|
|
396
|
+
'3064': OrderNotFillable,
|
|
397
|
+
'3065': MarketClosed,
|
|
398
|
+
'3066': ExchangeError,
|
|
399
|
+
'3067': MarketClosed,
|
|
400
|
+
'6007': InvalidOrder,
|
|
401
|
+
'6011': InvalidOrder,
|
|
402
|
+
'6012': InvalidOrder,
|
|
403
|
+
'6013': InvalidOrder,
|
|
404
|
+
'8301': ExchangeError,
|
|
405
|
+
'8305': ExchangeError,
|
|
406
|
+
'8306': ExchangeError,
|
|
407
|
+
'8307': ExchangeError,
|
|
408
|
+
'8310': InvalidAddress,
|
|
409
|
+
'8311': BadRequest,
|
|
410
|
+
'8313': BadRequest,
|
|
411
|
+
'8315': OperationRejected,
|
|
412
|
+
'8316': OperationRejected,
|
|
413
|
+
'8317': OperationRejected,
|
|
414
|
+
'8318': NotSupported,
|
|
415
|
+
'8319': NotSupported,
|
|
416
|
+
'8320': InvalidAddress,
|
|
417
|
+
'8322': BadRequest,
|
|
418
|
+
'8327': AuthenticationError,
|
|
419
|
+
'8329': ExchangeError,
|
|
420
|
+
'8331': InvalidAddress,
|
|
421
|
+
'8332': BadRequest,
|
|
422
|
+
'8333': BadRequest,
|
|
423
|
+
'8334': BadRequest,
|
|
424
|
+
'8335': InvalidAddress,
|
|
425
|
+
'8336': InvalidAddress,
|
|
426
|
+
'8399': ExchangeError, // Unknown error
|
|
427
|
+
},
|
|
428
|
+
'broad': {
|
|
429
|
+
'HttpInvalidParameterException': BadRequest,
|
|
430
|
+
'UNAUTHORIZED_COMMAND': AuthenticationError,
|
|
431
|
+
'QUERY_FILTER_ERROR': BadRequest,
|
|
432
|
+
'INVALID_SYMBOL': BadSymbol, // {"message":"Invalid symbol provided","errorCode":28004,"errorCodeName":"INVALID_SYMBOL"}
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* @method
|
|
439
|
+
* @name bullish#fetchTime
|
|
440
|
+
* @description fetches the current integer timestamp in milliseconds from the exchange server
|
|
441
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--time
|
|
442
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
443
|
+
* @returns {int} the current integer timestamp in milliseconds from the exchange server
|
|
444
|
+
*/
|
|
445
|
+
async fetchTime(params = {}) {
|
|
446
|
+
const response = await this.publicGetV1Time(params);
|
|
447
|
+
//
|
|
448
|
+
// {
|
|
449
|
+
// "datetime": "2025-05-05T20:05:50.999Z",
|
|
450
|
+
// "timestamp": 1746475550999
|
|
451
|
+
// }
|
|
452
|
+
//
|
|
453
|
+
return this.safeInteger(response, 'timestamp');
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* @method
|
|
457
|
+
* @name bullish#fetchCurrencies
|
|
458
|
+
* @description fetches all available currencies on an exchange
|
|
459
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/assets
|
|
460
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
461
|
+
* @returns {object} an associative dictionary of currencies
|
|
462
|
+
*/
|
|
463
|
+
async fetchCurrencies(params = {}) {
|
|
464
|
+
const response = await this.publicGetV1Assets(params);
|
|
465
|
+
//
|
|
466
|
+
// [
|
|
467
|
+
// {
|
|
468
|
+
// "assetId": "72",
|
|
469
|
+
// "symbol": "BTT1M",
|
|
470
|
+
// "name": "BitTorrent (millions)",
|
|
471
|
+
// "precision": "5",
|
|
472
|
+
// "minBalanceInterest": "0.00000",
|
|
473
|
+
// "apr": "10.00",
|
|
474
|
+
// "minFee": "0.00000",
|
|
475
|
+
// "maxBorrow": "0.00000",
|
|
476
|
+
// "totalOfferedLoanQuantity": "0.00000",
|
|
477
|
+
// "loanBorrowedQuantity": "0.00000",
|
|
478
|
+
// "collateralBands":
|
|
479
|
+
// [
|
|
480
|
+
// {
|
|
481
|
+
// "collateralPercentage": "90.00",
|
|
482
|
+
// "bandLimitUSD": "100000.0000"
|
|
483
|
+
// },
|
|
484
|
+
// {
|
|
485
|
+
// "collateralPercentage": "68.00",
|
|
486
|
+
// "bandLimitUSD": "300000.0000"
|
|
487
|
+
// },
|
|
488
|
+
// {
|
|
489
|
+
// "collateralPercentage": "25.00",
|
|
490
|
+
// "bandLimitUSD": "600000.0000"
|
|
491
|
+
// }
|
|
492
|
+
// ],
|
|
493
|
+
// "underlyingAsset":
|
|
494
|
+
// {
|
|
495
|
+
// "symbol": "BTT1M",
|
|
496
|
+
// "assetId": "72",
|
|
497
|
+
// "bpmMinReturnStart": "0.9200",
|
|
498
|
+
// "bpmMinReturnEnd": "0.9300",
|
|
499
|
+
// "bpmMaxReturnStart": "1.0800",
|
|
500
|
+
// "bpmMaxReturnEnd": "1.0800",
|
|
501
|
+
// "marketRiskFloorPctStart": "2.60",
|
|
502
|
+
// "marketRiskFloorPctEnd": "2.50",
|
|
503
|
+
// "bpmTransitionDateTimeStart": "2025-05-05T08:00:00.000Z",
|
|
504
|
+
// "bpmTransitionDateTimeEnd": "2025-05-08T08:00:00.000Z"
|
|
505
|
+
// }
|
|
506
|
+
// }, ...
|
|
507
|
+
// ]
|
|
508
|
+
//
|
|
509
|
+
const result = {};
|
|
510
|
+
for (let i = 0; i < response.length; i++) {
|
|
511
|
+
const currency = response[i];
|
|
512
|
+
const id = this.safeString(currency, 'symbol');
|
|
513
|
+
const code = this.safeCurrencyCode(id);
|
|
514
|
+
const name = this.safeString(currency, 'name');
|
|
515
|
+
const precision = this.safeString(currency, 'precision');
|
|
516
|
+
result[code] = {
|
|
517
|
+
'id': id,
|
|
518
|
+
'code': code,
|
|
519
|
+
'name': name,
|
|
520
|
+
'active': undefined,
|
|
521
|
+
'deposit': undefined,
|
|
522
|
+
'withdraw': undefined,
|
|
523
|
+
'fee': this.safeNumber(currency, 'minFee'),
|
|
524
|
+
'precision': this.parseNumber(this.parsePrecision(precision)),
|
|
525
|
+
'limits': {
|
|
526
|
+
'amount': { 'min': undefined, 'max': undefined },
|
|
527
|
+
'withdraw': { 'min': undefined, 'max': undefined },
|
|
528
|
+
},
|
|
529
|
+
'networks': {},
|
|
530
|
+
'type': 'crypto',
|
|
531
|
+
'info': currency,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
return result;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* @method
|
|
538
|
+
* @name bullish#fetchMarkets
|
|
539
|
+
* @description retrieves data on all markets for ace
|
|
540
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/markets
|
|
541
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
542
|
+
* @returns {object[]} an array of objects representing market data
|
|
543
|
+
*/
|
|
544
|
+
async fetchMarkets(params = {}) {
|
|
545
|
+
if (this.options['adjustForTimeDifference']) {
|
|
546
|
+
await this.loadTimeDifference();
|
|
547
|
+
}
|
|
548
|
+
const response = await this.publicGetV1Markets(params);
|
|
549
|
+
return this.parseMarkets(response);
|
|
550
|
+
}
|
|
551
|
+
parseMarket(market) {
|
|
552
|
+
//
|
|
553
|
+
// {
|
|
554
|
+
// "marketId": "20069",
|
|
555
|
+
// "symbol": "BTC-USDC-20250516",
|
|
556
|
+
// "quoteAssetId": "5",
|
|
557
|
+
// "baseAssetId": "1",
|
|
558
|
+
// "quoteSymbol": "USDC",
|
|
559
|
+
// "baseSymbol": "BTC",
|
|
560
|
+
// "quotePrecision": "4",
|
|
561
|
+
// "basePrecision": "8",
|
|
562
|
+
// "pricePrecision": "4",
|
|
563
|
+
// "quantityPrecision": "8",
|
|
564
|
+
// "costPrecision": "4",
|
|
565
|
+
// "minQuantityLimit": "0.00050000",
|
|
566
|
+
// "maxQuantityLimit": "200.00000000",
|
|
567
|
+
// "maxPriceLimit": null,
|
|
568
|
+
// "minPriceLimit": null,
|
|
569
|
+
// "maxCostLimit": null,
|
|
570
|
+
// "minCostLimit": null,
|
|
571
|
+
// "timeZone": "Etc/UTC",
|
|
572
|
+
// "tickSize": "0.1000",
|
|
573
|
+
// "liquidityTickSize": "100.0000",
|
|
574
|
+
// "liquidityPrecision": "4",
|
|
575
|
+
// "makerFee": "0",
|
|
576
|
+
// "takerFee": "2",
|
|
577
|
+
// "roundingCorrectionFactor": "0.00000100",
|
|
578
|
+
// "makerMinLiquidityAddition": "1000000",
|
|
579
|
+
// "orderTypes":
|
|
580
|
+
// [
|
|
581
|
+
// "LMT",
|
|
582
|
+
// "MKT",
|
|
583
|
+
// "STOP_LIMIT",
|
|
584
|
+
// "POST_ONLY"
|
|
585
|
+
// ],
|
|
586
|
+
// "spotTradingEnabled": true,
|
|
587
|
+
// "marginTradingEnabled": true,
|
|
588
|
+
// "marketEnabled": true,
|
|
589
|
+
// "createOrderEnabled": true,
|
|
590
|
+
// "cancelOrderEnabled": true,
|
|
591
|
+
// "liquidityInvestEnabled": true,
|
|
592
|
+
// "liquidityWithdrawEnabled": true,
|
|
593
|
+
// "feeTiers":
|
|
594
|
+
// [
|
|
595
|
+
// {
|
|
596
|
+
// "feeTierId": "1",
|
|
597
|
+
// "staticSpreadFee": "0.00000000",
|
|
598
|
+
// "isDislocationEnabled": false
|
|
599
|
+
// },
|
|
600
|
+
// {
|
|
601
|
+
// "feeTierId": "10",
|
|
602
|
+
// "staticSpreadFee": "0.00100000",
|
|
603
|
+
// "isDislocationEnabled": true
|
|
604
|
+
// },
|
|
605
|
+
// {
|
|
606
|
+
// "feeTierId": "11",
|
|
607
|
+
// "staticSpreadFee": "0.00150000",
|
|
608
|
+
// "isDislocationEnabled": false
|
|
609
|
+
// },
|
|
610
|
+
// {
|
|
611
|
+
// "feeTierId": "12",
|
|
612
|
+
// "staticSpreadFee": "0.00150000",
|
|
613
|
+
// "isDislocationEnabled": true
|
|
614
|
+
// },
|
|
615
|
+
// {
|
|
616
|
+
// "feeTierId": "13",
|
|
617
|
+
// "staticSpreadFee": "0.00300000",
|
|
618
|
+
// "isDislocationEnabled": false
|
|
619
|
+
// },
|
|
620
|
+
// {
|
|
621
|
+
// "feeTierId": "14",
|
|
622
|
+
// "staticSpreadFee": "0.00300000",
|
|
623
|
+
// "isDislocationEnabled": true
|
|
624
|
+
// },
|
|
625
|
+
// {
|
|
626
|
+
// "feeTierId": "15",
|
|
627
|
+
// "staticSpreadFee": "0.00500000",
|
|
628
|
+
// "isDislocationEnabled": false
|
|
629
|
+
// },
|
|
630
|
+
// {
|
|
631
|
+
// "feeTierId": "16",
|
|
632
|
+
// "staticSpreadFee": "0.00500000",
|
|
633
|
+
// "isDislocationEnabled": true
|
|
634
|
+
// },
|
|
635
|
+
// {
|
|
636
|
+
// "feeTierId": "17",
|
|
637
|
+
// "staticSpreadFee": "0.01000000",
|
|
638
|
+
// "isDislocationEnabled": false
|
|
639
|
+
// },
|
|
640
|
+
// {
|
|
641
|
+
// "feeTierId": "18",
|
|
642
|
+
// "staticSpreadFee": "0.01000000",
|
|
643
|
+
// "isDislocationEnabled": true
|
|
644
|
+
// },
|
|
645
|
+
// {
|
|
646
|
+
// "feeTierId": "19",
|
|
647
|
+
// "staticSpreadFee": "0.01500000",
|
|
648
|
+
// "isDislocationEnabled": false
|
|
649
|
+
// },
|
|
650
|
+
// {
|
|
651
|
+
// "feeTierId": "2",
|
|
652
|
+
// "staticSpreadFee": "0.00000000",
|
|
653
|
+
// "isDislocationEnabled": true
|
|
654
|
+
// },
|
|
655
|
+
// {
|
|
656
|
+
// "feeTierId": "20",
|
|
657
|
+
// "staticSpreadFee": "0.01500000",
|
|
658
|
+
// "isDislocationEnabled": true
|
|
659
|
+
// },
|
|
660
|
+
// {
|
|
661
|
+
// "feeTierId": "21",
|
|
662
|
+
// "staticSpreadFee": "0.02000000",
|
|
663
|
+
// "isDislocationEnabled": false
|
|
664
|
+
// },
|
|
665
|
+
// {
|
|
666
|
+
// "feeTierId": "22",
|
|
667
|
+
// "staticSpreadFee": "0.02000000",
|
|
668
|
+
// "isDislocationEnabled": true
|
|
669
|
+
// },
|
|
670
|
+
// {
|
|
671
|
+
// "feeTierId": "3",
|
|
672
|
+
// "staticSpreadFee": "0.00010000",
|
|
673
|
+
// "isDislocationEnabled": false
|
|
674
|
+
// },
|
|
675
|
+
// {
|
|
676
|
+
// "feeTierId": "4",
|
|
677
|
+
// "staticSpreadFee": "0.00010000",
|
|
678
|
+
// "isDislocationEnabled": true
|
|
679
|
+
// },
|
|
680
|
+
// {
|
|
681
|
+
// "feeTierId": "5",
|
|
682
|
+
// "staticSpreadFee": "0.00020000",
|
|
683
|
+
// "isDislocationEnabled": false
|
|
684
|
+
// },
|
|
685
|
+
// {
|
|
686
|
+
// "feeTierId": "6",
|
|
687
|
+
// "staticSpreadFee": "0.00020000",
|
|
688
|
+
// "isDislocationEnabled": true
|
|
689
|
+
// },
|
|
690
|
+
// {
|
|
691
|
+
// "feeTierId": "7",
|
|
692
|
+
// "staticSpreadFee": "0.00060000",
|
|
693
|
+
// "isDislocationEnabled": false
|
|
694
|
+
// },
|
|
695
|
+
// {
|
|
696
|
+
// "feeTierId": "8",
|
|
697
|
+
// "staticSpreadFee": "0.00060000",
|
|
698
|
+
// "isDislocationEnabled": true
|
|
699
|
+
// },
|
|
700
|
+
// {
|
|
701
|
+
// "feeTierId": "9",
|
|
702
|
+
// "staticSpreadFee": "0.00100000",
|
|
703
|
+
// "isDislocationEnabled": false
|
|
704
|
+
// }
|
|
705
|
+
// ],
|
|
706
|
+
// "marketType": "DATED_FUTURE",
|
|
707
|
+
// "contractMultiplier": "1",
|
|
708
|
+
// "settlementAssetSymbol": "USDC",
|
|
709
|
+
// "underlyingQuoteSymbol": "USDC",
|
|
710
|
+
// "underlyingBaseSymbol": "BTC",
|
|
711
|
+
// "openInterestLimitUSD": "100000000.0000",
|
|
712
|
+
// "concentrationRiskPercentage": "100.00",
|
|
713
|
+
// "concentrationRiskThresholdUSD": "30000000.0000",
|
|
714
|
+
// "expiryDatetime": "2025-05-16T08:00:00.000Z",
|
|
715
|
+
// "priceBuffer": "0.1",
|
|
716
|
+
// "feeGroupId": "4"
|
|
717
|
+
// }
|
|
718
|
+
//
|
|
719
|
+
// option
|
|
720
|
+
// {
|
|
721
|
+
// "marketId": "20997",
|
|
722
|
+
// "symbol": "BTC-USDC-20260130-160000-P",
|
|
723
|
+
// "quoteAssetId": "5",
|
|
724
|
+
// "baseAssetId": "1",
|
|
725
|
+
// "quoteSymbol": "USDC",
|
|
726
|
+
// "baseSymbol": "BTC",
|
|
727
|
+
// "quotePrecision": "4",
|
|
728
|
+
// "basePrecision": "8",
|
|
729
|
+
// "pricePrecision": "4",
|
|
730
|
+
// "quantityPrecision": "8",
|
|
731
|
+
// "costPrecision": "4",
|
|
732
|
+
// "minQuantityLimit": "0.00050000",
|
|
733
|
+
// "maxQuantityLimit": "200.00000000",
|
|
734
|
+
// "maxPriceLimit": null,
|
|
735
|
+
// "minPriceLimit": null,
|
|
736
|
+
// "maxCostLimit": null,
|
|
737
|
+
// "minCostLimit": null,
|
|
738
|
+
// "timeZone": "Etc/UTC",
|
|
739
|
+
// "tickSize": "10.0000",
|
|
740
|
+
// "makerFee": "0",
|
|
741
|
+
// "takerFee": "2",
|
|
742
|
+
// "roundingCorrectionFactor": "0.00000100",
|
|
743
|
+
// "makerMinLiquidityAddition": "-1",
|
|
744
|
+
// "orderTypes": [ "LMT", "MKT", "STOP_LIMIT", "POST_ONLY" ],
|
|
745
|
+
// "spotTradingEnabled": true,
|
|
746
|
+
// "marginTradingEnabled": true,
|
|
747
|
+
// "marketEnabled": true,
|
|
748
|
+
// "createOrderEnabled": true,
|
|
749
|
+
// "cancelOrderEnabled": true,
|
|
750
|
+
// "amendOrderEnabled": true,
|
|
751
|
+
// "marketType": "OPTION",
|
|
752
|
+
// "contractMultiplier": "1",
|
|
753
|
+
// "settlementAssetSymbol": "USDC",
|
|
754
|
+
// "underlyingQuoteSymbol": "USDC",
|
|
755
|
+
// "underlyingBaseSymbol": "BTC",
|
|
756
|
+
// "openInterestLimitUSD": "100000000.0000",
|
|
757
|
+
// "concentrationRiskPercentage": "100.00",
|
|
758
|
+
// "concentrationRiskThresholdUSD": "30000000.0000",
|
|
759
|
+
// "expiryDatetime": "2026-01-30T08:00:00.000Z",
|
|
760
|
+
// "priceBuffer": "0",
|
|
761
|
+
// "feeGroupId": "10",
|
|
762
|
+
// "optionStrikePrice": "160000.0000",
|
|
763
|
+
// "optionType": "PUT",
|
|
764
|
+
// "premiumCapRatio": "0.1000"
|
|
765
|
+
// }
|
|
766
|
+
//
|
|
767
|
+
const id = this.safeString(market, 'symbol');
|
|
768
|
+
const baseId = this.safeString(market, 'baseSymbol');
|
|
769
|
+
const quoteId = this.safeString(market, 'quoteSymbol');
|
|
770
|
+
const base = this.safeCurrencyCode(baseId);
|
|
771
|
+
const quote = this.safeCurrencyCode(quoteId);
|
|
772
|
+
let symbol = base + '/' + quote;
|
|
773
|
+
const basePrecision = this.safeString(market, 'basePrecision');
|
|
774
|
+
const quotePrecision = this.safeString(market, 'quotePrecision');
|
|
775
|
+
const amountPrecision = this.safeString(market, 'quantityPrecision');
|
|
776
|
+
const pricePrecision = this.safeString(market, 'pricePrecision');
|
|
777
|
+
const costPrecision = this.safeString(market, 'costPrecision');
|
|
778
|
+
const minQuantityLimit = this.safeString(market, 'minQuantityLimit');
|
|
779
|
+
const maxQuantityLimit = this.safeString(market, 'maxQuantityLimit');
|
|
780
|
+
const minPriceLimit = this.safeString(market, 'minPriceLimit');
|
|
781
|
+
const maxPriceLimit = this.safeString(market, 'maxPriceLimit');
|
|
782
|
+
const minCostLimit = this.safeString(market, 'minCostLimit');
|
|
783
|
+
const maxCostLimit = this.safeString(market, 'maxCostLimit');
|
|
784
|
+
const settleId = this.safeString(market, 'settlementAssetSymbol');
|
|
785
|
+
const settle = this.safeCurrencyCode(settleId);
|
|
786
|
+
const type = this.parseMarketType(this.safeString(market, 'marketType'), 'spot');
|
|
787
|
+
let spot = false;
|
|
788
|
+
let swap = false;
|
|
789
|
+
let future = false;
|
|
790
|
+
let option = false;
|
|
791
|
+
let contract = true;
|
|
792
|
+
let linear = undefined;
|
|
793
|
+
let inverse = undefined;
|
|
794
|
+
let expiryDatetime = undefined;
|
|
795
|
+
let contractSize = undefined;
|
|
796
|
+
let optionType = undefined;
|
|
797
|
+
let strike = undefined;
|
|
798
|
+
let margin = false;
|
|
799
|
+
if (type === 'spot') {
|
|
800
|
+
spot = true;
|
|
801
|
+
contract = false;
|
|
802
|
+
margin = this.safeBool(market, 'marginTradingEnabled');
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
contractSize = this.safeNumber(market, 'contractMultiplier');
|
|
806
|
+
symbol += ':' + settle;
|
|
807
|
+
linear = settle === quote;
|
|
808
|
+
inverse = !linear;
|
|
809
|
+
if (type === 'swap') {
|
|
810
|
+
swap = true;
|
|
811
|
+
}
|
|
812
|
+
else {
|
|
813
|
+
expiryDatetime = this.safeString(market, 'expiryDatetime');
|
|
814
|
+
const idParts = id.split('-');
|
|
815
|
+
const datePart = this.safeString(idParts, 2);
|
|
816
|
+
symbol += '-' + datePart;
|
|
817
|
+
if (type === 'future') {
|
|
818
|
+
future = true;
|
|
819
|
+
}
|
|
820
|
+
else if (type === 'option') {
|
|
821
|
+
option = true;
|
|
822
|
+
optionType = this.safeStringLower(market, 'optionType');
|
|
823
|
+
strike = this.parseToNumeric(this.safeString(market, 'optionStrikePrice'));
|
|
824
|
+
symbol += '-' + this.numberToString(strike) + '-' + this.safeString(idParts, 4);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return this.safeMarketStructure({
|
|
829
|
+
'id': id,
|
|
830
|
+
'symbol': symbol,
|
|
831
|
+
'base': base,
|
|
832
|
+
'baseId': baseId,
|
|
833
|
+
'quote': quote,
|
|
834
|
+
'quoteId': quoteId,
|
|
835
|
+
'settle': settle,
|
|
836
|
+
'settleId': settleId,
|
|
837
|
+
'type': type,
|
|
838
|
+
'spot': spot,
|
|
839
|
+
'margin': margin,
|
|
840
|
+
'swap': swap,
|
|
841
|
+
'future': future,
|
|
842
|
+
'option': option,
|
|
843
|
+
'contract': contract,
|
|
844
|
+
'linear': linear,
|
|
845
|
+
'inverse': inverse,
|
|
846
|
+
'taker': this.fees['trading']['taker'],
|
|
847
|
+
'maker': this.fees['trading']['maker'],
|
|
848
|
+
'contractSize': contractSize,
|
|
849
|
+
'expiry': this.parse8601(expiryDatetime),
|
|
850
|
+
'expiryDatetime': expiryDatetime,
|
|
851
|
+
'strike': strike,
|
|
852
|
+
'optionType': optionType,
|
|
853
|
+
'limits': {
|
|
854
|
+
'amount': {
|
|
855
|
+
'min': this.parseNumber(minQuantityLimit),
|
|
856
|
+
'max': this.parseNumber(maxQuantityLimit),
|
|
857
|
+
},
|
|
858
|
+
'price': {
|
|
859
|
+
'min': this.parseNumber(minPriceLimit),
|
|
860
|
+
'max': this.parseNumber(maxPriceLimit),
|
|
861
|
+
},
|
|
862
|
+
'cost': {
|
|
863
|
+
'min': this.parseNumber(minCostLimit),
|
|
864
|
+
'max': this.parseNumber(maxCostLimit),
|
|
865
|
+
},
|
|
866
|
+
'leverage': {
|
|
867
|
+
'min': undefined,
|
|
868
|
+
'max': undefined,
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
'precision': {
|
|
872
|
+
'amount': this.parseNumber(this.parsePrecision(amountPrecision)),
|
|
873
|
+
'price': this.parseNumber(this.parsePrecision(pricePrecision)),
|
|
874
|
+
'cost': this.parseNumber(this.parsePrecision(costPrecision)),
|
|
875
|
+
'base': this.parseNumber(this.parsePrecision(basePrecision)),
|
|
876
|
+
'quote': this.parseNumber(this.parsePrecision(quotePrecision)),
|
|
877
|
+
},
|
|
878
|
+
'active': this.safeBool(market, 'marketEnabled'),
|
|
879
|
+
'created': undefined,
|
|
880
|
+
'info': market,
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
parseMarketType(type, defaultType = undefined) {
|
|
884
|
+
const types = {
|
|
885
|
+
'SPOT': 'spot',
|
|
886
|
+
'PERPETUAL': 'swap',
|
|
887
|
+
'DATED_FUTURE': 'future',
|
|
888
|
+
'OPTION': 'option',
|
|
889
|
+
};
|
|
890
|
+
return this.safeString(types, type, defaultType);
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* @method
|
|
894
|
+
* @name bullish#fetchOrderBook
|
|
895
|
+
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
896
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/markets/-symbol-/orderbook/hybrid
|
|
897
|
+
* @param {string} symbol unified symbol of the market to fetch the order book for
|
|
898
|
+
* @param {int} [limit] the maximum amount of order book entries to return (not used by bullish)
|
|
899
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
900
|
+
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
901
|
+
*/
|
|
902
|
+
async fetchOrderBook(symbol, limit = undefined, params = {}) {
|
|
903
|
+
await this.loadMarkets();
|
|
904
|
+
const market = this.market(symbol);
|
|
905
|
+
const request = {
|
|
906
|
+
'symbol': market['id'],
|
|
907
|
+
};
|
|
908
|
+
const response = await this.publicGetV1MarketsSymbolOrderbookHybrid(this.extend(request, params));
|
|
909
|
+
//
|
|
910
|
+
// {
|
|
911
|
+
// "bids": [
|
|
912
|
+
// {
|
|
913
|
+
// "price": "1.00000000",
|
|
914
|
+
// "priceLevelQuantity": "1.00000000"
|
|
915
|
+
// }
|
|
916
|
+
// ],
|
|
917
|
+
// "asks": [
|
|
918
|
+
// {
|
|
919
|
+
// "price": "1.00000000",
|
|
920
|
+
// "priceLevelQuantity": "1.00000000"
|
|
921
|
+
// }
|
|
922
|
+
// ],
|
|
923
|
+
// "datetime": "2021-05-20T01:01:01.000Z",
|
|
924
|
+
// "timestamp": "1621490985000",
|
|
925
|
+
// "sequenceNumber": 999
|
|
926
|
+
// }
|
|
927
|
+
//
|
|
928
|
+
const timestamp = this.safeInteger(response, 'timestamp');
|
|
929
|
+
return this.parseOrderBook(response, symbol, timestamp, 'bids', 'asks', 'price', 'priceLevelQuantity');
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* @method
|
|
933
|
+
* @name bullish#fetchTrades
|
|
934
|
+
* @description get the list of most recent trades for a particular symbol
|
|
935
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/markets/-symbol-/trades
|
|
936
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/markets/-symbol-/trades
|
|
937
|
+
* @param {string} symbol unified symbol of the market to fetch trades for
|
|
938
|
+
* @param {int} [since] timestamp in ms of the earliest trade to fetch
|
|
939
|
+
* @param {int} [limit] the maximum amount of trades to fetch (max 100)
|
|
940
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
941
|
+
* @param {int} [params.until] timestamp in ms of the latest trade to fetch
|
|
942
|
+
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
943
|
+
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
|
|
944
|
+
*/
|
|
945
|
+
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
|
|
946
|
+
await this.loadMarkets();
|
|
947
|
+
const maxLimit = 100;
|
|
948
|
+
let paginate = false;
|
|
949
|
+
[paginate, params] = this.handleOptionAndParams(params, 'fetchFundingRateHistory', 'paginate');
|
|
950
|
+
if (paginate) {
|
|
951
|
+
params = this.handlePaginationParams('fetchTrades', since, params);
|
|
952
|
+
return await this.fetchPaginatedCallDynamic('fetchTrades', symbol, since, limit, params, maxLimit);
|
|
953
|
+
}
|
|
954
|
+
const market = this.market(symbol);
|
|
955
|
+
const request = {
|
|
956
|
+
'symbol': market['id'],
|
|
957
|
+
};
|
|
958
|
+
params = this.handleSinceAndUntil(since, params);
|
|
959
|
+
if (limit !== undefined) {
|
|
960
|
+
request['_pageSize'] = this.getClosestLimit(limit);
|
|
961
|
+
}
|
|
962
|
+
const response = await this.publicGetV1HistoryMarketsSymbolTrades(this.extend(request, params));
|
|
963
|
+
//
|
|
964
|
+
// [
|
|
965
|
+
// {
|
|
966
|
+
// "tradeId": "100178000000367159",
|
|
967
|
+
// "symbol": "BTCUSDC",
|
|
968
|
+
// "price": "103891.8977",
|
|
969
|
+
// "quantity": "0.00029411",
|
|
970
|
+
// "quoteAmount": "30.5556",
|
|
971
|
+
// "side": "BUY",
|
|
972
|
+
// "isTaker": true,
|
|
973
|
+
// "createdAtTimestamp": "1747768055826",
|
|
974
|
+
// "createdAtDatetime": "2025-05-20T19:07:35.826Z"
|
|
975
|
+
// }, ...
|
|
976
|
+
// ]
|
|
977
|
+
//
|
|
978
|
+
return this.parseTrades(response, market, since, limit);
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* @method
|
|
982
|
+
* @name bullish#fetchMyTrades
|
|
983
|
+
* @description fetch all trades made by the user
|
|
984
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/trades
|
|
985
|
+
* @param {string} [symbol] unified market symbol
|
|
986
|
+
* @param {int} [since] the earliest time in ms to fetch trades for
|
|
987
|
+
* @param {int} [limit] the maximum number of trades structures to retrieve
|
|
988
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
989
|
+
* @param {int} [params.until] the latest time in ms to fetch trades for
|
|
990
|
+
* @param {string} [params.orderId] the order id to fetch trades for
|
|
991
|
+
* @param {string} [params.clientOrderId] the client order id to fetch trades for
|
|
992
|
+
* @param {string} [params.tradingAccountId] the trading account id to fetch trades for
|
|
993
|
+
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
|
|
994
|
+
*/
|
|
995
|
+
async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
996
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
997
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
998
|
+
const request = {
|
|
999
|
+
'tradingAccountId': tradingAccountId,
|
|
1000
|
+
};
|
|
1001
|
+
let market = undefined;
|
|
1002
|
+
if (symbol !== undefined) {
|
|
1003
|
+
market = this.market(symbol);
|
|
1004
|
+
request['symbol'] = market['id'];
|
|
1005
|
+
}
|
|
1006
|
+
const clientOrderId = this.safeString(params, 'clientOrderId');
|
|
1007
|
+
let response = undefined;
|
|
1008
|
+
if (clientOrderId !== undefined) {
|
|
1009
|
+
response = await this.privateGetV1TradesClientOrderIdClientOrderId(this.extend(request, params));
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
let paginate = false;
|
|
1013
|
+
[paginate, params] = this.handleOptionAndParams(params, 'fetchMyTrades', 'paginate');
|
|
1014
|
+
if (paginate) {
|
|
1015
|
+
params = this.handlePaginationParams('fetchMyTrades', since, params);
|
|
1016
|
+
return await this.fetchPaginatedCallDynamic('fetchMyTrades', symbol, since, limit, params, 100);
|
|
1017
|
+
}
|
|
1018
|
+
params = this.handleSinceAndUntil(since, params);
|
|
1019
|
+
if (limit !== undefined) {
|
|
1020
|
+
request['_pageSize'] = this.getClosestLimit(limit);
|
|
1021
|
+
}
|
|
1022
|
+
//
|
|
1023
|
+
// [
|
|
1024
|
+
// {
|
|
1025
|
+
// "baseFee": "0.00000000",
|
|
1026
|
+
// "createdAtDatetime": "2025-05-18T15:57:28.132Z",
|
|
1027
|
+
// "createdAtTimestamp": "1747583848132",
|
|
1028
|
+
// "handle": null,
|
|
1029
|
+
// "isTaker": true,
|
|
1030
|
+
// "orderId": "844242293909618689",
|
|
1031
|
+
// "price": "103942.7048",
|
|
1032
|
+
// "publishedAtTimestamp": "1747769786131",
|
|
1033
|
+
// "quantity": "1.00000000",
|
|
1034
|
+
// "quoteAmount": "103942.7048",
|
|
1035
|
+
// "quoteFee": "0.0000",
|
|
1036
|
+
// "side": "BUY",
|
|
1037
|
+
// "symbol": "BTCUSDC",
|
|
1038
|
+
// "tradeId": "100178000000288892"
|
|
1039
|
+
// }, ...
|
|
1040
|
+
// ]
|
|
1041
|
+
//
|
|
1042
|
+
response = await this.privateGetV1HistoryTrades(this.extend(request, params));
|
|
1043
|
+
}
|
|
1044
|
+
return this.parseTrades(response, market, since, limit);
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* @method
|
|
1048
|
+
* @name bullish#fetchOrderTrades
|
|
1049
|
+
* @description fetch all the trades made from a single order
|
|
1050
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/trades
|
|
1051
|
+
* @param {string} id order id
|
|
1052
|
+
* @param {string} symbol unified market symbol
|
|
1053
|
+
* @param {int} [since] the earliest time in ms to fetch trades for
|
|
1054
|
+
* @param {int} [limit] the maximum number of trades to retrieve
|
|
1055
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1056
|
+
* @param {string} [params.clientOrderId] the client order id to fetch trades for
|
|
1057
|
+
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
|
|
1058
|
+
*/
|
|
1059
|
+
async fetchOrderTrades(id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1060
|
+
await this.loadMarkets();
|
|
1061
|
+
const clientOrderId = this.safeString(params, 'clientOrderId');
|
|
1062
|
+
if (clientOrderId === undefined) {
|
|
1063
|
+
params = this.extend({ 'orderId': id }, params);
|
|
1064
|
+
}
|
|
1065
|
+
return await this.fetchMyTrades(symbol, since, limit, params);
|
|
1066
|
+
}
|
|
1067
|
+
parseTrade(trade, market = undefined) {
|
|
1068
|
+
//
|
|
1069
|
+
// fetchTrades
|
|
1070
|
+
// [
|
|
1071
|
+
// {
|
|
1072
|
+
// "tradeId": "100178000000367159",
|
|
1073
|
+
// "symbol": "BTCUSDC",
|
|
1074
|
+
// "price": "103891.8977",
|
|
1075
|
+
// "quantity": "0.00029411",
|
|
1076
|
+
// "quoteAmount": "30.5556",
|
|
1077
|
+
// "side": "BUY",
|
|
1078
|
+
// "isTaker": true,
|
|
1079
|
+
// "createdAtTimestamp": "1747768055826",
|
|
1080
|
+
// "createdAtDatetime": "2025-05-20T19:07:35.826Z"
|
|
1081
|
+
// }, ...
|
|
1082
|
+
// ]
|
|
1083
|
+
//
|
|
1084
|
+
// [
|
|
1085
|
+
// {
|
|
1086
|
+
// "tradeId": "100020000000000060",
|
|
1087
|
+
// "symbol": "BTCUSDC",
|
|
1088
|
+
// "price": "1.00000000",
|
|
1089
|
+
// "quantity": "1.00000000",
|
|
1090
|
+
// "side": "BUY",
|
|
1091
|
+
// "isTaker": true,
|
|
1092
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
1093
|
+
// "createdAtTimestamp": "1621490985000"
|
|
1094
|
+
// }
|
|
1095
|
+
// ]
|
|
1096
|
+
//
|
|
1097
|
+
// fetchMyTrades
|
|
1098
|
+
// [
|
|
1099
|
+
// {
|
|
1100
|
+
// "baseFee": "0.00000000",
|
|
1101
|
+
// "createdAtDatetime": "2025-05-18T15:57:28.132Z",
|
|
1102
|
+
// "createdAtTimestamp": "1747583848132",
|
|
1103
|
+
// "handle": null,
|
|
1104
|
+
// "isTaker": true,
|
|
1105
|
+
// "orderId": "844242293909618689",
|
|
1106
|
+
// "price": "103942.7048",
|
|
1107
|
+
// "publishedAtTimestamp": "1747769786131",
|
|
1108
|
+
// "quantity": "1.00000000",
|
|
1109
|
+
// "quoteAmount": "103942.7048",
|
|
1110
|
+
// "quoteFee": "0.0000",
|
|
1111
|
+
// "side": "BUY",
|
|
1112
|
+
// "symbol": "BTCUSDC",
|
|
1113
|
+
// "tradeId": "100178000000288892"
|
|
1114
|
+
// }, ...
|
|
1115
|
+
// ]
|
|
1116
|
+
//
|
|
1117
|
+
const marketId = this.safeString(trade, 'symbol');
|
|
1118
|
+
market = this.safeMarket(marketId, market);
|
|
1119
|
+
const symbol = market['symbol'];
|
|
1120
|
+
const timestamp = this.safeInteger(trade, 'createdAtTimestamp');
|
|
1121
|
+
const price = this.safeString(trade, 'price');
|
|
1122
|
+
const amount = this.safeString(trade, 'quantity');
|
|
1123
|
+
const side = this.safeStringLower(trade, 'side');
|
|
1124
|
+
const isTaker = this.safeBool(trade, 'isTaker');
|
|
1125
|
+
const currency = market['quote'];
|
|
1126
|
+
const code = this.safeCurrencyCode(currency);
|
|
1127
|
+
const feeCost = this.safeNumber(trade, 'quoteFee');
|
|
1128
|
+
let fee = undefined;
|
|
1129
|
+
if (feeCost !== undefined) {
|
|
1130
|
+
fee = { 'currency': code, 'cost': feeCost };
|
|
1131
|
+
}
|
|
1132
|
+
let takerOrMaker = undefined;
|
|
1133
|
+
if (isTaker) {
|
|
1134
|
+
takerOrMaker = 'taker';
|
|
1135
|
+
}
|
|
1136
|
+
else {
|
|
1137
|
+
takerOrMaker = 'maker';
|
|
1138
|
+
}
|
|
1139
|
+
const orderId = this.safeString(trade, 'orderId');
|
|
1140
|
+
return this.safeTrade({
|
|
1141
|
+
'info': trade,
|
|
1142
|
+
'timestamp': timestamp,
|
|
1143
|
+
'datetime': this.iso8601(timestamp),
|
|
1144
|
+
'symbol': symbol,
|
|
1145
|
+
'id': this.safeString(trade, 'tradeId'),
|
|
1146
|
+
'order': orderId,
|
|
1147
|
+
'type': undefined,
|
|
1148
|
+
'takerOrMaker': takerOrMaker,
|
|
1149
|
+
'side': side,
|
|
1150
|
+
'price': price,
|
|
1151
|
+
'amount': amount,
|
|
1152
|
+
'cost': undefined,
|
|
1153
|
+
'fee': fee,
|
|
1154
|
+
}, market);
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* @method
|
|
1158
|
+
* @name bullish#fetchTicker
|
|
1159
|
+
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
1160
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/markets/-symbol-/tick
|
|
1161
|
+
* @param {string} symbol unified symbol of the market to fetch the ticker for
|
|
1162
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1163
|
+
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
1164
|
+
*/
|
|
1165
|
+
async fetchTicker(symbol, params = {}) {
|
|
1166
|
+
await this.loadMarkets();
|
|
1167
|
+
const market = this.market(symbol);
|
|
1168
|
+
const request = {
|
|
1169
|
+
'symbol': market['id'],
|
|
1170
|
+
};
|
|
1171
|
+
const response = await this.publicGetV1MarketsSymbolTick(this.extend(request, params));
|
|
1172
|
+
//
|
|
1173
|
+
// {
|
|
1174
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
1175
|
+
// "createdAtTimestamp": "1621490985000",
|
|
1176
|
+
// "high": "1.00000000",
|
|
1177
|
+
// "low": "1.00000000",
|
|
1178
|
+
// "bestBid": "1.00000000",
|
|
1179
|
+
// "bidVolume": "1.00000000",
|
|
1180
|
+
// "bestAsk": "1.00000000",
|
|
1181
|
+
// "askVolume": "1.00000000",
|
|
1182
|
+
// "vwap": "1.00000000",
|
|
1183
|
+
// "open": "1.00000000",
|
|
1184
|
+
// "close": "1.00000000",
|
|
1185
|
+
// "last": "1.00000000",
|
|
1186
|
+
// "change": "1.00000000",
|
|
1187
|
+
// "percentage": "1.00000000",
|
|
1188
|
+
// "average": "1.00000000",
|
|
1189
|
+
// "baseVolume": "1.00000000",
|
|
1190
|
+
// "quoteVolume": "1.00000000",
|
|
1191
|
+
// "bancorPrice": "1.00000000",
|
|
1192
|
+
// "markPrice": "19999.00",
|
|
1193
|
+
// "fundingRate": "0.01",
|
|
1194
|
+
// "openInterest": "100000.32452",
|
|
1195
|
+
// "lastTradeDatetime": "2021-05-20T01:01:01.000Z",
|
|
1196
|
+
// "lastTradeTimestamp": "1621490985000",
|
|
1197
|
+
// "lastTradeQuantity": "1.00000000",
|
|
1198
|
+
// "ammData": [
|
|
1199
|
+
// {
|
|
1200
|
+
// "feeTierId": "1",
|
|
1201
|
+
// "bidSpreadFee": "0.00040000",
|
|
1202
|
+
// "askSpreadFee": "0.00040000",
|
|
1203
|
+
// "baseReservesQuantity": "245.56257825",
|
|
1204
|
+
// "quoteReservesQuantity": "3424383.3629",
|
|
1205
|
+
// "currentPrice": "16856.0000"
|
|
1206
|
+
// }
|
|
1207
|
+
// ]
|
|
1208
|
+
// }
|
|
1209
|
+
//
|
|
1210
|
+
return this.parseTicker(response, market);
|
|
1211
|
+
}
|
|
1212
|
+
parseTicker(ticker, market = undefined) {
|
|
1213
|
+
//
|
|
1214
|
+
// {
|
|
1215
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
1216
|
+
// "createdAtTimestamp": "1621490985000",
|
|
1217
|
+
// "high": "1.00000000",
|
|
1218
|
+
// "low": "1.00000000",
|
|
1219
|
+
// "bestBid": "1.00000000",
|
|
1220
|
+
// "bidVolume": "1.00000000",
|
|
1221
|
+
// "bestAsk": "1.00000000",
|
|
1222
|
+
// "askVolume": "1.00000000",
|
|
1223
|
+
// "vwap": "1.00000000",
|
|
1224
|
+
// "open": "1.00000000",
|
|
1225
|
+
// "close": "1.00000000",
|
|
1226
|
+
// "last": "1.00000000",
|
|
1227
|
+
// "change": "1.00000000",
|
|
1228
|
+
// "percentage": "1.00000000",
|
|
1229
|
+
// "average": "1.00000000",
|
|
1230
|
+
// "baseVolume": "1.00000000",
|
|
1231
|
+
// "quoteVolume": "1.00000000",
|
|
1232
|
+
// "bancorPrice": "1.00000000",
|
|
1233
|
+
// "markPrice": "19999.00",
|
|
1234
|
+
// "fundingRate": "0.01",
|
|
1235
|
+
// "openInterest": "100000.32452",
|
|
1236
|
+
// "lastTradeDatetime": "2021-05-20T01:01:01.000Z",
|
|
1237
|
+
// "lastTradeTimestamp": "1621490985000",
|
|
1238
|
+
// "lastTradeQuantity": "1.00000000",
|
|
1239
|
+
// "ammData": [
|
|
1240
|
+
// {
|
|
1241
|
+
// "feeTierId": "1",
|
|
1242
|
+
// "bidSpreadFee": "0.00040000",
|
|
1243
|
+
// "askSpreadFee": "0.00040000",
|
|
1244
|
+
// "baseReservesQuantity": "245.56257825",
|
|
1245
|
+
// "quoteReservesQuantity": "3424383.3629",
|
|
1246
|
+
// "currentPrice": "16856.0000"
|
|
1247
|
+
// }
|
|
1248
|
+
// ]
|
|
1249
|
+
// }
|
|
1250
|
+
//
|
|
1251
|
+
const marketId = this.safeString(ticker, 'symbol');
|
|
1252
|
+
market = this.safeMarket(marketId, market);
|
|
1253
|
+
const timestamp = this.safeInteger(ticker, 'createdAtTimestamp');
|
|
1254
|
+
return this.safeTicker({
|
|
1255
|
+
'symbol': market['symbol'],
|
|
1256
|
+
'timestamp': timestamp,
|
|
1257
|
+
'datetime': this.iso8601(timestamp),
|
|
1258
|
+
'high': this.safeString(ticker, 'high'),
|
|
1259
|
+
'low': this.safeString(ticker, 'low'),
|
|
1260
|
+
'bid': this.safeString2(ticker, 'bid', 'bestBid'),
|
|
1261
|
+
'bidVolume': this.safeString(ticker, 'bidVolume'),
|
|
1262
|
+
'ask': this.safeString2(ticker, 'ask', 'bestAsk'),
|
|
1263
|
+
'askVolume': this.safeString(ticker, 'askVolume'),
|
|
1264
|
+
'vwap': this.safeString(ticker, 'vwap'),
|
|
1265
|
+
'open': this.safeString(ticker, 'open'),
|
|
1266
|
+
'close': this.safeString(ticker, 'close'),
|
|
1267
|
+
'last': this.safeString(ticker, 'last'),
|
|
1268
|
+
'previousClose': undefined,
|
|
1269
|
+
'change': this.safeString(ticker, 'change'),
|
|
1270
|
+
'percentage': this.safeString(ticker, 'percentage'),
|
|
1271
|
+
'average': this.safeString(ticker, 'average'),
|
|
1272
|
+
'baseVolume': this.safeString(ticker, 'baseVolume'),
|
|
1273
|
+
'quoteVolume': this.safeString(ticker, 'quoteVolume'),
|
|
1274
|
+
'markPrice': this.safeString(ticker, 'markPrice'),
|
|
1275
|
+
'info': ticker,
|
|
1276
|
+
}, market);
|
|
1277
|
+
}
|
|
1278
|
+
async safeDeterministicCall(method, symbol = undefined, since = undefined, limit = undefined, timeframe = undefined, params = {}) {
|
|
1279
|
+
let maxRetries = undefined;
|
|
1280
|
+
[maxRetries, params] = this.handleOptionAndParams(params, method, 'maxRetries', 3);
|
|
1281
|
+
let errors = 0;
|
|
1282
|
+
params = this.omit(params, 'until');
|
|
1283
|
+
// the exchange returns the most recent data, so we do not need to pass until into paginated calls
|
|
1284
|
+
// the correct util value will be calculated inside of the method
|
|
1285
|
+
while (errors <= maxRetries) {
|
|
1286
|
+
try {
|
|
1287
|
+
if (timeframe && method !== 'fetchFundingRateHistory') {
|
|
1288
|
+
return await this[method](symbol, timeframe, since, limit, params);
|
|
1289
|
+
}
|
|
1290
|
+
else {
|
|
1291
|
+
return await this[method](symbol, since, limit, params);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
catch (e) {
|
|
1295
|
+
if (e instanceof RateLimitExceeded) {
|
|
1296
|
+
throw e; // if we are rate limited, we should not retry and fail fast
|
|
1297
|
+
}
|
|
1298
|
+
errors += 1;
|
|
1299
|
+
if (errors > maxRetries) {
|
|
1300
|
+
throw e;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return [];
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* @method
|
|
1308
|
+
* @name bullish#fetchOHLCV
|
|
1309
|
+
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
1310
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/markets/-symbol-/candle
|
|
1311
|
+
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
|
|
1312
|
+
* @param {string} timeframe the length of time each candle represents
|
|
1313
|
+
* @param {int} [since] timestamp in ms of the earliest candle to fetch
|
|
1314
|
+
* @param {int} [limit] the maximum amount of candles to fetch (max 100)
|
|
1315
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1316
|
+
* @param {int} [params.until] timestamp in ms of the latest entry
|
|
1317
|
+
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
1318
|
+
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
|
|
1319
|
+
*/
|
|
1320
|
+
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
|
1321
|
+
await this.loadMarkets();
|
|
1322
|
+
const market = this.market(symbol);
|
|
1323
|
+
const maxLimit = 100;
|
|
1324
|
+
let paginate = false;
|
|
1325
|
+
[paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate');
|
|
1326
|
+
if (paginate) {
|
|
1327
|
+
return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxLimit);
|
|
1328
|
+
}
|
|
1329
|
+
let request = {
|
|
1330
|
+
'symbol': market['id'],
|
|
1331
|
+
'timeBucket': this.safeString(this.timeframes, timeframe, timeframe),
|
|
1332
|
+
'_pageSize': maxLimit,
|
|
1333
|
+
};
|
|
1334
|
+
[request, params] = this.handleUntilOption('createdAtDatetime[lte]', request, params);
|
|
1335
|
+
let until = this.safeInteger(request, 'createdAtDatetime[lte]');
|
|
1336
|
+
const duration = this.parseTimeframe(timeframe);
|
|
1337
|
+
const maxDelta = 1000 * duration * maxLimit;
|
|
1338
|
+
let startTime = since;
|
|
1339
|
+
// both of since and until are required
|
|
1340
|
+
if (startTime === undefined && until === undefined) {
|
|
1341
|
+
until = this.milliseconds();
|
|
1342
|
+
startTime = until - maxDelta;
|
|
1343
|
+
}
|
|
1344
|
+
else if (startTime === undefined) {
|
|
1345
|
+
startTime = until - maxDelta;
|
|
1346
|
+
}
|
|
1347
|
+
else if (until === undefined) {
|
|
1348
|
+
until = this.sum(startTime, maxDelta);
|
|
1349
|
+
}
|
|
1350
|
+
request['createdAtDatetime[gte]'] = this.iso8601(startTime);
|
|
1351
|
+
request['createdAtDatetime[lte]'] = this.iso8601(until);
|
|
1352
|
+
const response = await this.publicGetV1MarketsSymbolCandle(this.extend(request, params));
|
|
1353
|
+
//
|
|
1354
|
+
// [
|
|
1355
|
+
// {
|
|
1356
|
+
// "open": "100846.7490",
|
|
1357
|
+
// "high": "100972.4001",
|
|
1358
|
+
// "low": "100840.8129",
|
|
1359
|
+
// "close": "100972.2602",
|
|
1360
|
+
// "volume": "30.56064890",
|
|
1361
|
+
// "createdAtTimestamp": "1746720540000",
|
|
1362
|
+
// "createdAtDatetime": "2025-05-08T16:09:00.000Z",
|
|
1363
|
+
// "publishedAtTimestamp": "1746720636007"
|
|
1364
|
+
// }, ...
|
|
1365
|
+
// ]
|
|
1366
|
+
//
|
|
1367
|
+
return this.parseOHLCVs(response, market, timeframe, since, limit);
|
|
1368
|
+
}
|
|
1369
|
+
parseOHLCV(ohlcv, market = undefined) {
|
|
1370
|
+
return [
|
|
1371
|
+
this.safeInteger(ohlcv, 'createdAtTimestamp'),
|
|
1372
|
+
this.safeNumber(ohlcv, 'open'),
|
|
1373
|
+
this.safeNumber(ohlcv, 'high'),
|
|
1374
|
+
this.safeNumber(ohlcv, 'low'),
|
|
1375
|
+
this.safeNumber(ohlcv, 'close'),
|
|
1376
|
+
this.safeNumber(ohlcv, 'volume'),
|
|
1377
|
+
];
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* @method
|
|
1381
|
+
* @name bullish#fetchFundingRateHistory
|
|
1382
|
+
* @description fetches historical funding rate prices
|
|
1383
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/markets/-symbol-/funding-rate
|
|
1384
|
+
* @param {string} symbol unified symbol of the market to fetch the funding rate history for
|
|
1385
|
+
* @param {int} [since] not sent to exchange api, exchange api always returns the most recent data, only used to filter exchange response
|
|
1386
|
+
* @param {int} [limit] the maximum amount of funding rate structures to fetch
|
|
1387
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1388
|
+
* @returns {object[]} a list of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-history-structure}
|
|
1389
|
+
*/
|
|
1390
|
+
async fetchFundingRateHistory(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1391
|
+
if (symbol === undefined) {
|
|
1392
|
+
throw new ArgumentsRequired(this.id + ' fetchFundingRateHistory() requires a symbol argument');
|
|
1393
|
+
}
|
|
1394
|
+
await this.loadMarkets();
|
|
1395
|
+
const maxLimit = 100;
|
|
1396
|
+
let paginate = false;
|
|
1397
|
+
[paginate, params] = this.handleOptionAndParams(params, 'fetchFundingRateHistory', 'paginate');
|
|
1398
|
+
if (paginate) {
|
|
1399
|
+
params = this.handlePaginationParams('fetchFundingRateHistory', since, params);
|
|
1400
|
+
return await this.fetchPaginatedCallDynamic('fetchFundingRateHistory', symbol, since, limit, params, maxLimit);
|
|
1401
|
+
}
|
|
1402
|
+
const market = this.market(symbol);
|
|
1403
|
+
if (!market['swap']) {
|
|
1404
|
+
throw new BadRequest(this.id + ' fetchFundingRateHistory() supports swap markets only');
|
|
1405
|
+
}
|
|
1406
|
+
const request = {
|
|
1407
|
+
'symbol': market['id'],
|
|
1408
|
+
};
|
|
1409
|
+
if (limit !== undefined) {
|
|
1410
|
+
request['_pageSize'] = this.getClosestLimit(limit);
|
|
1411
|
+
}
|
|
1412
|
+
params = this.handleSinceAndUntil(since, params, 'updatedAtDatetime[gte]', 'updatedAtDatetime[lte]');
|
|
1413
|
+
const response = await this.publicGetV1HistoryMarketsSymbolFundingRate(this.extend(request, params));
|
|
1414
|
+
//
|
|
1415
|
+
// [
|
|
1416
|
+
// {
|
|
1417
|
+
// "fundingRate": "0.00125",
|
|
1418
|
+
// "updatedAtDatetime": "2025-05-18T09:06:04.074Z"
|
|
1419
|
+
// },
|
|
1420
|
+
// {
|
|
1421
|
+
// "fundingRate": "0.00125",
|
|
1422
|
+
// "updatedAtDatetime": "2025-05-18T08:59:59.033Z"
|
|
1423
|
+
// }, ...
|
|
1424
|
+
// ]
|
|
1425
|
+
//
|
|
1426
|
+
const rates = [];
|
|
1427
|
+
const result = this.toArray(response);
|
|
1428
|
+
for (let i = 0; i < result.length; i++) {
|
|
1429
|
+
const entry = result[i];
|
|
1430
|
+
const datetime = this.safeString(entry, 'updatedAtDatetime');
|
|
1431
|
+
rates.push({
|
|
1432
|
+
'info': entry,
|
|
1433
|
+
'symbol': symbol,
|
|
1434
|
+
'fundingRate': this.safeNumber(entry, 'fundingRate'),
|
|
1435
|
+
'timestamp': this.parse8601(datetime),
|
|
1436
|
+
'datetime': datetime,
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
const sorted = this.sortBy(rates, 'timestamp');
|
|
1440
|
+
return this.filterBySymbolSinceLimit(sorted, market['symbol'], since, limit);
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* @method
|
|
1444
|
+
* @name bullish#fetchOrders
|
|
1445
|
+
* @description fetches information on multiple orders made by the user
|
|
1446
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--orders
|
|
1447
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--history
|
|
1448
|
+
* @param {string} symbol unified market symbol of the market orders were made in
|
|
1449
|
+
* @param {int} [since] the earliest time in ms to fetch orders for
|
|
1450
|
+
* @param {int} [limit] the maximum number of order structures to retrieve (5, 25, 50, 100, default is 25)
|
|
1451
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1452
|
+
* @param {int} [params.until] timestamp in ms of the latest order to fetch
|
|
1453
|
+
* @param {string} [params.tradingAccountId] the trading account id (mandatory parameter)
|
|
1454
|
+
* @param {string} [params.orderId] the id of the order to fetch for
|
|
1455
|
+
* @param {string} [params.clientOrderId] the client id of the order to fetch for
|
|
1456
|
+
* @param {string} [params.status] filter by order status, 'OPEN', 'CANCELLED', 'CLOSED', 'REJECTED'
|
|
1457
|
+
* @param {bool} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
|
|
1458
|
+
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1459
|
+
*/
|
|
1460
|
+
async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1461
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
1462
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
1463
|
+
const paginate = this.safeBool(params, 'paginate', false);
|
|
1464
|
+
if (paginate) {
|
|
1465
|
+
params = this.handlePaginationParams('fetchOrders', since, params);
|
|
1466
|
+
return await this.fetchPaginatedCallDynamic('fetchOrders', symbol, since, limit, params, 100);
|
|
1467
|
+
}
|
|
1468
|
+
let market = undefined;
|
|
1469
|
+
const request = {
|
|
1470
|
+
'tradingAccountId': tradingAccountId,
|
|
1471
|
+
};
|
|
1472
|
+
if (symbol !== undefined) {
|
|
1473
|
+
market = this.market(symbol);
|
|
1474
|
+
request['symbol'] = market['id'];
|
|
1475
|
+
}
|
|
1476
|
+
params = this.handleSinceAndUntil(since, params);
|
|
1477
|
+
if (limit !== undefined) {
|
|
1478
|
+
request['_pageSize'] = this.getClosestLimit(limit);
|
|
1479
|
+
}
|
|
1480
|
+
let method = 'privateGetV2HistoryOrders';
|
|
1481
|
+
[method, params] = this.handleOptionAndParams(params, 'fetchOrders', 'method', method);
|
|
1482
|
+
let response = undefined;
|
|
1483
|
+
if (method === 'privateGetV2Orders') {
|
|
1484
|
+
//
|
|
1485
|
+
// [
|
|
1486
|
+
// {
|
|
1487
|
+
// "clientOrderId": "187",
|
|
1488
|
+
// "orderId": "297735387747975680",
|
|
1489
|
+
// "symbol": "BTCUSDC",
|
|
1490
|
+
// "price": "1.00000000",
|
|
1491
|
+
// "averageFillPrice": "1.00000000",
|
|
1492
|
+
// "stopPrice": "1.00000000",
|
|
1493
|
+
// "allowBorrow": false,
|
|
1494
|
+
// "quantity": "1.00000000",
|
|
1495
|
+
// "quantityFilled": "1.00000000",
|
|
1496
|
+
// "quoteAmount": "1.00000000",
|
|
1497
|
+
// "baseFee": "0.00100000",
|
|
1498
|
+
// "quoteFee": "0.0010",
|
|
1499
|
+
// "borrowedBaseQuantity": "1.00000000",
|
|
1500
|
+
// "borrowedQuoteQuantity": "1.00000000",
|
|
1501
|
+
// "isLiquidation": false,
|
|
1502
|
+
// "side": "BUY",
|
|
1503
|
+
// "type": "LMT",
|
|
1504
|
+
// "timeInForce": "GTC",
|
|
1505
|
+
// "status": "OPEN",
|
|
1506
|
+
// "statusReason": "User cancelled",
|
|
1507
|
+
// "statusReasonCode": "1002",
|
|
1508
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
1509
|
+
// "createdAtTimestamp": "1621490985000",
|
|
1510
|
+
// }
|
|
1511
|
+
// ]
|
|
1512
|
+
//
|
|
1513
|
+
response = await this.privateGetV2Orders(this.extend(request, params));
|
|
1514
|
+
}
|
|
1515
|
+
else if (method === 'privateGetV2HistoryOrders') {
|
|
1516
|
+
response = await this.privateGetV2HistoryOrders(this.extend(request, params));
|
|
1517
|
+
}
|
|
1518
|
+
else {
|
|
1519
|
+
throw new BadRequest(this.id + ' fetchOrders() method parameter must be either "privateGetV2Orders" or "privateGetV2HistoryOrders"');
|
|
1520
|
+
}
|
|
1521
|
+
return this.parseOrders(response, market, since, limit);
|
|
1522
|
+
}
|
|
1523
|
+
handlePaginationParams(method, since = undefined, params = {}) {
|
|
1524
|
+
const ninetyDays = 90 * 24 * 60 * 60 * 1000;
|
|
1525
|
+
const now = this.milliseconds();
|
|
1526
|
+
const allowedSince = now - ninetyDays;
|
|
1527
|
+
if ((since !== undefined) && (since < allowedSince)) {
|
|
1528
|
+
throw new BadRequest(this.id + ' ' + method + '() only allows fetching entries up to 90 days in the past');
|
|
1529
|
+
}
|
|
1530
|
+
params = this.omit(params, 'paginate');
|
|
1531
|
+
params = this.extend(params, { 'paginationDirection': 'backward' });
|
|
1532
|
+
const until = this.safeInteger(params, 'until');
|
|
1533
|
+
if (until === undefined) {
|
|
1534
|
+
params = this.extend(params, { 'until': now });
|
|
1535
|
+
}
|
|
1536
|
+
return params;
|
|
1537
|
+
}
|
|
1538
|
+
handleSinceAndUntil(since = undefined, params = {}, sinceKey = 'createdAtDatetime[gte]', untilKey = 'createdAtDatetime[lte]') {
|
|
1539
|
+
let until = this.safeInteger(params, 'until');
|
|
1540
|
+
if ((since !== undefined) || (until !== undefined)) {
|
|
1541
|
+
const timeDelta = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
1542
|
+
if (since === undefined) {
|
|
1543
|
+
since = until - timeDelta;
|
|
1544
|
+
params = this.omit(params, 'until');
|
|
1545
|
+
}
|
|
1546
|
+
else if (until === undefined) {
|
|
1547
|
+
until = this.sum(since, timeDelta);
|
|
1548
|
+
const now = this.milliseconds();
|
|
1549
|
+
if (until > now) {
|
|
1550
|
+
until = now;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
const sinceDate = this.iso8601(since);
|
|
1554
|
+
const untilDate = this.iso8601(until);
|
|
1555
|
+
params[sinceKey] = sinceDate;
|
|
1556
|
+
params[untilKey] = untilDate;
|
|
1557
|
+
}
|
|
1558
|
+
return params;
|
|
1559
|
+
}
|
|
1560
|
+
getClosestLimit(limit) {
|
|
1561
|
+
let pageSize = 5;
|
|
1562
|
+
if ((limit > 5) && (limit < 26)) {
|
|
1563
|
+
pageSize = 25;
|
|
1564
|
+
}
|
|
1565
|
+
else if ((limit > 25) && (limit < 51)) {
|
|
1566
|
+
pageSize = 50;
|
|
1567
|
+
}
|
|
1568
|
+
else if (limit > 50) {
|
|
1569
|
+
pageSize = 100;
|
|
1570
|
+
}
|
|
1571
|
+
return pageSize;
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* @method
|
|
1575
|
+
* @name bullish#fetchOpenOrders
|
|
1576
|
+
* @description fetch all unfilled currently open orders
|
|
1577
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--history
|
|
1578
|
+
* @param {string} symbol unified market symbol of the market orders were made in
|
|
1579
|
+
* @param {int} [since] the earliest time in ms to fetch orders for
|
|
1580
|
+
* @param {int} [limit] the maximum number of order structures to retrieve
|
|
1581
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1582
|
+
* @param {string} params.tradingAccountId the trading account id (mandatory parameter)
|
|
1583
|
+
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1584
|
+
*/
|
|
1585
|
+
async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1586
|
+
const request = {
|
|
1587
|
+
'status': 'OPEN',
|
|
1588
|
+
};
|
|
1589
|
+
return await this.fetchOrders(symbol, since, limit, this.extend(request, params));
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* @method
|
|
1593
|
+
* @name bullish#fetchCanceledOrders
|
|
1594
|
+
* @description fetches information on multiple canceled orders made by the user
|
|
1595
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--orders
|
|
1596
|
+
* @param {string} symbol unified market symbol of the canceled orders
|
|
1597
|
+
* @param {int} [since] timestamp in ms of the earliest order
|
|
1598
|
+
* @param {int} [limit] the max number of canceled orders to return
|
|
1599
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1600
|
+
* @param {string} [params.tradingAccountId] the trading account id (mandatory parameter)
|
|
1601
|
+
* @returns {object} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1602
|
+
*/
|
|
1603
|
+
async fetchCanceledOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1604
|
+
const request = {
|
|
1605
|
+
'status': 'CANCELLED',
|
|
1606
|
+
'method': 'privateGetV2Orders', // current endpoint distinquishes between CLOSED and CANCELLED orders
|
|
1607
|
+
};
|
|
1608
|
+
return await this.fetchOrders(symbol, since, limit, this.extend(request, params));
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* @method
|
|
1612
|
+
* @name bullish#fetchClosedOrders
|
|
1613
|
+
* @description fetches information on multiple closed orders made by the user
|
|
1614
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--orders
|
|
1615
|
+
* @param {string} symbol unified market symbol of the closed orders
|
|
1616
|
+
* @param {int} [since] timestamp in ms of the earliest order
|
|
1617
|
+
* @param {int} [limit] the max number of closed orders to return
|
|
1618
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1619
|
+
* @param {string} params.tradingAccountId the trading account id (mandatory parameter)
|
|
1620
|
+
* @returns {object} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1621
|
+
*/
|
|
1622
|
+
async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1623
|
+
const request = {
|
|
1624
|
+
'status': 'CLOSED',
|
|
1625
|
+
'method': 'privateGetV2Orders', // current endpoint distinquishes between CLOSED and CANCELLED orders
|
|
1626
|
+
};
|
|
1627
|
+
return await this.fetchOrders(symbol, since, limit, this.extend(request, params));
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* @method
|
|
1631
|
+
* @name bullish#fetchCanceledAndClosedOrders
|
|
1632
|
+
* @description fetches information on multiple canceled orders made by the user
|
|
1633
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--history
|
|
1634
|
+
* @param {string} symbol unified market symbol of the closed orders
|
|
1635
|
+
* @param {int} [since] timestamp in ms of the earliest order
|
|
1636
|
+
* @param {int} [limit] the max number of closed orders to return
|
|
1637
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1638
|
+
* @param {string} [params.tradingAccountId] the trading account id (mandatory parameter)
|
|
1639
|
+
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1640
|
+
*/
|
|
1641
|
+
async fetchCanceledAndClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1642
|
+
const request = {
|
|
1643
|
+
'status': 'CLOSED',
|
|
1644
|
+
'method': 'privateGetV2HistoryOrders', // current endpoint returns both CLOSED and CANCELLED orders
|
|
1645
|
+
};
|
|
1646
|
+
return await this.fetchOrders(symbol, since, limit, this.extend(request, params));
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* @method
|
|
1650
|
+
* @name bullish#fetchOrder
|
|
1651
|
+
* @description fetches information on an order made by the user
|
|
1652
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v2/orders/-orderId-
|
|
1653
|
+
* @param {string} id the order id
|
|
1654
|
+
* @param {string} [symbol] unified symbol of the market the order was made in
|
|
1655
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1656
|
+
* @param {string} [params.traidingAccountId] the trading account id (mandatory parameter)
|
|
1657
|
+
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1658
|
+
*/
|
|
1659
|
+
async fetchOrder(id, symbol = undefined, params = {}) {
|
|
1660
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
1661
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
1662
|
+
let market = undefined;
|
|
1663
|
+
if (symbol !== undefined) {
|
|
1664
|
+
market = this.market(symbol);
|
|
1665
|
+
}
|
|
1666
|
+
const request = {
|
|
1667
|
+
'orderId': id,
|
|
1668
|
+
'tradingAccountId': tradingAccountId,
|
|
1669
|
+
};
|
|
1670
|
+
const response = await this.privateGetV2OrdersOrderId(this.extend(request, params));
|
|
1671
|
+
//
|
|
1672
|
+
// {
|
|
1673
|
+
// "clientOrderId": "187",
|
|
1674
|
+
// "orderId": "297735387747975680",
|
|
1675
|
+
// "symbol": "BTCUSDC",
|
|
1676
|
+
// "price": "1.00000000",
|
|
1677
|
+
// "averageFillPrice": "1.00000000",
|
|
1678
|
+
// "stopPrice": "1.00000000",
|
|
1679
|
+
// "allowBorrow": false,
|
|
1680
|
+
// "quantity": "1.00000000",
|
|
1681
|
+
// "quantityFilled": "1.00000000",
|
|
1682
|
+
// "quoteAmount": "1.00000000",
|
|
1683
|
+
// "baseFee": "0.00100000",
|
|
1684
|
+
// "quoteFee": "0.0010",
|
|
1685
|
+
// "borrowedBaseQuantity": "1.00000000",
|
|
1686
|
+
// "borrowedQuoteQuantity": "1.00000000",
|
|
1687
|
+
// "isLiquidation": false,
|
|
1688
|
+
// "side": "BUY",
|
|
1689
|
+
// "type": "LMT",
|
|
1690
|
+
// "timeInForce": "GTC",
|
|
1691
|
+
// "status": "OPEN",
|
|
1692
|
+
// "statusReason": "User cancelled",
|
|
1693
|
+
// "statusReasonCode": "1002",
|
|
1694
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
1695
|
+
// "createdAtTimestamp": "1621490985000",
|
|
1696
|
+
// }
|
|
1697
|
+
//
|
|
1698
|
+
return this.parseOrder(response, market);
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* @method
|
|
1702
|
+
* @name bullish#createOrder
|
|
1703
|
+
* @description create a trade order
|
|
1704
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#post-/v2/orders
|
|
1705
|
+
* @param {string} symbol unified symbol of the market to create an order in
|
|
1706
|
+
* @param {string} type 'market' or 'limit' or 'STOP_LIMIT' or 'POST_ONLY'
|
|
1707
|
+
* @param {string} side 'buy' or 'sell'
|
|
1708
|
+
* @param {float} amount how much of currency you want to trade in units of base currency
|
|
1709
|
+
* @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
|
|
1710
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1711
|
+
* @param {string} [params.clientOrderId] a custom client order id
|
|
1712
|
+
* @param {float} [params.triggerPrice] the price at which a stop order is triggered at
|
|
1713
|
+
* @param {string} [params.timeInForce] the time in force for the order, either 'GTC' (Good Till Cancelled) or 'IOC' (Immediate or Cancel), default is 'GTC'
|
|
1714
|
+
* @param {bool} [params.allowBorrow] if true, the order will be allowed to borrow assets to fulfill the order (default is false)
|
|
1715
|
+
* @param {bool} [params.postOnly] if true, the order will only be posted to the order book and not executed immediately (default is false)
|
|
1716
|
+
* @param {string} params.traidingAccountId the trading account id (mandatory parameter)
|
|
1717
|
+
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1718
|
+
*/
|
|
1719
|
+
async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
|
|
1720
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
1721
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
1722
|
+
const market = this.market(symbol);
|
|
1723
|
+
const request = {
|
|
1724
|
+
'commandType': 'V3CreateOrder',
|
|
1725
|
+
'symbol': market['id'],
|
|
1726
|
+
'side': side.toUpperCase(),
|
|
1727
|
+
'quantity': this.amountToPrecision(symbol, amount),
|
|
1728
|
+
'tradingAccountId': tradingAccountId,
|
|
1729
|
+
};
|
|
1730
|
+
const isMarketOrder = ((type === 'market') || type === 'MARKET');
|
|
1731
|
+
let postOnly = false;
|
|
1732
|
+
[postOnly, params] = this.handlePostOnly(isMarketOrder, type === 'POST_ONLY', params);
|
|
1733
|
+
if (postOnly) {
|
|
1734
|
+
type = 'POST_ONLY';
|
|
1735
|
+
}
|
|
1736
|
+
let timeInForce = 'GTC'; // is mandatory
|
|
1737
|
+
[timeInForce, params] = this.handleOptionAndParams(params, 'createOrder', 'timeInForce', timeInForce);
|
|
1738
|
+
params['timeInForce'] = timeInForce.toUpperCase();
|
|
1739
|
+
if (!isMarketOrder) {
|
|
1740
|
+
request['price'] = this.priceToPrecision(symbol, price);
|
|
1741
|
+
}
|
|
1742
|
+
const triggerPrice = this.safeString(params, 'triggerPrice');
|
|
1743
|
+
if (triggerPrice !== undefined) {
|
|
1744
|
+
if (isMarketOrder) {
|
|
1745
|
+
throw new NotSupported(this.id + ' createOrder() does not support market trigger orders');
|
|
1746
|
+
}
|
|
1747
|
+
request['stopPrice'] = this.priceToPrecision(symbol, triggerPrice);
|
|
1748
|
+
type = 'STOP_LIMIT';
|
|
1749
|
+
params = this.omit(params, 'triggerPrice');
|
|
1750
|
+
}
|
|
1751
|
+
request['type'] = type.toUpperCase();
|
|
1752
|
+
const response = await this.privatePostV2Orders(this.extend(request, params));
|
|
1753
|
+
//
|
|
1754
|
+
// {
|
|
1755
|
+
// "message": "Command acknowledged - CreateOrder",
|
|
1756
|
+
// "requestId": "633910976353665024",
|
|
1757
|
+
// "orderId": "633910775316480001",
|
|
1758
|
+
// "clientOrderId": "1234567"
|
|
1759
|
+
// }
|
|
1760
|
+
//
|
|
1761
|
+
return this.parseOrder(response, market);
|
|
1762
|
+
}
|
|
1763
|
+
/**
|
|
1764
|
+
* @method
|
|
1765
|
+
* @name bullish#editOrder
|
|
1766
|
+
* @description edit a trade limit order
|
|
1767
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#post-/v2/command-amend
|
|
1768
|
+
* @param {string} id order id
|
|
1769
|
+
* @param {string} [symbol] unified symbol of the market to create an order in
|
|
1770
|
+
* @param {string} [type] 'limit' or 'POST_ONLY'
|
|
1771
|
+
* @param {string} [side] not used by bullish editOrder
|
|
1772
|
+
* @param {float} [amount] how much of the currency you want to trade in units of the base currency
|
|
1773
|
+
* @param {float} [price] the price for the order, in units of the quote currency, ignored in market orders
|
|
1774
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1775
|
+
* @param {string} [params.traidingAccountId] the trading account id (mandatory parameter)
|
|
1776
|
+
* @param {bool} [params.postOnly] if true, the order will only be posted to the order book and not executed immediately (default is false)
|
|
1777
|
+
* @param {string} [params.clientOrderId] a unique identifier for the order, automatically generated if not sent
|
|
1778
|
+
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1779
|
+
*/
|
|
1780
|
+
async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
|
|
1781
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
1782
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
1783
|
+
const market = this.market(symbol);
|
|
1784
|
+
const request = {
|
|
1785
|
+
'commandType': 'V1AmendOrder',
|
|
1786
|
+
'symbol': market['id'],
|
|
1787
|
+
'tradingAccountId': tradingAccountId,
|
|
1788
|
+
};
|
|
1789
|
+
const clientOrderId = this.safeString(params, 'clientOrderId');
|
|
1790
|
+
if (clientOrderId === undefined) {
|
|
1791
|
+
request['orderId'] = id;
|
|
1792
|
+
}
|
|
1793
|
+
if (type !== undefined) {
|
|
1794
|
+
request['type'] = type.toUpperCase();
|
|
1795
|
+
}
|
|
1796
|
+
const postOnly = this.safeBool(params, 'postOnly', false);
|
|
1797
|
+
if (postOnly) {
|
|
1798
|
+
params = this.omit(params, 'postOnly');
|
|
1799
|
+
request['type'] = 'POST_ONLY';
|
|
1800
|
+
}
|
|
1801
|
+
if (amount !== undefined) {
|
|
1802
|
+
request['quantity'] = this.amountToPrecision(symbol, amount);
|
|
1803
|
+
}
|
|
1804
|
+
if (price !== undefined) {
|
|
1805
|
+
request['price'] = this.priceToPrecision(symbol, price);
|
|
1806
|
+
}
|
|
1807
|
+
const response = await this.privatePostV2Command(this.extend(request, params));
|
|
1808
|
+
return this.parseOrder(response, market);
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* @method
|
|
1812
|
+
* @name bullish#cancelOrder
|
|
1813
|
+
* @description cancels an open order
|
|
1814
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#post-/v2/command-cancellations
|
|
1815
|
+
* @param {string} [id] order id
|
|
1816
|
+
* @param {string} symbol unified symbol of the market the order was made in
|
|
1817
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1818
|
+
* @param {string} params.commandType the command type, default is 'V3CancelOrder' (mandatory parameter)
|
|
1819
|
+
* @param {string} [params.traidingAccountId] the trading account id (mandatory parameter)
|
|
1820
|
+
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1821
|
+
*/
|
|
1822
|
+
async cancelOrder(id, symbol = undefined, params = {}) {
|
|
1823
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
1824
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
1825
|
+
if (symbol === undefined) {
|
|
1826
|
+
throw new ArgumentsRequired(this.id + ' cancelOrder() requires a symbol argument');
|
|
1827
|
+
}
|
|
1828
|
+
const market = this.market(symbol);
|
|
1829
|
+
const request = {
|
|
1830
|
+
'symbol': market['id'],
|
|
1831
|
+
'tradingAccountId': tradingAccountId,
|
|
1832
|
+
'commandType': this.safeString(params, 'commandType', 'V3CancelOrder'),
|
|
1833
|
+
'orderId': id,
|
|
1834
|
+
};
|
|
1835
|
+
const response = await this.privatePostV2Command(this.extend(request, params));
|
|
1836
|
+
//
|
|
1837
|
+
// {
|
|
1838
|
+
// "message": "Command acknowledged - CancelOrder",
|
|
1839
|
+
// "requestId": "844658480774644736",
|
|
1840
|
+
// "orderId": "297735387747975680",
|
|
1841
|
+
// "clientOrderId": null
|
|
1842
|
+
// }
|
|
1843
|
+
//
|
|
1844
|
+
return this.parseOrder(response, market);
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* @method
|
|
1848
|
+
* @name bullish#cancelAllOrders
|
|
1849
|
+
* @description cancel all open orders in a market
|
|
1850
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#post-/v2/command-cancellations
|
|
1851
|
+
* @param {string} [symbol] alpaca cancelAllOrders cannot setting symbol, it will cancel all open orders
|
|
1852
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1853
|
+
* @param {string} params.traidingAccountId the trading account id (mandatory parameter)
|
|
1854
|
+
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1855
|
+
*/
|
|
1856
|
+
async cancelAllOrders(symbol = undefined, params = {}) {
|
|
1857
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
1858
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
1859
|
+
const request = {
|
|
1860
|
+
'tradingAccountId': tradingAccountId,
|
|
1861
|
+
};
|
|
1862
|
+
let market = undefined;
|
|
1863
|
+
if (symbol !== undefined) {
|
|
1864
|
+
market = this.market(symbol);
|
|
1865
|
+
request['symbol'] = market['id'];
|
|
1866
|
+
request['commandType'] = 'V1CancelAllOrdersByMarket';
|
|
1867
|
+
}
|
|
1868
|
+
else {
|
|
1869
|
+
request['commandType'] = 'V1CancelAllOrders';
|
|
1870
|
+
}
|
|
1871
|
+
const response = await this.privatePostV2Command(this.extend(request, params));
|
|
1872
|
+
//
|
|
1873
|
+
// {
|
|
1874
|
+
// "message": "Command acknowledged - CancelAllOrders",
|
|
1875
|
+
// "requestId": "633900538459062272"
|
|
1876
|
+
// }
|
|
1877
|
+
//
|
|
1878
|
+
const orders = [response];
|
|
1879
|
+
return this.parseOrders(orders, market);
|
|
1880
|
+
}
|
|
1881
|
+
parseOrder(order, market = undefined) {
|
|
1882
|
+
//
|
|
1883
|
+
// fetchOrders, fetchOrder
|
|
1884
|
+
// {
|
|
1885
|
+
// "clientOrderId": "187",
|
|
1886
|
+
// "orderId": "297735387747975680",
|
|
1887
|
+
// "symbol": "BTCUSDC",
|
|
1888
|
+
// "price": "1.00000000",
|
|
1889
|
+
// "averageFillPrice": "1.00000000",
|
|
1890
|
+
// "stopPrice": "1.00000000",
|
|
1891
|
+
// "allowBorrow": false,
|
|
1892
|
+
// "quantity": "1.00000000",
|
|
1893
|
+
// "quantityFilled": "1.00000000",
|
|
1894
|
+
// "quoteAmount": "1.00000000",
|
|
1895
|
+
// "baseFee": "0.00100000",
|
|
1896
|
+
// "quoteFee": "0.0010",
|
|
1897
|
+
// "borrowedBaseQuantity": "1.00000000",
|
|
1898
|
+
// "borrowedQuoteQuantity": "1.00000000",
|
|
1899
|
+
// "isLiquidation": false,
|
|
1900
|
+
// "side": "BUY",
|
|
1901
|
+
// "type": "LMT",
|
|
1902
|
+
// "timeInForce": "GTC",
|
|
1903
|
+
// "status": "OPEN",
|
|
1904
|
+
// "statusReason": "User cancelled",
|
|
1905
|
+
// "statusReasonCode": "1002",
|
|
1906
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
1907
|
+
// "createdAtTimestamp": "1621490985000",
|
|
1908
|
+
// }
|
|
1909
|
+
//
|
|
1910
|
+
// createOrder
|
|
1911
|
+
// {
|
|
1912
|
+
// "message": "Command acknowledged - CreateOrder",
|
|
1913
|
+
// "requestId": "633910976353665024",
|
|
1914
|
+
// "orderId": "633910775316480001",
|
|
1915
|
+
// "clientOrderId": "1234567"
|
|
1916
|
+
// }
|
|
1917
|
+
//
|
|
1918
|
+
// cancelOrder
|
|
1919
|
+
// {
|
|
1920
|
+
// "message": "Command acknowledged - CancelOrder",
|
|
1921
|
+
// "requestId": "633910976353665024",
|
|
1922
|
+
// "orderId": "633910775316480001"
|
|
1923
|
+
// }
|
|
1924
|
+
//
|
|
1925
|
+
// cancelAllOrders
|
|
1926
|
+
// {
|
|
1927
|
+
// "message": "Command acknowledged - CancelAllOrders",
|
|
1928
|
+
// "requestId": "633900538459062272"
|
|
1929
|
+
// }
|
|
1930
|
+
//
|
|
1931
|
+
const marketId = this.safeString(order, 'symbol');
|
|
1932
|
+
if (market === undefined) {
|
|
1933
|
+
market = this.safeMarket(marketId);
|
|
1934
|
+
}
|
|
1935
|
+
const symbol = this.safeSymbol(marketId, market);
|
|
1936
|
+
const id = this.safeString(order, 'orderId');
|
|
1937
|
+
const timestamp = this.safeInteger(order, 'createdAtTimestamp');
|
|
1938
|
+
const type = this.safeString(order, 'type');
|
|
1939
|
+
const side = this.safeStringLower(order, 'side');
|
|
1940
|
+
const price = this.safeString(order, 'price');
|
|
1941
|
+
const amount = this.safeString(order, 'quantity');
|
|
1942
|
+
const filled = this.safeString(order, 'quantityFilled');
|
|
1943
|
+
let status = this.parseOrderStatus(this.safeString(order, 'status'));
|
|
1944
|
+
if (status === 'closed') {
|
|
1945
|
+
const statusReason = this.safeString(order, 'statusReason');
|
|
1946
|
+
if (statusReason === 'User cancelled') {
|
|
1947
|
+
status = 'canceled';
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
const timeInForce = this.safeString(order, 'timeInForce');
|
|
1951
|
+
const stopPrice = this.safeString(order, 'stopPrice');
|
|
1952
|
+
const cost = this.safeString(order, 'quoteAmount');
|
|
1953
|
+
const fee = {};
|
|
1954
|
+
const quoteFee = this.safeNumber(order, 'quoteFee');
|
|
1955
|
+
if (quoteFee !== undefined) {
|
|
1956
|
+
fee['cost'] = quoteFee;
|
|
1957
|
+
fee['currency'] = market['quote'];
|
|
1958
|
+
}
|
|
1959
|
+
const average = this.safeString(order, 'averageFillPrice');
|
|
1960
|
+
return this.safeOrder({
|
|
1961
|
+
'id': id,
|
|
1962
|
+
'clientOrderId': this.safeString(order, 'clientOrderId'),
|
|
1963
|
+
'timestamp': timestamp,
|
|
1964
|
+
'datetime': this.iso8601(timestamp),
|
|
1965
|
+
'lastTradeTimestamp': undefined,
|
|
1966
|
+
'status': status,
|
|
1967
|
+
'symbol': symbol,
|
|
1968
|
+
'type': this.parseOrderType(type),
|
|
1969
|
+
'timeInForce': timeInForce,
|
|
1970
|
+
'postOnly': type === 'POST_ONLY',
|
|
1971
|
+
'side': side,
|
|
1972
|
+
'price': price,
|
|
1973
|
+
'triggerPrice': stopPrice,
|
|
1974
|
+
'amount': amount,
|
|
1975
|
+
'filled': filled,
|
|
1976
|
+
'remaining': undefined,
|
|
1977
|
+
'cost': cost,
|
|
1978
|
+
'trades': undefined,
|
|
1979
|
+
'fee': fee,
|
|
1980
|
+
'info': order,
|
|
1981
|
+
'average': average,
|
|
1982
|
+
}, market);
|
|
1983
|
+
}
|
|
1984
|
+
parseOrderStatus(status) {
|
|
1985
|
+
const statuses = {
|
|
1986
|
+
'OPEN': 'open',
|
|
1987
|
+
'CLOSED': 'closed',
|
|
1988
|
+
'CANCELLED': 'canceled',
|
|
1989
|
+
'REJECTED': 'rejected',
|
|
1990
|
+
};
|
|
1991
|
+
return this.safeString(statuses, status, status);
|
|
1992
|
+
}
|
|
1993
|
+
parseOrderType(type) {
|
|
1994
|
+
const types = {
|
|
1995
|
+
'LMT': 'limit',
|
|
1996
|
+
'MKT': 'market',
|
|
1997
|
+
'POST_ONLY': 'limit',
|
|
1998
|
+
'STOP_LIMIT': 'limit',
|
|
1999
|
+
};
|
|
2000
|
+
return this.safeString(types, type, type);
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* @method
|
|
2004
|
+
* @name bullish#fetchDepositsWithdrawals
|
|
2005
|
+
* @description fetch history of deposits and withdrawals
|
|
2006
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/wallets/transactions
|
|
2007
|
+
* @param {string} [code] unified currency code for the currency of the deposit/withdrawals, default is undefined
|
|
2008
|
+
* @param {int} [since] timestamp in ms of the earliest deposit/withdrawal, default is undefined
|
|
2009
|
+
* @param {int} [limit] max number of deposit/withdrawals to return, default is undefined
|
|
2010
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2011
|
+
* @returns {object} a list of [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
|
|
2012
|
+
*/
|
|
2013
|
+
async fetchDepositsWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
|
|
2014
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2015
|
+
let request = {};
|
|
2016
|
+
[request, params] = this.handleUntilOption('createdAtDatetime[lte]', request, params);
|
|
2017
|
+
const until = this.safeInteger(request, 'createdAtDatetime[lte]');
|
|
2018
|
+
if (until !== undefined) {
|
|
2019
|
+
request['createdAtDatetime[lte]'] = this.iso8601(until);
|
|
2020
|
+
}
|
|
2021
|
+
if (since !== undefined) {
|
|
2022
|
+
request['createdAtDatetime[gte]'] = this.iso8601(since);
|
|
2023
|
+
}
|
|
2024
|
+
const response = await this.privateGetV1WalletsTransactions(this.extend(request, params));
|
|
2025
|
+
//
|
|
2026
|
+
// {
|
|
2027
|
+
// "data": [
|
|
2028
|
+
// {
|
|
2029
|
+
// "custodyTransactionId": "0x791fc85f16a84cbd5250d5517ecad497f564d2e5cc54d31466fe70b952fd58da",
|
|
2030
|
+
// "direction": "DEPOSIT",
|
|
2031
|
+
// "quantity": "150",
|
|
2032
|
+
// "symbol": "USDC",
|
|
2033
|
+
// "fee": "0",
|
|
2034
|
+
// "memo": "0x34625d5f0b6575503a0669994dea24271bfbd443",
|
|
2035
|
+
// "createdAtDateTime": "2025-11-04T14:31:17.000Z",
|
|
2036
|
+
// "updatedAtDateTime": "2025-11-04T14:44:17.500Z",
|
|
2037
|
+
// "status": "COMPLETE",
|
|
2038
|
+
// "statusReason": "OK",
|
|
2039
|
+
// "network": "ETH",
|
|
2040
|
+
// "transactionDetails": {
|
|
2041
|
+
// "address": "0x34625d5f0b6575503a0669994dea24271bfbd443",
|
|
2042
|
+
// "blockchainTxId": "0x791fc85f16a84cbd5250d5517ecad497f564d2e5cc54d31466fe70b952fd58da",
|
|
2043
|
+
// "swiftUetr": null,
|
|
2044
|
+
// "sources": [
|
|
2045
|
+
// {
|
|
2046
|
+
// "address": "0x2653435d52a5f49551ebb757f25b2c8bb954859b"
|
|
2047
|
+
// }
|
|
2048
|
+
// ]
|
|
2049
|
+
// }
|
|
2050
|
+
// }
|
|
2051
|
+
// ],
|
|
2052
|
+
// "links": {
|
|
2053
|
+
// "previous": null,
|
|
2054
|
+
// "next": null
|
|
2055
|
+
// },
|
|
2056
|
+
// "totalCount": 1
|
|
2057
|
+
// }
|
|
2058
|
+
//
|
|
2059
|
+
const data = this.safeList(response, 'data', []);
|
|
2060
|
+
let currency = undefined;
|
|
2061
|
+
if (code !== undefined) {
|
|
2062
|
+
currency = this.currency(code);
|
|
2063
|
+
}
|
|
2064
|
+
return this.parseTransactions(data, currency, since, limit);
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* @method
|
|
2068
|
+
* @name bullish#withdraw
|
|
2069
|
+
* @description make a withdrawal
|
|
2070
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#post-/v1/wallets/withdrawal
|
|
2071
|
+
* @param {string} code unified currency code
|
|
2072
|
+
* @param {float} amount the amount to withdraw
|
|
2073
|
+
* @param {string} address the address to withdraw to
|
|
2074
|
+
* @param {string} [tag]
|
|
2075
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2076
|
+
* @param {string} params.timestamp the timestamp of the withdrawal request (mandatory)
|
|
2077
|
+
* @param {string} params.nonce the nonce of the withdrawal request (mandatory)
|
|
2078
|
+
* @param {string} params.network network for withdraw (mandatory)
|
|
2079
|
+
* @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
|
|
2080
|
+
*/
|
|
2081
|
+
async withdraw(code, amount, address, tag = undefined, params = {}) {
|
|
2082
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2083
|
+
// todo check this method properly
|
|
2084
|
+
const currency = this.currency(code);
|
|
2085
|
+
const request = {
|
|
2086
|
+
'command': {
|
|
2087
|
+
'commandType': 'V1Withdraw',
|
|
2088
|
+
'destinationId': address,
|
|
2089
|
+
'symbol': currency['id'],
|
|
2090
|
+
'quantity': this.currencyToPrecision(code, amount),
|
|
2091
|
+
},
|
|
2092
|
+
};
|
|
2093
|
+
let networkCode = undefined;
|
|
2094
|
+
[networkCode, params] = this.handleNetworkCodeAndParams(params);
|
|
2095
|
+
if (networkCode !== undefined) {
|
|
2096
|
+
request['network'] = this.networkCodeToId(networkCode);
|
|
2097
|
+
}
|
|
2098
|
+
else {
|
|
2099
|
+
throw new ArgumentsRequired(this.id + ' withdraw() requires a network parameter');
|
|
2100
|
+
}
|
|
2101
|
+
const response = await this.privatePostV1WalletsWithdrawal(this.extend(request, params));
|
|
2102
|
+
//
|
|
2103
|
+
// {
|
|
2104
|
+
// "code": "00000",
|
|
2105
|
+
// "msg": "success",
|
|
2106
|
+
// "data": {
|
|
2107
|
+
// "orderId":888291686266343424",
|
|
2108
|
+
// "clientOrderId":"123"
|
|
2109
|
+
// }
|
|
2110
|
+
// }
|
|
2111
|
+
//
|
|
2112
|
+
return this.parseTransaction(response, currency);
|
|
2113
|
+
}
|
|
2114
|
+
parseTransaction(transaction, currency = undefined) {
|
|
2115
|
+
//
|
|
2116
|
+
// {
|
|
2117
|
+
// "custodyTransactionId": "0x791fc85f16a84cbd5250d5517ecad497f564d2e5cc54d31466fe70b952fd58da",
|
|
2118
|
+
// "direction": "DEPOSIT",
|
|
2119
|
+
// "quantity": "150",
|
|
2120
|
+
// "symbol": "USDC",
|
|
2121
|
+
// "fee": "0",
|
|
2122
|
+
// "memo": "0x34625d5f0b6575503a0669994dea24271bfbd443",
|
|
2123
|
+
// "createdAtDateTime": "2025-11-04T14:31:17.000Z",
|
|
2124
|
+
// "updatedAtDateTime": "2025-11-04T14:44:17.500Z",
|
|
2125
|
+
// "status": "COMPLETE",
|
|
2126
|
+
// "statusReason": "OK",
|
|
2127
|
+
// "network": "ETH",
|
|
2128
|
+
// "transactionDetails": {
|
|
2129
|
+
// "address": "0x34625d5f0b6575503a0669994dea24271bfbd443",
|
|
2130
|
+
// "blockchainTxId": "0x791fc85f16a84cbd5250d5517ecad497f564d2e5cc54d31466fe70b952fd58da",
|
|
2131
|
+
// "swiftUetr": null,
|
|
2132
|
+
// "sources": [
|
|
2133
|
+
// {
|
|
2134
|
+
// "address": "0x2653435d52a5f49551ebb757f25b2c8bb954859b"
|
|
2135
|
+
// }
|
|
2136
|
+
// ]
|
|
2137
|
+
// }
|
|
2138
|
+
// }
|
|
2139
|
+
//
|
|
2140
|
+
const id = this.safeString(transaction, 'custodyTransactionId');
|
|
2141
|
+
const type = this.safeString(transaction, 'direction');
|
|
2142
|
+
const timestamp = this.parse8601(this.safeString(transaction, 'createdAtDateTime'));
|
|
2143
|
+
const updated = this.parse8601(this.safeString(transaction, 'updatedAtDateTime'));
|
|
2144
|
+
const network = this.safeString(transaction, 'network');
|
|
2145
|
+
const transactionDetails = this.safeDict(transaction, 'transactionDetails');
|
|
2146
|
+
const txid = this.safeString(transactionDetails, 'blockchainTxId');
|
|
2147
|
+
const address = this.safeString(transactionDetails, 'address');
|
|
2148
|
+
const amount = this.safeNumber(transaction, 'quantity');
|
|
2149
|
+
const currencyId = this.safeString(transaction, 'symbol');
|
|
2150
|
+
const code = this.safeCurrencyCode(currencyId, currency);
|
|
2151
|
+
const status = this.safeString(transaction, 'status');
|
|
2152
|
+
const sources = this.safeList(transactionDetails, 'sources', []);
|
|
2153
|
+
const source = this.safeDict(sources, 0, {});
|
|
2154
|
+
const sourceAddress = this.safeString(source, 'address');
|
|
2155
|
+
const fee = {
|
|
2156
|
+
'currency': undefined,
|
|
2157
|
+
'cost': undefined,
|
|
2158
|
+
'rate': undefined,
|
|
2159
|
+
};
|
|
2160
|
+
const feeCost = this.safeNumber(transaction, 'fee');
|
|
2161
|
+
if (feeCost !== undefined) {
|
|
2162
|
+
fee['cost'] = feeCost;
|
|
2163
|
+
fee['currency'] = code;
|
|
2164
|
+
}
|
|
2165
|
+
return {
|
|
2166
|
+
'id': id,
|
|
2167
|
+
'txid': txid,
|
|
2168
|
+
'timestamp': timestamp,
|
|
2169
|
+
'datetime': this.iso8601(timestamp),
|
|
2170
|
+
'network': this.networkIdToCode(network),
|
|
2171
|
+
'addressFrom': sourceAddress,
|
|
2172
|
+
'address': address,
|
|
2173
|
+
'addressTo': address,
|
|
2174
|
+
'amount': amount,
|
|
2175
|
+
'type': this.parseTransactionType(type),
|
|
2176
|
+
'currency': code,
|
|
2177
|
+
'status': this.parseTransactionStatus(status),
|
|
2178
|
+
'updated': updated,
|
|
2179
|
+
'tagFrom': undefined,
|
|
2180
|
+
'tag': undefined,
|
|
2181
|
+
'tagTo': undefined,
|
|
2182
|
+
'comment': undefined,
|
|
2183
|
+
'internal': undefined,
|
|
2184
|
+
'fee': fee,
|
|
2185
|
+
'info': transaction,
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
parseTransactionType(type) {
|
|
2189
|
+
const types = {
|
|
2190
|
+
'DEPOSIT': 'deposit',
|
|
2191
|
+
'WITHDRAW': 'withdrawal',
|
|
2192
|
+
};
|
|
2193
|
+
return this.safeString(types, type, type);
|
|
2194
|
+
}
|
|
2195
|
+
parseTransactionStatus(status) {
|
|
2196
|
+
const statuses = {
|
|
2197
|
+
'COMPLETE': 'ok',
|
|
2198
|
+
'FAILED': 'failed',
|
|
2199
|
+
'PENDING': 'pending',
|
|
2200
|
+
'CANCELLED': 'canceled',
|
|
2201
|
+
};
|
|
2202
|
+
return this.safeString(statuses, status, status);
|
|
2203
|
+
}
|
|
2204
|
+
async loadAccount(params = {}) {
|
|
2205
|
+
let tradingAccountId = undefined;
|
|
2206
|
+
[tradingAccountId, params] = this.handleOptionAndParams(params, 'fetchMyTrades', 'tradingAccountId');
|
|
2207
|
+
if (tradingAccountId === undefined) {
|
|
2208
|
+
const response = await this.privateGetV1AccountsTradingAccounts(params);
|
|
2209
|
+
for (let i = 0; i < response.length; i++) {
|
|
2210
|
+
const account = response[i];
|
|
2211
|
+
const name = this.safeString(account, 'tradingAccountName');
|
|
2212
|
+
if (name === 'Primary Account') {
|
|
2213
|
+
tradingAccountId = this.safeString(account, 'tradingAccountId');
|
|
2214
|
+
break;
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
if (tradingAccountId === undefined) {
|
|
2219
|
+
throw new ArgumentsRequired(this.id + ' loadAccount() requires a tradingAccountId parameter in options["tradingAccountId"] or params["tradingAccountId"], fetchAccounts() was not able to find the Primary account');
|
|
2220
|
+
}
|
|
2221
|
+
this.options['tradingAccountId'] = tradingAccountId;
|
|
2222
|
+
return tradingAccountId;
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* @method
|
|
2226
|
+
* @name bullish#fetchAccounts
|
|
2227
|
+
* @description fetch all the accounts associated with a profile
|
|
2228
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--trading-accounts
|
|
2229
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2230
|
+
* @returns {object} a dictionary of [account structures]{@link https://docs.ccxt.com/#/?id=account-structure} indexed by the account type
|
|
2231
|
+
*/
|
|
2232
|
+
async fetchAccounts(params = {}) {
|
|
2233
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2234
|
+
const response = await this.privateGetV1AccountsTradingAccounts(params);
|
|
2235
|
+
//
|
|
2236
|
+
// [
|
|
2237
|
+
// {
|
|
2238
|
+
// "defaultedMarginUSD": "0.0000",
|
|
2239
|
+
// "endCustomerId": "222801149768465",
|
|
2240
|
+
// "fullLiquidationMarginUSD": "0.0000",
|
|
2241
|
+
// "initialMarginUSD": "0.0000",
|
|
2242
|
+
// "isBorrowing": "false",
|
|
2243
|
+
// "isConcentrationRiskEnabled": "true",
|
|
2244
|
+
// "isDefaulted": "false",
|
|
2245
|
+
// "isLending": "false",
|
|
2246
|
+
// "isPrimaryAccount": "true",
|
|
2247
|
+
// "liquidationMarginUSD": "0.0000",
|
|
2248
|
+
// "liquidityAddonUSD": "0.0000",
|
|
2249
|
+
// "makerFee": "0.00000000",
|
|
2250
|
+
// "marginProfile": {
|
|
2251
|
+
// "defaultedMarketRiskMultiplierPct": "50.00",
|
|
2252
|
+
// "fullLiquidationMarketRiskMultiplierPct": "75.00",
|
|
2253
|
+
// "initialMarketRiskMultiplierPct": "200.00",
|
|
2254
|
+
// "liquidationMarketRiskMultiplierPct": "100.00",
|
|
2255
|
+
// "warningMarketRiskMultiplierPct": "150.00"
|
|
2256
|
+
// },
|
|
2257
|
+
// "marketRiskUSD": "0.0000",
|
|
2258
|
+
// "maxInitialLeverage": "1",
|
|
2259
|
+
// "rateLimitToken": "7fc358f0bad4124528318ff415e24f1ad6e530321827162a5e35d8de8dcfc750",
|
|
2260
|
+
// "riskLimitUSD": "0.0000",
|
|
2261
|
+
// "takerFee": "0.00000002",
|
|
2262
|
+
// "totalBorrowedUSD": "0.0000",
|
|
2263
|
+
// "totalCollateralUSD": "0.0000",
|
|
2264
|
+
// "totalLiabilitiesUSD": "0.0000",
|
|
2265
|
+
// "tradeFeeRate": [
|
|
2266
|
+
// {
|
|
2267
|
+
// "feeGroupId": "1",
|
|
2268
|
+
// "makerFee": "0.00000000",
|
|
2269
|
+
// "takerFee": "0.00000000"
|
|
2270
|
+
// },
|
|
2271
|
+
// {
|
|
2272
|
+
// "feeGroupId": "2",
|
|
2273
|
+
// "makerFee": "0.00000000",
|
|
2274
|
+
// "takerFee": "0.00000000"
|
|
2275
|
+
// },
|
|
2276
|
+
// {
|
|
2277
|
+
// "feeGroupId": "3",
|
|
2278
|
+
// "makerFee": "0.00000000",
|
|
2279
|
+
// "takerFee": "0.00000000"
|
|
2280
|
+
// },
|
|
2281
|
+
// {
|
|
2282
|
+
// "feeGroupId": "4",
|
|
2283
|
+
// "makerFee": "0.00000000",
|
|
2284
|
+
// "takerFee": "0.00000000"
|
|
2285
|
+
// },
|
|
2286
|
+
// {
|
|
2287
|
+
// "feeGroupId": "5",
|
|
2288
|
+
// "makerFee": "0.00000000",
|
|
2289
|
+
// "takerFee": "0.00000000"
|
|
2290
|
+
// },
|
|
2291
|
+
// {
|
|
2292
|
+
// "feeGroupId": "6",
|
|
2293
|
+
// "makerFee": "0.00000000",
|
|
2294
|
+
// "takerFee": "0.00000000"
|
|
2295
|
+
// },
|
|
2296
|
+
// {
|
|
2297
|
+
// "feeGroupId": "7",
|
|
2298
|
+
// "makerFee": "0.00000000",
|
|
2299
|
+
// "takerFee": "0.00000000"
|
|
2300
|
+
// },
|
|
2301
|
+
// {
|
|
2302
|
+
// "feeGroupId": "8",
|
|
2303
|
+
// "makerFee": "0.00000000",
|
|
2304
|
+
// "takerFee": "0.00000000"
|
|
2305
|
+
// }
|
|
2306
|
+
// ],
|
|
2307
|
+
// "tradingAccountDescription": null,
|
|
2308
|
+
// "tradingAccountId": "111309424211255",
|
|
2309
|
+
// "tradingAccountName": "Primary Account",
|
|
2310
|
+
// "warningMarginUSD": "0.0000"
|
|
2311
|
+
// }
|
|
2312
|
+
// ]
|
|
2313
|
+
//
|
|
2314
|
+
return this.parseAccounts(response, params);
|
|
2315
|
+
}
|
|
2316
|
+
parseAccount(account) {
|
|
2317
|
+
return {
|
|
2318
|
+
'id': this.safeString(account, 'tradingAccountId'),
|
|
2319
|
+
'type': undefined,
|
|
2320
|
+
'code': undefined,
|
|
2321
|
+
'info': account,
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
/**
|
|
2325
|
+
* @method
|
|
2326
|
+
* @name bullish#fetchDepositAddress
|
|
2327
|
+
* @description fetch the deposit address for a currency associated with this account
|
|
2328
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/wallets/deposit-instructions/crypto/-symbol-
|
|
2329
|
+
* @param {string} code unified currency code
|
|
2330
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2331
|
+
* @param {string} [params.network] network for deposit address
|
|
2332
|
+
* @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
|
|
2333
|
+
*/
|
|
2334
|
+
async fetchDepositAddress(code, params = {}) {
|
|
2335
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2336
|
+
const currency = this.currency(code);
|
|
2337
|
+
const request = {
|
|
2338
|
+
'symbol': currency['id'],
|
|
2339
|
+
};
|
|
2340
|
+
const response = await this.privateGetV1WalletsDepositInstructionsCryptoSymbol(this.extend(request, params));
|
|
2341
|
+
//
|
|
2342
|
+
// [
|
|
2343
|
+
// {
|
|
2344
|
+
// "network": "ETH",
|
|
2345
|
+
// "address": "0xc2fc755082d052bb334763b144851a0031999f33",
|
|
2346
|
+
// "symbol": "ETH"
|
|
2347
|
+
// }
|
|
2348
|
+
// ]
|
|
2349
|
+
//
|
|
2350
|
+
const safeResponse = this.toArray(response);
|
|
2351
|
+
const length = safeResponse.length;
|
|
2352
|
+
let data = this.safeDict(safeResponse, 0, {});
|
|
2353
|
+
let network = undefined;
|
|
2354
|
+
[network, params] = this.handleNetworkCodeAndParams(params);
|
|
2355
|
+
const networkDefinedByUser = network !== undefined;
|
|
2356
|
+
if ((length > 1) || (networkDefinedByUser)) {
|
|
2357
|
+
// some currencies have multiple networks
|
|
2358
|
+
if (network === undefined) {
|
|
2359
|
+
// use default network if not specified and multiple are available
|
|
2360
|
+
network = this.defaultNetworkCode(code);
|
|
2361
|
+
}
|
|
2362
|
+
if (network !== undefined) {
|
|
2363
|
+
// find the entry that matches the network or return first entry if not found and user did not specify a network
|
|
2364
|
+
for (let i = 0; i < safeResponse.length; i++) {
|
|
2365
|
+
const entry = this.safeDict(safeResponse, i, {});
|
|
2366
|
+
const networkId = this.safeString(entry, 'network');
|
|
2367
|
+
const networkCode = this.networkIdToCode(networkId);
|
|
2368
|
+
if (network === networkCode) {
|
|
2369
|
+
data = entry;
|
|
2370
|
+
break;
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
if (networkDefinedByUser) {
|
|
2374
|
+
data = {}; // return an empty structure if the user-defined network was not found
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
return this.parseDepositAddress(data, currency);
|
|
2379
|
+
}
|
|
2380
|
+
parseDepositAddress(depositAddress, currency = undefined) {
|
|
2381
|
+
const id = this.safeString(depositAddress, 'symbol');
|
|
2382
|
+
const network = this.safeString(depositAddress, 'network');
|
|
2383
|
+
return {
|
|
2384
|
+
'info': depositAddress,
|
|
2385
|
+
'currency': this.safeCurrencyCode(id, currency),
|
|
2386
|
+
'network': this.networkIdToCode(network),
|
|
2387
|
+
'address': this.safeString(depositAddress, 'address'),
|
|
2388
|
+
'tag': undefined,
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
/**
|
|
2392
|
+
* @method
|
|
2393
|
+
* @name bullish#fetchBalance
|
|
2394
|
+
* @description query for balance and get the amount of funds available for trading or funds locked in orders
|
|
2395
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/accounts/asset
|
|
2396
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/accounts/asset/-symbol-
|
|
2397
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2398
|
+
* @param {string} params.tradingAccountId the trading account id (mandatory parameter)
|
|
2399
|
+
* @param {string} [params.code] unified currency code, default is undefined
|
|
2400
|
+
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
|
|
2401
|
+
*/
|
|
2402
|
+
async fetchBalance(params = {}) {
|
|
2403
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2404
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
2405
|
+
const request = {
|
|
2406
|
+
'tradingAccountId': tradingAccountId,
|
|
2407
|
+
};
|
|
2408
|
+
let response = undefined;
|
|
2409
|
+
const code = this.safeString(params, 'code');
|
|
2410
|
+
if (code !== undefined) {
|
|
2411
|
+
request['symbol'] = this.currency(code)['id'];
|
|
2412
|
+
response = await this.privateGetV1AccountsAssetSymbol(this.extend(request, params));
|
|
2413
|
+
return this.parseBalanceForSingleCurrency(response, code);
|
|
2414
|
+
}
|
|
2415
|
+
else {
|
|
2416
|
+
response = await this.privateGetV1AccountsAsset(this.extend(request, params));
|
|
2417
|
+
//
|
|
2418
|
+
// [
|
|
2419
|
+
// {
|
|
2420
|
+
// "assetId": "10",
|
|
2421
|
+
// "assetSymbol": "AAVE",
|
|
2422
|
+
// "availableQuantity": "10000000.00000000",
|
|
2423
|
+
// "borrowedQuantity": "0.00000000",
|
|
2424
|
+
// "loanedQuantity": "0.00000000",
|
|
2425
|
+
// "lockedQuantity": "0.00000000",
|
|
2426
|
+
// "publishedAtTimestamp": "1747942728870",
|
|
2427
|
+
// "tradingAccountId": "111309424211255",
|
|
2428
|
+
// "updatedAtDatetime": "2025-05-13T11:33:08.801Z",
|
|
2429
|
+
// "updatedAtTimestamp": "1747135988801"
|
|
2430
|
+
// }, ...
|
|
2431
|
+
// ]
|
|
2432
|
+
//
|
|
2433
|
+
return this.parseBalance(response);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
parseBalanceForSingleCurrency(response, code) {
|
|
2437
|
+
const result = { 'info': response };
|
|
2438
|
+
const account = this.account();
|
|
2439
|
+
account['free'] = this.safeString(response, 'availableQuantity');
|
|
2440
|
+
account['used'] = this.safeString(response, 'lockedQuantity');
|
|
2441
|
+
result[code] = account;
|
|
2442
|
+
return this.safeBalance(result);
|
|
2443
|
+
}
|
|
2444
|
+
parseBalance(response) {
|
|
2445
|
+
const result = {
|
|
2446
|
+
'info': response,
|
|
2447
|
+
};
|
|
2448
|
+
for (let i = 0; i < response.length; i++) {
|
|
2449
|
+
const balance = response[i];
|
|
2450
|
+
const symbol = this.safeString(balance, 'assetSymbol');
|
|
2451
|
+
const code = this.safeCurrencyCode(symbol);
|
|
2452
|
+
const account = this.account();
|
|
2453
|
+
account['total'] = this.safeString(balance, 'availableQuantity');
|
|
2454
|
+
account['used'] = this.safeString(balance, 'lockedQuantity');
|
|
2455
|
+
result[code] = account;
|
|
2456
|
+
}
|
|
2457
|
+
return this.safeBalance(result);
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* @method
|
|
2461
|
+
* @name bullish#fetchPositions
|
|
2462
|
+
* @description fetch all open positions
|
|
2463
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/derivatives-positions
|
|
2464
|
+
* @param {string[]|undefined} symbols list of unified market symbols
|
|
2465
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2466
|
+
* @param {string} params.tradingAccountId the trading account id
|
|
2467
|
+
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
|
|
2468
|
+
*/
|
|
2469
|
+
async fetchPositions(symbols = undefined, params = {}) {
|
|
2470
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2471
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
2472
|
+
const request = {
|
|
2473
|
+
'tradingAccountId': tradingAccountId,
|
|
2474
|
+
};
|
|
2475
|
+
const response = await this.privateGetV1DerivativesPositions(this.extend(request, params));
|
|
2476
|
+
//
|
|
2477
|
+
// [
|
|
2478
|
+
// {
|
|
2479
|
+
// "tradingAccountId": "111000000000001",
|
|
2480
|
+
// "symbol": "BTC-USDC-PERP",
|
|
2481
|
+
// "side": "BUY",
|
|
2482
|
+
// "quantity": "1.00000000",
|
|
2483
|
+
// "notional": "1.0000",
|
|
2484
|
+
// "entryNotional": "1.0000",
|
|
2485
|
+
// "mtmPnl": "1.0000",
|
|
2486
|
+
// "reportedMtmPnl": "1.0000",
|
|
2487
|
+
// "reportedFundingPnl": "1.0000",
|
|
2488
|
+
// "realizedPnl": "1.0000",
|
|
2489
|
+
// "settlementAssetSymbol": "USDC",
|
|
2490
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
2491
|
+
// "createdAtTimestamp": "1621490985000",
|
|
2492
|
+
// "updatedAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
2493
|
+
// "updatedAtTimestamp": "1621490985000"
|
|
2494
|
+
// }
|
|
2495
|
+
// ]
|
|
2496
|
+
//
|
|
2497
|
+
const results = this.parsePositions(response, symbols);
|
|
2498
|
+
return this.filterByArrayPositions(results, 'symbol', symbols, false);
|
|
2499
|
+
}
|
|
2500
|
+
parsePosition(position, market = undefined) {
|
|
2501
|
+
//
|
|
2502
|
+
// [
|
|
2503
|
+
// {
|
|
2504
|
+
// "tradingAccountId": "111000000000001",
|
|
2505
|
+
// "symbol": "BTC-USDC-PERP",
|
|
2506
|
+
// "side": "BUY",
|
|
2507
|
+
// "quantity": "1.00000000",
|
|
2508
|
+
// "notional": "1.0000",
|
|
2509
|
+
// "entryNotional": "1.0000",
|
|
2510
|
+
// "mtmPnl": "1.0000",
|
|
2511
|
+
// "reportedMtmPnl": "1.0000",
|
|
2512
|
+
// "reportedFundingPnl": "1.0000",
|
|
2513
|
+
// "realizedPnl": "1.0000",
|
|
2514
|
+
// "settlementAssetSymbol": "USDC",
|
|
2515
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
2516
|
+
// "createdAtTimestamp": "1621490985000",
|
|
2517
|
+
// "updatedAtDatetime": "2021-05-20T01:01:01.000Z",
|
|
2518
|
+
// "updatedAtTimestamp": "1621490985000"
|
|
2519
|
+
// }
|
|
2520
|
+
// ]
|
|
2521
|
+
//
|
|
2522
|
+
market = this.safeMarket(this.safeString(position, 'symbol'), market);
|
|
2523
|
+
const symbol = market['symbol'];
|
|
2524
|
+
const timestamp = this.safeInteger(position, 'createdAtTimestamp');
|
|
2525
|
+
const side = this.safeString(position, 'side');
|
|
2526
|
+
return this.safePosition({
|
|
2527
|
+
'info': position,
|
|
2528
|
+
'id': undefined,
|
|
2529
|
+
'symbol': symbol,
|
|
2530
|
+
'timestamp': timestamp,
|
|
2531
|
+
'datetime': this.iso8601(timestamp),
|
|
2532
|
+
'lastUpdateTimestamp': this.safeInteger(position, 'updatedAtTimestamp'),
|
|
2533
|
+
'hedged': undefined,
|
|
2534
|
+
'side': this.parsePositionSide(side),
|
|
2535
|
+
'contracts': this.safeNumber(position, 'quantity'),
|
|
2536
|
+
'contractSize': undefined,
|
|
2537
|
+
'entryPrice': undefined,
|
|
2538
|
+
'markPrice': undefined,
|
|
2539
|
+
'lastPrice': undefined,
|
|
2540
|
+
'notional': this.safeNumber(position, 'notional'),
|
|
2541
|
+
'leverage': undefined,
|
|
2542
|
+
'collateral': undefined,
|
|
2543
|
+
'initialMargin': undefined,
|
|
2544
|
+
'initialMarginPercentage': undefined,
|
|
2545
|
+
'maintenanceMargin': undefined,
|
|
2546
|
+
'maintenanceMarginPercentage': undefined,
|
|
2547
|
+
'unrealizedPnl': undefined,
|
|
2548
|
+
'liquidationPrice': undefined,
|
|
2549
|
+
'marginMode': undefined,
|
|
2550
|
+
'marginRatio': undefined,
|
|
2551
|
+
'percentage': undefined,
|
|
2552
|
+
'stopLossPrice': undefined,
|
|
2553
|
+
'takeProfitPrice': undefined,
|
|
2554
|
+
});
|
|
2555
|
+
}
|
|
2556
|
+
parsePositionSide(side) {
|
|
2557
|
+
const sides = {
|
|
2558
|
+
'BUY': 'long',
|
|
2559
|
+
'SELL': 'short',
|
|
2560
|
+
};
|
|
2561
|
+
return this.safeString(sides, side, side);
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* @method
|
|
2565
|
+
* @name bullish#fetchTransfers
|
|
2566
|
+
* @description fetch a history of internal transfers made on an account
|
|
2567
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/transfer
|
|
2568
|
+
* @param {string} code unified currency code of the currency transferred
|
|
2569
|
+
* @param {int} [since] the earliest time in ms to fetch transfers for
|
|
2570
|
+
* @param {int} [limit] the maximum number of transfer structures to retrieve
|
|
2571
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2572
|
+
* @param {int} params.until the latest time in ms to fetch transfers for (default time now)
|
|
2573
|
+
* @param {string} params.tradingAccountId the trading account id
|
|
2574
|
+
* @returns {object[]} a list of [transfer structures]{@link https://docs.ccxt.com/#/?id=transfer-structure}
|
|
2575
|
+
*/
|
|
2576
|
+
async fetchTransfers(code = undefined, since = undefined, limit = undefined, params = {}) {
|
|
2577
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2578
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
2579
|
+
const maxLimit = 100;
|
|
2580
|
+
let paginate = false;
|
|
2581
|
+
[paginate, params] = this.handleOptionAndParams(params, 'fetchTransfers', 'paginate');
|
|
2582
|
+
if (paginate) {
|
|
2583
|
+
params = this.handlePaginationParams('fetchTransfers', since, params);
|
|
2584
|
+
return await this.fetchPaginatedCallDynamic('fetchTransfers', code, since, limit, params, maxLimit);
|
|
2585
|
+
}
|
|
2586
|
+
const request = {
|
|
2587
|
+
'tradingAccountId': tradingAccountId,
|
|
2588
|
+
};
|
|
2589
|
+
let currency = undefined;
|
|
2590
|
+
if (code !== undefined) {
|
|
2591
|
+
currency = this.currency(code);
|
|
2592
|
+
request['assetSymbol'] = currency['id'];
|
|
2593
|
+
}
|
|
2594
|
+
const until = this.safeInteger(params, 'until');
|
|
2595
|
+
if ((since === undefined) && (until === undefined)) {
|
|
2596
|
+
// since and until are mandatory for this endpoint, set until to now if both are undefined
|
|
2597
|
+
const now = this.milliseconds();
|
|
2598
|
+
params = this.extend(params, { 'until': now });
|
|
2599
|
+
}
|
|
2600
|
+
params = this.handleSinceAndUntil(since, params);
|
|
2601
|
+
if (limit !== undefined) {
|
|
2602
|
+
request['_pageSize'] = this.getClosestLimit(limit);
|
|
2603
|
+
}
|
|
2604
|
+
const response = await this.privateGetV1HistoryTransfer(this.extend(request, params));
|
|
2605
|
+
//
|
|
2606
|
+
// [
|
|
2607
|
+
// {
|
|
2608
|
+
// "requestId": "1",
|
|
2609
|
+
// "toTradingAccountId": "111000000000001",
|
|
2610
|
+
// "fromTradingAccountId": "121000000000001",
|
|
2611
|
+
// "assetSymbol": "BTC",
|
|
2612
|
+
// "quantity": "1.00000000",
|
|
2613
|
+
// "status": "CLOSED",
|
|
2614
|
+
// "statusReasonCode": "6002",
|
|
2615
|
+
// "statusReason": "Executed",
|
|
2616
|
+
// "createdAtTimestamp": "1621490985000",
|
|
2617
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z"
|
|
2618
|
+
// }
|
|
2619
|
+
// ]
|
|
2620
|
+
//
|
|
2621
|
+
return this.parseTransfers(response, currency, since, limit);
|
|
2622
|
+
}
|
|
2623
|
+
/**
|
|
2624
|
+
* @method
|
|
2625
|
+
* @name bullish#transfer
|
|
2626
|
+
* @description transfer currency internally between wallets on the same account
|
|
2627
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#post-/v1/command-commandType-V1TransferAsset
|
|
2628
|
+
* @param {string} code unified currency codeåå
|
|
2629
|
+
* @param {float} amount amount to transfer
|
|
2630
|
+
* @param {string} fromAccount account ID to transfer from
|
|
2631
|
+
* @param {string} toAccount account ID to transfer to
|
|
2632
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2633
|
+
* @returns {object} a [transfer structure]{@link https://docs.ccxt.com/#/?id=transfer-structure}
|
|
2634
|
+
*/
|
|
2635
|
+
async transfer(code, amount, fromAccount, toAccount, params = {}) {
|
|
2636
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2637
|
+
// todo check this method properly
|
|
2638
|
+
const currency = this.currency(code);
|
|
2639
|
+
const request = {
|
|
2640
|
+
'commandType': 'V2TransferAsset',
|
|
2641
|
+
'assetSymbol': currency['id'],
|
|
2642
|
+
'quantity': this.currencyToPrecision(code, amount),
|
|
2643
|
+
'fromTradingAccountId': fromAccount,
|
|
2644
|
+
'toTradingAccountId': toAccount,
|
|
2645
|
+
};
|
|
2646
|
+
const response = await this.privatePostV2Command(this.extend(request, params));
|
|
2647
|
+
//
|
|
2648
|
+
// {
|
|
2649
|
+
// "message": "Command acknowledged - TransferAsset",
|
|
2650
|
+
// "requestId": "633909659774222336"
|
|
2651
|
+
// }
|
|
2652
|
+
//
|
|
2653
|
+
const transferOptions = this.safeDict(this.options, 'transfer', {});
|
|
2654
|
+
const fillResponseFromRequest = this.safeBool(transferOptions, 'fillResponseFromRequest', true);
|
|
2655
|
+
const transfer = this.parseTransfer(response, currency);
|
|
2656
|
+
if (fillResponseFromRequest) {
|
|
2657
|
+
transfer['fromAccount'] = fromAccount;
|
|
2658
|
+
transfer['toAccount'] = toAccount;
|
|
2659
|
+
transfer['amount'] = amount;
|
|
2660
|
+
transfer['currency'] = code;
|
|
2661
|
+
}
|
|
2662
|
+
return transfer;
|
|
2663
|
+
}
|
|
2664
|
+
parseTransfer(transfer, currency = undefined) {
|
|
2665
|
+
//
|
|
2666
|
+
// fetchTransfers
|
|
2667
|
+
// {
|
|
2668
|
+
// "requestId": "1",
|
|
2669
|
+
// "toTradingAccountId": "111000000000001",
|
|
2670
|
+
// "fromTradingAccountId": "121000000000001",
|
|
2671
|
+
// "assetSymbol": "BTC",
|
|
2672
|
+
// "quantity": "1.00000000",
|
|
2673
|
+
// "status": "CLOSED",
|
|
2674
|
+
// "statusReasonCode": "6002",
|
|
2675
|
+
// "statusReason": "Executed",
|
|
2676
|
+
// "createdAtTimestamp": "1621490985000",
|
|
2677
|
+
// "createdAtDatetime": "2021-05-20T01:01:01.000Z"
|
|
2678
|
+
// }
|
|
2679
|
+
//
|
|
2680
|
+
// transfer
|
|
2681
|
+
// {
|
|
2682
|
+
// "message": "Command acknowledged - TransferAsset",
|
|
2683
|
+
// "requestId": "633909659774222336"
|
|
2684
|
+
// }
|
|
2685
|
+
//
|
|
2686
|
+
const timestamp = this.safeInteger(transfer, 'createdAtTimestamp');
|
|
2687
|
+
const currencyId = this.safeString(transfer, 'assetSymbol');
|
|
2688
|
+
let status = this.safeString(transfer, 'status');
|
|
2689
|
+
if (status === undefined) {
|
|
2690
|
+
status = this.safeString(transfer, 'message');
|
|
2691
|
+
}
|
|
2692
|
+
return {
|
|
2693
|
+
'id': this.safeString(transfer, 'requestId'),
|
|
2694
|
+
'timestamp': timestamp,
|
|
2695
|
+
'datetime': this.iso8601(timestamp),
|
|
2696
|
+
'currency': this.safeCurrencyCode(currencyId, currency),
|
|
2697
|
+
'amount': this.safeNumber(transfer, 'quantity'),
|
|
2698
|
+
'fromAccount': this.safeString(transfer, 'fromTradingAccountId'),
|
|
2699
|
+
'toAccount': this.safeString(transfer, 'toTradingAccountId'),
|
|
2700
|
+
'status': this.parseTransferStatus(status),
|
|
2701
|
+
'info': transfer,
|
|
2702
|
+
};
|
|
2703
|
+
}
|
|
2704
|
+
parseTransferStatus(status) {
|
|
2705
|
+
const statuses = {
|
|
2706
|
+
'CLOSED': 'ok',
|
|
2707
|
+
'OPEN': 'pending',
|
|
2708
|
+
'REJECTED': 'failed',
|
|
2709
|
+
'Command acknowledged - TransferAsset': 'ok',
|
|
2710
|
+
};
|
|
2711
|
+
return this.safeString(statuses, status, status);
|
|
2712
|
+
}
|
|
2713
|
+
/**
|
|
2714
|
+
* @method
|
|
2715
|
+
* @name bullish#fetchBorrowRateHistory
|
|
2716
|
+
* @description retrieves a history of a currencies borrow interest rate at specific time slots
|
|
2717
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/borrow-interest
|
|
2718
|
+
* @param {string} code unified currency code
|
|
2719
|
+
* @param {int} [since] timestamp for the earliest borrow rate
|
|
2720
|
+
* @param {int} [limit] the maximum number of [borrow rate structures]{@link https://docs.ccxt.com/#/?id=borrow-rate-structure} to retrieve
|
|
2721
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2722
|
+
* @param {int} params.until the latest time in ms to fetch entries for
|
|
2723
|
+
* @param {string} params.tradingAccountId the trading account id
|
|
2724
|
+
* @returns {object[]} an array of [borrow rate structures]{@link https://docs.ccxt.com/#/?id=borrow-rate-structure}
|
|
2725
|
+
*/
|
|
2726
|
+
async fetchBorrowRateHistory(code, since = undefined, limit = undefined, params = {}) {
|
|
2727
|
+
await Promise.all([this.loadMarkets(), this.handleToken()]);
|
|
2728
|
+
const tradingAccountId = await this.loadAccount(params);
|
|
2729
|
+
const currency = this.currency(code);
|
|
2730
|
+
let request = {
|
|
2731
|
+
'assetSymbol': currency['id'],
|
|
2732
|
+
'tradingAccountId': tradingAccountId,
|
|
2733
|
+
};
|
|
2734
|
+
const now = this.milliseconds();
|
|
2735
|
+
let startTimestamp = since;
|
|
2736
|
+
[request, params] = this.handleUntilOption('createdAtDatetime[lte]', request, params);
|
|
2737
|
+
let until = this.safeInteger(request, 'createdAtDatetime[lte]');
|
|
2738
|
+
// current endpoint requires both since and until parameters
|
|
2739
|
+
if (startTimestamp === undefined) {
|
|
2740
|
+
startTimestamp = now - 1000 * 60 * 60 * 24 * 90; // Only the last 90 days of data is available for querying
|
|
2741
|
+
}
|
|
2742
|
+
if (until === undefined) {
|
|
2743
|
+
until = now;
|
|
2744
|
+
}
|
|
2745
|
+
request['createdAtDatetime[gte]'] = this.iso8601(startTimestamp);
|
|
2746
|
+
request['createdAtDatetime[lte]'] = this.iso8601(until);
|
|
2747
|
+
const response = await this.privateGetV1HistoryBorrowInterest(this.extend(request, params));
|
|
2748
|
+
//
|
|
2749
|
+
// [
|
|
2750
|
+
// {
|
|
2751
|
+
// "assetId": "1",
|
|
2752
|
+
// "assetSymbol": "BTC",
|
|
2753
|
+
// "borrowedQuantity": "1.00000000",
|
|
2754
|
+
// "totalBorrowedQuantity": "1.00000000",
|
|
2755
|
+
// "createdAtDatetime": "2020-08-21T08:00:00.000Z",
|
|
2756
|
+
// "createdAtTimestamp": "1621490985000"
|
|
2757
|
+
// }
|
|
2758
|
+
// ]
|
|
2759
|
+
//
|
|
2760
|
+
return this.parseBorrowRateHistory(response, code, since, limit);
|
|
2761
|
+
}
|
|
2762
|
+
parseBorrowRate(info, currency = undefined) {
|
|
2763
|
+
//
|
|
2764
|
+
// {
|
|
2765
|
+
// "assetId": "1",
|
|
2766
|
+
// "assetSymbol": "BTC",
|
|
2767
|
+
// "borrowedQuantity": "1.00000000",
|
|
2768
|
+
// "totalBorrowedQuantity": "1.00000000",
|
|
2769
|
+
// "createdAtDatetime": "2020-08-21T08:00:00.000Z",
|
|
2770
|
+
// "createdAtTimestamp": "1621490985000"
|
|
2771
|
+
// }
|
|
2772
|
+
//
|
|
2773
|
+
const timestamp = this.safeInteger(info, 'createdAtTimestamp');
|
|
2774
|
+
const currencyId = this.safeString(info, 'assetSymbol');
|
|
2775
|
+
return {
|
|
2776
|
+
'currency': this.safeCurrencyCode(currencyId, currency),
|
|
2777
|
+
'rate': this.safeNumber(info, 'borrowedQuantity'),
|
|
2778
|
+
'period': 86400000,
|
|
2779
|
+
'timestamp': timestamp,
|
|
2780
|
+
'datetime': this.iso8601(timestamp),
|
|
2781
|
+
'info': info,
|
|
2782
|
+
};
|
|
2783
|
+
}
|
|
2784
|
+
getTimestamp() {
|
|
2785
|
+
return this.milliseconds() - this.options['timeDifference'];
|
|
2786
|
+
}
|
|
2787
|
+
sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
|
2788
|
+
const request = this.omit(params, this.extractParams(path));
|
|
2789
|
+
const endpoint = '/' + this.implodeParams(path, params);
|
|
2790
|
+
let url = this.urls['api'][api] + endpoint;
|
|
2791
|
+
if (api === 'private') {
|
|
2792
|
+
this.checkRequiredCredentials();
|
|
2793
|
+
const nonce = this.microseconds().toString();
|
|
2794
|
+
const timestamp = this.getTimestamp().toString();
|
|
2795
|
+
if (method === 'GET') {
|
|
2796
|
+
const payload = timestamp + nonce + method + '/trading-api/' + path;
|
|
2797
|
+
const signature = this.hmac(this.encode(payload), this.encode(this.secret), sha256, 'hex');
|
|
2798
|
+
headers = {
|
|
2799
|
+
'BX-TIMESTAMP': timestamp,
|
|
2800
|
+
'BX-NONCE': nonce,
|
|
2801
|
+
'BX-SIGNATURE': signature,
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
else if (method === 'POST') {
|
|
2805
|
+
body = this.json(params);
|
|
2806
|
+
const payload = timestamp + nonce + method + '/trading-api/' + path + body;
|
|
2807
|
+
const digest = this.hash(this.encode(payload), sha256, 'hex');
|
|
2808
|
+
const signature = this.hmac(this.encode(digest), this.encode(this.secret), sha256, 'hex');
|
|
2809
|
+
headers = {
|
|
2810
|
+
'BX-TIMESTAMP': timestamp,
|
|
2811
|
+
'BX-NONCE': nonce,
|
|
2812
|
+
'BX-SIGNATURE': signature,
|
|
2813
|
+
'Content-Type': 'application/json',
|
|
2814
|
+
};
|
|
2815
|
+
headers['Content-Type'] = 'application/json';
|
|
2816
|
+
const rateLimitToken = this.safeString(request, 'rateLimitToken');
|
|
2817
|
+
if (rateLimitToken !== undefined) {
|
|
2818
|
+
headers['BX-RATE-LIMIT-TOKEN'] = rateLimitToken;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if (path === 'v1/users/hmac/login') {
|
|
2822
|
+
headers['BX-PUBLIC-KEY'] = this.apiKey;
|
|
2823
|
+
}
|
|
2824
|
+
else {
|
|
2825
|
+
const token = this.token;
|
|
2826
|
+
if ((token === undefined)) {
|
|
2827
|
+
throw new AuthenticationError(this.id + ' requires a token, please call signIn() first');
|
|
2828
|
+
}
|
|
2829
|
+
headers['Authorization'] = 'Bearer ' + token;
|
|
2830
|
+
// headers['BX-NONCE-WINDOW-ENABLED'] = 'false'; // default is false
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
if (method === 'GET') {
|
|
2834
|
+
const query = this.urlencode(request);
|
|
2835
|
+
if (query.length) {
|
|
2836
|
+
url += '?' + query;
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
|
2840
|
+
}
|
|
2841
|
+
/**
|
|
2842
|
+
* @method
|
|
2843
|
+
* @name bullish#signIn
|
|
2844
|
+
* @description sign in, must be called prior to using other authenticated methods
|
|
2845
|
+
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--add-authenticated-request-header
|
|
2846
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
2847
|
+
* @returns response from exchange
|
|
2848
|
+
*/
|
|
2849
|
+
async signIn(params = {}) {
|
|
2850
|
+
const response = await this.privateGetV1UsersHmacLogin(params);
|
|
2851
|
+
//
|
|
2852
|
+
// {
|
|
2853
|
+
// "authorizer": "113363EFA2CA00007368524E02000000",
|
|
2854
|
+
// "ownerAuthorizer": "113363EFA2CA00007368524E02000000",
|
|
2855
|
+
// "token": "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJiMXgtYXV0aC1zZXJ2aWNlIiwic3ViIjoiNDY0OTc4MzAiLCJleHAiOjE3NDczMzgzNDMsIlNUQUdFIjoiQVVUSEVOVElDQVRFRF9XSVRIX0JMT0NLQ0hBSU4ifQ.5FSyrihzc1wsJqAY8pVX36Y4ZXg3HopLJypPEbHg5bBK8FbL_oLxkj6zM_iOYL2a1x6-ICG0pQjr8hF_k8Yg-w"
|
|
2856
|
+
// }
|
|
2857
|
+
//
|
|
2858
|
+
const token = this.safeString(response, 'token');
|
|
2859
|
+
const authorizer = this.safeString(response, 'authorizer');
|
|
2860
|
+
this.options['authorizer'] = authorizer;
|
|
2861
|
+
this.token = token;
|
|
2862
|
+
this.options['tokenExpires'] = this.sum(this.milliseconds(), 1000 * 60 * 60 * 24); // token expires in 24 hours
|
|
2863
|
+
return token;
|
|
2864
|
+
}
|
|
2865
|
+
async handleToken(params = {}) {
|
|
2866
|
+
const now = this.milliseconds();
|
|
2867
|
+
const token = this.token;
|
|
2868
|
+
const tokenExpires = this.safeInteger(this.options, 'tokenExpires');
|
|
2869
|
+
if ((token === undefined) || (tokenExpires === undefined) || (now > tokenExpires)) {
|
|
2870
|
+
return await this.signIn();
|
|
2871
|
+
}
|
|
2872
|
+
else {
|
|
2873
|
+
return this.token;
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
handleErrors(httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
|
|
2877
|
+
if (response === undefined) {
|
|
2878
|
+
return undefined; // fallback to default error handler
|
|
2879
|
+
}
|
|
2880
|
+
//
|
|
2881
|
+
// {
|
|
2882
|
+
// "type": "HttpInvalidParameterException",
|
|
2883
|
+
// "message": "HTTP_INVALID_PARAMETER: '100m' is not a valid time bucket"
|
|
2884
|
+
// }
|
|
2885
|
+
//
|
|
2886
|
+
// {
|
|
2887
|
+
// "message": "Order size outside valid range",
|
|
2888
|
+
// "raw": null,
|
|
2889
|
+
// "errorCode": 6023,
|
|
2890
|
+
// "errorCodeName": "ORDER_SIZE_OUTSIDE_VALID_RANGE"
|
|
2891
|
+
// }
|
|
2892
|
+
//
|
|
2893
|
+
const code = this.safeString(response, 'errorCode');
|
|
2894
|
+
const type = this.safeString(response, 'type');
|
|
2895
|
+
if ((code !== undefined && code !== '0' && code !== '1001') || (type !== undefined && type === 'HttpInvalidParameterException')) {
|
|
2896
|
+
let message = '';
|
|
2897
|
+
const errorCodeName = this.safeString(response, 'errorCodeName');
|
|
2898
|
+
if (errorCodeName !== undefined) {
|
|
2899
|
+
message = errorCodeName;
|
|
2900
|
+
}
|
|
2901
|
+
else {
|
|
2902
|
+
message = type;
|
|
2903
|
+
}
|
|
2904
|
+
const feedback = this.id + ' ' + body;
|
|
2905
|
+
this.throwExactlyMatchedException(this.exceptions['exact'], message, feedback);
|
|
2906
|
+
this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
|
|
2907
|
+
this.throwExactlyMatchedException(this.exceptions['exact'], code, feedback);
|
|
2908
|
+
throw new ExchangeError(feedback); // unknown message
|
|
2909
|
+
}
|
|
2910
|
+
return undefined;
|
|
2911
|
+
}
|
|
2912
|
+
}
|