ccxt 4.5.0 → 4.5.2
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 +110 -112
- package/dist/ccxt.browser.min.js +5 -5
- package/dist/cjs/ccxt.js +1 -9
- package/dist/cjs/src/ascendex.js +1 -1
- package/dist/cjs/src/base/Exchange.js +13 -0
- package/dist/cjs/src/binance.js +20 -14
- package/dist/cjs/src/bitget.js +1 -1
- package/dist/cjs/src/coinbase.js +46 -34
- package/dist/cjs/src/gate.js +39 -18
- package/dist/cjs/src/gemini.js +1 -1
- package/dist/cjs/src/hibachi.js +1 -1
- package/dist/cjs/src/hyperliquid.js +16 -2
- package/dist/cjs/src/indodax.js +11 -12
- package/dist/cjs/src/kraken.js +1 -12
- package/dist/cjs/src/krakenfutures.js +25 -25
- package/dist/cjs/src/mexc.js +2 -1
- package/dist/cjs/src/okx.js +2 -2
- package/dist/cjs/src/poloniex.js +1 -1
- package/dist/cjs/src/pro/bitget.js +352 -75
- package/dist/cjs/src/pro/bitmart.js +1 -1
- package/dist/cjs/src/pro/bybit.js +8 -15
- package/dist/cjs/src/pro/gate.js +6 -1
- package/dist/cjs/src/pro/gemini.js +7 -2
- package/dist/cjs/src/pro/hyperliquid.js +9 -1
- package/dist/cjs/src/pro/kraken.js +5 -6
- package/dist/cjs/src/pro/lbank.js +55 -1
- package/dist/cjs/src/pro/mexc.js +1 -1
- package/dist/cjs/src/timex.js +35 -0
- package/dist/cjs/src/tradeogre.js +32 -0
- package/dist/cjs/src/wavesexchange.js +33 -0
- package/dist/cjs/src/zonda.js +12 -0
- package/js/ccxt.d.ts +2 -11
- package/js/ccxt.js +2 -8
- package/js/src/ascendex.js +1 -1
- package/js/src/base/Exchange.d.ts +1 -0
- package/js/src/base/Exchange.js +14 -1
- package/js/src/binance.d.ts +1 -1
- package/js/src/binance.js +20 -14
- package/js/src/bitget.js +1 -1
- package/js/src/coinbase.js +46 -34
- package/js/src/gate.d.ts +2 -1
- package/js/src/gate.js +39 -18
- package/js/src/gemini.js +1 -1
- package/js/src/hibachi.js +1 -1
- package/js/src/hyperliquid.d.ts +1 -0
- package/js/src/hyperliquid.js +16 -2
- package/js/src/indodax.js +11 -12
- package/js/src/kraken.d.ts +0 -1
- package/js/src/kraken.js +1 -12
- package/js/src/krakenfutures.d.ts +24 -24
- package/js/src/krakenfutures.js +25 -25
- package/js/src/mexc.js +2 -1
- package/js/src/okx.js +2 -2
- package/js/src/poloniex.js +1 -1
- package/js/src/pro/bitget.d.ts +12 -2
- package/js/src/pro/bitget.js +358 -75
- package/js/src/pro/bitmart.js +1 -1
- package/js/src/pro/bybit.js +8 -15
- package/js/src/pro/gate.d.ts +5 -0
- package/js/src/pro/gate.js +6 -1
- package/js/src/pro/gemini.d.ts +1 -1
- package/js/src/pro/gemini.js +7 -2
- package/js/src/pro/hyperliquid.js +9 -1
- package/js/src/pro/kraken.js +5 -6
- package/js/src/pro/lbank.d.ts +11 -1
- package/js/src/pro/lbank.js +55 -1
- package/js/src/pro/mexc.js +1 -1
- package/js/src/timex.js +35 -0
- package/js/src/tradeogre.js +32 -0
- package/js/src/wavesexchange.js +33 -0
- package/js/src/zonda.js +12 -0
- package/package.json +2 -1
- package/js/src/abstract/ellipx.d.ts +0 -28
- package/js/src/abstract/ellipx.js +0 -11
- package/js/src/abstract/vertex.d.ts +0 -22
- package/js/src/abstract/vertex.js +0 -11
- package/js/src/ellipx.d.ts +0 -237
- package/js/src/ellipx.js +0 -2071
- package/js/src/pro/vertex.d.ts +0 -104
- package/js/src/pro/vertex.js +0 -999
- package/js/src/vertex.d.ts +0 -346
- package/js/src/vertex.js +0 -3146
package/js/src/pro/vertex.js
DELETED
|
@@ -1,999 +0,0 @@
|
|
|
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 vertexRest from '../vertex.js';
|
|
9
|
-
import { AuthenticationError, NotSupported, ArgumentsRequired } from '../base/errors.js';
|
|
10
|
-
import { ArrayCacheBySymbolById, ArrayCache, ArrayCacheBySymbolBySide } from '../base/ws/Cache.js';
|
|
11
|
-
import { Precise } from '../base/Precise.js';
|
|
12
|
-
// ----------------------------------------------------------------------------
|
|
13
|
-
export default class vertex extends vertexRest {
|
|
14
|
-
describe() {
|
|
15
|
-
return this.deepExtend(super.describe(), {
|
|
16
|
-
'has': {
|
|
17
|
-
'ws': true,
|
|
18
|
-
'watchBalance': false,
|
|
19
|
-
'watchMyTrades': true,
|
|
20
|
-
'watchOHLCV': false,
|
|
21
|
-
'watchOrderBook': true,
|
|
22
|
-
'watchOrders': true,
|
|
23
|
-
'watchTicker': true,
|
|
24
|
-
'watchTickers': false,
|
|
25
|
-
'watchTrades': true,
|
|
26
|
-
'watchTradesForSymbols': false,
|
|
27
|
-
'watchPositions': true,
|
|
28
|
-
},
|
|
29
|
-
'urls': {
|
|
30
|
-
'api': {
|
|
31
|
-
'ws': 'wss://gateway.prod.vertexprotocol.com/v1/subscribe',
|
|
32
|
-
},
|
|
33
|
-
'test': {
|
|
34
|
-
'ws': 'wss://gateway.sepolia-test.vertexprotocol.com/v1/subscribe',
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
'requiredCredentials': {
|
|
38
|
-
'apiKey': false,
|
|
39
|
-
'secret': false,
|
|
40
|
-
'walletAddress': true,
|
|
41
|
-
'privateKey': true,
|
|
42
|
-
},
|
|
43
|
-
'options': {
|
|
44
|
-
'tradesLimit': 1000,
|
|
45
|
-
'ordersLimit': 1000,
|
|
46
|
-
'requestId': {},
|
|
47
|
-
'watchPositions': {
|
|
48
|
-
'fetchPositionsSnapshot': true,
|
|
49
|
-
'awaitPositionsSnapshot': true, // whether to wait for the positions snapshot before providing updates
|
|
50
|
-
},
|
|
51
|
-
'ws': {
|
|
52
|
-
'inflate': true,
|
|
53
|
-
'options': {
|
|
54
|
-
'headers': {
|
|
55
|
-
'Sec-WebSocket-Extensions': 'permessage-deflate', // requires permessage-deflate extension, maybe we can set this in client implementation when inflate is true
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
'streaming': {
|
|
61
|
-
// 'ping': this.ping,
|
|
62
|
-
'keepAlive': 30000,
|
|
63
|
-
},
|
|
64
|
-
'exceptions': {
|
|
65
|
-
'ws': {
|
|
66
|
-
'exact': {
|
|
67
|
-
'Auth is needed.': AuthenticationError,
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
requestId(url) {
|
|
74
|
-
const options = this.safeDict(this.options, 'requestId', {});
|
|
75
|
-
const previousValue = this.safeInteger(options, url, 0);
|
|
76
|
-
const newValue = this.sum(previousValue, 1);
|
|
77
|
-
this.options['requestId'][url] = newValue;
|
|
78
|
-
return newValue;
|
|
79
|
-
}
|
|
80
|
-
async watchPublic(messageHash, message) {
|
|
81
|
-
const url = this.urls['api']['ws'];
|
|
82
|
-
const requestId = this.requestId(url);
|
|
83
|
-
const subscribe = {
|
|
84
|
-
'id': requestId,
|
|
85
|
-
};
|
|
86
|
-
const request = this.extend(subscribe, message);
|
|
87
|
-
const wsOptions = {
|
|
88
|
-
'headers': {
|
|
89
|
-
'Sec-WebSocket-Extensions': 'permessage-deflate',
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
this.options['ws'] = {
|
|
93
|
-
'options': wsOptions,
|
|
94
|
-
};
|
|
95
|
-
return await this.watch(url, messageHash, request, messageHash, subscribe);
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* @method
|
|
99
|
-
* @name vertex#watchTrades
|
|
100
|
-
* @description watches information on multiple trades made in a market
|
|
101
|
-
* @see https://docs.vertexprotocol.com/developer-resources/api/subscriptions/streams
|
|
102
|
-
* @param {string} symbol unified market symbol of the market trades were made in
|
|
103
|
-
* @param {int} [since] the earliest time in ms to fetch trades for
|
|
104
|
-
* @param {int} [limit] the maximum number of trade structures to retrieve
|
|
105
|
-
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
106
|
-
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
|
|
107
|
-
*/
|
|
108
|
-
async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
|
|
109
|
-
await this.loadMarkets();
|
|
110
|
-
const market = this.market(symbol);
|
|
111
|
-
const name = 'trade';
|
|
112
|
-
const topic = market['id'] + '@' + name;
|
|
113
|
-
const request = {
|
|
114
|
-
'method': 'subscribe',
|
|
115
|
-
'stream': {
|
|
116
|
-
'type': name,
|
|
117
|
-
'product_id': this.parseToNumeric(market['id']),
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
const message = this.extend(request, params);
|
|
121
|
-
const trades = await this.watchPublic(topic, message);
|
|
122
|
-
if (this.newUpdates) {
|
|
123
|
-
limit = trades.getLimit(market['symbol'], limit);
|
|
124
|
-
}
|
|
125
|
-
return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
|
|
126
|
-
}
|
|
127
|
-
handleTrade(client, message) {
|
|
128
|
-
//
|
|
129
|
-
// {
|
|
130
|
-
// "type": "trade",
|
|
131
|
-
// "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
|
|
132
|
-
// "product_id": 1,
|
|
133
|
-
// "price": "1000", // price the trade happened at, multiplied by 1e18
|
|
134
|
-
// // both taker_qty and maker_qty have the same value;
|
|
135
|
-
// // set to filled amount (min amount of taker and maker) when matching against book
|
|
136
|
-
// // set to matched amm base amount when matching against amm
|
|
137
|
-
// "taker_qty": "1000",
|
|
138
|
-
// "maker_qty": "1000",
|
|
139
|
-
// "is_taker_buyer": true,
|
|
140
|
-
// "is_maker_amm": true // true when maker is amm
|
|
141
|
-
// }
|
|
142
|
-
//
|
|
143
|
-
const topic = this.safeString(message, 'type');
|
|
144
|
-
const marketId = this.safeString(message, 'product_id');
|
|
145
|
-
const trade = this.parseWsTrade(message);
|
|
146
|
-
const symbol = trade['symbol'];
|
|
147
|
-
if (!(symbol in this.trades)) {
|
|
148
|
-
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
|
|
149
|
-
const stored = new ArrayCache(limit);
|
|
150
|
-
this.trades[symbol] = stored;
|
|
151
|
-
}
|
|
152
|
-
const trades = this.trades[symbol];
|
|
153
|
-
trades.append(trade);
|
|
154
|
-
this.trades[symbol] = trades;
|
|
155
|
-
client.resolve(trades, marketId + '@' + topic);
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* @method
|
|
159
|
-
* @name vertex#watchMyTrades
|
|
160
|
-
* @description watches information on multiple trades made by the user
|
|
161
|
-
* @see https://docs.vertexprotocol.com/developer-resources/api/subscriptions/streams
|
|
162
|
-
* @param {string} symbol unified market symbol of the market orders were made in
|
|
163
|
-
* @param {int} [since] the earliest time in ms to fetch orders for
|
|
164
|
-
* @param {int} [limit] the maximum number of order structures to retrieve
|
|
165
|
-
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
166
|
-
* @param {string} [params.user] user address, will default to this.walletAddress if not provided
|
|
167
|
-
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
168
|
-
*/
|
|
169
|
-
async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
170
|
-
if (symbol === undefined) {
|
|
171
|
-
throw new ArgumentsRequired(this.id + ' watchMyTrades requires a symbol.');
|
|
172
|
-
}
|
|
173
|
-
await this.loadMarkets();
|
|
174
|
-
let userAddress = undefined;
|
|
175
|
-
[userAddress, params] = this.handlePublicAddress('watchMyTrades', params);
|
|
176
|
-
const market = this.market(symbol);
|
|
177
|
-
const name = 'fill';
|
|
178
|
-
const topic = market['id'] + '@' + name;
|
|
179
|
-
const request = {
|
|
180
|
-
'method': 'subscribe',
|
|
181
|
-
'stream': {
|
|
182
|
-
'type': name,
|
|
183
|
-
'product_id': this.parseToNumeric(market['id']),
|
|
184
|
-
'subaccount': this.convertAddressToSender(userAddress),
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
const message = this.extend(request, params);
|
|
188
|
-
const trades = await this.watchPublic(topic, message);
|
|
189
|
-
if (this.newUpdates) {
|
|
190
|
-
limit = trades.getLimit(symbol, limit);
|
|
191
|
-
}
|
|
192
|
-
return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
|
|
193
|
-
}
|
|
194
|
-
handleMyTrades(client, message) {
|
|
195
|
-
//
|
|
196
|
-
// {
|
|
197
|
-
// "type": "fill",
|
|
198
|
-
// "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
|
|
199
|
-
// "product_id": 1,
|
|
200
|
-
// // the subaccount that placed this order
|
|
201
|
-
// "subaccount": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43746573743000000000000000",
|
|
202
|
-
// // hash of the order that uniquely identifies it
|
|
203
|
-
// "order_digest": "0xf4f7a8767faf0c7f72251a1f9e5da590f708fd9842bf8fcdeacbaa0237958fff",
|
|
204
|
-
// // the amount filled, multiplied by 1e18
|
|
205
|
-
// "filled_qty": "1000",
|
|
206
|
-
// // the amount outstanding unfilled, multiplied by 1e18
|
|
207
|
-
// "remaining_qty": "2000",
|
|
208
|
-
// // the original order amount, multiplied by 1e18
|
|
209
|
-
// "original_qty": "3000",
|
|
210
|
-
// // fill price
|
|
211
|
-
// "price": "24991000000000000000000",
|
|
212
|
-
// // true for `taker`, false for `maker`
|
|
213
|
-
// "is_taker": true,
|
|
214
|
-
// "is_bid": true,
|
|
215
|
-
// // true when matching against amm
|
|
216
|
-
// "is_against_amm": true,
|
|
217
|
-
// // an optional `order id` that can be provided when placing an order
|
|
218
|
-
// "id": 100
|
|
219
|
-
// }
|
|
220
|
-
//
|
|
221
|
-
const topic = this.safeString(message, 'type');
|
|
222
|
-
const marketId = this.safeString(message, 'product_id');
|
|
223
|
-
if (this.myTrades === undefined) {
|
|
224
|
-
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
|
|
225
|
-
this.myTrades = new ArrayCacheBySymbolById(limit);
|
|
226
|
-
}
|
|
227
|
-
const trades = this.myTrades;
|
|
228
|
-
const parsed = this.parseWsTrade(message);
|
|
229
|
-
trades.append(parsed);
|
|
230
|
-
client.resolve(trades, marketId + '@' + topic);
|
|
231
|
-
}
|
|
232
|
-
parseWsTrade(trade, market = undefined) {
|
|
233
|
-
//
|
|
234
|
-
// watchTrades
|
|
235
|
-
// {
|
|
236
|
-
// "type": "trade",
|
|
237
|
-
// "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
|
|
238
|
-
// "product_id": 1,
|
|
239
|
-
// "price": "1000", // price the trade happened at, multiplied by 1e18
|
|
240
|
-
// // both taker_qty and maker_qty have the same value;
|
|
241
|
-
// // set to filled amount (min amount of taker and maker) when matching against book
|
|
242
|
-
// // set to matched amm base amount when matching against amm
|
|
243
|
-
// "taker_qty": "1000",
|
|
244
|
-
// "maker_qty": "1000",
|
|
245
|
-
// "is_taker_buyer": true,
|
|
246
|
-
// "is_maker_amm": true // true when maker is amm
|
|
247
|
-
// }
|
|
248
|
-
// watchMyTrades
|
|
249
|
-
// {
|
|
250
|
-
// "type": "fill",
|
|
251
|
-
// "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
|
|
252
|
-
// "product_id": 1,
|
|
253
|
-
// // the subaccount that placed this order
|
|
254
|
-
// "subaccount": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43746573743000000000000000",
|
|
255
|
-
// // hash of the order that uniquely identifies it
|
|
256
|
-
// "order_digest": "0xf4f7a8767faf0c7f72251a1f9e5da590f708fd9842bf8fcdeacbaa0237958fff",
|
|
257
|
-
// // the amount filled, multiplied by 1e18
|
|
258
|
-
// "filled_qty": "1000",
|
|
259
|
-
// // the amount outstanding unfilled, multiplied by 1e18
|
|
260
|
-
// "remaining_qty": "2000",
|
|
261
|
-
// // the original order amount, multiplied by 1e18
|
|
262
|
-
// "original_qty": "3000",
|
|
263
|
-
// // fill price
|
|
264
|
-
// "price": "24991000000000000000000",
|
|
265
|
-
// // true for `taker`, false for `maker`
|
|
266
|
-
// "is_taker": true,
|
|
267
|
-
// "is_bid": true,
|
|
268
|
-
// // true when matching against amm
|
|
269
|
-
// "is_against_amm": true,
|
|
270
|
-
// // an optional `order id` that can be provided when placing an order
|
|
271
|
-
// "id": 100
|
|
272
|
-
// }
|
|
273
|
-
//
|
|
274
|
-
const marketId = this.safeString(trade, 'product_id');
|
|
275
|
-
market = this.safeMarket(marketId, market);
|
|
276
|
-
const symbol = market['symbol'];
|
|
277
|
-
const price = this.convertFromX18(this.safeString(trade, 'price'));
|
|
278
|
-
const amount = this.convertFromX18(this.safeString2(trade, 'taker_qty', 'filled_qty'));
|
|
279
|
-
const cost = Precise.stringMul(price, amount);
|
|
280
|
-
const timestamp = this.safeIntegerProduct(trade, 'timestamp', 0.000001);
|
|
281
|
-
let takerOrMaker = undefined;
|
|
282
|
-
const isTaker = this.safeBool(trade, 'is_taker');
|
|
283
|
-
if (isTaker !== undefined) {
|
|
284
|
-
takerOrMaker = (isTaker) ? 'taker' : 'maker';
|
|
285
|
-
}
|
|
286
|
-
let side = undefined;
|
|
287
|
-
const isBid = this.safeBool(trade, 'is_bid');
|
|
288
|
-
if (isBid !== undefined) {
|
|
289
|
-
side = (isBid) ? 'buy' : 'sell';
|
|
290
|
-
}
|
|
291
|
-
return this.safeTrade({
|
|
292
|
-
'id': undefined,
|
|
293
|
-
'timestamp': timestamp,
|
|
294
|
-
'datetime': this.iso8601(timestamp),
|
|
295
|
-
'symbol': symbol,
|
|
296
|
-
'side': side,
|
|
297
|
-
'price': price,
|
|
298
|
-
'amount': amount,
|
|
299
|
-
'cost': cost,
|
|
300
|
-
'order': this.safeString2(trade, 'digest', 'id'),
|
|
301
|
-
'takerOrMaker': takerOrMaker,
|
|
302
|
-
'type': undefined,
|
|
303
|
-
'fee': undefined,
|
|
304
|
-
'info': trade,
|
|
305
|
-
}, market);
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* @method
|
|
309
|
-
* @name vertex#watchTicker
|
|
310
|
-
* @see https://docs.vertexprotocol.com/developer-resources/api/subscriptions/streams
|
|
311
|
-
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
312
|
-
* @param {string} symbol unified symbol of the market to fetch the ticker for
|
|
313
|
-
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
314
|
-
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
315
|
-
*/
|
|
316
|
-
async watchTicker(symbol, params = {}) {
|
|
317
|
-
await this.loadMarkets();
|
|
318
|
-
const name = 'best_bid_offer';
|
|
319
|
-
const market = this.market(symbol);
|
|
320
|
-
const topic = market['id'] + '@' + name;
|
|
321
|
-
const request = {
|
|
322
|
-
'method': 'subscribe',
|
|
323
|
-
'stream': {
|
|
324
|
-
'type': name,
|
|
325
|
-
'product_id': this.parseToNumeric(market['id']),
|
|
326
|
-
},
|
|
327
|
-
};
|
|
328
|
-
const message = this.extend(request, params);
|
|
329
|
-
return await this.watchPublic(topic, message);
|
|
330
|
-
}
|
|
331
|
-
parseWsTicker(ticker, market = undefined) {
|
|
332
|
-
//
|
|
333
|
-
// {
|
|
334
|
-
// "type": "best_bid_offer",
|
|
335
|
-
// "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
|
|
336
|
-
// "product_id": 1,
|
|
337
|
-
// "bid_price": "1000", // the highest bid price, multiplied by 1e18
|
|
338
|
-
// "bid_qty": "1000", // quantity at the huighest bid, multiplied by 1e18.
|
|
339
|
-
// // i.e. if this is USDC with 6 decimals, one USDC
|
|
340
|
-
// // would be 1e12
|
|
341
|
-
// "ask_price": "1000", // lowest ask price
|
|
342
|
-
// "ask_qty": "1000" // quantity at the lowest ask
|
|
343
|
-
// }
|
|
344
|
-
//
|
|
345
|
-
const timestamp = this.safeIntegerProduct(ticker, 'timestamp', 0.000001);
|
|
346
|
-
return this.safeTicker({
|
|
347
|
-
'symbol': this.safeSymbol(undefined, market),
|
|
348
|
-
'timestamp': timestamp,
|
|
349
|
-
'datetime': this.iso8601(timestamp),
|
|
350
|
-
'high': this.safeString(ticker, 'high'),
|
|
351
|
-
'low': this.safeString(ticker, 'low'),
|
|
352
|
-
'bid': this.convertFromX18(this.safeString(ticker, 'bid_price')),
|
|
353
|
-
'bidVolume': this.convertFromX18(this.safeString(ticker, 'bid_qty')),
|
|
354
|
-
'ask': this.convertFromX18(this.safeString(ticker, 'ask_price')),
|
|
355
|
-
'askVolume': this.convertFromX18(this.safeString(ticker, 'ask_qty')),
|
|
356
|
-
'vwap': undefined,
|
|
357
|
-
'open': undefined,
|
|
358
|
-
'close': undefined,
|
|
359
|
-
'last': undefined,
|
|
360
|
-
'previousClose': undefined,
|
|
361
|
-
'change': undefined,
|
|
362
|
-
'percentage': undefined,
|
|
363
|
-
'average': undefined,
|
|
364
|
-
'baseVolume': undefined,
|
|
365
|
-
'quoteVolume': undefined,
|
|
366
|
-
'info': ticker,
|
|
367
|
-
}, market);
|
|
368
|
-
}
|
|
369
|
-
handleTicker(client, message) {
|
|
370
|
-
//
|
|
371
|
-
// {
|
|
372
|
-
// "type": "best_bid_offer",
|
|
373
|
-
// "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
|
|
374
|
-
// "product_id": 1,
|
|
375
|
-
// "bid_price": "1000", // the highest bid price, multiplied by 1e18
|
|
376
|
-
// "bid_qty": "1000", // quantity at the huighest bid, multiplied by 1e18.
|
|
377
|
-
// // i.e. if this is USDC with 6 decimals, one USDC
|
|
378
|
-
// // would be 1e12
|
|
379
|
-
// "ask_price": "1000", // lowest ask price
|
|
380
|
-
// "ask_qty": "1000" // quantity at the lowest ask
|
|
381
|
-
// }
|
|
382
|
-
//
|
|
383
|
-
const marketId = this.safeString(message, 'product_id');
|
|
384
|
-
const market = this.safeMarket(marketId);
|
|
385
|
-
const ticker = this.parseWsTicker(message, market);
|
|
386
|
-
ticker['symbol'] = market['symbol'];
|
|
387
|
-
this.tickers[market['symbol']] = ticker;
|
|
388
|
-
client.resolve(ticker, marketId + '@best_bid_offer');
|
|
389
|
-
return message;
|
|
390
|
-
}
|
|
391
|
-
/**
|
|
392
|
-
* @method
|
|
393
|
-
* @name vertex#watchOrderBook
|
|
394
|
-
* @see https://docs.vertexprotocol.com/developer-resources/api/subscriptions/streams
|
|
395
|
-
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
396
|
-
* @param {string} symbol unified symbol of the market to fetch the order book for
|
|
397
|
-
* @param {int} [limit] the maximum amount of order book entries to return.
|
|
398
|
-
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
399
|
-
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
400
|
-
*/
|
|
401
|
-
async watchOrderBook(symbol, limit = undefined, params = {}) {
|
|
402
|
-
await this.loadMarkets();
|
|
403
|
-
const name = 'book_depth';
|
|
404
|
-
const market = this.market(symbol);
|
|
405
|
-
const messageHash = market['id'] + '@' + name;
|
|
406
|
-
const url = this.urls['api']['ws'];
|
|
407
|
-
const requestId = this.requestId(url);
|
|
408
|
-
const request = {
|
|
409
|
-
'id': requestId,
|
|
410
|
-
'method': 'subscribe',
|
|
411
|
-
'stream': {
|
|
412
|
-
'type': name,
|
|
413
|
-
'product_id': this.parseToNumeric(market['id']),
|
|
414
|
-
},
|
|
415
|
-
};
|
|
416
|
-
const subscription = {
|
|
417
|
-
'id': requestId.toString(),
|
|
418
|
-
'name': name,
|
|
419
|
-
'symbol': symbol,
|
|
420
|
-
'method': this.handleOrderBookSubscription,
|
|
421
|
-
'limit': limit,
|
|
422
|
-
'params': params,
|
|
423
|
-
};
|
|
424
|
-
const message = this.extend(request, params);
|
|
425
|
-
const orderbook = await this.watch(url, messageHash, message, messageHash, subscription);
|
|
426
|
-
return orderbook.limit();
|
|
427
|
-
}
|
|
428
|
-
handleOrderBookSubscription(client, message, subscription) {
|
|
429
|
-
const defaultLimit = this.safeInteger(this.options, 'watchOrderBookLimit', 1000);
|
|
430
|
-
const limit = this.safeInteger(subscription, 'limit', defaultLimit);
|
|
431
|
-
const symbol = this.safeString(subscription, 'symbol'); // watchOrderBook
|
|
432
|
-
if (symbol in this.orderbooks) {
|
|
433
|
-
delete this.orderbooks[symbol];
|
|
434
|
-
}
|
|
435
|
-
this.orderbooks[symbol] = this.orderBook({}, limit);
|
|
436
|
-
this.spawn(this.fetchOrderBookSnapshot, client, message, subscription);
|
|
437
|
-
}
|
|
438
|
-
async fetchOrderBookSnapshot(client, message, subscription) {
|
|
439
|
-
const symbol = this.safeString(subscription, 'symbol');
|
|
440
|
-
const market = this.market(symbol);
|
|
441
|
-
const messageHash = market['id'] + '@book_depth';
|
|
442
|
-
try {
|
|
443
|
-
const defaultLimit = this.safeInteger(this.options, 'watchOrderBookLimit', 1000);
|
|
444
|
-
const limit = this.safeInteger(subscription, 'limit', defaultLimit);
|
|
445
|
-
const params = this.safeValue(subscription, 'params');
|
|
446
|
-
const snapshot = await this.fetchRestOrderBookSafe(symbol, limit, params);
|
|
447
|
-
if (this.safeValue(this.orderbooks, symbol) === undefined) {
|
|
448
|
-
// if the orderbook is dropped before the snapshot is received
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
const orderbook = this.orderbooks[symbol];
|
|
452
|
-
orderbook.reset(snapshot);
|
|
453
|
-
const messages = orderbook.cache;
|
|
454
|
-
for (let i = 0; i < messages.length; i++) {
|
|
455
|
-
const messageItem = messages[i];
|
|
456
|
-
const lastTimestamp = this.parseToInt(Precise.stringDiv(this.safeString(messageItem, 'last_max_timestamp'), '1000000'));
|
|
457
|
-
if (lastTimestamp < orderbook['timestamp']) {
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
this.handleOrderBookMessage(client, messageItem, orderbook);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
this.orderbooks[symbol] = orderbook;
|
|
465
|
-
client.resolve(orderbook, messageHash);
|
|
466
|
-
}
|
|
467
|
-
catch (e) {
|
|
468
|
-
delete client.subscriptions[messageHash];
|
|
469
|
-
client.reject(e, messageHash);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
handleOrderBook(client, message) {
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
// the feed does not include a snapshot, just the deltas
|
|
476
|
-
//
|
|
477
|
-
// {
|
|
478
|
-
// "type":"book_depth",
|
|
479
|
-
// // book depth aggregates a number of events once every 50ms
|
|
480
|
-
// // these are the minimum and maximum timestamps from
|
|
481
|
-
// // events that contributed to this response
|
|
482
|
-
// "min_timestamp": "1683805381879572835",
|
|
483
|
-
// "max_timestamp": "1683805381879572835",
|
|
484
|
-
// // the max_timestamp of the last book_depth event for this product
|
|
485
|
-
// "last_max_timestamp": "1683805381771464799",
|
|
486
|
-
// "product_id":1,
|
|
487
|
-
// // changes to the bid side of the book in the form of [[price, new_qty]]
|
|
488
|
-
// "bids":[["21594490000000000000000","51007390115411548"]],
|
|
489
|
-
// // changes to the ask side of the book in the form of [[price, new_qty]]
|
|
490
|
-
// "asks":[["21694490000000000000000","0"],["21695050000000000000000","0"]]
|
|
491
|
-
// }
|
|
492
|
-
//
|
|
493
|
-
const marketId = this.safeString(message, 'product_id');
|
|
494
|
-
const market = this.safeMarket(marketId);
|
|
495
|
-
const symbol = market['symbol'];
|
|
496
|
-
if (!(symbol in this.orderbooks)) {
|
|
497
|
-
this.orderbooks[symbol] = this.orderBook();
|
|
498
|
-
}
|
|
499
|
-
const orderbook = this.orderbooks[symbol];
|
|
500
|
-
const timestamp = this.safeInteger(orderbook, 'timestamp');
|
|
501
|
-
if (timestamp === undefined) {
|
|
502
|
-
// Buffer the events you receive from the stream.
|
|
503
|
-
orderbook.cache.push(message);
|
|
504
|
-
}
|
|
505
|
-
else {
|
|
506
|
-
const lastTimestamp = this.parseToInt(Precise.stringDiv(this.safeString(message, 'last_max_timestamp'), '1000000'));
|
|
507
|
-
if (lastTimestamp > timestamp) {
|
|
508
|
-
this.handleOrderBookMessage(client, message, orderbook);
|
|
509
|
-
client.resolve(orderbook, marketId + '@book_depth');
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
handleOrderBookMessage(client, message, orderbook) {
|
|
514
|
-
const timestamp = this.parseToInt(Precise.stringDiv(this.safeString(message, 'last_max_timestamp'), '1000000'));
|
|
515
|
-
// convert from X18
|
|
516
|
-
const data = {
|
|
517
|
-
'bids': [],
|
|
518
|
-
'asks': [],
|
|
519
|
-
};
|
|
520
|
-
const bids = this.safeList(message, 'bids', []);
|
|
521
|
-
for (let i = 0; i < bids.length; i++) {
|
|
522
|
-
const bid = bids[i];
|
|
523
|
-
data['bids'].push([
|
|
524
|
-
this.convertFromX18(bid[0]),
|
|
525
|
-
this.convertFromX18(bid[1]),
|
|
526
|
-
]);
|
|
527
|
-
}
|
|
528
|
-
const asks = this.safeList(message, 'asks', []);
|
|
529
|
-
for (let i = 0; i < asks.length; i++) {
|
|
530
|
-
const ask = asks[i];
|
|
531
|
-
data['asks'].push([
|
|
532
|
-
this.convertFromX18(ask[0]),
|
|
533
|
-
this.convertFromX18(ask[1]),
|
|
534
|
-
]);
|
|
535
|
-
}
|
|
536
|
-
this.handleDeltas(orderbook['asks'], this.safeList(data, 'asks', []));
|
|
537
|
-
this.handleDeltas(orderbook['bids'], this.safeList(data, 'bids', []));
|
|
538
|
-
orderbook['timestamp'] = timestamp;
|
|
539
|
-
orderbook['datetime'] = this.iso8601(timestamp);
|
|
540
|
-
return orderbook;
|
|
541
|
-
}
|
|
542
|
-
handleDelta(bookside, delta) {
|
|
543
|
-
const price = this.safeFloat(delta, 0);
|
|
544
|
-
const amount = this.safeFloat(delta, 1);
|
|
545
|
-
bookside.store(price, amount);
|
|
546
|
-
}
|
|
547
|
-
handleDeltas(bookside, deltas) {
|
|
548
|
-
for (let i = 0; i < deltas.length; i++) {
|
|
549
|
-
this.handleDelta(bookside, deltas[i]);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
handleSubscriptionStatus(client, message) {
|
|
553
|
-
//
|
|
554
|
-
// {
|
|
555
|
-
// "result": null,
|
|
556
|
-
// "id": 1574649734450
|
|
557
|
-
// }
|
|
558
|
-
//
|
|
559
|
-
const id = this.safeString(message, 'id');
|
|
560
|
-
const subscriptionsById = this.indexBy(client.subscriptions, 'id');
|
|
561
|
-
const subscription = this.safeValue(subscriptionsById, id, {});
|
|
562
|
-
const method = this.safeValue(subscription, 'method');
|
|
563
|
-
if (method !== undefined) {
|
|
564
|
-
method.call(this, client, message, subscription);
|
|
565
|
-
}
|
|
566
|
-
return message;
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* @method
|
|
570
|
-
* @name vertex#watchPositions
|
|
571
|
-
* @see https://docs.vertexprotocol.com/developer-resources/api/subscriptions/streams
|
|
572
|
-
* @description watch all open positions
|
|
573
|
-
* @param {string[]|undefined} symbols list of unified market symbols
|
|
574
|
-
* @param since
|
|
575
|
-
* @param limit
|
|
576
|
-
* @param {object} params extra parameters specific to the exchange API endpoint
|
|
577
|
-
* @param {string} [params.user] user address, will default to this.walletAddress if not provided
|
|
578
|
-
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
|
|
579
|
-
*/
|
|
580
|
-
async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
|
|
581
|
-
await this.loadMarkets();
|
|
582
|
-
symbols = this.marketSymbols(symbols);
|
|
583
|
-
if (!this.isEmpty(symbols)) {
|
|
584
|
-
if (symbols.length > 1) {
|
|
585
|
-
throw new NotSupported(this.id + ' watchPositions require only one symbol.');
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
else {
|
|
589
|
-
throw new ArgumentsRequired(this.id + ' watchPositions require one symbol.');
|
|
590
|
-
}
|
|
591
|
-
let userAddress = undefined;
|
|
592
|
-
[userAddress, params] = this.handlePublicAddress('watchPositions', params);
|
|
593
|
-
const url = this.urls['api']['ws'];
|
|
594
|
-
const client = this.client(url);
|
|
595
|
-
this.setPositionsCache(client, symbols, params);
|
|
596
|
-
const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', true);
|
|
597
|
-
const awaitPositionsSnapshot = this.handleOption('watchPositions', 'awaitPositionsSnapshot', true);
|
|
598
|
-
if (fetchPositionsSnapshot && awaitPositionsSnapshot && this.positions === undefined) {
|
|
599
|
-
const snapshot = await client.future('fetchPositionsSnapshot');
|
|
600
|
-
return this.filterBySymbolsSinceLimit(snapshot, symbols, since, limit, true);
|
|
601
|
-
}
|
|
602
|
-
const name = 'position_change';
|
|
603
|
-
const market = this.market(symbols[0]);
|
|
604
|
-
const topic = market['id'] + '@' + name;
|
|
605
|
-
const request = {
|
|
606
|
-
'method': 'subscribe',
|
|
607
|
-
'stream': {
|
|
608
|
-
'type': name,
|
|
609
|
-
'product_id': this.parseToNumeric(market['id']),
|
|
610
|
-
'subaccount': this.convertAddressToSender(userAddress),
|
|
611
|
-
},
|
|
612
|
-
};
|
|
613
|
-
const message = this.extend(request, params);
|
|
614
|
-
const newPositions = await this.watchPublic(topic, message);
|
|
615
|
-
if (this.newUpdates) {
|
|
616
|
-
limit = newPositions.getLimit(symbols[0], limit);
|
|
617
|
-
}
|
|
618
|
-
return this.filterBySymbolsSinceLimit(this.positions, symbols, since, limit, true);
|
|
619
|
-
}
|
|
620
|
-
setPositionsCache(client, symbols = undefined, params = {}) {
|
|
621
|
-
const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', false);
|
|
622
|
-
if (fetchPositionsSnapshot) {
|
|
623
|
-
const messageHash = 'fetchPositionsSnapshot';
|
|
624
|
-
if (!(messageHash in client.futures)) {
|
|
625
|
-
client.future(messageHash);
|
|
626
|
-
this.spawn(this.loadPositionsSnapshot, client, messageHash, symbols, params);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
this.positions = new ArrayCacheBySymbolBySide();
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
async loadPositionsSnapshot(client, messageHash, symbols, params) {
|
|
634
|
-
const positions = await this.fetchPositions(symbols, params);
|
|
635
|
-
this.positions = new ArrayCacheBySymbolBySide();
|
|
636
|
-
const cache = this.positions;
|
|
637
|
-
for (let i = 0; i < positions.length; i++) {
|
|
638
|
-
const position = positions[i];
|
|
639
|
-
cache.append(position);
|
|
640
|
-
}
|
|
641
|
-
// don't remove the future from the .futures cache
|
|
642
|
-
const future = client.futures[messageHash];
|
|
643
|
-
future.resolve(cache);
|
|
644
|
-
client.resolve(cache, 'positions');
|
|
645
|
-
}
|
|
646
|
-
handlePositions(client, message) {
|
|
647
|
-
//
|
|
648
|
-
// {
|
|
649
|
-
// "type":"position_change",
|
|
650
|
-
// "timestamp": "1676151190656903000", // timestamp of event in nanoseconds
|
|
651
|
-
// "product_id":1,
|
|
652
|
-
// // whether this is a position change for the LP token for this product
|
|
653
|
-
// "is_lp":false,
|
|
654
|
-
// // subaccount who's position changed
|
|
655
|
-
// "subaccount":"0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43706d00000000000000000000",
|
|
656
|
-
// // new amount for this product
|
|
657
|
-
// "amount":"51007390115411548",
|
|
658
|
-
// // new quote balance for this product; zero for everything except non lp perps
|
|
659
|
-
// // the negative of the entry cost of the perp
|
|
660
|
-
// "v_quote_amount":"0"
|
|
661
|
-
// }
|
|
662
|
-
//
|
|
663
|
-
if (this.positions === undefined) {
|
|
664
|
-
this.positions = new ArrayCacheBySymbolBySide();
|
|
665
|
-
}
|
|
666
|
-
const cache = this.positions;
|
|
667
|
-
const topic = this.safeString(message, 'type');
|
|
668
|
-
const marketId = this.safeString(message, 'product_id');
|
|
669
|
-
const market = this.safeMarket(marketId);
|
|
670
|
-
const position = this.parseWsPosition(message, market);
|
|
671
|
-
cache.append(position);
|
|
672
|
-
client.resolve(position, marketId + '@' + topic);
|
|
673
|
-
}
|
|
674
|
-
parseWsPosition(position, market = undefined) {
|
|
675
|
-
//
|
|
676
|
-
// {
|
|
677
|
-
// "type":"position_change",
|
|
678
|
-
// "timestamp": "1676151190656903000", // timestamp of event in nanoseconds
|
|
679
|
-
// "product_id":1,
|
|
680
|
-
// // whether this is a position change for the LP token for this product
|
|
681
|
-
// "is_lp":false,
|
|
682
|
-
// // subaccount who's position changed
|
|
683
|
-
// "subaccount":"0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43706d00000000000000000000",
|
|
684
|
-
// // new amount for this product
|
|
685
|
-
// "amount":"51007390115411548",
|
|
686
|
-
// // new quote balance for this product; zero for everything except non lp perps
|
|
687
|
-
// // the negative of the entry cost of the perp
|
|
688
|
-
// "v_quote_amount":"0"
|
|
689
|
-
// }
|
|
690
|
-
//
|
|
691
|
-
const marketId = this.safeString(position, 'product_id');
|
|
692
|
-
market = this.safeMarket(marketId);
|
|
693
|
-
const contractSize = this.convertFromX18(this.safeString(position, 'amount'));
|
|
694
|
-
let side = 'buy';
|
|
695
|
-
if (Precise.stringLt(contractSize, '1')) {
|
|
696
|
-
side = 'sell';
|
|
697
|
-
}
|
|
698
|
-
const timestamp = this.parseToInt(Precise.stringDiv(this.safeString(position, 'timestamp'), '1000000'));
|
|
699
|
-
return this.safePosition({
|
|
700
|
-
'info': position,
|
|
701
|
-
'id': undefined,
|
|
702
|
-
'symbol': this.safeString(market, 'symbol'),
|
|
703
|
-
'timestamp': timestamp,
|
|
704
|
-
'datetime': this.iso8601(timestamp),
|
|
705
|
-
'lastUpdateTimestamp': undefined,
|
|
706
|
-
'initialMargin': undefined,
|
|
707
|
-
'initialMarginPercentage': undefined,
|
|
708
|
-
'maintenanceMargin': undefined,
|
|
709
|
-
'maintenanceMarginPercentage': undefined,
|
|
710
|
-
'entryPrice': undefined,
|
|
711
|
-
'notional': undefined,
|
|
712
|
-
'leverage': undefined,
|
|
713
|
-
'unrealizedPnl': undefined,
|
|
714
|
-
'contracts': undefined,
|
|
715
|
-
'contractSize': this.parseNumber(contractSize),
|
|
716
|
-
'marginRatio': undefined,
|
|
717
|
-
'liquidationPrice': undefined,
|
|
718
|
-
'markPrice': undefined,
|
|
719
|
-
'lastPrice': undefined,
|
|
720
|
-
'collateral': undefined,
|
|
721
|
-
'marginMode': 'cross',
|
|
722
|
-
'marginType': undefined,
|
|
723
|
-
'side': side,
|
|
724
|
-
'percentage': undefined,
|
|
725
|
-
'hedged': undefined,
|
|
726
|
-
'stopLossPrice': undefined,
|
|
727
|
-
'takeProfitPrice': undefined,
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
handleAuth(client, message) {
|
|
731
|
-
//
|
|
732
|
-
// { result: null, id: 1 }
|
|
733
|
-
//
|
|
734
|
-
const messageHash = 'authenticated';
|
|
735
|
-
const error = this.safeString(message, 'error');
|
|
736
|
-
if (error === undefined) {
|
|
737
|
-
// client.resolve (message, messageHash);
|
|
738
|
-
const future = this.safeValue(client.futures, 'authenticated');
|
|
739
|
-
future.resolve(true);
|
|
740
|
-
}
|
|
741
|
-
else {
|
|
742
|
-
const authError = new AuthenticationError(this.json(message));
|
|
743
|
-
client.reject(authError, messageHash);
|
|
744
|
-
// allows further authentication attempts
|
|
745
|
-
if (messageHash in client.subscriptions) {
|
|
746
|
-
delete client.subscriptions['authenticated'];
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
buildWsAuthenticationSig(message, chainId, verifyingContractAddress) {
|
|
751
|
-
const messageTypes = {
|
|
752
|
-
'StreamAuthentication': [
|
|
753
|
-
{ 'name': 'sender', 'type': 'bytes32' },
|
|
754
|
-
{ 'name': 'expiration', 'type': 'uint64' },
|
|
755
|
-
],
|
|
756
|
-
};
|
|
757
|
-
return this.buildSig(chainId, messageTypes, message, verifyingContractAddress);
|
|
758
|
-
}
|
|
759
|
-
async authenticate(params = {}) {
|
|
760
|
-
this.checkRequiredCredentials();
|
|
761
|
-
const url = this.urls['api']['ws'];
|
|
762
|
-
const client = this.client(url);
|
|
763
|
-
const messageHash = 'authenticated';
|
|
764
|
-
const future = client.future(messageHash);
|
|
765
|
-
const authenticated = this.safeValue(client.subscriptions, messageHash);
|
|
766
|
-
if (authenticated === undefined) {
|
|
767
|
-
const requestId = this.requestId(url);
|
|
768
|
-
const contracts = await this.queryContracts();
|
|
769
|
-
const chainId = this.safeString(contracts, 'chain_id');
|
|
770
|
-
const verifyingContractAddress = this.safeString(contracts, 'endpoint_addr');
|
|
771
|
-
const now = this.nonce();
|
|
772
|
-
const nonce = now + 90000;
|
|
773
|
-
const authentication = {
|
|
774
|
-
'sender': this.convertAddressToSender(this.walletAddress),
|
|
775
|
-
'expiration': nonce,
|
|
776
|
-
};
|
|
777
|
-
const request = {
|
|
778
|
-
'id': requestId,
|
|
779
|
-
'method': 'authenticate',
|
|
780
|
-
'tx': {
|
|
781
|
-
'sender': authentication['sender'],
|
|
782
|
-
'expiration': this.numberToString(authentication['expiration']),
|
|
783
|
-
},
|
|
784
|
-
'signature': this.buildWsAuthenticationSig(authentication, chainId, verifyingContractAddress),
|
|
785
|
-
};
|
|
786
|
-
const message = this.extend(request, params);
|
|
787
|
-
this.watch(url, messageHash, message, messageHash);
|
|
788
|
-
}
|
|
789
|
-
return await future;
|
|
790
|
-
}
|
|
791
|
-
async watchPrivate(messageHash, message, params = {}) {
|
|
792
|
-
await this.authenticate(params);
|
|
793
|
-
const url = this.urls['api']['ws'];
|
|
794
|
-
const requestId = this.requestId(url);
|
|
795
|
-
const subscribe = {
|
|
796
|
-
'id': requestId,
|
|
797
|
-
};
|
|
798
|
-
const request = this.extend(subscribe, message);
|
|
799
|
-
return await this.watch(url, messageHash, request, messageHash, subscribe);
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* @method
|
|
803
|
-
* @name vertex#watchOrders
|
|
804
|
-
* @description watches information on multiple orders made by the user
|
|
805
|
-
* @see https://docs.vertexprotocol.com/developer-resources/api/subscriptions/streams
|
|
806
|
-
* @param {string} symbol unified market symbol of the market orders were made in
|
|
807
|
-
* @param {int} [since] the earliest time in ms to fetch orders for
|
|
808
|
-
* @param {int} [limit] the maximum number of order structures to retrieve
|
|
809
|
-
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
810
|
-
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
811
|
-
*/
|
|
812
|
-
async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
813
|
-
if (symbol === undefined) {
|
|
814
|
-
throw new ArgumentsRequired(this.id + ' watchOrders requires a symbol.');
|
|
815
|
-
}
|
|
816
|
-
this.checkRequiredCredentials();
|
|
817
|
-
await this.loadMarkets();
|
|
818
|
-
const name = 'order_update';
|
|
819
|
-
const market = this.market(symbol);
|
|
820
|
-
const topic = market['id'] + '@' + name;
|
|
821
|
-
const request = {
|
|
822
|
-
'method': 'subscribe',
|
|
823
|
-
'stream': {
|
|
824
|
-
'type': name,
|
|
825
|
-
'subaccount': this.convertAddressToSender(this.walletAddress),
|
|
826
|
-
'product_id': this.parseToNumeric(market['id']),
|
|
827
|
-
},
|
|
828
|
-
};
|
|
829
|
-
const message = this.extend(request, params);
|
|
830
|
-
const orders = await this.watchPrivate(topic, message);
|
|
831
|
-
if (this.newUpdates) {
|
|
832
|
-
limit = orders.getLimit(symbol, limit);
|
|
833
|
-
}
|
|
834
|
-
return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
|
|
835
|
-
}
|
|
836
|
-
parseWsOrderStatus(status) {
|
|
837
|
-
if (status !== undefined) {
|
|
838
|
-
const statuses = {
|
|
839
|
-
'filled': 'open',
|
|
840
|
-
'placed': 'open',
|
|
841
|
-
'cancelled': 'canceled',
|
|
842
|
-
};
|
|
843
|
-
return this.safeString(statuses, status, status);
|
|
844
|
-
}
|
|
845
|
-
return status;
|
|
846
|
-
}
|
|
847
|
-
parseWsOrder(order, market = undefined) {
|
|
848
|
-
//
|
|
849
|
-
// {
|
|
850
|
-
// "type": "order_update",
|
|
851
|
-
// // timestamp of the event in nanoseconds
|
|
852
|
-
// "timestamp": "1695081920633151000",
|
|
853
|
-
// "product_id": 1,
|
|
854
|
-
// // order digest
|
|
855
|
-
// "digest": "0xf7712b63ccf70358db8f201e9bf33977423e7a63f6a16f6dab180bdd580f7c6c",
|
|
856
|
-
// // remaining amount to be filled.
|
|
857
|
-
// // will be `0` if the order is either fully filled or cancelled.
|
|
858
|
-
// "amount": "82000000000000000",
|
|
859
|
-
// // any of: "filled", "cancelled", "placed"
|
|
860
|
-
// "reason": "filled"
|
|
861
|
-
// // an optional `order id` that can be provided when placing an order
|
|
862
|
-
// "id": 100
|
|
863
|
-
// }
|
|
864
|
-
//
|
|
865
|
-
const marketId = this.safeString(order, 'product_id');
|
|
866
|
-
const timestamp = this.parseToInt(Precise.stringDiv(this.safeString(order, 'timestamp'), '1000000'));
|
|
867
|
-
const remainingString = this.convertFromX18(this.safeString(order, 'amount'));
|
|
868
|
-
const remaining = this.parseToNumeric(remainingString);
|
|
869
|
-
let status = this.parseWsOrderStatus(this.safeString(order, 'reason'));
|
|
870
|
-
if (Precise.stringEq(remainingString, '0') && status === 'open') {
|
|
871
|
-
status = 'closed';
|
|
872
|
-
}
|
|
873
|
-
market = this.safeMarket(marketId, market);
|
|
874
|
-
const symbol = market['symbol'];
|
|
875
|
-
return this.safeOrder({
|
|
876
|
-
'info': order,
|
|
877
|
-
'id': this.safeString2(order, 'digest', 'id'),
|
|
878
|
-
'clientOrderId': undefined,
|
|
879
|
-
'timestamp': timestamp,
|
|
880
|
-
'datetime': this.iso8601(timestamp),
|
|
881
|
-
'lastTradeTimestamp': undefined,
|
|
882
|
-
'lastUpdateTimestamp': undefined,
|
|
883
|
-
'symbol': symbol,
|
|
884
|
-
'type': undefined,
|
|
885
|
-
'timeInForce': undefined,
|
|
886
|
-
'postOnly': undefined,
|
|
887
|
-
'reduceOnly': undefined,
|
|
888
|
-
'side': undefined,
|
|
889
|
-
'price': undefined,
|
|
890
|
-
'triggerPrice': undefined,
|
|
891
|
-
'amount': undefined,
|
|
892
|
-
'cost': undefined,
|
|
893
|
-
'average': undefined,
|
|
894
|
-
'filled': undefined,
|
|
895
|
-
'remaining': remaining,
|
|
896
|
-
'status': status,
|
|
897
|
-
'fee': undefined,
|
|
898
|
-
'trades': undefined,
|
|
899
|
-
}, market);
|
|
900
|
-
}
|
|
901
|
-
handleOrderUpdate(client, message) {
|
|
902
|
-
//
|
|
903
|
-
// {
|
|
904
|
-
// "type": "order_update",
|
|
905
|
-
// // timestamp of the event in nanoseconds
|
|
906
|
-
// "timestamp": "1695081920633151000",
|
|
907
|
-
// "product_id": 1,
|
|
908
|
-
// // order digest
|
|
909
|
-
// "digest": "0xf7712b63ccf70358db8f201e9bf33977423e7a63f6a16f6dab180bdd580f7c6c",
|
|
910
|
-
// // remaining amount to be filled.
|
|
911
|
-
// // will be `0` if the order is either fully filled or cancelled.
|
|
912
|
-
// "amount": "82000000000000000",
|
|
913
|
-
// // any of: "filled", "cancelled", "placed"
|
|
914
|
-
// "reason": "filled"
|
|
915
|
-
// // an optional `order id` that can be provided when placing an order
|
|
916
|
-
// "id": 100
|
|
917
|
-
// }
|
|
918
|
-
//
|
|
919
|
-
const topic = this.safeString(message, 'type');
|
|
920
|
-
const marketId = this.safeString(message, 'product_id');
|
|
921
|
-
const parsed = this.parseWsOrder(message);
|
|
922
|
-
const symbol = this.safeString(parsed, 'symbol');
|
|
923
|
-
const orderId = this.safeString(parsed, 'id');
|
|
924
|
-
if (symbol !== undefined) {
|
|
925
|
-
if (this.orders === undefined) {
|
|
926
|
-
const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
|
|
927
|
-
this.orders = new ArrayCacheBySymbolById(limit);
|
|
928
|
-
}
|
|
929
|
-
const cachedOrders = this.orders;
|
|
930
|
-
const orders = this.safeDict(cachedOrders.hashmap, symbol, {});
|
|
931
|
-
const order = this.safeDict(orders, orderId);
|
|
932
|
-
if (order !== undefined) {
|
|
933
|
-
parsed['timestamp'] = this.safeInteger(order, 'timestamp');
|
|
934
|
-
parsed['datetime'] = this.safeString(order, 'datetime');
|
|
935
|
-
}
|
|
936
|
-
cachedOrders.append(parsed);
|
|
937
|
-
client.resolve(this.orders, marketId + '@' + topic);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
handleErrorMessage(client, message) {
|
|
941
|
-
//
|
|
942
|
-
// {
|
|
943
|
-
// result: null,
|
|
944
|
-
// error: 'error parsing request: missing field `expiration`',
|
|
945
|
-
// id: 0
|
|
946
|
-
// }
|
|
947
|
-
//
|
|
948
|
-
const errorMessage = this.safeString(message, 'error');
|
|
949
|
-
try {
|
|
950
|
-
if (errorMessage !== undefined) {
|
|
951
|
-
const feedback = this.id + ' ' + this.json(message);
|
|
952
|
-
this.throwExactlyMatchedException(this.exceptions['exact'], errorMessage, feedback);
|
|
953
|
-
}
|
|
954
|
-
return false;
|
|
955
|
-
}
|
|
956
|
-
catch (error) {
|
|
957
|
-
if (error instanceof AuthenticationError) {
|
|
958
|
-
const messageHash = 'authenticated';
|
|
959
|
-
client.reject(error, messageHash);
|
|
960
|
-
if (messageHash in client.subscriptions) {
|
|
961
|
-
delete client.subscriptions[messageHash];
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
else {
|
|
965
|
-
client.reject(error);
|
|
966
|
-
}
|
|
967
|
-
return true;
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
handleMessage(client, message) {
|
|
971
|
-
if (this.handleErrorMessage(client, message)) {
|
|
972
|
-
return;
|
|
973
|
-
}
|
|
974
|
-
const methods = {
|
|
975
|
-
'trade': this.handleTrade,
|
|
976
|
-
'best_bid_offer': this.handleTicker,
|
|
977
|
-
'book_depth': this.handleOrderBook,
|
|
978
|
-
'fill': this.handleMyTrades,
|
|
979
|
-
'position_change': this.handlePositions,
|
|
980
|
-
'order_update': this.handleOrderUpdate,
|
|
981
|
-
};
|
|
982
|
-
const event = this.safeString(message, 'type');
|
|
983
|
-
const method = this.safeValue(methods, event);
|
|
984
|
-
if (method !== undefined) {
|
|
985
|
-
method.call(this, client, message);
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
const requestId = this.safeString(message, 'id');
|
|
989
|
-
if (requestId !== undefined) {
|
|
990
|
-
this.handleSubscriptionStatus(client, message);
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
// check whether it's authentication
|
|
994
|
-
const auth = this.safeValue(client.futures, 'authenticated');
|
|
995
|
-
if (auth !== undefined) {
|
|
996
|
-
this.handleAuth(client, message);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
}
|