ccxt 4.2.25 → 4.2.27
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.js +2499 -348
- package/dist/ccxt.browser.min.js +7 -7
- package/dist/cjs/ccxt.js +4 -1
- package/dist/cjs/src/abstract/coinmetro.js +9 -0
- package/dist/cjs/src/base/Exchange.js +5 -4
- package/dist/cjs/src/base/ws/Client.js +4 -1
- package/dist/cjs/src/bingx.js +46 -6
- package/dist/cjs/src/coinbase.js +6 -1
- package/dist/cjs/src/coinmetro.js +1909 -0
- package/dist/cjs/src/pro/bingx.js +155 -1
- package/dist/cjs/src/upbit.js +21 -11
- package/js/ccxt.d.ts +5 -2
- package/js/ccxt.js +4 -2
- package/js/src/abstract/bingx.d.ts +1 -0
- package/js/src/abstract/coinmetro.d.ts +36 -0
- package/js/src/abstract/coinmetro.js +11 -0
- package/js/src/base/Exchange.js +5 -4
- package/js/src/base/ws/Client.js +4 -1
- package/js/src/bingx.js +46 -6
- package/js/src/coinbase.js +6 -1
- package/js/src/coinmetro.d.ts +79 -0
- package/js/src/coinmetro.js +1910 -0
- package/js/src/pro/bingx.d.ts +4 -1
- package/js/src/pro/bingx.js +155 -1
- package/js/src/upbit.d.ts +3 -3
- package/js/src/upbit.js +21 -11
- package/package.json +1 -1
- package/skip-tests.json +7 -0
|
@@ -0,0 +1,1910 @@
|
|
|
1
|
+
// ----------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
|
4
|
+
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
|
5
|
+
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
import Exchange from './abstract/coinmetro.js';
|
|
9
|
+
import { ArgumentsRequired, BadRequest, BadSymbol, InsufficientFunds, InvalidOrder, ExchangeError, OrderNotFound, PermissionDenied, RateLimitExceeded } from './base/errors.js';
|
|
10
|
+
import { DECIMAL_PLACES } from './base/functions/number.js';
|
|
11
|
+
import { Precise } from './base/Precise.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* @class coinmetro
|
|
15
|
+
* @augments Exchange
|
|
16
|
+
*/
|
|
17
|
+
export default class coinmetro extends Exchange {
|
|
18
|
+
describe() {
|
|
19
|
+
return this.deepExtend(super.describe(), {
|
|
20
|
+
'id': 'coinmetro',
|
|
21
|
+
'name': 'Coinmetro',
|
|
22
|
+
'countries': ['EE'],
|
|
23
|
+
'version': 'v1',
|
|
24
|
+
'rateLimit': 200,
|
|
25
|
+
'certified': false,
|
|
26
|
+
'pro': false,
|
|
27
|
+
'has': {
|
|
28
|
+
'CORS': undefined,
|
|
29
|
+
'spot': true,
|
|
30
|
+
'margin': true,
|
|
31
|
+
'swap': false,
|
|
32
|
+
'future': false,
|
|
33
|
+
'option': false,
|
|
34
|
+
'addMargin': false,
|
|
35
|
+
'borrowCrossMargin': true,
|
|
36
|
+
'borrowIsolatedMargin': false,
|
|
37
|
+
'cancelAllOrders': false,
|
|
38
|
+
'cancelOrder': true,
|
|
39
|
+
'cancelOrders': false,
|
|
40
|
+
'closeAllPositions': false,
|
|
41
|
+
'closePosition': true,
|
|
42
|
+
'createDepositAddress': false,
|
|
43
|
+
'createOrder': true,
|
|
44
|
+
'createPostOnlyOrder': false,
|
|
45
|
+
'createReduceOnlyOrder': false,
|
|
46
|
+
'createStopLimitOrder': true,
|
|
47
|
+
'createStopMarketOrder': true,
|
|
48
|
+
'createStopOrder': true,
|
|
49
|
+
'deposit': false,
|
|
50
|
+
'editOrder': false,
|
|
51
|
+
'fetchAccounts': false,
|
|
52
|
+
'fetchBalance': true,
|
|
53
|
+
'fetchBidsAsks': true,
|
|
54
|
+
'fetchBorrowInterest': false,
|
|
55
|
+
'fetchBorrowRateHistories': false,
|
|
56
|
+
'fetchBorrowRateHistory': false,
|
|
57
|
+
'fetchCanceledAndClosedOrders': true,
|
|
58
|
+
'fetchCanceledOrders': false,
|
|
59
|
+
'fetchClosedOrder': false,
|
|
60
|
+
'fetchClosedOrders': false,
|
|
61
|
+
'fetchCrossBorrowRate': false,
|
|
62
|
+
'fetchCrossBorrowRates': false,
|
|
63
|
+
'fetchCurrencies': true,
|
|
64
|
+
'fetchDeposit': false,
|
|
65
|
+
'fetchDepositAddress': false,
|
|
66
|
+
'fetchDepositAddresses': false,
|
|
67
|
+
'fetchDepositAddressesByNetwork': false,
|
|
68
|
+
'fetchDeposits': false,
|
|
69
|
+
'fetchDepositsWithdrawals': false,
|
|
70
|
+
'fetchDepositWithdrawFee': false,
|
|
71
|
+
'fetchDepositWithdrawFees': false,
|
|
72
|
+
'fetchFundingHistory': false,
|
|
73
|
+
'fetchFundingRate': false,
|
|
74
|
+
'fetchFundingRateHistory': false,
|
|
75
|
+
'fetchFundingRates': false,
|
|
76
|
+
'fetchIndexOHLCV': false,
|
|
77
|
+
'fetchIsolatedBorrowRate': false,
|
|
78
|
+
'fetchIsolatedBorrowRates': false,
|
|
79
|
+
'fetchL3OrderBook': false,
|
|
80
|
+
'fetchLedger': true,
|
|
81
|
+
'fetchLeverage': false,
|
|
82
|
+
'fetchLeverageTiers': false,
|
|
83
|
+
'fetchMarketLeverageTiers': false,
|
|
84
|
+
'fetchMarkets': true,
|
|
85
|
+
'fetchMarkOHLCV': false,
|
|
86
|
+
'fetchMyTrades': true,
|
|
87
|
+
'fetchOHLCV': true,
|
|
88
|
+
'fetchOpenInterestHistory': false,
|
|
89
|
+
'fetchOpenOrder': false,
|
|
90
|
+
'fetchOpenOrders': true,
|
|
91
|
+
'fetchOrder': true,
|
|
92
|
+
'fetchOrderBook': true,
|
|
93
|
+
'fetchOrderBooks': false,
|
|
94
|
+
'fetchOrders': false,
|
|
95
|
+
'fetchOrderTrades': false,
|
|
96
|
+
'fetchPosition': false,
|
|
97
|
+
'fetchPositions': false,
|
|
98
|
+
'fetchPositionsRisk': false,
|
|
99
|
+
'fetchPremiumIndexOHLCV': false,
|
|
100
|
+
'fetchStatus': false,
|
|
101
|
+
'fetchTicker': false,
|
|
102
|
+
'fetchTickers': true,
|
|
103
|
+
'fetchTime': false,
|
|
104
|
+
'fetchTrades': true,
|
|
105
|
+
'fetchTradingFee': false,
|
|
106
|
+
'fetchTradingFees': false,
|
|
107
|
+
'fetchTradingLimits': false,
|
|
108
|
+
'fetchTransactionFee': false,
|
|
109
|
+
'fetchTransactionFees': false,
|
|
110
|
+
'fetchTransactions': false,
|
|
111
|
+
'fetchTransfers': false,
|
|
112
|
+
'fetchWithdrawal': false,
|
|
113
|
+
'fetchWithdrawals': false,
|
|
114
|
+
'fetchWithdrawalWhitelist': false,
|
|
115
|
+
'reduceMargin': false,
|
|
116
|
+
'repayCrossMargin': false,
|
|
117
|
+
'repayIsolatedMargin': false,
|
|
118
|
+
'setLeverage': false,
|
|
119
|
+
'setMargin': false,
|
|
120
|
+
'setMarginMode': false,
|
|
121
|
+
'setPositionMode': false,
|
|
122
|
+
'signIn': false,
|
|
123
|
+
'transfer': false,
|
|
124
|
+
'withdraw': false,
|
|
125
|
+
'ws': false,
|
|
126
|
+
},
|
|
127
|
+
'timeframes': {
|
|
128
|
+
'1m': '60000',
|
|
129
|
+
'5m': '300000',
|
|
130
|
+
'30m': '1800000',
|
|
131
|
+
'4h': '14400000',
|
|
132
|
+
'1d': '86400000',
|
|
133
|
+
},
|
|
134
|
+
'urls': {
|
|
135
|
+
'logo': 'https://github.com/ccxt/ccxt/assets/43336371/e86f87ec-6ba3-4410-962b-f7988c5db539',
|
|
136
|
+
'api': {
|
|
137
|
+
'public': 'https://api.coinmetro.com',
|
|
138
|
+
'private': 'https://api.coinmetro.com',
|
|
139
|
+
},
|
|
140
|
+
'test': {
|
|
141
|
+
'public': 'https://api.coinmetro.com',
|
|
142
|
+
'private': 'https://api.coinmetro.com/open',
|
|
143
|
+
},
|
|
144
|
+
'www': 'https://coinmetro.com/',
|
|
145
|
+
'doc': [
|
|
146
|
+
'https://documenter.getpostman.com/view/3653795/SVfWN6KS',
|
|
147
|
+
],
|
|
148
|
+
'fees': 'https://help.coinmetro.com/hc/en-gb/articles/6844007317789-What-are-the-fees-on-Coinmetro-',
|
|
149
|
+
'referral': 'https://go.coinmetro.com/?ref=crypto24',
|
|
150
|
+
},
|
|
151
|
+
'api': {
|
|
152
|
+
'public': {
|
|
153
|
+
'get': {
|
|
154
|
+
'demo/temp': 1,
|
|
155
|
+
'exchange/candles/{pair}/{timeframe}/{from}/{to}': 3,
|
|
156
|
+
'exchange/prices': 1,
|
|
157
|
+
'exchange/ticks/{pair}/{from}': 3,
|
|
158
|
+
'assets': 1,
|
|
159
|
+
'markets': 1,
|
|
160
|
+
'exchange/book/{pair}': 3,
|
|
161
|
+
'exchange/bookUpdates/{pair}/{from}': 1, // not unified
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
'private': {
|
|
165
|
+
'get': {
|
|
166
|
+
'users/balances': 1,
|
|
167
|
+
'users/wallets/history/{since}': 1.67,
|
|
168
|
+
'exchange/orders/status/{orderID}': 1,
|
|
169
|
+
'exchange/orders/active': 1,
|
|
170
|
+
'exchange/orders/history/{since}': 1.67,
|
|
171
|
+
'exchange/fills/{since}': 1.67,
|
|
172
|
+
'exchange/margin': 1, // not unified
|
|
173
|
+
},
|
|
174
|
+
'post': {
|
|
175
|
+
'jwt': 1,
|
|
176
|
+
'jwtDevice': 1,
|
|
177
|
+
'devices': 1,
|
|
178
|
+
'jwt-read-only': 1,
|
|
179
|
+
'exchange/orders/create': 1,
|
|
180
|
+
'exchange/orders/modify/{orderID}': 1,
|
|
181
|
+
'exchange/swap': 1,
|
|
182
|
+
'exchange/swap/confirm/{swapId}': 1,
|
|
183
|
+
'exchange/orders/close/{orderID}': 1,
|
|
184
|
+
'exchange/orders/hedge': 1, // not unified
|
|
185
|
+
},
|
|
186
|
+
'put': {
|
|
187
|
+
'jwt': 1,
|
|
188
|
+
'exchange/orders/cancel/{orderID}': 1,
|
|
189
|
+
'users/margin/collateral': 1,
|
|
190
|
+
'users/margin/primary/{currency}': 1, // not unified
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
'requiredCredentials': {
|
|
195
|
+
'apiKey': false,
|
|
196
|
+
'secret': false,
|
|
197
|
+
'uid': true,
|
|
198
|
+
'token': true,
|
|
199
|
+
},
|
|
200
|
+
'fees': {
|
|
201
|
+
'trading': {
|
|
202
|
+
'feeSide': 'get',
|
|
203
|
+
'tierBased': false,
|
|
204
|
+
'percentage': true,
|
|
205
|
+
'taker': this.parseNumber('0.001'),
|
|
206
|
+
'maker': this.parseNumber('0'),
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
'precisionMode': DECIMAL_PLACES,
|
|
210
|
+
// exchange-specific options
|
|
211
|
+
'options': {
|
|
212
|
+
'currenciesByIdForParseMarket': undefined,
|
|
213
|
+
'currencyIdsListForParseMarket': undefined,
|
|
214
|
+
},
|
|
215
|
+
'exceptions': {
|
|
216
|
+
// https://trade-docs.coinmetro.co/?javascript--nodejs#message-codes
|
|
217
|
+
'exact': {
|
|
218
|
+
'Both buyingCurrency and sellingCurrency are required': InvalidOrder,
|
|
219
|
+
'One and only one of buyingQty and sellingQty is required': InvalidOrder,
|
|
220
|
+
'Invalid buyingCurrency': InvalidOrder,
|
|
221
|
+
'Invalid \'from\'': BadRequest,
|
|
222
|
+
'Invalid sellingCurrency': InvalidOrder,
|
|
223
|
+
'Invalid buyingQty': InvalidOrder,
|
|
224
|
+
'Invalid sellingQty': InvalidOrder,
|
|
225
|
+
'Insufficient balance': InsufficientFunds,
|
|
226
|
+
'Expiration date is in the past or too near in the future': InvalidOrder,
|
|
227
|
+
'Forbidden': PermissionDenied,
|
|
228
|
+
'Order Not Found': OrderNotFound,
|
|
229
|
+
'since must be a millisecond timestamp': BadRequest,
|
|
230
|
+
'This pair is disabled on margin': BadSymbol, // 422 Unprocessable Entity {"message":"This pair is disabled on margin"}
|
|
231
|
+
},
|
|
232
|
+
'broad': {
|
|
233
|
+
'accessing from a new IP': PermissionDenied,
|
|
234
|
+
'available to allocate as collateral': InsufficientFunds,
|
|
235
|
+
'At least': BadRequest,
|
|
236
|
+
'collateral is not allowed': BadRequest,
|
|
237
|
+
'Insufficient liquidity': InvalidOrder,
|
|
238
|
+
'Insufficient order size': InvalidOrder,
|
|
239
|
+
'Invalid quantity': InvalidOrder,
|
|
240
|
+
'Invalid Stop Loss': InvalidOrder,
|
|
241
|
+
'Invalid stop price!': InvalidOrder,
|
|
242
|
+
'Not enough balance': InsufficientFunds,
|
|
243
|
+
'Not enough margin': InsufficientFunds,
|
|
244
|
+
'orderType missing': BadRequest,
|
|
245
|
+
'Server Timeout': ExchangeError,
|
|
246
|
+
'Time in force has to be IOC or FOK for market orders': InvalidOrder,
|
|
247
|
+
'Too many attempts': RateLimitExceeded, // 429 Too Many Requests {"message":"Too many attempts. Try again in 3 seconds"}
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
async fetchCurrencies(params = {}) {
|
|
253
|
+
/**
|
|
254
|
+
* @method
|
|
255
|
+
* @name coinmetro#fetchCurrencies
|
|
256
|
+
* @description fetches all available currencies on an exchange
|
|
257
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#d5876d43-a3fe-4479-8c58-24d0f044edfb
|
|
258
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
259
|
+
* @returns {object} an associative dictionary of currencies
|
|
260
|
+
*/
|
|
261
|
+
const response = await this.publicGetAssets(params);
|
|
262
|
+
//
|
|
263
|
+
// [
|
|
264
|
+
// {
|
|
265
|
+
// "symbol": "BTC",
|
|
266
|
+
// "name": "Bitcoin",
|
|
267
|
+
// "color": "#FFA500",
|
|
268
|
+
// "type": "coin",
|
|
269
|
+
// "canDeposit": true,
|
|
270
|
+
// "canWithdraw": true,
|
|
271
|
+
// "canTrade": true,
|
|
272
|
+
// "notabeneDecimals": 8,
|
|
273
|
+
// "canMarket": true,
|
|
274
|
+
// "maxSwap": 10000,
|
|
275
|
+
// "digits": 6,
|
|
276
|
+
// "multiplier": 1000000,
|
|
277
|
+
// "bookDigits": 8,
|
|
278
|
+
// "bookMultiplier": 100000000,
|
|
279
|
+
// "sentimentData": {
|
|
280
|
+
// "sentiment": 51.59555555555555,
|
|
281
|
+
// "interest": 1.127511216044664
|
|
282
|
+
// },
|
|
283
|
+
// "minQty": 0.0001
|
|
284
|
+
// },
|
|
285
|
+
// {
|
|
286
|
+
// "symbol": "EUR",
|
|
287
|
+
// "name": "Euro",
|
|
288
|
+
// "color": "#1246FF",
|
|
289
|
+
// "type": "fiat",
|
|
290
|
+
// "canDeposit": true,
|
|
291
|
+
// "canWithdraw": true,
|
|
292
|
+
// "canTrade": true,
|
|
293
|
+
// "canMarket": true,
|
|
294
|
+
// "maxSwap": 10000,
|
|
295
|
+
// "digits": 2,
|
|
296
|
+
// "multiplier": 100,
|
|
297
|
+
// "bookDigits": 3,
|
|
298
|
+
// "bookMultiplier": 1000,
|
|
299
|
+
// "minQty": 5
|
|
300
|
+
// }
|
|
301
|
+
// ...
|
|
302
|
+
// ]
|
|
303
|
+
//
|
|
304
|
+
const result = {};
|
|
305
|
+
for (let i = 0; i < response.length; i++) {
|
|
306
|
+
const currency = response[i];
|
|
307
|
+
const id = this.safeString(currency, 'symbol');
|
|
308
|
+
const code = this.safeCurrencyCode(id);
|
|
309
|
+
const withdraw = this.safeValue(currency, 'canWithdraw');
|
|
310
|
+
const deposit = this.safeValue(currency, 'canDeposit');
|
|
311
|
+
const canTrade = this.safeValue(currency, 'canTrade');
|
|
312
|
+
const active = canTrade ? withdraw : true;
|
|
313
|
+
const precision = this.safeInteger(currency, 'digits');
|
|
314
|
+
const minAmount = this.safeNumber(currency, 'minQty');
|
|
315
|
+
result[code] = this.safeCurrencyStructure({
|
|
316
|
+
'id': id,
|
|
317
|
+
'code': code,
|
|
318
|
+
'name': code,
|
|
319
|
+
'info': currency,
|
|
320
|
+
'active': active,
|
|
321
|
+
'deposit': deposit,
|
|
322
|
+
'withdraw': withdraw,
|
|
323
|
+
'fee': undefined,
|
|
324
|
+
'precision': precision,
|
|
325
|
+
'limits': {
|
|
326
|
+
'amount': { 'min': minAmount, 'max': undefined },
|
|
327
|
+
'withdraw': { 'min': undefined, 'max': undefined },
|
|
328
|
+
},
|
|
329
|
+
'networks': {},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
if (this.safeValue(this.options, 'currenciesByIdForParseMarket') === undefined) {
|
|
333
|
+
const currenciesById = this.indexBy(result, 'id');
|
|
334
|
+
this.options['currenciesByIdForParseMarket'] = currenciesById;
|
|
335
|
+
this.options['currencyIdsListForParseMarket'] = Object.keys(currenciesById);
|
|
336
|
+
}
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
async fetchMarkets(params = {}) {
|
|
340
|
+
/**
|
|
341
|
+
* @method
|
|
342
|
+
* @name coinmetro#fetchMarkets
|
|
343
|
+
* @description retrieves data on all markets for coinmetro
|
|
344
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#9fd18008-338e-4863-b07d-722878a46832
|
|
345
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
346
|
+
* @returns {object[]} an array of objects representing market data
|
|
347
|
+
*/
|
|
348
|
+
const response = await this.publicGetMarkets(params);
|
|
349
|
+
if (this.safeValue(this.options, 'currenciesByIdForParseMarket') === undefined) {
|
|
350
|
+
await this.fetchCurrencies();
|
|
351
|
+
}
|
|
352
|
+
//
|
|
353
|
+
// [
|
|
354
|
+
// {
|
|
355
|
+
// "pair": "PERPEUR",
|
|
356
|
+
// "precision": 5,
|
|
357
|
+
// "margin": false
|
|
358
|
+
// },
|
|
359
|
+
// {
|
|
360
|
+
// "pair": "PERPUSD",
|
|
361
|
+
// "precision": 5,
|
|
362
|
+
// "margin": false
|
|
363
|
+
// },
|
|
364
|
+
// {
|
|
365
|
+
// "pair": "YFIEUR",
|
|
366
|
+
// "precision": 5,
|
|
367
|
+
// "margin": false
|
|
368
|
+
// },
|
|
369
|
+
// ...
|
|
370
|
+
// ]
|
|
371
|
+
//
|
|
372
|
+
return this.parseMarkets(response);
|
|
373
|
+
}
|
|
374
|
+
parseMarket(market) {
|
|
375
|
+
const id = this.safeString(market, 'pair');
|
|
376
|
+
const parsedMarketId = this.parseMarketId(id);
|
|
377
|
+
const baseId = this.safeString(parsedMarketId, 'baseId');
|
|
378
|
+
const quoteId = this.safeString(parsedMarketId, 'quoteId');
|
|
379
|
+
const base = this.safeCurrencyCode(baseId);
|
|
380
|
+
const quote = this.safeCurrencyCode(quoteId);
|
|
381
|
+
const basePrecisionAndLimits = this.parseMarketPrecisionAndLimits(baseId);
|
|
382
|
+
const quotePrecisionAndLimits = this.parseMarketPrecisionAndLimits(quoteId);
|
|
383
|
+
const margin = this.safeValue(market, 'margin', false);
|
|
384
|
+
const tradingFees = this.safeValue(this.fees, 'trading', {});
|
|
385
|
+
return this.safeMarketStructure({
|
|
386
|
+
'id': id,
|
|
387
|
+
'symbol': base + '/' + quote,
|
|
388
|
+
'base': base,
|
|
389
|
+
'quote': quote,
|
|
390
|
+
'settle': undefined,
|
|
391
|
+
'baseId': baseId,
|
|
392
|
+
'quoteId': quoteId,
|
|
393
|
+
'settleId': undefined,
|
|
394
|
+
'type': 'spot',
|
|
395
|
+
'spot': true,
|
|
396
|
+
'margin': margin,
|
|
397
|
+
'swap': false,
|
|
398
|
+
'future': false,
|
|
399
|
+
'option': false,
|
|
400
|
+
'active': true,
|
|
401
|
+
'contract': false,
|
|
402
|
+
'linear': undefined,
|
|
403
|
+
'inverse': undefined,
|
|
404
|
+
'taker': this.safeNumber(tradingFees, 'taker'),
|
|
405
|
+
'maker': this.safeNumber(tradingFees, 'maker'),
|
|
406
|
+
'contractSize': undefined,
|
|
407
|
+
'expiry': undefined,
|
|
408
|
+
'expiryDatetime': undefined,
|
|
409
|
+
'strike': undefined,
|
|
410
|
+
'optionType': undefined,
|
|
411
|
+
'precision': {
|
|
412
|
+
'amount': basePrecisionAndLimits['precision'],
|
|
413
|
+
'price': quotePrecisionAndLimits['precision'],
|
|
414
|
+
'base': basePrecisionAndLimits['precision'],
|
|
415
|
+
'quote': quotePrecisionAndLimits['precision'],
|
|
416
|
+
},
|
|
417
|
+
'limits': {
|
|
418
|
+
'leverage': {
|
|
419
|
+
'min': undefined,
|
|
420
|
+
'max': undefined,
|
|
421
|
+
},
|
|
422
|
+
'amount': {
|
|
423
|
+
'min': basePrecisionAndLimits['minLimit'],
|
|
424
|
+
'max': undefined,
|
|
425
|
+
},
|
|
426
|
+
'price': {
|
|
427
|
+
'min': undefined,
|
|
428
|
+
'max': undefined,
|
|
429
|
+
},
|
|
430
|
+
'cost': {
|
|
431
|
+
'min': quotePrecisionAndLimits['minLimit'],
|
|
432
|
+
'max': undefined,
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
'created': undefined,
|
|
436
|
+
'info': market,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
parseMarketId(marketId) {
|
|
440
|
+
let baseId = undefined;
|
|
441
|
+
let quoteId = undefined;
|
|
442
|
+
const currencyIds = this.safeValue(this.options, 'currencyIdsListForParseMarket', []);
|
|
443
|
+
for (let i = 0; i < currencyIds.length; i++) {
|
|
444
|
+
const currencyId = currencyIds[i];
|
|
445
|
+
const entryIndex = marketId.indexOf(currencyId);
|
|
446
|
+
if (entryIndex !== -1) {
|
|
447
|
+
const restId = marketId.replace(currencyId, '');
|
|
448
|
+
if (this.inArray(restId, currencyIds)) {
|
|
449
|
+
if (entryIndex === 0) {
|
|
450
|
+
baseId = currencyId;
|
|
451
|
+
quoteId = restId;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
baseId = restId;
|
|
455
|
+
quoteId = currencyId;
|
|
456
|
+
}
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const result = {
|
|
462
|
+
'baseId': baseId,
|
|
463
|
+
'quoteId': quoteId,
|
|
464
|
+
};
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
parseMarketPrecisionAndLimits(currencyId) {
|
|
468
|
+
const currencies = this.safeValue(this.options, 'currenciesByIdForParseMarket', {});
|
|
469
|
+
const currency = this.safeValue(currencies, currencyId, {});
|
|
470
|
+
const precision = this.safeInteger(currency, 'precision');
|
|
471
|
+
const limits = this.safeValue(currency, 'limits', {});
|
|
472
|
+
const amountLimits = this.safeValue(limits, 'amount', {});
|
|
473
|
+
const minLimit = this.safeNumber(amountLimits, 'min');
|
|
474
|
+
const result = {
|
|
475
|
+
'precision': precision,
|
|
476
|
+
'minLimit': minLimit,
|
|
477
|
+
};
|
|
478
|
+
return result;
|
|
479
|
+
}
|
|
480
|
+
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
|
481
|
+
/**
|
|
482
|
+
* @method
|
|
483
|
+
* @name coinmetro#fetchOHLCV
|
|
484
|
+
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
485
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#13cfb5bc-7bfb-4847-85e1-e0f35dfb3573
|
|
486
|
+
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
|
|
487
|
+
* @param {string} timeframe the length of time each candle represents
|
|
488
|
+
* @param {int} [since] timestamp in ms of the earliest candle to fetch
|
|
489
|
+
* @param {int} [limit] the maximum amount of candles to fetch
|
|
490
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
491
|
+
* @param {int} [params.until] the latest time in ms to fetch entries for
|
|
492
|
+
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
|
|
493
|
+
*/
|
|
494
|
+
await this.loadMarkets();
|
|
495
|
+
const market = this.market(symbol);
|
|
496
|
+
const request = {
|
|
497
|
+
'pair': market['id'],
|
|
498
|
+
'timeframe': this.safeString(this.timeframes, timeframe, timeframe),
|
|
499
|
+
};
|
|
500
|
+
let until = undefined;
|
|
501
|
+
if (since !== undefined) {
|
|
502
|
+
request['from'] = since;
|
|
503
|
+
if (limit !== undefined) {
|
|
504
|
+
const duration = this.parseTimeframe(timeframe) * 1000;
|
|
505
|
+
until = this.sum(since, duration * (limit));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
request['from'] = ':from'; // this endpoint doesn't accept empty from and to params (setting them into the value described in the documentation)
|
|
510
|
+
}
|
|
511
|
+
until = this.safeInteger2(params, 'till', 'until', until);
|
|
512
|
+
if (until !== undefined) {
|
|
513
|
+
params = this.omit(params, ['till', 'until']);
|
|
514
|
+
request['to'] = until;
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
request['to'] = ':to';
|
|
518
|
+
}
|
|
519
|
+
const response = await this.publicGetExchangeCandlesPairTimeframeFromTo(this.extend(request, params));
|
|
520
|
+
//
|
|
521
|
+
// {
|
|
522
|
+
// "candleHistory": [
|
|
523
|
+
// {
|
|
524
|
+
// "pair": "ETHUSDT",
|
|
525
|
+
// "timeframe": 86400000,
|
|
526
|
+
// "timestamp": 1697673600000,
|
|
527
|
+
// "c": 1567.4409353098604,
|
|
528
|
+
// "h": 1566.7514068472303,
|
|
529
|
+
// "l": 1549.4563666936847,
|
|
530
|
+
// "o": 1563.4490341395904,
|
|
531
|
+
// "v": 0
|
|
532
|
+
// },
|
|
533
|
+
// {
|
|
534
|
+
// "pair": "ETHUSDT",
|
|
535
|
+
// "timeframe": 86400000,
|
|
536
|
+
// "timestamp": 1697760000000,
|
|
537
|
+
// "c": 1603.7831363339324,
|
|
538
|
+
// "h": 1625.0356823666407,
|
|
539
|
+
// "l": 1565.4629390011505,
|
|
540
|
+
// "o": 1566.8387619426028,
|
|
541
|
+
// "v": 0
|
|
542
|
+
// },
|
|
543
|
+
// ...
|
|
544
|
+
// ]
|
|
545
|
+
// }
|
|
546
|
+
//
|
|
547
|
+
const candleHistory = this.safeValue(response, 'candleHistory', []);
|
|
548
|
+
return this.parseOHLCVs(candleHistory, market, timeframe, since, limit);
|
|
549
|
+
}
|
|
550
|
+
parseOHLCV(ohlcv, market = undefined) {
|
|
551
|
+
return [
|
|
552
|
+
this.safeInteger(ohlcv, 'timestamp'),
|
|
553
|
+
this.safeNumber(ohlcv, 'o'),
|
|
554
|
+
this.safeNumber(ohlcv, 'h'),
|
|
555
|
+
this.safeNumber(ohlcv, 'l'),
|
|
556
|
+
this.safeNumber(ohlcv, 'c'),
|
|
557
|
+
this.safeNumber(ohlcv, 'v'),
|
|
558
|
+
];
|
|
559
|
+
}
|
|
560
|
+
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
|
|
561
|
+
/**
|
|
562
|
+
* @method
|
|
563
|
+
* @name coinmetro#fetchTrades
|
|
564
|
+
* @description get the list of most recent trades for a particular symbol
|
|
565
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#6ee5d698-06da-4570-8c84-914185e05065
|
|
566
|
+
* @param {string} symbol unified symbol of the market to fetch trades for
|
|
567
|
+
* @param {int} [since] timestamp in ms of the earliest trade to fetch
|
|
568
|
+
* @param {int} [limit] the maximum amount of trades to fetch (default 200, max 500)
|
|
569
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
570
|
+
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
|
|
571
|
+
*/
|
|
572
|
+
await this.loadMarkets();
|
|
573
|
+
const market = this.market(symbol);
|
|
574
|
+
const request = {
|
|
575
|
+
'pair': market['id'],
|
|
576
|
+
};
|
|
577
|
+
if (since !== undefined) {
|
|
578
|
+
request['from'] = since;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
// this endpoint accepts empty from param
|
|
582
|
+
request['from'] = '';
|
|
583
|
+
}
|
|
584
|
+
const response = await this.publicGetExchangeTicksPairFrom(this.extend(request, params));
|
|
585
|
+
//
|
|
586
|
+
// {
|
|
587
|
+
// "tickHistory": [
|
|
588
|
+
// {
|
|
589
|
+
// "pair": "ETHUSDT",
|
|
590
|
+
// "price": 2077.5623,
|
|
591
|
+
// "qty": 0.002888,
|
|
592
|
+
// "timestamp": 1700684689420,
|
|
593
|
+
// "seqNum": 10644554718
|
|
594
|
+
// },
|
|
595
|
+
// {
|
|
596
|
+
// "pair": "ETHUSDT",
|
|
597
|
+
// "price": 2078.3848,
|
|
598
|
+
// "qty": 0.003368,
|
|
599
|
+
// "timestamp": 1700684738410,
|
|
600
|
+
// "seqNum": 10644559561
|
|
601
|
+
// },
|
|
602
|
+
// {
|
|
603
|
+
// "pair": "ETHUSDT",
|
|
604
|
+
// "price": 2077.1513,
|
|
605
|
+
// "qty": 0.00337,
|
|
606
|
+
// "timestamp": 1700684816853,
|
|
607
|
+
// "seqNum": 10644567113
|
|
608
|
+
// },
|
|
609
|
+
// ...
|
|
610
|
+
// ]
|
|
611
|
+
// }
|
|
612
|
+
//
|
|
613
|
+
const tickHistory = this.safeValue(response, 'tickHistory', []);
|
|
614
|
+
return this.parseTrades(tickHistory, market, since, limit);
|
|
615
|
+
}
|
|
616
|
+
async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
617
|
+
/**
|
|
618
|
+
* @method
|
|
619
|
+
* @name coinmetro#fetchMyTrades
|
|
620
|
+
* @description fetch all trades made by the user
|
|
621
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#4d48ae69-8ee2-44d1-a268-71f84e557b7b
|
|
622
|
+
* @param {string} symbol unified market symbol
|
|
623
|
+
* @param {int} [since] the earliest time in ms to fetch trades for
|
|
624
|
+
* @param {int} [limit] the maximum number of trades structures to retrieve (default 500, max 1000)
|
|
625
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
626
|
+
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
|
|
627
|
+
*/
|
|
628
|
+
await this.loadMarkets();
|
|
629
|
+
let market = undefined;
|
|
630
|
+
if (symbol !== undefined) {
|
|
631
|
+
market = this.market(symbol);
|
|
632
|
+
}
|
|
633
|
+
const request = {};
|
|
634
|
+
if (since !== undefined) {
|
|
635
|
+
request['since'] = since;
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
// the exchange requires a value for the since param
|
|
639
|
+
request['since'] = 0;
|
|
640
|
+
}
|
|
641
|
+
const response = await this.privateGetExchangeFillsSince(this.extend(request, params));
|
|
642
|
+
//
|
|
643
|
+
// [
|
|
644
|
+
// {
|
|
645
|
+
// "pair": "ETHUSDC",
|
|
646
|
+
// "seqNumber": 10873722343,
|
|
647
|
+
// "timestamp": 1702570610747,
|
|
648
|
+
// "qty": 0.002,
|
|
649
|
+
// "price": 2282,
|
|
650
|
+
// "side": "buy",
|
|
651
|
+
// "orderID": "65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c"
|
|
652
|
+
// },
|
|
653
|
+
// ...
|
|
654
|
+
// ]
|
|
655
|
+
//
|
|
656
|
+
return this.parseTrades(response, market, since, limit);
|
|
657
|
+
}
|
|
658
|
+
parseTrade(trade, market = undefined) {
|
|
659
|
+
//
|
|
660
|
+
// fetchTrades
|
|
661
|
+
// {
|
|
662
|
+
// "pair": "ETHUSDT",
|
|
663
|
+
// "price": 2077.1513,
|
|
664
|
+
// "qty": 0.00337,
|
|
665
|
+
// "timestamp": 1700684816853,
|
|
666
|
+
// "seqNum": 10644567113
|
|
667
|
+
// },
|
|
668
|
+
//
|
|
669
|
+
// fetchMyTrades
|
|
670
|
+
// {
|
|
671
|
+
// "pair": "ETHUSDC",
|
|
672
|
+
// "seqNumber": 10873722343,
|
|
673
|
+
// "timestamp": 1702570610747,
|
|
674
|
+
// "qty": 0.002,
|
|
675
|
+
// "price": 2282,
|
|
676
|
+
// "side": "buy",
|
|
677
|
+
// "orderID": "65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c"
|
|
678
|
+
// }
|
|
679
|
+
//
|
|
680
|
+
// fetchOrders
|
|
681
|
+
// {
|
|
682
|
+
// "_id": "657b31d360a9542449381bdc",
|
|
683
|
+
// "seqNumber": 10873722343,
|
|
684
|
+
// "timestamp": 1702570610747,
|
|
685
|
+
// "qty": 0.002,
|
|
686
|
+
// "price": 2282,
|
|
687
|
+
// "side": "buy"
|
|
688
|
+
// }
|
|
689
|
+
//
|
|
690
|
+
// {
|
|
691
|
+
// "pair":"ETHUSDC",
|
|
692
|
+
// "seqNumber":"10873722343",
|
|
693
|
+
// "timestamp":"1702570610747",
|
|
694
|
+
// "qty":"0.002",
|
|
695
|
+
// "price":"2282",
|
|
696
|
+
// "side":"buy",
|
|
697
|
+
// "orderID":"65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c",
|
|
698
|
+
// "userID":"65671262d93d9525ac009e36"
|
|
699
|
+
// }
|
|
700
|
+
//
|
|
701
|
+
const marketId = this.safeString2(trade, 'symbol', 'pair');
|
|
702
|
+
market = this.safeMarket(marketId, market);
|
|
703
|
+
const symbol = market['symbol'];
|
|
704
|
+
const id = this.safeStringN(trade, ['_id', 'seqNum', 'seqNumber']);
|
|
705
|
+
const timestamp = this.safeInteger(trade, 'timestamp');
|
|
706
|
+
const priceString = this.safeString(trade, 'price');
|
|
707
|
+
const amountString = this.safeString(trade, 'qty');
|
|
708
|
+
const order = this.safeString(trade, 'orderID');
|
|
709
|
+
const side = this.safeString(trade, 'side');
|
|
710
|
+
return this.safeTrade({
|
|
711
|
+
'id': id,
|
|
712
|
+
'order': order,
|
|
713
|
+
'timestamp': timestamp,
|
|
714
|
+
'datetime': this.iso8601(timestamp),
|
|
715
|
+
'symbol': symbol,
|
|
716
|
+
'type': undefined,
|
|
717
|
+
'side': side,
|
|
718
|
+
'takerOrMaker': undefined,
|
|
719
|
+
'price': priceString,
|
|
720
|
+
'amount': amountString,
|
|
721
|
+
'cost': undefined,
|
|
722
|
+
'fee': undefined,
|
|
723
|
+
'info': trade,
|
|
724
|
+
}, market);
|
|
725
|
+
}
|
|
726
|
+
async fetchOrderBook(symbol, limit = undefined, params = {}) {
|
|
727
|
+
/**
|
|
728
|
+
* @method
|
|
729
|
+
* @name coinmetro#fetchOrderBook
|
|
730
|
+
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
731
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#26ad80d7-8c46-41b5-9208-386f439a8b87
|
|
732
|
+
* @param {string} symbol unified symbol of the market to fetch the order book for
|
|
733
|
+
* @param {int} [limit] the maximum amount of order book entries to return (default 100, max 200)
|
|
734
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
735
|
+
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
736
|
+
*/
|
|
737
|
+
await this.loadMarkets();
|
|
738
|
+
const market = this.market(symbol);
|
|
739
|
+
const request = {
|
|
740
|
+
'pair': market['id'],
|
|
741
|
+
};
|
|
742
|
+
const response = await this.publicGetExchangeBookPair(this.extend(request, params));
|
|
743
|
+
//
|
|
744
|
+
// {
|
|
745
|
+
// "book": {
|
|
746
|
+
// "pair": "ETHUSDT",
|
|
747
|
+
// "seqNumber": 10800409239,
|
|
748
|
+
// "ask": {
|
|
749
|
+
// "2354.2861": 3.75,
|
|
750
|
+
// "2354.3138": 19,
|
|
751
|
+
// "2354.7538": 80,
|
|
752
|
+
// "2355.5430": 260,
|
|
753
|
+
// "2356.4611": 950,
|
|
754
|
+
// "2361.7150": 1500,
|
|
755
|
+
// "206194.0000": 0.01
|
|
756
|
+
// },
|
|
757
|
+
// "bid": {
|
|
758
|
+
// "2352.6339": 3.75,
|
|
759
|
+
// "2352.6002": 19,
|
|
760
|
+
// "2352.2402": 80,
|
|
761
|
+
// "2351.4582": 260,
|
|
762
|
+
// "2349.3111": 950,
|
|
763
|
+
// "2343.8601": 1500,
|
|
764
|
+
// "1.0000": 5
|
|
765
|
+
// },
|
|
766
|
+
// "checksum": 2108177337
|
|
767
|
+
// }
|
|
768
|
+
// }
|
|
769
|
+
//
|
|
770
|
+
const book = this.safeValue(response, 'book', {});
|
|
771
|
+
const rawBids = this.safeValue(book, 'bid', {});
|
|
772
|
+
const rawAsks = this.safeValue(book, 'ask', {});
|
|
773
|
+
const rawOrderbook = {
|
|
774
|
+
'bids': rawBids,
|
|
775
|
+
'asks': rawAsks,
|
|
776
|
+
};
|
|
777
|
+
const orderbook = this.parseOrderBook(rawOrderbook, symbol);
|
|
778
|
+
orderbook['nonce'] = this.safeInteger(book, 'seqNumber');
|
|
779
|
+
return orderbook;
|
|
780
|
+
}
|
|
781
|
+
parseBidsAsks(bidasks, priceKey = 0, amountKey = 1, countOrIdKey = 2) {
|
|
782
|
+
const prices = Object.keys(bidasks);
|
|
783
|
+
const result = [];
|
|
784
|
+
for (let i = 0; i < prices.length; i++) {
|
|
785
|
+
const priceString = this.safeString(prices, i);
|
|
786
|
+
const price = this.safeNumber(prices, i);
|
|
787
|
+
const volume = this.safeNumber(bidasks, priceString);
|
|
788
|
+
result.push([price, volume]);
|
|
789
|
+
}
|
|
790
|
+
return result;
|
|
791
|
+
}
|
|
792
|
+
async fetchTickers(symbols = undefined, params = {}) {
|
|
793
|
+
/**
|
|
794
|
+
* @method
|
|
795
|
+
* @name coinmetro#fetchTickers
|
|
796
|
+
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
|
797
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#6ecd1cd1-f162-45a3-8b3b-de690332a485
|
|
798
|
+
* @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
|
799
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
800
|
+
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
801
|
+
*/
|
|
802
|
+
await this.loadMarkets();
|
|
803
|
+
const response = await this.publicGetExchangePrices(params);
|
|
804
|
+
//
|
|
805
|
+
// {
|
|
806
|
+
// "latestPrices": [
|
|
807
|
+
// {
|
|
808
|
+
// "pair": "PERPEUR",
|
|
809
|
+
// "timestamp": 1702549840393,
|
|
810
|
+
// "price": 0.7899997816001223,
|
|
811
|
+
// "qty": 1e-12,
|
|
812
|
+
// "ask": 0.8,
|
|
813
|
+
// "bid": 0.7799995632002446
|
|
814
|
+
// },
|
|
815
|
+
// {
|
|
816
|
+
// "pair": "PERPUSD",
|
|
817
|
+
// "timestamp": 1702549841973,
|
|
818
|
+
// "price": 0.8615317721366659,
|
|
819
|
+
// "qty": 1e-12,
|
|
820
|
+
// "ask": 0.8742333599999257,
|
|
821
|
+
// "bid": 0.8490376365388491
|
|
822
|
+
// },
|
|
823
|
+
// ...
|
|
824
|
+
// ],
|
|
825
|
+
// "24hInfo": [
|
|
826
|
+
// {
|
|
827
|
+
// "delta": 0.25396444229149906,
|
|
828
|
+
// "h": 0.78999978160012,
|
|
829
|
+
// "l": 0.630001740844,
|
|
830
|
+
// "v": 54.910000002833996,
|
|
831
|
+
// "pair": "PERPEUR",
|
|
832
|
+
// "sentimentData": {
|
|
833
|
+
// "sentiment": 36.71333333333333,
|
|
834
|
+
// "interest": 0.47430830039525695
|
|
835
|
+
// }
|
|
836
|
+
// },
|
|
837
|
+
// {
|
|
838
|
+
// "delta": 0.26915154078134096,
|
|
839
|
+
// "h": 0.86220315458898,
|
|
840
|
+
// "l": 0.67866757035154,
|
|
841
|
+
// "v": 2.835000000000001e-9,
|
|
842
|
+
// "pair": "PERPUSD",
|
|
843
|
+
// "sentimentData": {
|
|
844
|
+
// "sentiment": 36.71333333333333,
|
|
845
|
+
// "interest": 0.47430830039525695
|
|
846
|
+
// }
|
|
847
|
+
// },
|
|
848
|
+
// ...
|
|
849
|
+
// ]
|
|
850
|
+
// }
|
|
851
|
+
//
|
|
852
|
+
const latestPrices = this.safeValue(response, 'latestPrices', []);
|
|
853
|
+
const twentyFourHInfos = this.safeValue(response, '24hInfo', []);
|
|
854
|
+
const tickersObject = {};
|
|
855
|
+
// merging info from two lists into one
|
|
856
|
+
for (let i = 0; i < latestPrices.length; i++) {
|
|
857
|
+
const latestPrice = latestPrices[i];
|
|
858
|
+
const marketId = this.safeString(latestPrice, 'pair');
|
|
859
|
+
if (marketId !== undefined) {
|
|
860
|
+
tickersObject[marketId] = latestPrice;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
for (let i = 0; i < twentyFourHInfos.length; i++) {
|
|
864
|
+
const twentyFourHInfo = twentyFourHInfos[i];
|
|
865
|
+
const marketId = this.safeString(twentyFourHInfo, 'pair');
|
|
866
|
+
if (marketId !== undefined) {
|
|
867
|
+
const latestPrice = this.safeValue(tickersObject, marketId, {});
|
|
868
|
+
tickersObject[marketId] = this.extend(twentyFourHInfo, latestPrice);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
const tickers = Object.values(tickersObject);
|
|
872
|
+
return this.parseTickers(tickers, symbols);
|
|
873
|
+
}
|
|
874
|
+
async fetchBidsAsks(symbols = undefined, params = {}) {
|
|
875
|
+
/**
|
|
876
|
+
* @method
|
|
877
|
+
* @name coinmetro#fetchBidsAsks
|
|
878
|
+
* @description fetches the bid and ask price and volume for multiple markets
|
|
879
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#6ecd1cd1-f162-45a3-8b3b-de690332a485
|
|
880
|
+
* @param {string[]} [symbols] unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
|
|
881
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
882
|
+
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
883
|
+
*/
|
|
884
|
+
await this.loadMarkets();
|
|
885
|
+
const response = await this.publicGetExchangePrices(params);
|
|
886
|
+
const latestPrices = this.safeValue(response, 'latestPrices', []);
|
|
887
|
+
return this.parseTickers(latestPrices, symbols);
|
|
888
|
+
}
|
|
889
|
+
parseTicker(ticker, market = undefined) {
|
|
890
|
+
//
|
|
891
|
+
// {
|
|
892
|
+
// "pair": "PERPUSD",
|
|
893
|
+
// "timestamp": 1702549841973,
|
|
894
|
+
// "price": 0.8615317721366659,
|
|
895
|
+
// "qty": 1e-12,
|
|
896
|
+
// "ask": 0.8742333599999257,
|
|
897
|
+
// "bid": 0.8490376365388491
|
|
898
|
+
// "delta": 0.26915154078134096,
|
|
899
|
+
// "h": 0.86220315458898,
|
|
900
|
+
// "l": 0.67866757035154,
|
|
901
|
+
// "v": 2.835000000000001e-9,
|
|
902
|
+
// "sentimentData": {
|
|
903
|
+
// "sentiment": 36.71333333333333,
|
|
904
|
+
// "interest": 0.47430830039525695
|
|
905
|
+
// }
|
|
906
|
+
// }
|
|
907
|
+
//
|
|
908
|
+
const marketId = this.safeString(ticker, 'pair');
|
|
909
|
+
market = this.safeMarket(marketId, market);
|
|
910
|
+
const timestamp = this.safeInteger(ticker, 'timestamp');
|
|
911
|
+
const bid = this.safeString(ticker, 'bid');
|
|
912
|
+
const ask = this.safeString(ticker, 'ask');
|
|
913
|
+
const high = this.safeString(ticker, 'h');
|
|
914
|
+
const low = this.safeString(ticker, 'l');
|
|
915
|
+
const last = this.safeString(ticker, 'price');
|
|
916
|
+
const baseVolume = this.safeString(ticker, 'v');
|
|
917
|
+
const delta = this.safeString(ticker, 'delta');
|
|
918
|
+
const percentage = Precise.stringMul(delta, '100');
|
|
919
|
+
return this.safeTicker({
|
|
920
|
+
'symbol': market['symbol'],
|
|
921
|
+
'timestamp': timestamp,
|
|
922
|
+
'datetime': this.iso8601(timestamp),
|
|
923
|
+
'open': undefined,
|
|
924
|
+
'high': high,
|
|
925
|
+
'low': low,
|
|
926
|
+
'close': undefined,
|
|
927
|
+
'last': last,
|
|
928
|
+
'bid': bid,
|
|
929
|
+
'bidVolume': undefined,
|
|
930
|
+
'ask': ask,
|
|
931
|
+
'askVolume': undefined,
|
|
932
|
+
'vwap': undefined,
|
|
933
|
+
'previousClose': undefined,
|
|
934
|
+
'change': undefined,
|
|
935
|
+
'percentage': percentage,
|
|
936
|
+
'average': undefined,
|
|
937
|
+
'baseVolume': baseVolume,
|
|
938
|
+
'quoteVolume': undefined,
|
|
939
|
+
'info': ticker,
|
|
940
|
+
}, market);
|
|
941
|
+
}
|
|
942
|
+
async fetchBalance(params = {}) {
|
|
943
|
+
/**
|
|
944
|
+
* @method
|
|
945
|
+
* @name coinmetro#fetchBalance
|
|
946
|
+
* @description query for balance and get the amount of funds available for trading or funds locked in orders
|
|
947
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#698ae067-43dd-4e19-a0ac-d9ba91381816
|
|
948
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
949
|
+
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
|
|
950
|
+
*/
|
|
951
|
+
await this.loadMarkets();
|
|
952
|
+
const response = await this.privateGetUsersBalances(params);
|
|
953
|
+
return this.parseBalance(response);
|
|
954
|
+
}
|
|
955
|
+
parseBalance(response) {
|
|
956
|
+
//
|
|
957
|
+
// {
|
|
958
|
+
// "USDC": {
|
|
959
|
+
// "USDC": 99,
|
|
960
|
+
// "EUR": 91.16,
|
|
961
|
+
// "BTC": 0.002334
|
|
962
|
+
// },
|
|
963
|
+
// "XCM": {
|
|
964
|
+
// "XCM": 0,
|
|
965
|
+
// "EUR": 0,
|
|
966
|
+
// "BTC": 0
|
|
967
|
+
// },
|
|
968
|
+
// "TOTAL": {
|
|
969
|
+
// "EUR": 91.16,
|
|
970
|
+
// "BTC": 0.002334
|
|
971
|
+
// },
|
|
972
|
+
// "REF": {
|
|
973
|
+
// "XCM": 0,
|
|
974
|
+
// "EUR": 0,
|
|
975
|
+
// "BTC": 0
|
|
976
|
+
// }
|
|
977
|
+
// }
|
|
978
|
+
//
|
|
979
|
+
const result = {
|
|
980
|
+
'info': response,
|
|
981
|
+
};
|
|
982
|
+
const balances = this.omit(response, ['TOTAL', 'REF']);
|
|
983
|
+
const currencyIds = Object.keys(balances);
|
|
984
|
+
for (let i = 0; i < currencyIds.length; i++) {
|
|
985
|
+
const currencyId = currencyIds[i];
|
|
986
|
+
const code = this.safeCurrencyCode(currencyId);
|
|
987
|
+
const account = this.account();
|
|
988
|
+
const currency = this.safeValue(balances, currencyId, {});
|
|
989
|
+
account['total'] = this.safeString(currency, currencyId);
|
|
990
|
+
result[code] = account;
|
|
991
|
+
}
|
|
992
|
+
return this.safeBalance(result);
|
|
993
|
+
}
|
|
994
|
+
async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) {
|
|
995
|
+
/**
|
|
996
|
+
* @method
|
|
997
|
+
* @name coinmetro#fetchLedger
|
|
998
|
+
* @description fetch the history of changes, actions done by the user or operations that altered balance of the user
|
|
999
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#4e7831f7-a0e7-4c3e-9336-1d0e5dcb15cf
|
|
1000
|
+
* @param {string} code unified currency code, default is undefined
|
|
1001
|
+
* @param {int} [since] timestamp in ms of the earliest ledger entry, default is undefined
|
|
1002
|
+
* @param {int} [limit] max number of ledger entrys to return (default 200, max 500)
|
|
1003
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1004
|
+
* @param {int} [params.until] the latest time in ms to fetch entries for
|
|
1005
|
+
* @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger-structure}
|
|
1006
|
+
*/
|
|
1007
|
+
await this.loadMarkets();
|
|
1008
|
+
const request = {};
|
|
1009
|
+
if (since !== undefined) {
|
|
1010
|
+
request['since'] = since;
|
|
1011
|
+
}
|
|
1012
|
+
else {
|
|
1013
|
+
// this endpoint accepts empty since param
|
|
1014
|
+
request['since'] = '';
|
|
1015
|
+
}
|
|
1016
|
+
let currency = undefined;
|
|
1017
|
+
if (code !== undefined) {
|
|
1018
|
+
currency = this.currency(code);
|
|
1019
|
+
}
|
|
1020
|
+
const response = await this.privateGetUsersWalletsHistorySince(this.extend(request, params));
|
|
1021
|
+
//
|
|
1022
|
+
// {
|
|
1023
|
+
// "list": [
|
|
1024
|
+
// {
|
|
1025
|
+
// "currency": "USDC",
|
|
1026
|
+
// "label": "USDC",
|
|
1027
|
+
// "userId": "65671262d93d9525ac009e36",
|
|
1028
|
+
// "balance": 0,
|
|
1029
|
+
// "disabled": false,
|
|
1030
|
+
// "balanceHistory": [
|
|
1031
|
+
// {
|
|
1032
|
+
// "description": "Deposit - 657973a9b6eadf0f33d70100",
|
|
1033
|
+
// "JSONdata": {
|
|
1034
|
+
// "fees": 0,
|
|
1035
|
+
// "notes": "Via Crypto",
|
|
1036
|
+
// "txHash": "0x2e4875185b0f312d8e24b2d26d46bf9877db798b608ad2ff97b2b8bc7d8134e5",
|
|
1037
|
+
// "last4Digits": null,
|
|
1038
|
+
// "IBAN": null,
|
|
1039
|
+
// "alternativeChain": "polygon",
|
|
1040
|
+
// "referenceId": "657973a9b6eadf0f33d70100",
|
|
1041
|
+
// "status": "completed",
|
|
1042
|
+
// "tracked": true
|
|
1043
|
+
// },
|
|
1044
|
+
// "amount": 99,
|
|
1045
|
+
// "timestamp": "2023-12-13T09:04:51.270Z",
|
|
1046
|
+
// "amountEUR": 91.79310117335974
|
|
1047
|
+
// },
|
|
1048
|
+
// {
|
|
1049
|
+
// "description": "Order 65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c SeqNum 10873722342",
|
|
1050
|
+
// "JSONdata": {
|
|
1051
|
+
// "price": "2282.00 ETH/USDC",
|
|
1052
|
+
// "fees": 0,
|
|
1053
|
+
// "notes": "Order 3a8c5b4d6c"
|
|
1054
|
+
// },
|
|
1055
|
+
// "amount": -4.564,
|
|
1056
|
+
// "timestamp": "2023-12-14T16:16:50.760Z",
|
|
1057
|
+
// "amountEUR": -4.150043849187587
|
|
1058
|
+
// },
|
|
1059
|
+
// ...
|
|
1060
|
+
// ]
|
|
1061
|
+
// },
|
|
1062
|
+
// {
|
|
1063
|
+
// "currency": "ETH",
|
|
1064
|
+
// "label": "ETH",
|
|
1065
|
+
// "userId": "65671262d93d9525ac009e36",
|
|
1066
|
+
// "balance": 0,
|
|
1067
|
+
// "disabled": false,
|
|
1068
|
+
// "balanceHistory": [
|
|
1069
|
+
// {
|
|
1070
|
+
// "description": "Order 65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c SeqNum 10873722342",
|
|
1071
|
+
// "JSONdata": {
|
|
1072
|
+
// "price": "2282.00 ETH/USDC",
|
|
1073
|
+
// "fees": 0.000002,
|
|
1074
|
+
// "notes": "Order 3a8c5b4d6c"
|
|
1075
|
+
// },
|
|
1076
|
+
// "amount": 0.001998,
|
|
1077
|
+
// "timestamp": "2023-12-14T16:16:50.761Z",
|
|
1078
|
+
// "amountEUR": 4.144849415806856
|
|
1079
|
+
// },
|
|
1080
|
+
// ...
|
|
1081
|
+
// ]
|
|
1082
|
+
// },
|
|
1083
|
+
// {
|
|
1084
|
+
// "currency": "DOGE",
|
|
1085
|
+
// "label": "DOGE",
|
|
1086
|
+
// "userId": "65671262d93d9525ac009e36",
|
|
1087
|
+
// "balance": 0,
|
|
1088
|
+
// "disabled": false,
|
|
1089
|
+
// "balanceHistory": [
|
|
1090
|
+
// {
|
|
1091
|
+
// "description": "Order 65671262d93d9525ac009e361702905785319b5d9016dc20736034d13ca6a - Swap",
|
|
1092
|
+
// "JSONdata": {
|
|
1093
|
+
// "swap": true,
|
|
1094
|
+
// "subtype": "swap",
|
|
1095
|
+
// "fees": 0,
|
|
1096
|
+
// "price": "0.0905469 DOGE/USDC",
|
|
1097
|
+
// "notes": "Swap 034d13ca6a"
|
|
1098
|
+
// },
|
|
1099
|
+
// "amount": 70,
|
|
1100
|
+
// "timestamp": "2023-12-18T13:23:05.836Z",
|
|
1101
|
+
// "amountEUR": 5.643627624549227
|
|
1102
|
+
// }
|
|
1103
|
+
// ]
|
|
1104
|
+
// },
|
|
1105
|
+
// ...
|
|
1106
|
+
// ]
|
|
1107
|
+
// }
|
|
1108
|
+
//
|
|
1109
|
+
const ledgerByCurrencies = this.safeValue(response, 'list', []);
|
|
1110
|
+
const ledger = [];
|
|
1111
|
+
for (let i = 0; i < ledgerByCurrencies.length; i++) {
|
|
1112
|
+
const currencyLedger = ledgerByCurrencies[i];
|
|
1113
|
+
const currencyId = this.safeString(currencyLedger, 'currency');
|
|
1114
|
+
const balanceHistory = this.safeValue(currencyLedger, 'balanceHistory', []);
|
|
1115
|
+
for (let j = 0; j < balanceHistory.length; j++) {
|
|
1116
|
+
const rawLedgerEntry = balanceHistory[j];
|
|
1117
|
+
rawLedgerEntry['currencyId'] = currencyId;
|
|
1118
|
+
ledger.push(rawLedgerEntry);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return this.parseLedger(ledger, currency, since, limit);
|
|
1122
|
+
}
|
|
1123
|
+
parseLedgerEntry(item, currency = undefined) {
|
|
1124
|
+
const datetime = this.safeString(item, 'timestamp');
|
|
1125
|
+
const currencyId = this.safeString(item, 'currencyId');
|
|
1126
|
+
item = this.omit(item, 'currencyId');
|
|
1127
|
+
currency = this.safeCurrency(currencyId, currency);
|
|
1128
|
+
const description = this.safeString(item, 'description', '');
|
|
1129
|
+
const [type, referenceId] = this.parseLedgerEntryDescription(description);
|
|
1130
|
+
const JSONdata = this.safeValue(item, 'JSONdata', {});
|
|
1131
|
+
const feeCost = this.safeString(JSONdata, 'fees');
|
|
1132
|
+
const fee = {
|
|
1133
|
+
'cost': feeCost,
|
|
1134
|
+
'currency': undefined,
|
|
1135
|
+
};
|
|
1136
|
+
let amount = this.safeString(item, 'amount');
|
|
1137
|
+
let direction = undefined;
|
|
1138
|
+
if (amount !== undefined) {
|
|
1139
|
+
if (Precise.stringLt(amount, '0')) {
|
|
1140
|
+
direction = 'out';
|
|
1141
|
+
amount = Precise.stringAbs(amount);
|
|
1142
|
+
}
|
|
1143
|
+
else if (Precise.stringGt(amount, '0')) {
|
|
1144
|
+
direction = 'in';
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return this.safeLedgerEntry({
|
|
1148
|
+
'info': item,
|
|
1149
|
+
'id': undefined,
|
|
1150
|
+
'timestamp': this.parse8601(datetime),
|
|
1151
|
+
'datetime': datetime,
|
|
1152
|
+
'direction': direction,
|
|
1153
|
+
'account': undefined,
|
|
1154
|
+
'referenceId': referenceId,
|
|
1155
|
+
'referenceAccount': undefined,
|
|
1156
|
+
'type': type,
|
|
1157
|
+
'currency': currency,
|
|
1158
|
+
'amount': amount,
|
|
1159
|
+
'before': undefined,
|
|
1160
|
+
'after': undefined,
|
|
1161
|
+
'status': undefined,
|
|
1162
|
+
'fee': fee,
|
|
1163
|
+
}, currency);
|
|
1164
|
+
}
|
|
1165
|
+
parseLedgerEntryDescription(description) {
|
|
1166
|
+
let descriptionArray = [];
|
|
1167
|
+
if (description !== undefined) {
|
|
1168
|
+
descriptionArray = description.split(' ');
|
|
1169
|
+
}
|
|
1170
|
+
let type = undefined;
|
|
1171
|
+
let referenceId = undefined;
|
|
1172
|
+
if (descriptionArray.length > 1) {
|
|
1173
|
+
type = this.parseLedgerEntryType(descriptionArray[0]);
|
|
1174
|
+
if (descriptionArray[1] !== '-') {
|
|
1175
|
+
referenceId = descriptionArray[1];
|
|
1176
|
+
}
|
|
1177
|
+
else {
|
|
1178
|
+
referenceId = this.safeString(descriptionArray, 2);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return [type, referenceId];
|
|
1182
|
+
}
|
|
1183
|
+
parseLedgerEntryType(type) {
|
|
1184
|
+
const types = {
|
|
1185
|
+
'Deposit': 'transaction',
|
|
1186
|
+
'Withdraw': 'transaction',
|
|
1187
|
+
'Order': 'trade',
|
|
1188
|
+
};
|
|
1189
|
+
return this.safeString(types, type, type);
|
|
1190
|
+
}
|
|
1191
|
+
async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
|
|
1192
|
+
/**
|
|
1193
|
+
* @method
|
|
1194
|
+
* @name coinmetro#createOrder
|
|
1195
|
+
* @description create a trade order
|
|
1196
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#a4895a1d-3f50-40ae-8231-6962ef06c771
|
|
1197
|
+
* @param {string} symbol unified symbol of the market to create an order in
|
|
1198
|
+
* @param {string} type 'market' or 'limit'
|
|
1199
|
+
* @param {string} side 'buy' or 'sell'
|
|
1200
|
+
* @param {float} amount how much of currency you want to trade in units of base currency
|
|
1201
|
+
* @param {float} [price] the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
|
|
1202
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1203
|
+
* @param {float} [params.cost] the quote quantity that can be used as an alternative for the amount in market orders
|
|
1204
|
+
* @param {string} [params.timeInForce] "GTC", "IOC", "FOK", "GTD"
|
|
1205
|
+
* @param {number} [params.expirationTime] timestamp in millisecond, for GTD orders only
|
|
1206
|
+
* @param {float} [params.triggerPrice] the price at which a trigger order is triggered at
|
|
1207
|
+
* @param {float} [params.stopLossPrice] *margin only* The price at which a stop loss order is triggered at
|
|
1208
|
+
* @param {float} [params.takeProfitPrice] *margin only* The price at which a take profit order is triggered at
|
|
1209
|
+
* @param {bool} [params.margin] true for creating a margin order
|
|
1210
|
+
* @param {string} [params.fillStyle] fill style of the limit order: "sell" fulfills selling quantity "buy" fulfills buying quantity "base" fulfills base currency quantity "quote" fulfills quote currency quantity
|
|
1211
|
+
* @param {string} [params.clientOrderId] client's comment
|
|
1212
|
+
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1213
|
+
*/
|
|
1214
|
+
await this.loadMarkets();
|
|
1215
|
+
const market = this.market(symbol);
|
|
1216
|
+
let request = {};
|
|
1217
|
+
request['orderType'] = type;
|
|
1218
|
+
let precisedAmount = undefined;
|
|
1219
|
+
if (amount !== undefined) {
|
|
1220
|
+
precisedAmount = this.amountToPrecision(symbol, amount);
|
|
1221
|
+
}
|
|
1222
|
+
let cost = this.safeValue(params, 'cost');
|
|
1223
|
+
params = this.omit(params, 'cost');
|
|
1224
|
+
if (type === 'limit') {
|
|
1225
|
+
if ((price === undefined) && (cost === undefined)) {
|
|
1226
|
+
throw new ArgumentsRequired(this.id + ' createOrder() requires a price or params.cost argument for a ' + type + ' order');
|
|
1227
|
+
}
|
|
1228
|
+
else if ((price !== undefined) && (amount !== undefined)) {
|
|
1229
|
+
const costString = Precise.stringMul(this.numberToString(price), this.numberToString(precisedAmount));
|
|
1230
|
+
cost = this.parseToNumeric(costString);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
let precisedCost = undefined;
|
|
1234
|
+
if (cost !== undefined) {
|
|
1235
|
+
precisedCost = this.costToPrecision(symbol, cost);
|
|
1236
|
+
}
|
|
1237
|
+
if (side === 'sell') {
|
|
1238
|
+
request = this.handleCreateOrderSide(market['baseId'], market['quoteId'], precisedAmount, precisedCost, request);
|
|
1239
|
+
}
|
|
1240
|
+
else if (side === 'buy') {
|
|
1241
|
+
request = this.handleCreateOrderSide(market['quoteId'], market['baseId'], precisedCost, precisedAmount, request);
|
|
1242
|
+
}
|
|
1243
|
+
const timeInForce = this.safeValue(params, 'timeInForce');
|
|
1244
|
+
if (timeInForce !== undefined) {
|
|
1245
|
+
params = this.omit(params, 'timeInForce');
|
|
1246
|
+
request['timeInForce'] = this.encodeOrderTimeInForce(timeInForce);
|
|
1247
|
+
}
|
|
1248
|
+
const stopPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
|
|
1249
|
+
if (stopPrice !== undefined) {
|
|
1250
|
+
params = this.omit(params, ['triggerPrice']);
|
|
1251
|
+
request['stopPrice'] = this.priceToPrecision(symbol, stopPrice);
|
|
1252
|
+
}
|
|
1253
|
+
const userData = this.safeValue(params, 'userData', {});
|
|
1254
|
+
const comment = this.safeString2(params, 'clientOrderId', 'comment');
|
|
1255
|
+
if (comment !== undefined) {
|
|
1256
|
+
params = this.omit(params, ['clientOrderId']);
|
|
1257
|
+
userData['comment'] = comment;
|
|
1258
|
+
}
|
|
1259
|
+
const stopLossPrice = this.safeString(params, 'stopLossPrice');
|
|
1260
|
+
if (stopLossPrice !== undefined) {
|
|
1261
|
+
params = this.omit(params, 'stopLossPrice');
|
|
1262
|
+
userData['stopLoss'] = this.priceToPrecision(symbol, stopLossPrice);
|
|
1263
|
+
}
|
|
1264
|
+
const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
|
|
1265
|
+
if (takeProfitPrice !== undefined) {
|
|
1266
|
+
params = this.omit(params, 'takeProfitPrice');
|
|
1267
|
+
userData['takeProfit'] = this.priceToPrecision(symbol, takeProfitPrice);
|
|
1268
|
+
}
|
|
1269
|
+
if (!this.isEmpty(userData)) {
|
|
1270
|
+
request['userData'] = userData;
|
|
1271
|
+
}
|
|
1272
|
+
const response = await this.privatePostExchangeOrdersCreate(this.extend(request, params));
|
|
1273
|
+
//
|
|
1274
|
+
// {
|
|
1275
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1276
|
+
// "orderID": "65671262d93d9525ac009e36170257448481749b7ee2893bafec2",
|
|
1277
|
+
// "orderType": "market",
|
|
1278
|
+
// "buyingCurrency": "ETH",
|
|
1279
|
+
// "sellingCurrency": "USDC",
|
|
1280
|
+
// "buyingQty": 0.002,
|
|
1281
|
+
// "timeInForce": 4,
|
|
1282
|
+
// "boughtQty": 0.002,
|
|
1283
|
+
// "soldQty": 4.587,
|
|
1284
|
+
// "creationTime": 1702574484829,
|
|
1285
|
+
// "seqNumber": 10874285330,
|
|
1286
|
+
// "firstFillTime": 1702574484831,
|
|
1287
|
+
// "lastFillTime": 1702574484831,
|
|
1288
|
+
// "fills": [
|
|
1289
|
+
// {
|
|
1290
|
+
// "seqNumber": 10874285329,
|
|
1291
|
+
// "timestamp": 1702574484831,
|
|
1292
|
+
// "qty": 0.002,
|
|
1293
|
+
// "price": 2293.5,
|
|
1294
|
+
// "side": "buy"
|
|
1295
|
+
// }
|
|
1296
|
+
// ],
|
|
1297
|
+
// "completionTime": 1702574484831,
|
|
1298
|
+
// "takerQty": 0.002
|
|
1299
|
+
// }
|
|
1300
|
+
//
|
|
1301
|
+
return this.parseOrder(response, market);
|
|
1302
|
+
}
|
|
1303
|
+
handleCreateOrderSide(sellingCurrency, buyingCurrency, sellingQty, buyingQty, request = {}) {
|
|
1304
|
+
request['sellingCurrency'] = sellingCurrency;
|
|
1305
|
+
request['buyingCurrency'] = buyingCurrency;
|
|
1306
|
+
if (sellingQty !== undefined) {
|
|
1307
|
+
request['sellingQty'] = sellingQty;
|
|
1308
|
+
}
|
|
1309
|
+
if (buyingQty !== undefined) {
|
|
1310
|
+
request['buyingQty'] = buyingQty;
|
|
1311
|
+
}
|
|
1312
|
+
return request;
|
|
1313
|
+
}
|
|
1314
|
+
encodeOrderTimeInForce(timeInForce) {
|
|
1315
|
+
const timeInForceTypes = {
|
|
1316
|
+
'GTC': 1,
|
|
1317
|
+
'IOC': 2,
|
|
1318
|
+
'GTD': 3,
|
|
1319
|
+
'FOK': 4,
|
|
1320
|
+
};
|
|
1321
|
+
return this.safeValue(timeInForceTypes, timeInForce, timeInForce);
|
|
1322
|
+
}
|
|
1323
|
+
async cancelOrder(id, symbol = undefined, params = {}) {
|
|
1324
|
+
/**
|
|
1325
|
+
* @method
|
|
1326
|
+
* @name coinmetro#cancelOrder
|
|
1327
|
+
* @description cancels an open order
|
|
1328
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#eaea86da-16ca-4c56-9f00-5b1cb2ad89f8
|
|
1329
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#47f913fb-8cab-49f4-bc78-d980e6ced316
|
|
1330
|
+
* @param {string} id order id
|
|
1331
|
+
* @param {string} symbol not used by coinmetro cancelOrder ()
|
|
1332
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1333
|
+
* @param {string} [params.margin] true for cancelling a margin order
|
|
1334
|
+
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1335
|
+
*/
|
|
1336
|
+
await this.loadMarkets();
|
|
1337
|
+
const request = {
|
|
1338
|
+
'orderID': id,
|
|
1339
|
+
};
|
|
1340
|
+
const marginMode = undefined;
|
|
1341
|
+
[params, params] = this.handleMarginModeAndParams('cancelOrder', params);
|
|
1342
|
+
const isMargin = this.safeValue(params, 'margin', false);
|
|
1343
|
+
params = this.omit(params, 'margin');
|
|
1344
|
+
let response = undefined;
|
|
1345
|
+
if (isMargin || (marginMode !== undefined)) {
|
|
1346
|
+
response = await this.privatePostExchangeOrdersCloseOrderID(this.extend(request, params));
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
response = await this.privatePutExchangeOrdersCancelOrderID(this.extend(request, params));
|
|
1350
|
+
}
|
|
1351
|
+
//
|
|
1352
|
+
// {
|
|
1353
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1354
|
+
// "orderID": "65671262d93d9525ac009e3617026635256739c996fe17d7cd5d4",
|
|
1355
|
+
// "orderType": "limit",
|
|
1356
|
+
// "buyingCurrency": "ETH",
|
|
1357
|
+
// "sellingCurrency": "USDC",
|
|
1358
|
+
// "fillStyle": "sell",
|
|
1359
|
+
// "orderPlatform": "trade-v3",
|
|
1360
|
+
// "timeInForce": 1,
|
|
1361
|
+
// "buyingQty": 0.005655,
|
|
1362
|
+
// "sellingQty": 11.31,
|
|
1363
|
+
// "boughtQty": 0,
|
|
1364
|
+
// "soldQty": 0,
|
|
1365
|
+
// "creationTime": 1702663525713,
|
|
1366
|
+
// "seqNumber": 10915220048,
|
|
1367
|
+
// "completionTime": 1702928369053
|
|
1368
|
+
// }
|
|
1369
|
+
//
|
|
1370
|
+
return this.parseOrder(response);
|
|
1371
|
+
}
|
|
1372
|
+
async closePosition(symbol, side = undefined, params = {}) {
|
|
1373
|
+
/**
|
|
1374
|
+
* @method
|
|
1375
|
+
* @name coinmetro#cancelOrder
|
|
1376
|
+
* @description closes an open position
|
|
1377
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#47f913fb-8cab-49f4-bc78-d980e6ced316
|
|
1378
|
+
* @param {string} symbol not used by coinmetro closePosition ()
|
|
1379
|
+
* @param {string} [side] not used by coinmetro closePosition ()
|
|
1380
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1381
|
+
* @param {string} [params.orderID] order id
|
|
1382
|
+
* @param {number} [params.fraction] fraction of order to close, between 0 and 1 (defaults to 1)
|
|
1383
|
+
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1384
|
+
*/
|
|
1385
|
+
await this.loadMarkets();
|
|
1386
|
+
const orderId = this.safeString(params, 'orderId');
|
|
1387
|
+
if (orderId === undefined) {
|
|
1388
|
+
throw new ArgumentsRequired(this.id + ' closePosition() requires a orderId parameter');
|
|
1389
|
+
}
|
|
1390
|
+
const request = {
|
|
1391
|
+
'orderID': orderId,
|
|
1392
|
+
};
|
|
1393
|
+
const response = await this.privatePostExchangeOrdersCloseOrderID(this.extend(request, params));
|
|
1394
|
+
//
|
|
1395
|
+
// {
|
|
1396
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1397
|
+
// "orderID": "65671262d93d9525ac009e3617030152811996e5b352556d3d7d8_CL",
|
|
1398
|
+
// "orderType": "market",
|
|
1399
|
+
// "buyingCurrency": "ETH",
|
|
1400
|
+
// "sellingCurrency": "EUR",
|
|
1401
|
+
// "margin": true,
|
|
1402
|
+
// "buyingQty": 0.03,
|
|
1403
|
+
// "timeInForce": 4,
|
|
1404
|
+
// "boughtQty": 0.03,
|
|
1405
|
+
// "soldQty": 59.375,
|
|
1406
|
+
// "creationTime": 1703015488482,
|
|
1407
|
+
// "seqNumber": 10925321179,
|
|
1408
|
+
// "firstFillTime": 1703015488483,
|
|
1409
|
+
// "lastFillTime": 1703015488483,
|
|
1410
|
+
// "fills": [
|
|
1411
|
+
// {
|
|
1412
|
+
// "seqNumber": 10925321178,
|
|
1413
|
+
// "timestamp": 1703015488483,
|
|
1414
|
+
// "qty": 0.03,
|
|
1415
|
+
// "price": 1979.1666666666667,
|
|
1416
|
+
// "side": "buy"
|
|
1417
|
+
// }
|
|
1418
|
+
// ],
|
|
1419
|
+
// "completionTime": 1703015488483,
|
|
1420
|
+
// "takerQty": 0.03
|
|
1421
|
+
// }
|
|
1422
|
+
//
|
|
1423
|
+
return this.parseOrder(response);
|
|
1424
|
+
}
|
|
1425
|
+
async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1426
|
+
/**
|
|
1427
|
+
* @method
|
|
1428
|
+
* @name coinmetro#fetchOpenOrders
|
|
1429
|
+
* @description fetch all unfilled currently open orders
|
|
1430
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#518afd7a-4338-439c-a651-d4fdaa964138
|
|
1431
|
+
* @param {string} symbol unified market symbol
|
|
1432
|
+
* @param {int} [since] the earliest time in ms to fetch open orders for
|
|
1433
|
+
* @param {int} [limit] the maximum number of open order structures to retrieve
|
|
1434
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1435
|
+
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1436
|
+
*/
|
|
1437
|
+
await this.loadMarkets();
|
|
1438
|
+
let market = undefined;
|
|
1439
|
+
if (symbol !== undefined) {
|
|
1440
|
+
market = this.market(symbol);
|
|
1441
|
+
}
|
|
1442
|
+
const response = await this.privateGetExchangeOrdersActive(params);
|
|
1443
|
+
const orders = this.parseOrders(response, market, since, limit);
|
|
1444
|
+
for (let i = 0; i < orders.length; i++) {
|
|
1445
|
+
const order = orders[i];
|
|
1446
|
+
order['status'] = 'open';
|
|
1447
|
+
}
|
|
1448
|
+
return orders;
|
|
1449
|
+
}
|
|
1450
|
+
async fetchCanceledAndClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
1451
|
+
/**
|
|
1452
|
+
* @method
|
|
1453
|
+
* @name coinmetro#fetchCanceledAndClosedOrders
|
|
1454
|
+
* @description fetches information on multiple canceled and closed orders made by the user
|
|
1455
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#4d48ae69-8ee2-44d1-a268-71f84e557b7b
|
|
1456
|
+
* @param {string} symbol unified market symbol of the market orders were made in
|
|
1457
|
+
* @param {int} [since] the earliest time in ms to fetch orders for
|
|
1458
|
+
* @param {int} [limit] the maximum number of order structures to retrieve
|
|
1459
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1460
|
+
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1461
|
+
*/
|
|
1462
|
+
await this.loadMarkets();
|
|
1463
|
+
let market = undefined;
|
|
1464
|
+
if (symbol !== undefined) {
|
|
1465
|
+
market = this.market(symbol);
|
|
1466
|
+
}
|
|
1467
|
+
const request = {};
|
|
1468
|
+
if (since !== undefined) {
|
|
1469
|
+
request['since'] = since;
|
|
1470
|
+
}
|
|
1471
|
+
const response = await this.privateGetExchangeOrdersHistorySince(this.extend(request, params));
|
|
1472
|
+
return this.parseOrders(response, market, since, limit);
|
|
1473
|
+
}
|
|
1474
|
+
async fetchOrder(id, symbol = undefined, params = {}) {
|
|
1475
|
+
/**
|
|
1476
|
+
* @method
|
|
1477
|
+
* @name coinmetro#fetchOrder
|
|
1478
|
+
* @description fetches information on an order made by the user
|
|
1479
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#95bbed87-db1c-47a7-a03e-aa247e91d5a6
|
|
1480
|
+
* @param {int|string} id order id
|
|
1481
|
+
* @param {string} symbol not used by coinmetro fetchOrder ()
|
|
1482
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1483
|
+
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
1484
|
+
*/
|
|
1485
|
+
await this.loadMarkets();
|
|
1486
|
+
const request = {
|
|
1487
|
+
'orderID': id,
|
|
1488
|
+
};
|
|
1489
|
+
const response = await this.privateGetExchangeOrdersStatusOrderID(this.extend(request, params));
|
|
1490
|
+
//
|
|
1491
|
+
// {
|
|
1492
|
+
// "_id": "657b4e6d60a954244939ac6f",
|
|
1493
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1494
|
+
// "orderID": "65671262d93d9525ac009e361702576531985b78465468b9cc544",
|
|
1495
|
+
// "orderType": "market",
|
|
1496
|
+
// "buyingCurrency": "ETH",
|
|
1497
|
+
// "sellingCurrency": "USDC",
|
|
1498
|
+
// "buyingQty": 0.004,
|
|
1499
|
+
// "timeInForce": 4,
|
|
1500
|
+
// "boughtQty": 0.004,
|
|
1501
|
+
// "soldQty": 9.236,
|
|
1502
|
+
// "creationTime": 1702576531995,
|
|
1503
|
+
// "seqNumber": 10874644062,
|
|
1504
|
+
// "firstFillTime": 1702576531995,
|
|
1505
|
+
// "lastFillTime": 1702576531995,
|
|
1506
|
+
// "fills": [
|
|
1507
|
+
// {
|
|
1508
|
+
// "_id": "657b4e6d60a954244939ac70",
|
|
1509
|
+
// "seqNumber": 10874644061,
|
|
1510
|
+
// "timestamp": 1702576531995,
|
|
1511
|
+
// "qty": 0.004,
|
|
1512
|
+
// "price": 2309,
|
|
1513
|
+
// "side": "buy"
|
|
1514
|
+
// }
|
|
1515
|
+
// ],
|
|
1516
|
+
// "completionTime": 1702576531995,
|
|
1517
|
+
// "takerQty": 0.004,
|
|
1518
|
+
// "fees": 0.000004,
|
|
1519
|
+
// "isAncillary": false,
|
|
1520
|
+
// "margin": false,
|
|
1521
|
+
// "trade": false,
|
|
1522
|
+
// "canceled": false
|
|
1523
|
+
// }
|
|
1524
|
+
//
|
|
1525
|
+
return this.parseOrder(response);
|
|
1526
|
+
}
|
|
1527
|
+
parseOrder(order, market = undefined) {
|
|
1528
|
+
//
|
|
1529
|
+
// createOrder market
|
|
1530
|
+
// {
|
|
1531
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1532
|
+
// "orderID": "65671262d93d9525ac009e36170257448481749b7ee2893bafec2",
|
|
1533
|
+
// "orderType": "market",
|
|
1534
|
+
// "buyingCurrency": "ETH",
|
|
1535
|
+
// "sellingCurrency": "USDC",
|
|
1536
|
+
// "buyingQty": 0.002,
|
|
1537
|
+
// "timeInForce": 4,
|
|
1538
|
+
// "boughtQty": 0.002,
|
|
1539
|
+
// "soldQty": 4.587,
|
|
1540
|
+
// "creationTime": 1702574484829,
|
|
1541
|
+
// "seqNumber": 10874285330,
|
|
1542
|
+
// "firstFillTime": 1702574484831,
|
|
1543
|
+
// "lastFillTime": 1702574484831,
|
|
1544
|
+
// "fills": [
|
|
1545
|
+
// {
|
|
1546
|
+
// "seqNumber": 10874285329,
|
|
1547
|
+
// "timestamp": 1702574484831,
|
|
1548
|
+
// "qty": 0.002,
|
|
1549
|
+
// "price": 2293.5,
|
|
1550
|
+
// "side": "buy"
|
|
1551
|
+
// }
|
|
1552
|
+
// ],
|
|
1553
|
+
// "completionTime": 1702574484831,
|
|
1554
|
+
// "takerQty": 0.002
|
|
1555
|
+
// }
|
|
1556
|
+
//
|
|
1557
|
+
// createOrder limit
|
|
1558
|
+
// {
|
|
1559
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1560
|
+
// "orderID": "65671262d93d9525ac009e3617026635256739c996fe17d7cd5d4",
|
|
1561
|
+
// "orderType": "limit",
|
|
1562
|
+
// "buyingCurrency": "ETH",
|
|
1563
|
+
// "sellingCurrency": "USDC",
|
|
1564
|
+
// "fillStyle": "sell",
|
|
1565
|
+
// "orderPlatform": "trade-v3",
|
|
1566
|
+
// "timeInForce": 1,
|
|
1567
|
+
// "buyingQty": 0.005655,
|
|
1568
|
+
// "sellingQty": 11.31,
|
|
1569
|
+
// "boughtQty": 0,
|
|
1570
|
+
// "soldQty": 0,
|
|
1571
|
+
// "creationTime": 1702663525713,
|
|
1572
|
+
// "seqNumber": 10885528683,
|
|
1573
|
+
// "fees": 0,
|
|
1574
|
+
// "fills": [],
|
|
1575
|
+
// "isAncillary": false,
|
|
1576
|
+
// "margin": false,
|
|
1577
|
+
// "trade": false
|
|
1578
|
+
// }
|
|
1579
|
+
//
|
|
1580
|
+
// fetchOrders market
|
|
1581
|
+
// {
|
|
1582
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1583
|
+
// "orderID": "65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c",
|
|
1584
|
+
// "orderType": "market",
|
|
1585
|
+
// "buyingCurrency": "ETH",
|
|
1586
|
+
// "sellingCurrency": "USDC",
|
|
1587
|
+
// "buyingQty": 0.002,
|
|
1588
|
+
// "timeInForce": 4,
|
|
1589
|
+
// "boughtQty": 0.002,
|
|
1590
|
+
// "soldQty": 4.564,
|
|
1591
|
+
// "creationTime": 1702570610746,
|
|
1592
|
+
// "seqNumber": 10873722344,
|
|
1593
|
+
// "firstFillTime": 1702570610747,
|
|
1594
|
+
// "lastFillTime": 1702570610747,
|
|
1595
|
+
// "fills": [
|
|
1596
|
+
// {
|
|
1597
|
+
// "_id": "657b31d360a9542449381bdc",
|
|
1598
|
+
// "seqNumber": 10873722343,
|
|
1599
|
+
// "timestamp": 1702570610747,
|
|
1600
|
+
// "qty": 0.002,
|
|
1601
|
+
// "price": 2282,
|
|
1602
|
+
// "side": "buy"
|
|
1603
|
+
// }
|
|
1604
|
+
// ],
|
|
1605
|
+
// "completionTime": 1702570610747,
|
|
1606
|
+
// "takerQty": 0.002,
|
|
1607
|
+
// "fees": 0.000002,
|
|
1608
|
+
// "isAncillary": false,
|
|
1609
|
+
// "margin": false,
|
|
1610
|
+
// "trade": false,
|
|
1611
|
+
// "canceled": false,
|
|
1612
|
+
// "__v": 0
|
|
1613
|
+
// }
|
|
1614
|
+
//
|
|
1615
|
+
// fetchOrders margin
|
|
1616
|
+
// {
|
|
1617
|
+
// "userData": {
|
|
1618
|
+
// "takeProfit": 1700,
|
|
1619
|
+
// "stopLoss": 2100
|
|
1620
|
+
// },
|
|
1621
|
+
// "_id": "658201d060a95424499394a2",
|
|
1622
|
+
// "seqNumber": 10925300213,
|
|
1623
|
+
// "orderType": "limit",
|
|
1624
|
+
// "buyingCurrency": "EUR",
|
|
1625
|
+
// "sellingCurrency": "ETH",
|
|
1626
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1627
|
+
// "closedQty": 0.03,
|
|
1628
|
+
// "sellingQty": 0.03,
|
|
1629
|
+
// "buyingQty": 58.8,
|
|
1630
|
+
// "creationTime": 1703015281205,
|
|
1631
|
+
// "margin": true,
|
|
1632
|
+
// "timeInForce": 1,
|
|
1633
|
+
// "boughtQty": 59.31,
|
|
1634
|
+
// "orderID": "65671262d93d9525ac009e3617030152811996e5b352556d3d7d8",
|
|
1635
|
+
// "lastFillTime": 1703015281206,
|
|
1636
|
+
// "soldQty": 0.03,
|
|
1637
|
+
// "closedTime": 1703015488488,
|
|
1638
|
+
// "closedVal": 59.375,
|
|
1639
|
+
// "trade": true,
|
|
1640
|
+
// "takerQty": 59.31,
|
|
1641
|
+
// "firstFillTime": 1703015281206,
|
|
1642
|
+
// "completionTime": 1703015281206,
|
|
1643
|
+
// "fills": [
|
|
1644
|
+
// {
|
|
1645
|
+
// "_id": "658201d060a95424499394a3",
|
|
1646
|
+
// "seqNumber": 10925300212,
|
|
1647
|
+
// "side": "sell",
|
|
1648
|
+
// "price": 1977,
|
|
1649
|
+
// "qty": 0.03,
|
|
1650
|
+
// "timestamp": 1703015281206
|
|
1651
|
+
// },
|
|
1652
|
+
// {
|
|
1653
|
+
// "_id": "658201d060a95424499394a4",
|
|
1654
|
+
// "seqNumber": 10925321178,
|
|
1655
|
+
// "timestamp": 1703015488483,
|
|
1656
|
+
// "qty": 0.03,
|
|
1657
|
+
// "price": 1979.1666666666667,
|
|
1658
|
+
// "side": "buy"
|
|
1659
|
+
// }
|
|
1660
|
+
// ],
|
|
1661
|
+
// "fees": 0.11875000200000001,
|
|
1662
|
+
// "settledQtys": {
|
|
1663
|
+
// "ETH": -0.000092842104710025
|
|
1664
|
+
// },
|
|
1665
|
+
// "isAncillary": false,
|
|
1666
|
+
// "canceled": false
|
|
1667
|
+
// }
|
|
1668
|
+
//
|
|
1669
|
+
// fetchOrder
|
|
1670
|
+
// {
|
|
1671
|
+
// "_id": "657b4e6d60a954244939ac6f",
|
|
1672
|
+
// "userID": "65671262d93d9525ac009e36",
|
|
1673
|
+
// "orderID": "65671262d93d9525ac009e361702576531985b78465468b9cc544",
|
|
1674
|
+
// "orderType": "market",
|
|
1675
|
+
// "buyingCurrency": "ETH",
|
|
1676
|
+
// "sellingCurrency": "USDC",
|
|
1677
|
+
// "buyingQty": 0.004,
|
|
1678
|
+
// "timeInForce": 4,
|
|
1679
|
+
// "boughtQty": 0.004,
|
|
1680
|
+
// "soldQty": 9.236,
|
|
1681
|
+
// "creationTime": 1702576531995,
|
|
1682
|
+
// "seqNumber": 10874644062,
|
|
1683
|
+
// "firstFillTime": 1702576531995,
|
|
1684
|
+
// "lastFillTime": 1702576531995,
|
|
1685
|
+
// "fills": [
|
|
1686
|
+
// {
|
|
1687
|
+
// "_id": "657b4e6d60a954244939ac70",
|
|
1688
|
+
// "seqNumber": 10874644061,
|
|
1689
|
+
// "timestamp": 1702576531995,
|
|
1690
|
+
// "qty": 0.004,
|
|
1691
|
+
// "price": 2309,
|
|
1692
|
+
// "side": "buy"
|
|
1693
|
+
// }
|
|
1694
|
+
// ],
|
|
1695
|
+
// "completionTime": 1702576531995,
|
|
1696
|
+
// "takerQty": 0.004,
|
|
1697
|
+
// "fees": 0.000004,
|
|
1698
|
+
// "isAncillary": false,
|
|
1699
|
+
// "margin": false,
|
|
1700
|
+
// "trade": false,
|
|
1701
|
+
// "canceled": false
|
|
1702
|
+
// }
|
|
1703
|
+
//
|
|
1704
|
+
let timestamp = this.safeInteger(order, 'creationTime');
|
|
1705
|
+
const isCanceled = this.safeValue(order, 'canceled');
|
|
1706
|
+
let status = undefined;
|
|
1707
|
+
if (isCanceled === true) {
|
|
1708
|
+
if (timestamp === undefined) {
|
|
1709
|
+
timestamp = this.safeInteger(order, 'completionTime'); // market orders with bad price gain IOC - we mark them as 'rejected'?
|
|
1710
|
+
status = 'rejected'; // these orders don't have the 'creationTime` param and have 'canceled': true
|
|
1711
|
+
}
|
|
1712
|
+
else {
|
|
1713
|
+
status = 'canceled';
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
else {
|
|
1717
|
+
status = this.safeString(order, 'status');
|
|
1718
|
+
order = this.omit(order, 'status'); // we mark orders from fetchOpenOrders with param 'status': 'open'
|
|
1719
|
+
}
|
|
1720
|
+
const type = this.safeString(order, 'orderType');
|
|
1721
|
+
let buyingQty = this.safeString(order, 'buyingQty');
|
|
1722
|
+
let sellingQty = this.safeString(order, 'sellingQty');
|
|
1723
|
+
const boughtQty = this.safeString(order, 'boughtQty');
|
|
1724
|
+
const soldQty = this.safeString(order, 'soldQty');
|
|
1725
|
+
if (type === 'market') {
|
|
1726
|
+
if ((buyingQty === undefined) && (boughtQty !== undefined) && (boughtQty !== '0')) {
|
|
1727
|
+
buyingQty = boughtQty;
|
|
1728
|
+
}
|
|
1729
|
+
if ((sellingQty === undefined) && (soldQty !== undefined) && (soldQty !== '0')) {
|
|
1730
|
+
sellingQty = soldQty;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
const buyingCurrencyId = this.safeString(order, 'buyingCurrency', '');
|
|
1734
|
+
const sellingCurrencyId = this.safeString(order, 'sellingCurrency', '');
|
|
1735
|
+
const byuingIdPlusSellingId = buyingCurrencyId + sellingCurrencyId;
|
|
1736
|
+
const sellingIdPlusBuyingId = sellingCurrencyId + buyingCurrencyId;
|
|
1737
|
+
let side = undefined;
|
|
1738
|
+
let marketId = undefined;
|
|
1739
|
+
let baseAmount = buyingQty;
|
|
1740
|
+
let quoteAmount = buyingQty;
|
|
1741
|
+
let filled = undefined;
|
|
1742
|
+
let cost = undefined;
|
|
1743
|
+
let feeInBaseOrQuote = undefined;
|
|
1744
|
+
const marketsById = this.indexBy(this.markets, 'id');
|
|
1745
|
+
if (this.safeValue(marketsById, byuingIdPlusSellingId) !== undefined) {
|
|
1746
|
+
side = 'buy';
|
|
1747
|
+
marketId = byuingIdPlusSellingId;
|
|
1748
|
+
quoteAmount = sellingQty;
|
|
1749
|
+
filled = boughtQty;
|
|
1750
|
+
cost = soldQty;
|
|
1751
|
+
feeInBaseOrQuote = 'base';
|
|
1752
|
+
}
|
|
1753
|
+
else if (this.safeValue(marketsById, sellingIdPlusBuyingId) !== undefined) {
|
|
1754
|
+
side = 'sell';
|
|
1755
|
+
marketId = sellingIdPlusBuyingId;
|
|
1756
|
+
baseAmount = sellingQty;
|
|
1757
|
+
filled = soldQty;
|
|
1758
|
+
cost = boughtQty;
|
|
1759
|
+
feeInBaseOrQuote = 'quote';
|
|
1760
|
+
}
|
|
1761
|
+
let price = undefined;
|
|
1762
|
+
if ((baseAmount !== undefined) && (quoteAmount !== undefined)) {
|
|
1763
|
+
price = Precise.stringDiv(quoteAmount, baseAmount);
|
|
1764
|
+
}
|
|
1765
|
+
market = this.safeMarket(marketId, market);
|
|
1766
|
+
let fee = undefined;
|
|
1767
|
+
const feeCost = this.safeString(order, 'fees');
|
|
1768
|
+
if ((feeCost !== undefined) && (feeInBaseOrQuote !== undefined)) {
|
|
1769
|
+
fee = {
|
|
1770
|
+
'currency': market[feeInBaseOrQuote],
|
|
1771
|
+
'cost': feeCost,
|
|
1772
|
+
'rate': undefined,
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
const trades = this.safeValue(order, 'fills', []);
|
|
1776
|
+
const userData = this.safeValue(order, 'userData', {});
|
|
1777
|
+
const triggerPrice = this.safeString(order, 'stopPrice');
|
|
1778
|
+
const clientOrderId = this.safeString(userData, 'comment');
|
|
1779
|
+
const takeProfitPrice = this.safeString(userData, 'takeProfit');
|
|
1780
|
+
const stopLossPrice = this.safeString(userData, 'stopLoss');
|
|
1781
|
+
return this.safeOrder({
|
|
1782
|
+
'id': this.safeString(order, 'orderID'),
|
|
1783
|
+
'clientOrderId': clientOrderId,
|
|
1784
|
+
'timestamp': timestamp,
|
|
1785
|
+
'datetime': this.iso8601(timestamp),
|
|
1786
|
+
'lastTradeTimestamp': this.safeInteger(order, 'lastFillTime'),
|
|
1787
|
+
'status': status,
|
|
1788
|
+
'symbol': market['symbol'],
|
|
1789
|
+
'type': type,
|
|
1790
|
+
'timeInForce': this.parseOrderTimeInForce(this.safeInteger(order, 'timeInForce')),
|
|
1791
|
+
'side': side,
|
|
1792
|
+
'price': price,
|
|
1793
|
+
'triggerPrice': triggerPrice,
|
|
1794
|
+
'takeProfitPrice': takeProfitPrice,
|
|
1795
|
+
'stopLossPrice': stopLossPrice,
|
|
1796
|
+
'average': undefined,
|
|
1797
|
+
'amount': baseAmount,
|
|
1798
|
+
'cost': cost,
|
|
1799
|
+
'filled': filled,
|
|
1800
|
+
'remaining': undefined,
|
|
1801
|
+
'fee': fee,
|
|
1802
|
+
'fees': undefined,
|
|
1803
|
+
'trades': trades,
|
|
1804
|
+
'info': order,
|
|
1805
|
+
}, market);
|
|
1806
|
+
}
|
|
1807
|
+
parseOrderTimeInForce(timeInForce) {
|
|
1808
|
+
const timeInForceTypes = [
|
|
1809
|
+
undefined,
|
|
1810
|
+
'GTC',
|
|
1811
|
+
'IOC',
|
|
1812
|
+
'GTD',
|
|
1813
|
+
'FOK',
|
|
1814
|
+
];
|
|
1815
|
+
return this.safeValue(timeInForceTypes, timeInForce, timeInForce);
|
|
1816
|
+
}
|
|
1817
|
+
async borrowCrossMargin(code, amount, params = {}) {
|
|
1818
|
+
/**
|
|
1819
|
+
* @method
|
|
1820
|
+
* @name coinmetro#borrowCrossMargin
|
|
1821
|
+
* @description create a loan to borrow margin
|
|
1822
|
+
* @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#5b90b3b9-e5db-4d07-ac9d-d680a06fd110
|
|
1823
|
+
* @param {string} code unified currency code of the currency to borrow
|
|
1824
|
+
* @param {float} amount the amount to borrow
|
|
1825
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
1826
|
+
* @returns {object} a [margin loan structure]{@link https://docs.ccxt.com/#/?id=margin-loan-structure}
|
|
1827
|
+
*/
|
|
1828
|
+
await this.loadMarkets();
|
|
1829
|
+
const currency = this.currency(code);
|
|
1830
|
+
const currencyId = currency['id'];
|
|
1831
|
+
const request = {};
|
|
1832
|
+
request[currencyId] = this.currencyToPrecision(code, amount);
|
|
1833
|
+
const response = await this.privatePutUsersMarginCollateral(this.extend(request, params));
|
|
1834
|
+
//
|
|
1835
|
+
// { "message": "OK" }
|
|
1836
|
+
//
|
|
1837
|
+
const result = this.safeValue(response, 'result', {});
|
|
1838
|
+
const transaction = this.parseMarginLoan(result, currency);
|
|
1839
|
+
return this.extend(transaction, {
|
|
1840
|
+
'amount': amount,
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
parseMarginLoan(info, currency = undefined) {
|
|
1844
|
+
const currencyId = this.safeString(info, 'coin');
|
|
1845
|
+
return {
|
|
1846
|
+
'id': undefined,
|
|
1847
|
+
'currency': this.safeCurrencyCode(currencyId, currency),
|
|
1848
|
+
'amount': undefined,
|
|
1849
|
+
'symbol': undefined,
|
|
1850
|
+
'timestamp': undefined,
|
|
1851
|
+
'datetime': undefined,
|
|
1852
|
+
'info': info,
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
|
1856
|
+
const request = this.omit(params, this.extractParams(path));
|
|
1857
|
+
const endpoint = '/' + this.implodeParams(path, params);
|
|
1858
|
+
let url = this.urls['api'][api] + endpoint;
|
|
1859
|
+
const query = this.urlencode(request);
|
|
1860
|
+
if (api === 'private') {
|
|
1861
|
+
if (headers === undefined) {
|
|
1862
|
+
headers = {};
|
|
1863
|
+
}
|
|
1864
|
+
headers['CCXT'] = true;
|
|
1865
|
+
if (url === 'https://api.coinmetro.com/jwt') { // handle with headers for login endpoint
|
|
1866
|
+
headers['X-Device-Id'] = 'bypass';
|
|
1867
|
+
if (this.twofa !== undefined) {
|
|
1868
|
+
headers['X-OTP'] = this.twofa;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
else if (url === 'https://api.coinmetro.com/jwtDevice') { // handle with headers for long lived token login endpoint
|
|
1872
|
+
headers['X-Device-Id'] = this.uid;
|
|
1873
|
+
if (this.twofa !== undefined) {
|
|
1874
|
+
headers['X-OTP'] = this.twofa;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
else {
|
|
1878
|
+
headers['Authorization'] = 'Bearer ' + this.token;
|
|
1879
|
+
if (!url.startsWith('https://api.coinmetro.com/open')) { // if not sandbox endpoint
|
|
1880
|
+
this.checkRequiredCredentials();
|
|
1881
|
+
headers['X-Device-Id'] = this.uid;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
if ((method === 'POST') || (method === 'PUT')) {
|
|
1885
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
1886
|
+
body = this.urlencode(request);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
else if (query.length !== 0) {
|
|
1890
|
+
url += '?' + query;
|
|
1891
|
+
}
|
|
1892
|
+
while (url.endsWith('/')) {
|
|
1893
|
+
url = url.slice(0, url.length - 1);
|
|
1894
|
+
}
|
|
1895
|
+
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
|
1896
|
+
}
|
|
1897
|
+
handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
|
|
1898
|
+
if (response === undefined) {
|
|
1899
|
+
return undefined;
|
|
1900
|
+
}
|
|
1901
|
+
if ((code !== 200) && (code !== 201) && (code !== 202)) {
|
|
1902
|
+
const feedback = this.id + ' ' + body;
|
|
1903
|
+
const message = this.safeString(response, 'message');
|
|
1904
|
+
this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
|
|
1905
|
+
this.throwExactlyMatchedException(this.exceptions['exact'], message, feedback);
|
|
1906
|
+
throw new ExchangeError(feedback);
|
|
1907
|
+
}
|
|
1908
|
+
return undefined;
|
|
1909
|
+
}
|
|
1910
|
+
}
|