ccxt 4.5.5 → 4.5.6
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 +4 -4
- package/dist/ccxt.browser.min.js +15 -15
- package/dist/cjs/ccxt.js +6 -6
- package/dist/cjs/src/abstract/toobit.js +11 -0
- package/dist/cjs/src/abstract/tradeogre.js +1 -1
- package/dist/cjs/src/backpack.js +1 -1
- package/dist/cjs/src/base/Exchange.js +129 -2
- package/dist/cjs/src/bigone.js +4 -4
- package/dist/cjs/src/binance.js +79 -20
- package/dist/cjs/src/bingx.js +5 -2
- package/dist/cjs/src/bitget.js +16 -9
- package/dist/cjs/src/bybit.js +186 -127
- package/dist/cjs/src/coinsph.js +4 -1
- package/dist/cjs/src/cryptocom.js +6 -3
- package/dist/cjs/src/gate.js +1 -1
- package/dist/cjs/src/gemini.js +2 -2
- package/dist/cjs/src/hyperliquid.js +3 -0
- package/dist/cjs/src/kraken.js +6 -2
- package/dist/cjs/src/kucoin.js +1 -2
- package/dist/cjs/src/luno.js +4 -1
- package/dist/cjs/src/mexc.js +4 -1
- package/dist/cjs/src/okcoin.js +4 -1
- package/dist/cjs/src/okx.js +34 -3
- package/dist/cjs/src/phemex.js +1 -1
- package/dist/cjs/src/pro/apex.js +1 -0
- package/dist/cjs/src/pro/backpack.js +1 -1
- package/dist/cjs/src/pro/binance.js +150 -19
- package/dist/cjs/src/pro/bitget.js +332 -76
- package/dist/cjs/src/pro/cex.js +1 -0
- package/dist/cjs/src/pro/independentreserve.js +1 -0
- package/dist/cjs/src/pro/mexc.js +23 -23
- package/dist/cjs/src/pro/okx.js +46 -10
- package/dist/cjs/src/pro/toobit.js +1163 -0
- package/dist/cjs/src/pro/tradeogre.js +1 -1
- package/dist/cjs/src/toobit.js +2999 -0
- package/dist/cjs/src/tradeogre.js +1 -1
- package/js/ccxt.d.ts +8 -8
- package/js/ccxt.js +6 -6
- package/js/src/abstract/myokx.d.ts +1 -0
- package/js/src/abstract/okx.d.ts +1 -0
- package/js/src/abstract/okxus.d.ts +1 -0
- package/js/src/abstract/toobit.d.ts +66 -0
- package/js/src/backpack.js +1 -1
- package/js/src/base/Exchange.d.ts +9 -0
- package/js/src/base/Exchange.js +129 -2
- package/js/src/bigone.js +4 -4
- package/js/src/binance.d.ts +9 -0
- package/js/src/binance.js +79 -20
- package/js/src/bingx.js +5 -2
- package/js/src/bitget.js +16 -9
- package/js/src/bybit.d.ts +8 -0
- package/js/src/bybit.js +186 -127
- package/js/src/coinsph.js +4 -1
- package/js/src/cryptocom.js +6 -3
- package/js/src/gate.js +1 -1
- package/js/src/gemini.js +2 -2
- package/js/src/hyperliquid.js +3 -0
- package/js/src/kraken.d.ts +1 -1
- package/js/src/kraken.js +6 -2
- package/js/src/kucoin.js +1 -2
- package/js/src/luno.js +4 -1
- package/js/src/mexc.js +4 -1
- package/js/src/okcoin.js +4 -1
- package/js/src/okx.js +34 -3
- package/js/src/phemex.js +1 -1
- package/js/src/pro/apex.js +1 -0
- package/js/src/pro/backpack.d.ts +1 -1
- package/js/src/pro/backpack.js +1 -1
- package/js/src/pro/binance.d.ts +24 -0
- package/js/src/pro/binance.js +150 -19
- package/js/src/pro/bitget.d.ts +6 -0
- package/js/src/pro/bitget.js +332 -76
- package/js/src/pro/cex.js +1 -0
- package/js/src/pro/independentreserve.js +1 -0
- package/js/src/pro/mexc.js +23 -23
- package/js/src/pro/okx.d.ts +7 -1
- package/js/src/pro/okx.js +46 -10
- package/js/src/pro/toobit.d.ts +174 -0
- package/js/src/pro/toobit.js +1162 -0
- package/js/src/toobit.d.ts +456 -0
- package/js/src/toobit.js +2992 -0
- package/package.json +1 -1
- package/dist/373.ccxt.browser.js +0 -7630
- package/dist/373.ccxt.browser.min.js +0 -1
- package/js/src/abstract/tradeogre.d.ts +0 -21
- package/js/src/pro/tradeogre.d.ts +0 -49
- package/js/src/pro/tradeogre.js +0 -278
- package/js/src/tradeogre.d.ts +0 -149
- package/js/src/tradeogre.js +0 -872
- /package/js/src/abstract/{tradeogre.js → toobit.js} +0 -0
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var toobit$1 = require('../toobit.js');
|
|
6
|
+
var errors = require('../base/errors.js');
|
|
7
|
+
var Cache = require('../base/ws/Cache.js');
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
class toobit extends toobit$1["default"] {
|
|
12
|
+
describe() {
|
|
13
|
+
return this.deepExtend(super.describe(), {
|
|
14
|
+
'has': {
|
|
15
|
+
'ws': true,
|
|
16
|
+
'watchBalance': true,
|
|
17
|
+
'watchMyTrades': true,
|
|
18
|
+
'watchOHLCV': true,
|
|
19
|
+
'watchOHLCVForSymbols': true,
|
|
20
|
+
'watchOrderBook': true,
|
|
21
|
+
'watchOrderBookForSymbols': true,
|
|
22
|
+
'watchOrders': true,
|
|
23
|
+
'watchTicker': true,
|
|
24
|
+
'watchTickers': true,
|
|
25
|
+
'watchTrades': true,
|
|
26
|
+
'watchTradesForSymbols': true,
|
|
27
|
+
// 'watchPosition': false,
|
|
28
|
+
},
|
|
29
|
+
'urls': {
|
|
30
|
+
'api': {
|
|
31
|
+
'ws': {
|
|
32
|
+
'common': 'wss://stream.toobit.com',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
'options': {
|
|
37
|
+
'ws': {
|
|
38
|
+
'timeframes': {
|
|
39
|
+
'1m': '1m',
|
|
40
|
+
'3m': '3m',
|
|
41
|
+
'5m': '5m',
|
|
42
|
+
'15m': '15m',
|
|
43
|
+
'30m': '30m',
|
|
44
|
+
'1h': '1h',
|
|
45
|
+
'2h': '2h',
|
|
46
|
+
'4h': '4h',
|
|
47
|
+
'6h': '6h',
|
|
48
|
+
'8h': '8h',
|
|
49
|
+
'12h': '12h',
|
|
50
|
+
'1d': '1d',
|
|
51
|
+
'1w': '1w',
|
|
52
|
+
'1M': '1M',
|
|
53
|
+
},
|
|
54
|
+
'watchOrderBook': {
|
|
55
|
+
'channel': 'depth', // depth, diffDepth
|
|
56
|
+
},
|
|
57
|
+
'listenKeyRefreshRate': 1200000, // 20 mins
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
'streaming': {
|
|
61
|
+
'keepAlive': (60 - 1) * 5 * 1000,
|
|
62
|
+
'ping': this.ping,
|
|
63
|
+
},
|
|
64
|
+
'exceptions': {
|
|
65
|
+
'ws': {
|
|
66
|
+
'exact': {},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
ping(client) {
|
|
72
|
+
return {
|
|
73
|
+
'ping': this.milliseconds(),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
handleMessage(client, message) {
|
|
77
|
+
//
|
|
78
|
+
// public
|
|
79
|
+
//
|
|
80
|
+
// {
|
|
81
|
+
// topic: "trade",
|
|
82
|
+
// symbol: "DOGEUSDT",
|
|
83
|
+
// symbolName: "DOGEUSDT",
|
|
84
|
+
// params: {
|
|
85
|
+
// realtimeInterval: "24h",
|
|
86
|
+
// binary: "false",
|
|
87
|
+
// },
|
|
88
|
+
// data: [
|
|
89
|
+
// {
|
|
90
|
+
// v: "4864732022868004630",
|
|
91
|
+
// t: 1757243788405,
|
|
92
|
+
// p: "0.21804",
|
|
93
|
+
// q: "80",
|
|
94
|
+
// m: true,
|
|
95
|
+
// },
|
|
96
|
+
// ],
|
|
97
|
+
// f: true, // initial first snapshot or not
|
|
98
|
+
// sendTime: 1757244002117,
|
|
99
|
+
// shared: false,
|
|
100
|
+
// }
|
|
101
|
+
//
|
|
102
|
+
// private
|
|
103
|
+
//
|
|
104
|
+
// [
|
|
105
|
+
// {
|
|
106
|
+
// e: 'outboundContractAccountInfo',
|
|
107
|
+
// E: '1758228398234',
|
|
108
|
+
// T: true,
|
|
109
|
+
// W: true,
|
|
110
|
+
// D: true,
|
|
111
|
+
// B: [ [Object] ]
|
|
112
|
+
// }
|
|
113
|
+
// ]
|
|
114
|
+
//
|
|
115
|
+
const topic = this.safeString(message, 'topic');
|
|
116
|
+
if (this.handleErrorMessage(client, message)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
//
|
|
120
|
+
// handle ping-pong: { ping: 1758540450000 }
|
|
121
|
+
//
|
|
122
|
+
const pongTimestamp = this.safeInteger(message, 'pong');
|
|
123
|
+
if (pongTimestamp !== undefined) {
|
|
124
|
+
this.handleIncomingPong(client, pongTimestamp);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const methods = {
|
|
128
|
+
'trade': this.handleTrades,
|
|
129
|
+
'kline': this.handleOHLCV,
|
|
130
|
+
'realtimes': this.handleTickers,
|
|
131
|
+
'depth': this.handleOrderBookPartialSnapshot,
|
|
132
|
+
'diffDepth': this.handleOrderBook,
|
|
133
|
+
'outboundAccountInfo': this.handleBalance,
|
|
134
|
+
'outboundContractAccountInfo': this.handleBalance,
|
|
135
|
+
'executionReport': this.handleOrder,
|
|
136
|
+
'contractExecutionReport': this.handleOrder,
|
|
137
|
+
'ticketInfo': this.handleMyTrade,
|
|
138
|
+
'outboundContractPositionInfo': this.handlePositions,
|
|
139
|
+
};
|
|
140
|
+
const method = this.safeValue(methods, topic);
|
|
141
|
+
if (method !== undefined) {
|
|
142
|
+
method.call(this, client, message);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// check private streams
|
|
146
|
+
for (let i = 0; i < message.length; i++) {
|
|
147
|
+
const item = message[i];
|
|
148
|
+
const event = this.safeString(item, 'e');
|
|
149
|
+
const method2 = this.safeValue(methods, event);
|
|
150
|
+
if (method2 !== undefined) {
|
|
151
|
+
method2.call(this, client, item);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
handleIncomingPong(client, pongTimestamp) {
|
|
157
|
+
client.lastPong = pongTimestamp;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* @method
|
|
161
|
+
* @name toobit#watchTrades
|
|
162
|
+
* @description watches information on multiple trades made in a market
|
|
163
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#trade-streams
|
|
164
|
+
* @param {string} symbol unified market symbol of the market trades were made in
|
|
165
|
+
* @param {int} [since] the earliest time in ms to fetch trades for
|
|
166
|
+
* @param {int} [limit] the maximum number of trade structures to retrieve
|
|
167
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
168
|
+
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
|
|
169
|
+
*/
|
|
170
|
+
async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
|
|
171
|
+
return await this.watchTradesForSymbols([symbol], since, limit, params);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* @method
|
|
175
|
+
* @name toobit#watchTradesForSymbols
|
|
176
|
+
* @description get the list of most recent trades for a list of symbols
|
|
177
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#trade-streams
|
|
178
|
+
* @param {string[]} symbols unified symbol of the market to fetch trades for
|
|
179
|
+
* @param {int} [since] timestamp in ms of the earliest trade to fetch
|
|
180
|
+
* @param {int} [limit] the maximum amount of trades to fetch
|
|
181
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
182
|
+
* @param {string} [params.name] the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
|
|
183
|
+
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
|
|
184
|
+
*/
|
|
185
|
+
async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
|
|
186
|
+
await this.loadMarkets();
|
|
187
|
+
symbols = this.marketSymbols(symbols, undefined, false);
|
|
188
|
+
const messageHashes = [];
|
|
189
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
190
|
+
const symbol = symbols[i];
|
|
191
|
+
const market = this.market(symbol);
|
|
192
|
+
messageHashes.push('trade::' + symbol);
|
|
193
|
+
market['id'];
|
|
194
|
+
}
|
|
195
|
+
const marketIds = this.marketIds(symbols);
|
|
196
|
+
const url = this.urls['api']['ws']['common'] + '/quote/ws/v1';
|
|
197
|
+
const request = {
|
|
198
|
+
'symbol': marketIds.join(','),
|
|
199
|
+
'topic': 'trade',
|
|
200
|
+
'event': 'sub',
|
|
201
|
+
};
|
|
202
|
+
const trades = await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes);
|
|
203
|
+
if (this.newUpdates) {
|
|
204
|
+
const first = this.safeValue(trades, 0);
|
|
205
|
+
const tradeSymbol = this.safeString(first, 'symbol');
|
|
206
|
+
limit = trades.getLimit(tradeSymbol, limit);
|
|
207
|
+
}
|
|
208
|
+
return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
|
|
209
|
+
}
|
|
210
|
+
handleTrades(client, message) {
|
|
211
|
+
//
|
|
212
|
+
// {
|
|
213
|
+
// symbol: "DOGEUSDT",
|
|
214
|
+
// symbolName: "DOGEUSDT",
|
|
215
|
+
// topic: "trade",
|
|
216
|
+
// params: {
|
|
217
|
+
// realtimeInterval: "24h",
|
|
218
|
+
// binary: "false",
|
|
219
|
+
// },
|
|
220
|
+
// data: [
|
|
221
|
+
// {
|
|
222
|
+
// v: "4864732022868004630",
|
|
223
|
+
// t: 1757243788405,
|
|
224
|
+
// p: "0.21804",
|
|
225
|
+
// q: "80",
|
|
226
|
+
// m: true,
|
|
227
|
+
// },
|
|
228
|
+
// ],
|
|
229
|
+
// f: true, // initial first snapshot or not
|
|
230
|
+
// sendTime: 1757244002117,
|
|
231
|
+
// shared: false,
|
|
232
|
+
// }
|
|
233
|
+
//
|
|
234
|
+
const marketId = this.safeString(message, 'symbol');
|
|
235
|
+
const symbol = this.safeSymbol(marketId);
|
|
236
|
+
if (!(symbol in this.trades)) {
|
|
237
|
+
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
|
|
238
|
+
this.trades[symbol] = new Cache.ArrayCache(limit);
|
|
239
|
+
}
|
|
240
|
+
const stored = this.trades[symbol];
|
|
241
|
+
const data = this.safeList(message, 'data', []);
|
|
242
|
+
const parsed = this.parseWsTrades(data);
|
|
243
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
244
|
+
const trade = parsed[i];
|
|
245
|
+
trade['symbol'] = symbol;
|
|
246
|
+
stored.append(trade);
|
|
247
|
+
}
|
|
248
|
+
const messageHash = 'trade::' + symbol;
|
|
249
|
+
client.resolve(stored, messageHash);
|
|
250
|
+
}
|
|
251
|
+
parseWsTrade(trade, market = undefined) {
|
|
252
|
+
return this.parseTrade(trade, market);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* @method
|
|
256
|
+
* @name toobit#watchOHLCV
|
|
257
|
+
* @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
258
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#kline-candlestick-streams
|
|
259
|
+
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
|
|
260
|
+
* @param {string} timeframe the length of time each candle represents
|
|
261
|
+
* @param {int} [since] timestamp in ms of the earliest candle to fetch
|
|
262
|
+
* @param {int} [limit] the maximum amount of candles to fetch
|
|
263
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
264
|
+
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
|
|
265
|
+
*/
|
|
266
|
+
async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
|
267
|
+
params['callerMethodName'] = 'watchOHLCV';
|
|
268
|
+
const result = await this.watchOHLCVForSymbols([[symbol, timeframe]], since, limit, params);
|
|
269
|
+
return result[symbol][timeframe];
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* @method
|
|
273
|
+
* @name toobit#watchOHLCVForSymbols
|
|
274
|
+
* @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
275
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#kline-candlestick-streams
|
|
276
|
+
* @param {string[][]} symbolsAndTimeframes array of arrays containing unified symbols and timeframes to fetch OHLCV data for, example [['BTC/USDT', '1m'], ['LTC/USDT', '5m']]
|
|
277
|
+
* @param {int} [since] timestamp in ms of the earliest candle to fetch
|
|
278
|
+
* @param {int} [limit] the maximum amount of candles to fetch
|
|
279
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
280
|
+
* @returns {object} A list of candles ordered as timestamp, open, high, low, close, volume
|
|
281
|
+
*/
|
|
282
|
+
async watchOHLCVForSymbols(symbolsAndTimeframes, since = undefined, limit = undefined, params = {}) {
|
|
283
|
+
await this.loadMarkets();
|
|
284
|
+
const url = this.urls['api']['ws']['common'] + '/quote/ws/v1';
|
|
285
|
+
const messageHashes = [];
|
|
286
|
+
const timeframes = this.safeDict(this.options['ws'], 'timeframes', {});
|
|
287
|
+
const marketIds = [];
|
|
288
|
+
let selectedTimeframe = undefined;
|
|
289
|
+
for (let i = 0; i < symbolsAndTimeframes.length; i++) {
|
|
290
|
+
const data = symbolsAndTimeframes[i];
|
|
291
|
+
const symbolStr = this.safeString(data, 0);
|
|
292
|
+
const market = this.market(symbolStr);
|
|
293
|
+
const marketId = market['id'];
|
|
294
|
+
const unfiedTimeframe = this.safeString(data, 1, '1m');
|
|
295
|
+
const rawTimeframe = this.safeString(timeframes, unfiedTimeframe, unfiedTimeframe);
|
|
296
|
+
if (selectedTimeframe !== undefined && selectedTimeframe !== rawTimeframe) {
|
|
297
|
+
throw new errors.NotSupported(this.id + ' watchOHLCVForSymbols() only supports a single timeframe for all symbols');
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
selectedTimeframe = rawTimeframe;
|
|
301
|
+
}
|
|
302
|
+
marketIds.push(marketId);
|
|
303
|
+
messageHashes.push('ohlcv::' + symbolStr + '::' + unfiedTimeframe);
|
|
304
|
+
}
|
|
305
|
+
const request = {
|
|
306
|
+
'symbol': marketIds.join(','),
|
|
307
|
+
'topic': 'kline_' + selectedTimeframe,
|
|
308
|
+
'event': 'sub',
|
|
309
|
+
};
|
|
310
|
+
const [symbol, timeframe, stored] = await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes);
|
|
311
|
+
if (this.newUpdates) {
|
|
312
|
+
limit = stored.getLimit(symbol, limit);
|
|
313
|
+
}
|
|
314
|
+
const filtered = this.filterBySinceLimit(stored, since, limit, 0, true);
|
|
315
|
+
return this.createOHLCVObject(symbol, timeframe, filtered);
|
|
316
|
+
}
|
|
317
|
+
handleOHLCV(client, message) {
|
|
318
|
+
//
|
|
319
|
+
// {
|
|
320
|
+
// symbol: 'DOGEUSDT',
|
|
321
|
+
// symbolName: 'DOGEUSDT',
|
|
322
|
+
// klineType: '1m',
|
|
323
|
+
// topic: 'kline',
|
|
324
|
+
// params: { realtimeInterval: '24h', klineType: '1m', binary: 'false' },
|
|
325
|
+
// data: [
|
|
326
|
+
// {
|
|
327
|
+
// t: 1757251200000,
|
|
328
|
+
// s: 'DOGEUSDT',
|
|
329
|
+
// sn: 'DOGEUSDT',
|
|
330
|
+
// c: '0.21889',
|
|
331
|
+
// h: '0.21898',
|
|
332
|
+
// l: '0.21889',
|
|
333
|
+
// o: '0.21897',
|
|
334
|
+
// v: '5247',
|
|
335
|
+
// st: 0
|
|
336
|
+
// }
|
|
337
|
+
// ],
|
|
338
|
+
// f: true,
|
|
339
|
+
// sendTime: 1757251217643,
|
|
340
|
+
// shared: false
|
|
341
|
+
// }
|
|
342
|
+
//
|
|
343
|
+
const marketId = this.safeString(message, 'symbol');
|
|
344
|
+
const market = this.market(marketId);
|
|
345
|
+
const symbol = market['symbol'];
|
|
346
|
+
const params = this.safeDict(message, 'params', {});
|
|
347
|
+
const timeframeId = this.safeString(params, 'klineType');
|
|
348
|
+
const timeframe = this.findTimeframe(timeframeId);
|
|
349
|
+
if (!(symbol in this.ohlcvs)) {
|
|
350
|
+
this.ohlcvs[symbol] = {};
|
|
351
|
+
}
|
|
352
|
+
if (!(timeframe in this.ohlcvs[symbol])) {
|
|
353
|
+
const limit = this.safeInteger(this.options['ws'], 'OHLCVLimit', 1000);
|
|
354
|
+
this.ohlcvs[symbol][timeframe] = new Cache.ArrayCacheByTimestamp(limit);
|
|
355
|
+
}
|
|
356
|
+
const stored = this.ohlcvs[symbol][timeframe];
|
|
357
|
+
const data = this.safeList(message, 'data', []);
|
|
358
|
+
for (let i = 0; i < data.length; i++) {
|
|
359
|
+
const parsed = this.parseWsOHLCV(data[i], market);
|
|
360
|
+
stored.append(parsed);
|
|
361
|
+
}
|
|
362
|
+
const messageHash = 'ohlcv::' + symbol + '::' + timeframe;
|
|
363
|
+
const resolveData = [symbol, timeframe, stored];
|
|
364
|
+
client.resolve(resolveData, messageHash);
|
|
365
|
+
}
|
|
366
|
+
parseWsOHLCV(ohlcv, market = undefined) {
|
|
367
|
+
//
|
|
368
|
+
// {
|
|
369
|
+
// t: 1757251200000,
|
|
370
|
+
// o: '0.21897',
|
|
371
|
+
// h: '0.21898',
|
|
372
|
+
// l: '0.21889',
|
|
373
|
+
// c: '0.21889',
|
|
374
|
+
// v: '5247',
|
|
375
|
+
// s: 'DOGEUSDT',
|
|
376
|
+
// sn: 'DOGEUSDT',
|
|
377
|
+
// st: 0
|
|
378
|
+
// }
|
|
379
|
+
//
|
|
380
|
+
const parsed = this.parseOHLCV(ohlcv, market);
|
|
381
|
+
return parsed;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* @method
|
|
385
|
+
* @name toobit#watchTicker
|
|
386
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#individual-symbol-ticker-streams
|
|
387
|
+
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
388
|
+
* @param {string} symbol unified symbol of the market to fetch the ticker for
|
|
389
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
390
|
+
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
391
|
+
*/
|
|
392
|
+
async watchTicker(symbol, params = {}) {
|
|
393
|
+
await this.loadMarkets();
|
|
394
|
+
symbol = this.symbol(symbol);
|
|
395
|
+
const tickers = await this.watchTickers([symbol], params);
|
|
396
|
+
return tickers[symbol];
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* @method
|
|
400
|
+
* @name toobit#watchTickers
|
|
401
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#individual-symbol-ticker-streams
|
|
402
|
+
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
|
|
403
|
+
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
|
|
404
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
405
|
+
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
406
|
+
*/
|
|
407
|
+
async watchTickers(symbols = undefined, params = {}) {
|
|
408
|
+
await this.loadMarkets();
|
|
409
|
+
symbols = this.marketSymbols(symbols, undefined, false);
|
|
410
|
+
const messageHashes = [];
|
|
411
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
412
|
+
const symbol = symbols[i];
|
|
413
|
+
const market = this.market(symbol);
|
|
414
|
+
messageHashes.push('ticker::' + symbol);
|
|
415
|
+
market['id'];
|
|
416
|
+
}
|
|
417
|
+
const marketIds = this.marketIds(symbols);
|
|
418
|
+
const url = this.urls['api']['ws']['common'] + '/quote/ws/v1';
|
|
419
|
+
const request = {
|
|
420
|
+
'symbol': marketIds.join(','),
|
|
421
|
+
'topic': 'realtimes',
|
|
422
|
+
'event': 'sub',
|
|
423
|
+
};
|
|
424
|
+
const ticker = await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes);
|
|
425
|
+
if (this.newUpdates) {
|
|
426
|
+
const result = {};
|
|
427
|
+
result[ticker['symbol']] = ticker;
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
return this.filterByArray(this.tickers, 'symbol', symbols);
|
|
431
|
+
}
|
|
432
|
+
handleTickers(client, message) {
|
|
433
|
+
//
|
|
434
|
+
// {
|
|
435
|
+
// "symbol": "DOGEUSDT",
|
|
436
|
+
// "symbolName": "DOGEUSDT",
|
|
437
|
+
// "topic": "realtimes",
|
|
438
|
+
// "params": {
|
|
439
|
+
// "realtimeInterval": "24h"
|
|
440
|
+
// },
|
|
441
|
+
// "data": [
|
|
442
|
+
// {
|
|
443
|
+
// "t": 1757257643683,
|
|
444
|
+
// "s": "DOGEUSDT",
|
|
445
|
+
// "o": "0.21462",
|
|
446
|
+
// "h": "0.22518",
|
|
447
|
+
// "l": "0.21229",
|
|
448
|
+
// "c": "0.2232",
|
|
449
|
+
// "v": "283337017",
|
|
450
|
+
// "qv": "62063771.42702",
|
|
451
|
+
// "sn": "DOGEUSDT",
|
|
452
|
+
// "m": "0.04",
|
|
453
|
+
// "e": 301,
|
|
454
|
+
// "c24h": "0.2232",
|
|
455
|
+
// "h24h": "0.22518",
|
|
456
|
+
// "l24h": "0.21229",
|
|
457
|
+
// "o24h": "0.21462",
|
|
458
|
+
// "v24h": "283337017",
|
|
459
|
+
// "qv24h": "62063771.42702",
|
|
460
|
+
// "m24h": "0.04"
|
|
461
|
+
// }
|
|
462
|
+
// ],
|
|
463
|
+
// "f": false,
|
|
464
|
+
// "sendTime": 1757257643751,
|
|
465
|
+
// "shared": false
|
|
466
|
+
// }
|
|
467
|
+
//
|
|
468
|
+
const data = this.safeList(message, 'data');
|
|
469
|
+
const newTickers = {};
|
|
470
|
+
for (let i = 0; i < data.length; i++) {
|
|
471
|
+
const ticker = data[i];
|
|
472
|
+
const parsed = this.parseWsTicker(ticker);
|
|
473
|
+
const symbol = parsed['symbol'];
|
|
474
|
+
this.tickers[symbol] = parsed;
|
|
475
|
+
newTickers[symbol] = parsed;
|
|
476
|
+
const messageHash = 'ticker::' + symbol;
|
|
477
|
+
client.resolve(parsed, messageHash);
|
|
478
|
+
}
|
|
479
|
+
client.resolve(newTickers, 'tickers');
|
|
480
|
+
}
|
|
481
|
+
parseWsTicker(ticker, market = undefined) {
|
|
482
|
+
return this.parseTicker(ticker, market);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* @method
|
|
486
|
+
* @name toobit#watchOrderBook
|
|
487
|
+
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
488
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#partial-book-depth-streams
|
|
489
|
+
* @param {string} symbol unified symbol of the market to fetch the order book for
|
|
490
|
+
* @param {int} [limit] the maximum amount of order book entries to return.
|
|
491
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
492
|
+
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
493
|
+
*/
|
|
494
|
+
async watchOrderBook(symbol, limit = undefined, params = {}) {
|
|
495
|
+
return await this.watchOrderBookForSymbols([symbol], limit, params);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* @method
|
|
499
|
+
* @name toobit#watchOrderBookForSymbols
|
|
500
|
+
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
501
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#partial-book-depth-streams
|
|
502
|
+
* @param {string[]} symbols unified array of symbols
|
|
503
|
+
* @param {int} [limit] the maximum amount of order book entries to return.
|
|
504
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
505
|
+
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
506
|
+
*/
|
|
507
|
+
async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
|
|
508
|
+
await this.loadMarkets();
|
|
509
|
+
symbols = this.marketSymbols(symbols, undefined, false);
|
|
510
|
+
let channel = undefined;
|
|
511
|
+
[channel, params] = this.handleOptionAndParams(params, 'watchOrderBook', 'channel', 'depth');
|
|
512
|
+
const messageHashes = [];
|
|
513
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
514
|
+
const symbol = symbols[i];
|
|
515
|
+
const market = this.market(symbol);
|
|
516
|
+
messageHashes.push('orderBook::' + symbol + '::' + channel);
|
|
517
|
+
market['id'];
|
|
518
|
+
}
|
|
519
|
+
const marketIds = this.marketIds(symbols);
|
|
520
|
+
const url = this.urls['api']['ws']['common'] + '/quote/ws/v1';
|
|
521
|
+
const request = {
|
|
522
|
+
'symbol': marketIds.join(','),
|
|
523
|
+
'topic': channel,
|
|
524
|
+
'event': 'sub',
|
|
525
|
+
};
|
|
526
|
+
const orderbook = await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes);
|
|
527
|
+
return orderbook.limit();
|
|
528
|
+
}
|
|
529
|
+
handleOrderBook(client, message) {
|
|
530
|
+
//
|
|
531
|
+
// {
|
|
532
|
+
// symbol: 'DOGEUSDT',
|
|
533
|
+
// symbolName: 'DOGEUSDT',
|
|
534
|
+
// topic: 'depth',
|
|
535
|
+
// params: { realtimeInterval: '24h' },
|
|
536
|
+
// data: [
|
|
537
|
+
// {
|
|
538
|
+
// e: 301,
|
|
539
|
+
// t: 1757304842860,
|
|
540
|
+
// v: '9814355_1E-18',
|
|
541
|
+
// b: [Array],
|
|
542
|
+
// a: [Array],
|
|
543
|
+
// o: 0
|
|
544
|
+
// }
|
|
545
|
+
// ],
|
|
546
|
+
// f: false,
|
|
547
|
+
// sendTime: 1757304843047,
|
|
548
|
+
// shared: false
|
|
549
|
+
// }
|
|
550
|
+
//
|
|
551
|
+
const isSnapshot = this.safeBool(message, 'f', false);
|
|
552
|
+
if (isSnapshot) {
|
|
553
|
+
this.setOrderBookSnapshot(client, message, 'diffDepth');
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const marketId = this.safeString(message, 'symbol');
|
|
557
|
+
const market = this.safeMarket(marketId);
|
|
558
|
+
const symbol = market['symbol'];
|
|
559
|
+
const data = this.safeList(message, 'data', []);
|
|
560
|
+
for (let i = 0; i < data.length; i++) {
|
|
561
|
+
const entry = data[i];
|
|
562
|
+
const messageHash = 'orderBook::' + symbol + '::' + 'diffDepth';
|
|
563
|
+
if (!(symbol in this.orderbooks)) {
|
|
564
|
+
const limit = this.safeInteger(this.options['ws'], 'orderBookLimit', 1000);
|
|
565
|
+
this.orderbooks[symbol] = this.orderBook({}, limit);
|
|
566
|
+
}
|
|
567
|
+
const orderBook = this.orderbooks[symbol];
|
|
568
|
+
const timestamp = this.safeInteger(entry, 't');
|
|
569
|
+
const bids = this.safeList(entry, 'b', []);
|
|
570
|
+
const asks = this.safeList(entry, 'a', []);
|
|
571
|
+
this.handleDeltas(orderBook['asks'], asks);
|
|
572
|
+
this.handleDeltas(orderBook['bids'], bids);
|
|
573
|
+
orderBook['timestamp'] = timestamp;
|
|
574
|
+
this.orderbooks[symbol] = orderBook;
|
|
575
|
+
client.resolve(orderBook, messageHash);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
handleDelta(bookside, delta) {
|
|
579
|
+
const bidAsk = this.parseBidAsk(delta);
|
|
580
|
+
bookside.storeArray(bidAsk);
|
|
581
|
+
}
|
|
582
|
+
handleOrderBookPartialSnapshot(client, message) {
|
|
583
|
+
//
|
|
584
|
+
// {
|
|
585
|
+
// symbol: 'DOGEUSDT',
|
|
586
|
+
// symbolName: 'DOGEUSDT',
|
|
587
|
+
// topic: 'depth',
|
|
588
|
+
// params: { realtimeInterval: '24h' },
|
|
589
|
+
// data: [
|
|
590
|
+
// {
|
|
591
|
+
// e: 301,
|
|
592
|
+
// s: 'DOGEUSDT',
|
|
593
|
+
// t: 1757304842860,
|
|
594
|
+
// v: '9814355_1E-18',
|
|
595
|
+
// b: [Array],
|
|
596
|
+
// a: [Array],
|
|
597
|
+
// o: 0
|
|
598
|
+
// }
|
|
599
|
+
// ],
|
|
600
|
+
// f: false,
|
|
601
|
+
// sendTime: 1757304843047,
|
|
602
|
+
// shared: false
|
|
603
|
+
// }
|
|
604
|
+
//
|
|
605
|
+
this.setOrderBookSnapshot(client, message, 'depth');
|
|
606
|
+
}
|
|
607
|
+
setOrderBookSnapshot(client, message, channel) {
|
|
608
|
+
const data = this.safeList(message, 'data', []);
|
|
609
|
+
const length = data.length;
|
|
610
|
+
if (length === 0) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
for (let i = 0; i < length; i++) {
|
|
614
|
+
const entry = data[i];
|
|
615
|
+
const marketId = this.safeString(entry, 's');
|
|
616
|
+
const symbol = this.safeSymbol(marketId);
|
|
617
|
+
const messageHash = 'orderBook::' + symbol + '::' + channel;
|
|
618
|
+
if (!(symbol in this.orderbooks)) {
|
|
619
|
+
const limit = this.safeInteger(this.options['ws'], 'orderBookLimit', 1000);
|
|
620
|
+
this.orderbooks[symbol] = this.orderBook({}, limit);
|
|
621
|
+
}
|
|
622
|
+
const orderbook = this.orderbooks[symbol];
|
|
623
|
+
const timestamp = this.safeInteger(entry, 't');
|
|
624
|
+
const snapshot = this.parseOrderBook(entry, symbol, timestamp, 'b', 'a');
|
|
625
|
+
orderbook.reset(snapshot);
|
|
626
|
+
client.resolve(orderbook, messageHash);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* @method
|
|
631
|
+
* @name toobit#watchBalance
|
|
632
|
+
* @description query for balance and get the amount of funds available for trading or funds locked in orders
|
|
633
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#payload-account-update
|
|
634
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
635
|
+
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
|
|
636
|
+
*/
|
|
637
|
+
async watchBalance(params = {}) {
|
|
638
|
+
await this.loadMarkets();
|
|
639
|
+
await this.authenticate();
|
|
640
|
+
let marketType = undefined;
|
|
641
|
+
[marketType, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params);
|
|
642
|
+
const isSpot = (marketType === 'spot');
|
|
643
|
+
const type = isSpot ? 'spot' : 'contract';
|
|
644
|
+
const spotSubHash = 'spot:balance';
|
|
645
|
+
const swapSubHash = 'contract:private';
|
|
646
|
+
const spotMessageHash = 'spot:balance';
|
|
647
|
+
const swapMessageHash = 'contract:balance';
|
|
648
|
+
const messageHash = isSpot ? spotMessageHash : swapMessageHash;
|
|
649
|
+
const subscriptionHash = isSpot ? spotSubHash : swapSubHash;
|
|
650
|
+
const url = this.getUserStreamUrl();
|
|
651
|
+
const client = this.client(url);
|
|
652
|
+
this.setBalanceCache(client, marketType, subscriptionHash, params);
|
|
653
|
+
client.future(type + ':fetchBalanceSnapshot');
|
|
654
|
+
return await this.watch(url, messageHash, params, subscriptionHash);
|
|
655
|
+
}
|
|
656
|
+
setBalanceCache(client, marketType, subscriptionHash = undefined, params = {}) {
|
|
657
|
+
if (subscriptionHash in client.subscriptions) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
const type = (marketType === 'spot') ? 'spot' : 'contract';
|
|
661
|
+
const messageHash = type + ':fetchBalanceSnapshot';
|
|
662
|
+
if (!(messageHash in client.futures)) {
|
|
663
|
+
client.future(messageHash);
|
|
664
|
+
this.spawn(this.loadBalanceSnapshot, client, messageHash, marketType);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
handleBalance(client, message) {
|
|
668
|
+
//
|
|
669
|
+
// spot
|
|
670
|
+
//
|
|
671
|
+
// [
|
|
672
|
+
// {
|
|
673
|
+
// e: 'outboundAccountInfo',
|
|
674
|
+
// E: '1758226989725',
|
|
675
|
+
// T: true,
|
|
676
|
+
// W: true,
|
|
677
|
+
// D: true,
|
|
678
|
+
// B: [
|
|
679
|
+
// {
|
|
680
|
+
// a: "USDT",
|
|
681
|
+
// f: "6.37242839",
|
|
682
|
+
// l: "0",
|
|
683
|
+
// },
|
|
684
|
+
// ]
|
|
685
|
+
// }
|
|
686
|
+
// ]
|
|
687
|
+
//
|
|
688
|
+
// contract
|
|
689
|
+
//
|
|
690
|
+
// [
|
|
691
|
+
// {
|
|
692
|
+
// e: 'outboundContractAccountInfo',
|
|
693
|
+
// E: '1758226989742',
|
|
694
|
+
// T: true,
|
|
695
|
+
// W: true,
|
|
696
|
+
// D: true,
|
|
697
|
+
// B: [ [Object] ]
|
|
698
|
+
// }
|
|
699
|
+
// ]
|
|
700
|
+
//
|
|
701
|
+
const channel = this.safeString(message, 'e');
|
|
702
|
+
const data = this.safeList(message, 'B', []);
|
|
703
|
+
const timestamp = this.safeInteger(message, 'E');
|
|
704
|
+
const type = (channel === 'outboundContractAccountInfo') ? 'contract' : 'spot';
|
|
705
|
+
if (!(type in this.balance)) {
|
|
706
|
+
this.balance[type] = {};
|
|
707
|
+
}
|
|
708
|
+
this.balance[type]['info'] = data;
|
|
709
|
+
this.balance[type]['timestamp'] = timestamp;
|
|
710
|
+
this.balance[type]['datetime'] = this.iso8601(timestamp);
|
|
711
|
+
for (let i = 0; i < data.length; i++) {
|
|
712
|
+
const balance = data[i];
|
|
713
|
+
const currencyId = this.safeString(balance, 'a');
|
|
714
|
+
const code = this.safeCurrencyCode(currencyId);
|
|
715
|
+
const account = this.account();
|
|
716
|
+
account['info'] = balance;
|
|
717
|
+
account['used'] = this.safeString(balance, 'l');
|
|
718
|
+
account['free'] = this.safeString(balance, 'f');
|
|
719
|
+
this.balance[type][code] = account;
|
|
720
|
+
}
|
|
721
|
+
this.balance[type] = this.safeBalance(this.balance[type]);
|
|
722
|
+
client.resolve(this.balance[type], type + ':balance');
|
|
723
|
+
}
|
|
724
|
+
async loadBalanceSnapshot(client, messageHash, marketType) {
|
|
725
|
+
const response = await this.fetchBalance({ 'type': marketType });
|
|
726
|
+
const type = (marketType === 'spot') ? 'spot' : 'contract';
|
|
727
|
+
this.balance[type] = this.extend(response, this.safeDict(this.balance, type, {}));
|
|
728
|
+
// don't remove the future from the .futures cache
|
|
729
|
+
const future = client.futures[messageHash];
|
|
730
|
+
future.resolve();
|
|
731
|
+
client.resolve(this.balance[type], type + ':fetchBalanceSnapshot');
|
|
732
|
+
client.resolve(this.balance[type], type + ':balance'); // we should also resolve right away after snapshot, so user doesn't double-fetch balance
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* @method
|
|
736
|
+
* @name toobit#watchOrders
|
|
737
|
+
* @description watches information on multiple orders made by the user
|
|
738
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#payload-order-update
|
|
739
|
+
* @param {string} symbol unified market symbol of the market orders were made in
|
|
740
|
+
* @param {int} [since] the earliest time in ms to fetch orders for
|
|
741
|
+
* @param {int} [limit] the maximum number of order structures to retrieve
|
|
742
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
743
|
+
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
744
|
+
*/
|
|
745
|
+
async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
746
|
+
await this.loadMarkets();
|
|
747
|
+
await this.authenticate();
|
|
748
|
+
const market = this.marketOrNull(symbol);
|
|
749
|
+
symbol = this.safeString(market, 'symbol', symbol);
|
|
750
|
+
let messageHash = 'orders';
|
|
751
|
+
if (symbol !== undefined) {
|
|
752
|
+
messageHash = messageHash + ':' + symbol;
|
|
753
|
+
}
|
|
754
|
+
const url = this.getUserStreamUrl();
|
|
755
|
+
const orders = await this.watch(url, messageHash, params, messageHash);
|
|
756
|
+
if (this.newUpdates) {
|
|
757
|
+
limit = orders.getLimit(symbol, limit);
|
|
758
|
+
}
|
|
759
|
+
return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
|
|
760
|
+
}
|
|
761
|
+
handleOrder(client, message) {
|
|
762
|
+
//
|
|
763
|
+
// {
|
|
764
|
+
// "e": "executionReport",
|
|
765
|
+
// "E": "1758311011844",
|
|
766
|
+
// "s": "DOGEUSDT",
|
|
767
|
+
// "c": "1758311011948",
|
|
768
|
+
// "S": "BUY",
|
|
769
|
+
// "o": "LIMIT",
|
|
770
|
+
// "f": "GTC",
|
|
771
|
+
// "q": "22",
|
|
772
|
+
// "p": "0.23",
|
|
773
|
+
// "pt": "INPUT",
|
|
774
|
+
// "X": "NEW",
|
|
775
|
+
// "i": "2043255292855185152",
|
|
776
|
+
// "l": "0", // Last executed quantity
|
|
777
|
+
// "z": "0", // Cumulative filled quantity
|
|
778
|
+
// "L": "0", // Last executed price
|
|
779
|
+
// "n": "0",
|
|
780
|
+
// "N": "",
|
|
781
|
+
// "u": true,
|
|
782
|
+
// "w": true,
|
|
783
|
+
// "m": false,
|
|
784
|
+
// "O": "1758311011833",
|
|
785
|
+
// "U": "1758311011841",
|
|
786
|
+
// "Z": "0",
|
|
787
|
+
// "C": false,
|
|
788
|
+
// "v": "0",
|
|
789
|
+
// "rp": "0",
|
|
790
|
+
// "td": "0"
|
|
791
|
+
// }
|
|
792
|
+
//
|
|
793
|
+
if (this.orders === undefined) {
|
|
794
|
+
const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
|
|
795
|
+
this.orders = new Cache.ArrayCacheBySymbolById(limit);
|
|
796
|
+
}
|
|
797
|
+
const orders = this.orders;
|
|
798
|
+
const order = this.parseWsOrder(message);
|
|
799
|
+
orders.append(order);
|
|
800
|
+
let messageHash = 'orders';
|
|
801
|
+
client.resolve(orders, messageHash);
|
|
802
|
+
messageHash = 'orders:' + this.safeString(order, 'symbol');
|
|
803
|
+
client.resolve(orders, messageHash);
|
|
804
|
+
}
|
|
805
|
+
parseWsOrder(order, market = undefined) {
|
|
806
|
+
const timestamp = this.safeInteger(order, 'O');
|
|
807
|
+
const marketId = this.safeString(order, 's');
|
|
808
|
+
const symbol = this.safeSymbol(marketId, market);
|
|
809
|
+
const priceType = this.safeStringLower(order, 'pt');
|
|
810
|
+
const rawOrderType = this.safeStringLower(order, 'o');
|
|
811
|
+
let orderType = undefined;
|
|
812
|
+
if (priceType === 'market') {
|
|
813
|
+
orderType = 'market';
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
orderType = rawOrderType;
|
|
817
|
+
}
|
|
818
|
+
const feeCost = this.safeNumber(order, 'n');
|
|
819
|
+
let fee = undefined;
|
|
820
|
+
if (feeCost !== undefined) {
|
|
821
|
+
fee = {
|
|
822
|
+
'cost': feeCost,
|
|
823
|
+
'currency': undefined,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
return this.safeOrder({
|
|
827
|
+
'info': order,
|
|
828
|
+
'id': this.safeString(order, 'i'),
|
|
829
|
+
'clientOrderId': this.safeString(order, 'c'),
|
|
830
|
+
'timestamp': timestamp,
|
|
831
|
+
'datetime': this.iso8601(timestamp),
|
|
832
|
+
'lastUpdateTimestamp': this.safeInteger2(order, 'U', 'E'),
|
|
833
|
+
'symbol': symbol,
|
|
834
|
+
'type': orderType,
|
|
835
|
+
'timeInForce': this.safeStringUpper(order, 'f'),
|
|
836
|
+
'postOnly': undefined,
|
|
837
|
+
'side': this.safeStringLower(order, 'S'),
|
|
838
|
+
'price': this.safeString(order, 'L'),
|
|
839
|
+
'stopPrice': undefined,
|
|
840
|
+
'triggerPrice': undefined,
|
|
841
|
+
'amount': this.safeString(order, 'q'),
|
|
842
|
+
'cost': undefined,
|
|
843
|
+
'average': this.safeString(order, 'p'),
|
|
844
|
+
'filled': this.safeString(order, 'z'),
|
|
845
|
+
'remaining': undefined,
|
|
846
|
+
'status': this.parseOrderStatus(this.safeString(order, 'X')),
|
|
847
|
+
'fee': fee,
|
|
848
|
+
'trades': undefined,
|
|
849
|
+
}, market);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* @method
|
|
853
|
+
* @name toobit#watchMyTrades
|
|
854
|
+
* @description watches information on multiple trades made by the user
|
|
855
|
+
* @see https://toobit-docs.github.io/apidocs/spot/v1/en/#payload-ticket-push
|
|
856
|
+
* @param {string} symbol unified market symbol of the market trades were made in
|
|
857
|
+
* @param {int} [since] the earliest time in ms to fetch trades for
|
|
858
|
+
* @param {int} [limit] the maximum number of trade structures to retrieve
|
|
859
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
860
|
+
* @param {boolean} [params.unifiedMargin] use unified margin account
|
|
861
|
+
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
|
|
862
|
+
*/
|
|
863
|
+
async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
864
|
+
await this.loadMarkets();
|
|
865
|
+
await this.authenticate();
|
|
866
|
+
const market = this.marketOrNull(symbol);
|
|
867
|
+
symbol = this.safeString(market, 'symbol', symbol);
|
|
868
|
+
let messageHash = 'myTrades';
|
|
869
|
+
if (symbol !== undefined) {
|
|
870
|
+
messageHash = messageHash + ':' + symbol;
|
|
871
|
+
}
|
|
872
|
+
const url = this.getUserStreamUrl();
|
|
873
|
+
const trades = await this.watch(url, messageHash, params, messageHash);
|
|
874
|
+
if (this.newUpdates) {
|
|
875
|
+
limit = trades.getLimit(symbol, limit);
|
|
876
|
+
}
|
|
877
|
+
return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
|
|
878
|
+
}
|
|
879
|
+
handleMyTrade(client, message) {
|
|
880
|
+
//
|
|
881
|
+
// {
|
|
882
|
+
// "e": "ticketInfo",
|
|
883
|
+
// "E": "1758314657847",
|
|
884
|
+
// "s": "DOGEUSDT",
|
|
885
|
+
// "q": "22.0",
|
|
886
|
+
// "t": "1758314657842",
|
|
887
|
+
// "p": "0.26667",
|
|
888
|
+
// "T": "4864732022877055421",
|
|
889
|
+
// "o": "2043285877770284800",
|
|
890
|
+
// "c": "1758314657002",
|
|
891
|
+
// "a": "1783404067076253952",
|
|
892
|
+
// "m": false,
|
|
893
|
+
// "S": "BUY"
|
|
894
|
+
// }
|
|
895
|
+
//
|
|
896
|
+
let myTrades = this.myTrades;
|
|
897
|
+
if (myTrades === undefined) {
|
|
898
|
+
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
|
|
899
|
+
myTrades = new Cache.ArrayCacheBySymbolById(limit);
|
|
900
|
+
}
|
|
901
|
+
const trade = this.parseMyTrade(message);
|
|
902
|
+
myTrades.append(trade);
|
|
903
|
+
let messageHash = 'myTrades:' + trade['symbol'];
|
|
904
|
+
client.resolve(myTrades, messageHash);
|
|
905
|
+
messageHash = 'myTrades';
|
|
906
|
+
client.resolve(myTrades, messageHash);
|
|
907
|
+
}
|
|
908
|
+
parseMyTrade(trade, market = undefined) {
|
|
909
|
+
const marketId = this.safeString(trade, 's');
|
|
910
|
+
const ts = this.safeString(trade, 't');
|
|
911
|
+
return this.safeTrade({
|
|
912
|
+
'info': trade,
|
|
913
|
+
'id': this.safeString(trade, 'T'),
|
|
914
|
+
'timestamp': ts,
|
|
915
|
+
'datetime': this.iso8601(ts),
|
|
916
|
+
'symbol': this.safeSymbol(marketId, market),
|
|
917
|
+
'order': this.safeString(trade, 'o'),
|
|
918
|
+
'type': undefined,
|
|
919
|
+
'side': this.safeStringLower(trade, 'S'),
|
|
920
|
+
'takerOrMaker': this.safeBool(trade, 'm') ? 'maker' : 'taker',
|
|
921
|
+
'price': this.safeString(trade, 'p'),
|
|
922
|
+
'amount': this.safeString(trade, 'q'),
|
|
923
|
+
'cost': undefined,
|
|
924
|
+
'fee': undefined,
|
|
925
|
+
}, market);
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* @method
|
|
929
|
+
* @name toobit#watchPositions
|
|
930
|
+
* @see https://toobit-docs.github.io/apidocs/usdt_swap/v1/en/#event-position-update
|
|
931
|
+
* @description watch all open positions
|
|
932
|
+
* @param {string[]} [symbols] list of unified market symbols
|
|
933
|
+
* @param {int} [since] the earliest time in ms to fetch positions for
|
|
934
|
+
* @param {int} [limit] the maximum number of positions to retrieve
|
|
935
|
+
* @param {object} params extra parameters specific to the exchange API endpoint
|
|
936
|
+
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
|
|
937
|
+
*/
|
|
938
|
+
async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
|
|
939
|
+
await this.loadMarkets();
|
|
940
|
+
await this.authenticate();
|
|
941
|
+
let messageHash = '';
|
|
942
|
+
if (!this.isEmpty(symbols)) {
|
|
943
|
+
symbols = this.marketSymbols(symbols);
|
|
944
|
+
messageHash = '::' + symbols.join(',');
|
|
945
|
+
}
|
|
946
|
+
const url = this.getUserStreamUrl();
|
|
947
|
+
const client = this.client(url);
|
|
948
|
+
await this.authenticate(url);
|
|
949
|
+
this.setPositionsCache(client, symbols);
|
|
950
|
+
const cache = this.positions;
|
|
951
|
+
if (cache === undefined) {
|
|
952
|
+
const snapshot = await client.future('fetchPositionsSnapshot');
|
|
953
|
+
return this.filterBySymbolsSinceLimit(snapshot, symbols, since, limit, true);
|
|
954
|
+
}
|
|
955
|
+
const newPositions = await this.watch(url, messageHash, undefined, messageHash);
|
|
956
|
+
if (this.newUpdates) {
|
|
957
|
+
return newPositions;
|
|
958
|
+
}
|
|
959
|
+
return this.filterBySymbolsSinceLimit(cache, symbols, since, limit, true);
|
|
960
|
+
}
|
|
961
|
+
setPositionsCache(client, type, symbols = undefined, isPortfolioMargin = false) {
|
|
962
|
+
if (this.positions === undefined) {
|
|
963
|
+
this.positions = {};
|
|
964
|
+
}
|
|
965
|
+
if (type in this.positions) {
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
const fetchPositionsSnapshot = this.handleOption('watchPositions', 'fetchPositionsSnapshot', false);
|
|
969
|
+
if (fetchPositionsSnapshot) {
|
|
970
|
+
const messageHash = type + ':fetchPositionsSnapshot';
|
|
971
|
+
if (!(messageHash in client.futures)) {
|
|
972
|
+
client.future(messageHash);
|
|
973
|
+
this.spawn(this.loadPositionsSnapshot, client, messageHash, type, isPortfolioMargin);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
this.positions[type] = new Cache.ArrayCacheBySymbolBySide();
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
async loadPositionsSnapshot(client, messageHash, type) {
|
|
981
|
+
const params = {
|
|
982
|
+
'type': type,
|
|
983
|
+
};
|
|
984
|
+
const positions = await this.fetchPositions(undefined, params);
|
|
985
|
+
this.positions[type] = new Cache.ArrayCacheBySymbolBySide();
|
|
986
|
+
const cache = this.positions[type];
|
|
987
|
+
for (let i = 0; i < positions.length; i++) {
|
|
988
|
+
const position = positions[i];
|
|
989
|
+
cache.append(position);
|
|
990
|
+
}
|
|
991
|
+
// don't remove the future from the .futures cache
|
|
992
|
+
const future = client.futures[messageHash];
|
|
993
|
+
future.resolve(cache);
|
|
994
|
+
client.resolve(cache, type + ':positions');
|
|
995
|
+
}
|
|
996
|
+
handlePositions(client, message) {
|
|
997
|
+
//
|
|
998
|
+
// [
|
|
999
|
+
// {
|
|
1000
|
+
// e: 'outboundContractPositionInfo',
|
|
1001
|
+
// E: '1758316454554',
|
|
1002
|
+
// A: '1783404067076253954',
|
|
1003
|
+
// s: 'DOGE-SWAP-USDT',
|
|
1004
|
+
// S: 'LONG',
|
|
1005
|
+
// p: '0',
|
|
1006
|
+
// P: '0',
|
|
1007
|
+
// a: '0',
|
|
1008
|
+
// f: '0.1228',
|
|
1009
|
+
// m: '0',
|
|
1010
|
+
// r: '0',
|
|
1011
|
+
// up: '0',
|
|
1012
|
+
// pr: '0',
|
|
1013
|
+
// pv: '0',
|
|
1014
|
+
// v: '3.0',
|
|
1015
|
+
// mt: 'CROSS',
|
|
1016
|
+
// mm: '0',
|
|
1017
|
+
// mp: '0.265410000000000000'
|
|
1018
|
+
// }
|
|
1019
|
+
// ]
|
|
1020
|
+
//
|
|
1021
|
+
const subscriptions = Object.keys(client.subscriptions);
|
|
1022
|
+
const accountType = subscriptions[0];
|
|
1023
|
+
if (this.positions === undefined) {
|
|
1024
|
+
this.positions = {};
|
|
1025
|
+
}
|
|
1026
|
+
if (!(accountType in this.positions)) {
|
|
1027
|
+
this.positions[accountType] = new Cache.ArrayCacheBySymbolBySide();
|
|
1028
|
+
}
|
|
1029
|
+
const cache = this.positions[accountType];
|
|
1030
|
+
const newPositions = [];
|
|
1031
|
+
for (let i = 0; i < message.length; i++) {
|
|
1032
|
+
const rawPosition = message[i];
|
|
1033
|
+
const position = this.parseWsPosition(rawPosition);
|
|
1034
|
+
const timestamp = this.safeInteger(rawPosition, 'E');
|
|
1035
|
+
position['timestamp'] = timestamp;
|
|
1036
|
+
position['datetime'] = this.iso8601(timestamp);
|
|
1037
|
+
newPositions.push(position);
|
|
1038
|
+
cache.append(position);
|
|
1039
|
+
}
|
|
1040
|
+
const messageHashes = this.findMessageHashes(client, accountType + ':positions::');
|
|
1041
|
+
for (let i = 0; i < messageHashes.length; i++) {
|
|
1042
|
+
const messageHash = messageHashes[i];
|
|
1043
|
+
const parts = messageHash.split('::');
|
|
1044
|
+
const symbolsString = parts[1];
|
|
1045
|
+
const symbols = symbolsString.split(',');
|
|
1046
|
+
const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
|
|
1047
|
+
if (!this.isEmpty(positions)) {
|
|
1048
|
+
client.resolve(positions, messageHash);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
client.resolve(newPositions, accountType + ':positions');
|
|
1052
|
+
}
|
|
1053
|
+
parseWsPosition(position, market = undefined) {
|
|
1054
|
+
const marketId = this.safeString(position, 's');
|
|
1055
|
+
return this.safePosition({
|
|
1056
|
+
'info': position,
|
|
1057
|
+
'id': undefined,
|
|
1058
|
+
'symbol': this.safeSymbol(marketId, undefined),
|
|
1059
|
+
'notional': this.omitZero(this.safeString(position, 'pv')),
|
|
1060
|
+
'marginMode': this.safeStringLower(position, 'mt'),
|
|
1061
|
+
'liquidationPrice': this.safeString(position, 'f'),
|
|
1062
|
+
'entryPrice': this.safeString(position, 'p'),
|
|
1063
|
+
'unrealizedPnl': this.safeString(position, 'up'),
|
|
1064
|
+
'realizedPnl': this.safeNumber(position, 'r'),
|
|
1065
|
+
'percentage': undefined,
|
|
1066
|
+
'contracts': undefined,
|
|
1067
|
+
'contractSize': undefined,
|
|
1068
|
+
'markPrice': this.safeString(position, 'mp'),
|
|
1069
|
+
'side': this.safeStringLower(position, 'S'),
|
|
1070
|
+
'hedged': undefined,
|
|
1071
|
+
'timestamp': undefined,
|
|
1072
|
+
'datetime': undefined,
|
|
1073
|
+
'maintenanceMargin': this.safeString(position, 'mm'),
|
|
1074
|
+
'maintenanceMarginPercentage': undefined,
|
|
1075
|
+
'collateral': undefined,
|
|
1076
|
+
'initialMargin': this.omitZero(this.safeString(position, 'm')),
|
|
1077
|
+
'initialMarginPercentage': undefined,
|
|
1078
|
+
'leverage': this.safeString(position, 'v'),
|
|
1079
|
+
'marginRatio': undefined,
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
async authenticate(params = {}) {
|
|
1083
|
+
const client = this.client(this.getUserStreamUrl());
|
|
1084
|
+
const messageHash = 'authenticated';
|
|
1085
|
+
const future = client.future(messageHash);
|
|
1086
|
+
const authenticated = this.safeValue(client.subscriptions, messageHash);
|
|
1087
|
+
if (authenticated === undefined) {
|
|
1088
|
+
this.checkRequiredCredentials();
|
|
1089
|
+
const time = this.milliseconds();
|
|
1090
|
+
const lastAuthenticatedTime = this.safeInteger(this.options['ws'], 'lastAuthenticatedTime', 0);
|
|
1091
|
+
const listenKeyRefreshRate = this.safeInteger(this.options['ws'], 'listenKeyRefreshRate', 1200000);
|
|
1092
|
+
const delay = this.sum(listenKeyRefreshRate, 10000);
|
|
1093
|
+
if (time - lastAuthenticatedTime > delay) {
|
|
1094
|
+
try {
|
|
1095
|
+
client.subscriptions[messageHash] = true;
|
|
1096
|
+
const response = await this.privatePostApiV1UserDataStream(params);
|
|
1097
|
+
this.options['ws']['listenKey'] = this.safeString(response, 'listenKey');
|
|
1098
|
+
this.options['ws']['lastAuthenticatedTime'] = time;
|
|
1099
|
+
future.resolve(true);
|
|
1100
|
+
this.delay(listenKeyRefreshRate, this.keepAliveListenKey, params);
|
|
1101
|
+
}
|
|
1102
|
+
catch (e) {
|
|
1103
|
+
const err = new errors.AuthenticationError(this.id + ' ' + this.json(e));
|
|
1104
|
+
client.reject(err, messageHash);
|
|
1105
|
+
if (messageHash in client.subscriptions) {
|
|
1106
|
+
delete client.subscriptions[messageHash];
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return await future;
|
|
1112
|
+
}
|
|
1113
|
+
async keepAliveListenKey(params = {}) {
|
|
1114
|
+
const options = this.safeValue(this.options, 'ws', {});
|
|
1115
|
+
const listenKey = this.safeString(options, 'listenKey');
|
|
1116
|
+
if (listenKey === undefined) {
|
|
1117
|
+
// A network error happened: we can't renew a listen key that does not exist.
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
try {
|
|
1121
|
+
const response = await this.privatePostApiV1UserDataStream(params);
|
|
1122
|
+
this.options['ws']['listenKey'] = this.safeString(response, 'listenKey');
|
|
1123
|
+
this.options['ws']['lastAuthenticatedTime'] = this.milliseconds();
|
|
1124
|
+
}
|
|
1125
|
+
catch (error) {
|
|
1126
|
+
const url = this.getUserStreamUrl();
|
|
1127
|
+
const client = this.client(url);
|
|
1128
|
+
const messageHashes = Object.keys(client.futures);
|
|
1129
|
+
for (let i = 0; i < messageHashes.length; i++) {
|
|
1130
|
+
const messageHash = messageHashes[i];
|
|
1131
|
+
client.reject(error, messageHash);
|
|
1132
|
+
}
|
|
1133
|
+
this.options['ws']['listenKey'] = undefined;
|
|
1134
|
+
this.options['ws']['lastAuthenticatedTime'] = 0;
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
// whether or not to schedule another listenKey keepAlive request
|
|
1138
|
+
const listenKeyRefreshRate = this.safeInteger(this.options, 'listenKeyRefreshRate', 1200000);
|
|
1139
|
+
this.delay(listenKeyRefreshRate, this.keepAliveListenKey, params);
|
|
1140
|
+
}
|
|
1141
|
+
getUserStreamUrl() {
|
|
1142
|
+
return this.urls['api']['ws']['common'] + '/api/v1/ws/' + this.options['ws']['listenKey'];
|
|
1143
|
+
}
|
|
1144
|
+
handleErrorMessage(client, message) {
|
|
1145
|
+
//
|
|
1146
|
+
// {
|
|
1147
|
+
// "code": '-100010',
|
|
1148
|
+
// "desc": "Invalid Symbols!"
|
|
1149
|
+
// }
|
|
1150
|
+
//
|
|
1151
|
+
const code = this.safeString(message, 'code');
|
|
1152
|
+
if (code !== undefined) {
|
|
1153
|
+
const desc = this.safeString(message, 'desc');
|
|
1154
|
+
const msg = this.id + ' code: ' + code + ' message: ' + desc;
|
|
1155
|
+
const exception = new errors.ExchangeError(msg); // c# fix
|
|
1156
|
+
client.reject(exception);
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
return false;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
exports["default"] = toobit;
|