ccxt 4.3.88__py2.py3-none-any.whl → 4.3.90__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccxt/__init__.py +1 -1
- ccxt/ace.py +1 -0
- ccxt/alpaca.py +3 -2
- ccxt/ascendex.py +102 -116
- ccxt/async_support/__init__.py +1 -1
- ccxt/async_support/ace.py +1 -0
- ccxt/async_support/alpaca.py +3 -2
- ccxt/async_support/ascendex.py +102 -116
- ccxt/async_support/base/exchange.py +1 -1
- ccxt/async_support/bigone.py +1 -0
- ccxt/async_support/bingx.py +30 -17
- ccxt/async_support/bit2c.py +1 -0
- ccxt/async_support/bitbank.py +1 -0
- ccxt/async_support/bitfinex.py +1 -0
- ccxt/async_support/bitfinex2.py +21 -22
- ccxt/async_support/bitflyer.py +1 -0
- ccxt/async_support/bitget.py +3 -2
- ccxt/async_support/bitmart.py +4 -8
- ccxt/async_support/bitmex.py +1 -0
- ccxt/async_support/bitopro.py +1 -0
- ccxt/async_support/bitrue.py +62 -71
- ccxt/async_support/bitso.py +1 -0
- ccxt/async_support/bitstamp.py +1 -0
- ccxt/async_support/bitvavo.py +1 -0
- ccxt/async_support/blockchaincom.py +1 -0
- ccxt/async_support/btcalpha.py +1 -0
- ccxt/async_support/btcbox.py +1 -0
- ccxt/async_support/btcmarkets.py +1 -0
- ccxt/async_support/bybit.py +2 -0
- ccxt/async_support/cex.py +1 -0
- ccxt/async_support/coinbaseexchange.py +1 -0
- ccxt/async_support/coinbaseinternational.py +2 -1
- ccxt/async_support/coinex.py +1 -16
- ccxt/async_support/cryptocom.py +0 -12
- ccxt/async_support/hitbtc.py +1 -0
- ccxt/async_support/huobijp.py +0 -8
- ccxt/async_support/kraken.py +48 -48
- ccxt/async_support/latoken.py +1 -0
- ccxt/async_support/mexc.py +1 -61
- ccxt/async_support/okcoin.py +4 -9
- ccxt/async_support/okx.py +1 -8
- ccxt/async_support/onetrading.py +1 -0
- ccxt/async_support/phemex.py +1 -0
- ccxt/async_support/poloniexfutures.py +1 -0
- ccxt/async_support/probit.py +1 -0
- ccxt/async_support/vertex.py +1 -0
- ccxt/async_support/whitebit.py +5 -3
- ccxt/async_support/woo.py +1 -0
- ccxt/async_support/woofipro.py +1 -0
- ccxt/base/exchange.py +6 -4
- ccxt/bigone.py +1 -0
- ccxt/bingx.py +30 -17
- ccxt/bit2c.py +1 -0
- ccxt/bitbank.py +1 -0
- ccxt/bitfinex.py +1 -0
- ccxt/bitfinex2.py +21 -22
- ccxt/bitflyer.py +1 -0
- ccxt/bitget.py +3 -2
- ccxt/bitmart.py +4 -8
- ccxt/bitmex.py +1 -0
- ccxt/bitopro.py +1 -0
- ccxt/bitrue.py +62 -71
- ccxt/bitso.py +1 -0
- ccxt/bitstamp.py +1 -0
- ccxt/bitvavo.py +1 -0
- ccxt/blockchaincom.py +1 -0
- ccxt/btcalpha.py +1 -0
- ccxt/btcbox.py +1 -0
- ccxt/btcmarkets.py +1 -0
- ccxt/bybit.py +2 -0
- ccxt/cex.py +1 -0
- ccxt/coinbaseexchange.py +1 -0
- ccxt/coinbaseinternational.py +2 -1
- ccxt/coinex.py +1 -16
- ccxt/cryptocom.py +0 -12
- ccxt/hitbtc.py +1 -0
- ccxt/huobijp.py +0 -8
- ccxt/kraken.py +48 -48
- ccxt/latoken.py +1 -0
- ccxt/mexc.py +1 -61
- ccxt/okcoin.py +4 -9
- ccxt/okx.py +1 -8
- ccxt/onetrading.py +1 -0
- ccxt/phemex.py +1 -0
- ccxt/poloniexfutures.py +1 -0
- ccxt/pro/__init__.py +1 -1
- ccxt/pro/binance.py +280 -0
- ccxt/pro/bingx.py +235 -85
- ccxt/pro/bithumb.py +4 -0
- ccxt/pro/bitvavo.py +1 -0
- ccxt/pro/bybit.py +253 -2
- ccxt/pro/cex.py +1 -0
- ccxt/pro/coinex.py +941 -662
- ccxt/pro/lbank.py +1 -2
- ccxt/pro/okx.py +142 -2
- ccxt/probit.py +1 -0
- ccxt/vertex.py +1 -0
- ccxt/whitebit.py +5 -3
- ccxt/woo.py +1 -0
- ccxt/woofipro.py +1 -0
- {ccxt-4.3.88.dist-info → ccxt-4.3.90.dist-info}/METADATA +4 -4
- {ccxt-4.3.88.dist-info → ccxt-4.3.90.dist-info}/RECORD +105 -105
- {ccxt-4.3.88.dist-info → ccxt-4.3.90.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.3.88.dist-info → ccxt-4.3.90.dist-info}/WHEEL +0 -0
- {ccxt-4.3.88.dist-info → ccxt-4.3.90.dist-info}/top_level.txt +0 -0
ccxt/pro/coinex.py
CHANGED
@@ -4,7 +4,8 @@
|
|
4
4
|
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
5
5
|
|
6
6
|
import ccxt.async_support
|
7
|
-
from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById
|
7
|
+
from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById
|
8
|
+
import hashlib
|
8
9
|
from ccxt.base.types import Balances, Int, Order, OrderBook, Str, Strings, Ticker, Tickers, Trade
|
9
10
|
from ccxt.async_support.base.ws.client import Client
|
10
11
|
from typing import List
|
@@ -12,9 +13,9 @@ from ccxt.base.errors import ExchangeError
|
|
12
13
|
from ccxt.base.errors import AuthenticationError
|
13
14
|
from ccxt.base.errors import BadRequest
|
14
15
|
from ccxt.base.errors import NotSupported
|
16
|
+
from ccxt.base.errors import RateLimitExceeded
|
15
17
|
from ccxt.base.errors import ExchangeNotAvailable
|
16
18
|
from ccxt.base.errors import RequestTimeout
|
17
|
-
from ccxt.base.precise import Precise
|
18
19
|
|
19
20
|
|
20
21
|
class coinex(ccxt.async_support.coinex):
|
@@ -24,25 +25,30 @@ class coinex(ccxt.async_support.coinex):
|
|
24
25
|
'has': {
|
25
26
|
'ws': True,
|
26
27
|
'watchBalance': True,
|
28
|
+
'watchBidsAsks': True,
|
27
29
|
'watchTicker': True,
|
28
30
|
'watchTickers': True,
|
29
31
|
'watchTrades': True,
|
30
|
-
'
|
32
|
+
'watchTradesForSymbols': True,
|
33
|
+
'watchMyTrades': True,
|
31
34
|
'watchOrders': True,
|
32
35
|
'watchOrderBook': True,
|
33
|
-
'
|
34
|
-
'
|
36
|
+
'watchOrderBookForSymbols': True,
|
37
|
+
'watchOHLCV': False,
|
38
|
+
'fetchOHLCVWs': False,
|
35
39
|
},
|
36
40
|
'urls': {
|
37
41
|
'api': {
|
38
42
|
'ws': {
|
39
|
-
'spot': 'wss://socket.coinex.com/',
|
40
|
-
'swap': 'wss://
|
43
|
+
'spot': 'wss://socket.coinex.com/v2/spot/',
|
44
|
+
'swap': 'wss://socket.coinex.com/v2/futures/',
|
41
45
|
},
|
42
46
|
},
|
43
47
|
},
|
44
48
|
'options': {
|
45
|
-
'
|
49
|
+
'ws': {
|
50
|
+
'gunzip': True,
|
51
|
+
},
|
46
52
|
'timeframes': {
|
47
53
|
'1m': 60,
|
48
54
|
'3m': 180,
|
@@ -62,21 +68,32 @@ class coinex(ccxt.async_support.coinex):
|
|
62
68
|
'watchOrderBook': {
|
63
69
|
'limits': [5, 10, 20, 50],
|
64
70
|
'defaultLimit': 50,
|
65
|
-
'aggregations': ['10', '1', '0', '0.1', '0.01'],
|
71
|
+
'aggregations': ['1000', '100', '10', '1', '0', '0.1', '0.01', '0.001', '0.0001', '0.00001', '0.000001', '0.0000001', '0.00000001', '0.000000001', '0.0000000001', '0.00000000001'],
|
66
72
|
'defaultAggregation': '0',
|
67
73
|
},
|
68
74
|
},
|
69
75
|
'streaming': {
|
70
76
|
},
|
71
77
|
'exceptions': {
|
72
|
-
'
|
73
|
-
'
|
74
|
-
'
|
75
|
-
'
|
76
|
-
'
|
77
|
-
'
|
78
|
-
'
|
78
|
+
'exact': {
|
79
|
+
'20001': BadRequest, # Invalid argument
|
80
|
+
'20002': NotSupported, # Method unavailable
|
81
|
+
'21001': AuthenticationError, # Authentication required
|
82
|
+
'21002': AuthenticationError, # Incorrect signature
|
83
|
+
'23001': RequestTimeout, # Request service timeout
|
84
|
+
'23002': RateLimitExceeded, # Requests too frequently
|
85
|
+
'24001': ExchangeError, # Internal error
|
86
|
+
'24002': ExchangeNotAvailable, # Service unavailable temporarily
|
87
|
+
'30001': BadRequest, # Invalid argument
|
88
|
+
'30002': NotSupported, # Method unavailable
|
89
|
+
'31001': AuthenticationError, # Authentication required
|
90
|
+
'31002': AuthenticationError, # Incorrect signature
|
91
|
+
'33001': RequestTimeout, # Request service timeout
|
92
|
+
'33002': RateLimitExceeded, # Requests too frequently
|
93
|
+
'34001': ExchangeError, # Internal error
|
94
|
+
'34002': ExchangeNotAvailable, # Service unavailable temporarily
|
79
95
|
},
|
96
|
+
'broad': {},
|
80
97
|
},
|
81
98
|
})
|
82
99
|
|
@@ -91,61 +108,66 @@ class coinex(ccxt.async_support.coinex):
|
|
91
108
|
#
|
92
109
|
# {
|
93
110
|
# "method": "state.update",
|
94
|
-
# "
|
95
|
-
# "
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
111
|
+
# "data": {
|
112
|
+
# "state_list": [
|
113
|
+
# {
|
114
|
+
# "market": "LATUSDT",
|
115
|
+
# "last": "0.008157",
|
116
|
+
# "open": "0.008286",
|
117
|
+
# "close": "0.008157",
|
118
|
+
# "high": "0.008390",
|
119
|
+
# "low": "0.008106",
|
120
|
+
# "volume": "807714.49139758",
|
121
|
+
# "volume_sell": "286170.69645599",
|
122
|
+
# "volume_buy": "266161.23236408",
|
123
|
+
# "value": "6689.21644207",
|
124
|
+
# "period": 86400
|
125
|
+
# },
|
126
|
+
# ]
|
127
|
+
# },
|
128
|
+
# "id": null
|
108
129
|
# }
|
109
130
|
#
|
110
131
|
# swap
|
111
132
|
#
|
112
133
|
# {
|
113
134
|
# "method": "state.update",
|
114
|
-
# "
|
115
|
-
# "
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
#
|
135
|
+
# "data": {
|
136
|
+
# "state_list": [
|
137
|
+
# {
|
138
|
+
# "market": "ETHUSD_SIGNPRICE",
|
139
|
+
# "last": "1892.29",
|
140
|
+
# "open": "1884.62",
|
141
|
+
# "close": "1892.29",
|
142
|
+
# "high": "1894.09",
|
143
|
+
# "low": "1863.72",
|
144
|
+
# "volume": "0",
|
145
|
+
# "value": "0",
|
146
|
+
# "volume_sell": "0",
|
147
|
+
# "volume_buy": "0",
|
148
|
+
# "open_interest_size": "0",
|
149
|
+
# "insurance_fund_size": "0",
|
150
|
+
# "latest_funding_rate": "0",
|
151
|
+
# "next_funding_rate": "0",
|
152
|
+
# "latest_funding_time": 0,
|
153
|
+
# "next_funding_time": 0,
|
154
|
+
# "period": 86400
|
155
|
+
# },
|
156
|
+
# ]
|
157
|
+
# ],
|
158
|
+
# "id": null
|
136
159
|
# }
|
137
160
|
#
|
138
161
|
defaultType = self.safe_string(self.options, 'defaultType')
|
139
|
-
|
140
|
-
rawTickers = self.
|
141
|
-
keys = list(rawTickers.keys())
|
162
|
+
data = self.safe_dict(message, 'data', {})
|
163
|
+
rawTickers = self.safe_list(data, 'state_list', [])
|
142
164
|
newTickers = []
|
143
|
-
for i in range(0, len(
|
144
|
-
|
145
|
-
|
165
|
+
for i in range(0, len(rawTickers)):
|
166
|
+
entry = rawTickers[i]
|
167
|
+
marketId = self.safe_string(entry, 'market')
|
146
168
|
symbol = self.safe_symbol(marketId, None, None, defaultType)
|
147
169
|
market = self.safe_market(marketId, None, None, defaultType)
|
148
|
-
parsedTicker = self.parse_ws_ticker(
|
170
|
+
parsedTicker = self.parse_ws_ticker(entry, market)
|
149
171
|
self.tickers[symbol] = parsedTicker
|
150
172
|
newTickers.append(parsedTicker)
|
151
173
|
messageHashes = self.find_message_hashes(client, 'tickers::')
|
@@ -166,52 +188,53 @@ class coinex(ccxt.async_support.coinex):
|
|
166
188
|
# spot
|
167
189
|
#
|
168
190
|
# {
|
169
|
-
# "
|
170
|
-
# "
|
171
|
-
# "
|
172
|
-
# "
|
173
|
-
# "
|
174
|
-
# "
|
175
|
-
# "
|
176
|
-
# "
|
177
|
-
# "
|
178
|
-
# "
|
191
|
+
# "market": "LATUSDT",
|
192
|
+
# "last": "0.008157",
|
193
|
+
# "open": "0.008286",
|
194
|
+
# "close": "0.008157",
|
195
|
+
# "high": "0.008390",
|
196
|
+
# "low": "0.008106",
|
197
|
+
# "volume": "807714.49139758",
|
198
|
+
# "volume_sell": "286170.69645599",
|
199
|
+
# "volume_buy": "266161.23236408",
|
200
|
+
# "value": "6689.21644207",
|
201
|
+
# "period": 86400
|
179
202
|
# }
|
180
203
|
#
|
181
204
|
# swap
|
182
205
|
#
|
183
206
|
# {
|
184
|
-
# "
|
185
|
-
# "
|
186
|
-
# "
|
187
|
-
# "
|
188
|
-
# "
|
189
|
-
# "
|
190
|
-
# "
|
191
|
-
# "
|
192
|
-
# "
|
193
|
-
# "
|
194
|
-
# "
|
195
|
-
# "
|
196
|
-
# "
|
197
|
-
# "
|
198
|
-
# "
|
199
|
-
# "
|
200
|
-
# "
|
201
|
-
# "buy_total": "25.7814"
|
207
|
+
# "market": "ETHUSD_SIGNPRICE",
|
208
|
+
# "last": "1892.29",
|
209
|
+
# "open": "1884.62",
|
210
|
+
# "close": "1892.29",
|
211
|
+
# "high": "1894.09",
|
212
|
+
# "low": "1863.72",
|
213
|
+
# "volume": "0",
|
214
|
+
# "value": "0",
|
215
|
+
# "volume_sell": "0",
|
216
|
+
# "volume_buy": "0",
|
217
|
+
# "open_interest_size": "0",
|
218
|
+
# "insurance_fund_size": "0",
|
219
|
+
# "latest_funding_rate": "0",
|
220
|
+
# "next_funding_rate": "0",
|
221
|
+
# "latest_funding_time": 0,
|
222
|
+
# "next_funding_time": 0,
|
223
|
+
# "period": 86400
|
202
224
|
# }
|
203
225
|
#
|
204
226
|
defaultType = self.safe_string(self.options, 'defaultType')
|
227
|
+
marketId = self.safe_string(ticker, 'market')
|
205
228
|
return self.safe_ticker({
|
206
|
-
'symbol': self.safe_symbol(
|
229
|
+
'symbol': self.safe_symbol(marketId, market, None, defaultType),
|
207
230
|
'timestamp': None,
|
208
231
|
'datetime': None,
|
209
232
|
'high': self.safe_string(ticker, 'high'),
|
210
233
|
'low': self.safe_string(ticker, 'low'),
|
211
234
|
'bid': None,
|
212
|
-
'bidVolume': self.safe_string(ticker, '
|
235
|
+
'bidVolume': self.safe_string(ticker, 'volume_buy'),
|
213
236
|
'ask': None,
|
214
|
-
'askVolume': self.safe_string(ticker, '
|
237
|
+
'askVolume': self.safe_string(ticker, 'volume_sell'),
|
215
238
|
'vwap': None,
|
216
239
|
'open': self.safe_string(ticker, 'open'),
|
217
240
|
'close': self.safe_string(ticker, 'close'),
|
@@ -221,84 +244,274 @@ class coinex(ccxt.async_support.coinex):
|
|
221
244
|
'percentage': None,
|
222
245
|
'average': None,
|
223
246
|
'baseVolume': self.safe_string(ticker, 'volume'),
|
224
|
-
'quoteVolume': self.safe_string(ticker, '
|
247
|
+
'quoteVolume': self.safe_string(ticker, 'value'),
|
225
248
|
'info': ticker,
|
226
249
|
}, market)
|
227
250
|
|
228
251
|
async def watch_balance(self, params={}) -> Balances:
|
229
252
|
"""
|
230
253
|
watch balance and get the amount of funds available for trading or funds locked in orders
|
254
|
+
:see: https://docs.coinex.com/api/v2/assets/balance/ws/spot_balance
|
255
|
+
:see: https://docs.coinex.com/api/v2/assets/balance/ws/futures_balance
|
231
256
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
232
257
|
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
233
258
|
"""
|
234
259
|
await self.load_markets()
|
235
|
-
await self.authenticate(params)
|
236
|
-
messageHash = 'balance'
|
237
260
|
type = None
|
238
|
-
type, params = self.handle_market_type_and_params('watchBalance', None, params)
|
261
|
+
type, params = self.handle_market_type_and_params('watchBalance', None, params, 'spot')
|
262
|
+
await self.authenticate(type)
|
239
263
|
url = self.urls['api']['ws'][type]
|
240
264
|
currencies = list(self.currencies_by_id.keys())
|
265
|
+
if currencies is None:
|
266
|
+
currencies = []
|
267
|
+
messageHash = 'balances'
|
268
|
+
if type == 'spot':
|
269
|
+
messageHash += ':spot'
|
270
|
+
else:
|
271
|
+
messageHash += ':swap'
|
241
272
|
subscribe: dict = {
|
242
|
-
'method': '
|
243
|
-
'params': currencies,
|
273
|
+
'method': 'balance.subscribe',
|
274
|
+
'params': {'ccy_list': currencies},
|
244
275
|
'id': self.request_id(),
|
245
276
|
}
|
246
277
|
request = self.deep_extend(subscribe, params)
|
247
278
|
return await self.watch(url, messageHash, request, messageHash)
|
248
279
|
|
249
280
|
def handle_balance(self, client: Client, message):
|
281
|
+
#
|
282
|
+
# spot
|
250
283
|
#
|
251
284
|
# {
|
252
|
-
# "method": "
|
253
|
-
# "
|
254
|
-
#
|
255
|
-
#
|
256
|
-
# "
|
257
|
-
# "
|
258
|
-
#
|
259
|
-
#
|
260
|
-
#
|
285
|
+
# "method": "balance.update",
|
286
|
+
# "data": {
|
287
|
+
# "balance_list": [
|
288
|
+
# {
|
289
|
+
# "margin_market": "BTCUSDT",
|
290
|
+
# "ccy": "BTC",
|
291
|
+
# "available": "44.62207740",
|
292
|
+
# "frozen": "0.00000000",
|
293
|
+
# "updated_at": 1689152421692
|
294
|
+
# },
|
295
|
+
# ]
|
296
|
+
# },
|
261
297
|
# "id": null
|
262
298
|
# }
|
263
299
|
#
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
300
|
+
# swap
|
301
|
+
#
|
302
|
+
# {
|
303
|
+
# "method": "balance.update",
|
304
|
+
# "data": {
|
305
|
+
# "balance_list": [
|
306
|
+
# {
|
307
|
+
# "ccy": "USDT",
|
308
|
+
# "available": "97.92470982756335000001",
|
309
|
+
# "frozen": "0.00000000000000000000",
|
310
|
+
# "margin": "0.61442700000000000000",
|
311
|
+
# "transferrable": "97.92470982756335000001",
|
312
|
+
# "unrealized_pnl": "-0.00807000000000000000",
|
313
|
+
# "equity": "97.92470982756335000001"
|
314
|
+
# },
|
315
|
+
# ]
|
316
|
+
# },
|
317
|
+
# "id": null
|
318
|
+
# }
|
319
|
+
#
|
320
|
+
if self.balance is None:
|
321
|
+
self.balance = {}
|
322
|
+
data = self.safe_dict(message, 'data', {})
|
323
|
+
balances = self.safe_list(data, 'balance_list', [])
|
324
|
+
firstEntry = balances[0]
|
325
|
+
updated = self.safe_integer(firstEntry, 'updated_at')
|
326
|
+
unrealizedPnl = self.safe_string(firstEntry, 'unrealized_pnl')
|
327
|
+
isSpot = (updated is not None)
|
328
|
+
isSwap = (unrealizedPnl is not None)
|
329
|
+
info = None
|
330
|
+
account = None
|
331
|
+
rawBalances = []
|
332
|
+
if isSpot:
|
333
|
+
account = 'spot'
|
334
|
+
for i in range(0, len(balances)):
|
335
|
+
rawBalances = self.array_concat(rawBalances, balances)
|
336
|
+
info = rawBalances
|
337
|
+
if isSwap:
|
338
|
+
account = 'swap'
|
339
|
+
for i in range(0, len(balances)):
|
340
|
+
rawBalances = self.array_concat(rawBalances, balances)
|
341
|
+
info = rawBalances
|
342
|
+
for i in range(0, len(rawBalances)):
|
343
|
+
entry = rawBalances[i]
|
344
|
+
self.parse_ws_balance(entry, account)
|
345
|
+
messageHash = None
|
346
|
+
if account is not None:
|
347
|
+
if self.safe_value(self.balance, account) is None:
|
348
|
+
self.balance[account] = {}
|
349
|
+
self.balance[account]['info'] = info
|
350
|
+
self.balance[account] = self.safe_balance(self.balance[account])
|
351
|
+
messageHash = 'balances:' + account
|
352
|
+
client.resolve(self.balance[account], messageHash)
|
353
|
+
|
354
|
+
def parse_ws_balance(self, balance, accountType=None):
|
355
|
+
#
|
356
|
+
# spot
|
357
|
+
#
|
358
|
+
# {
|
359
|
+
# "margin_market": "BTCUSDT",
|
360
|
+
# "ccy": "BTC",
|
361
|
+
# "available": "44.62207740",
|
362
|
+
# "frozen": "0.00000000",
|
363
|
+
# "updated_at": 1689152421692
|
364
|
+
# }
|
365
|
+
#
|
366
|
+
# swap
|
367
|
+
#
|
368
|
+
# {
|
369
|
+
# "ccy": "USDT",
|
370
|
+
# "available": "97.92470982756335000001",
|
371
|
+
# "frozen": "0.00000000000000000000",
|
372
|
+
# "margin": "0.61442700000000000000",
|
373
|
+
# "transferrable": "97.92470982756335000001",
|
374
|
+
# "unrealized_pnl": "-0.00807000000000000000",
|
375
|
+
# "equity": "97.92470982756335000001"
|
376
|
+
# }
|
377
|
+
#
|
378
|
+
account = self.account()
|
379
|
+
currencyId = self.safe_string(balance, 'ccy')
|
380
|
+
code = self.safe_currency_code(currencyId)
|
381
|
+
account['free'] = self.safe_string(balance, 'available')
|
382
|
+
account['used'] = self.safe_string(balance, 'frozen')
|
383
|
+
if accountType is not None:
|
384
|
+
if self.safe_value(self.balance, accountType) is None:
|
385
|
+
self.balance[accountType] = {}
|
386
|
+
self.balance[accountType][code] = account
|
387
|
+
else:
|
276
388
|
self.balance[code] = account
|
277
|
-
|
278
|
-
|
279
|
-
|
389
|
+
|
390
|
+
async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
391
|
+
"""
|
392
|
+
watches information on multiple trades made by the user
|
393
|
+
:see: https://docs.coinex.com/api/v2/spot/deal/ws/user-deals
|
394
|
+
:see: https://docs.coinex.com/api/v2/futures/deal/ws/user-deals
|
395
|
+
:param str [symbol]: unified symbol of the market the trades were made in
|
396
|
+
:param int [since]: the earliest time in ms to watch trades
|
397
|
+
:param int [limit]: the maximum number of trade structures to retrieve
|
398
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
399
|
+
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
400
|
+
"""
|
401
|
+
await self.load_markets()
|
402
|
+
market = None
|
403
|
+
if symbol is not None:
|
404
|
+
market = self.market(symbol)
|
405
|
+
symbol = market['symbol']
|
406
|
+
type = None
|
407
|
+
type, params = self.handle_market_type_and_params('watchMyTrades', market, params, 'spot')
|
408
|
+
await self.authenticate(type)
|
409
|
+
url = self.urls['api']['ws'][type]
|
410
|
+
subscribedSymbols = []
|
411
|
+
messageHash = 'myTrades'
|
412
|
+
if market is not None:
|
413
|
+
messageHash += ':' + symbol
|
414
|
+
subscribedSymbols.append(market['id'])
|
415
|
+
else:
|
416
|
+
if type == 'spot':
|
417
|
+
messageHash += ':spot'
|
418
|
+
else:
|
419
|
+
messageHash += ':swap'
|
420
|
+
message: dict = {
|
421
|
+
'method': 'user_deals.subscribe',
|
422
|
+
'params': {'market_list': subscribedSymbols},
|
423
|
+
'id': self.request_id(),
|
424
|
+
}
|
425
|
+
request = self.deep_extend(message, params)
|
426
|
+
trades = await self.watch(url, messageHash, request, messageHash)
|
427
|
+
if self.newUpdates:
|
428
|
+
limit = trades.getLimit(symbol, limit)
|
429
|
+
return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True)
|
430
|
+
|
431
|
+
def handle_my_trades(self, client: Client, message):
|
432
|
+
#
|
433
|
+
# {
|
434
|
+
# "method": "user_deals.update",
|
435
|
+
# "data": {
|
436
|
+
# "deal_id": 3514376759,
|
437
|
+
# "created_at": 1689152421692,
|
438
|
+
# "market": "BTCUSDT",
|
439
|
+
# "side": "buy",
|
440
|
+
# "order_id": 8678890,
|
441
|
+
# "margin_market": "BTCUSDT",
|
442
|
+
# "price": "30718.42",
|
443
|
+
# "amount": "0.00000325",
|
444
|
+
# "role": "taker",
|
445
|
+
# "fee": "0.0299",
|
446
|
+
# "fee_ccy": "USDT"
|
447
|
+
# },
|
448
|
+
# "id": null
|
449
|
+
# }
|
450
|
+
#
|
451
|
+
data = self.safe_dict(message, 'data', {})
|
452
|
+
marketId = self.safe_string(data, 'market')
|
453
|
+
isSpot = client.url.find('spot') > -1
|
454
|
+
defaultType = 'spot' if isSpot else 'swap'
|
455
|
+
market = self.safe_market(marketId, None, None, defaultType)
|
456
|
+
symbol = market['symbol']
|
457
|
+
messageHash = 'myTrades:' + symbol
|
458
|
+
messageWithType = 'myTrades:' + market['type']
|
459
|
+
stored = self.safe_value(self.trades, symbol)
|
460
|
+
if stored is None:
|
461
|
+
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
462
|
+
stored = ArrayCache(limit)
|
463
|
+
self.trades[symbol] = stored
|
464
|
+
parsed = self.parse_ws_trade(data, market)
|
465
|
+
stored.append(parsed)
|
466
|
+
self.trades[symbol] = stored
|
467
|
+
client.resolve(self.trades[symbol], messageWithType)
|
468
|
+
client.resolve(self.trades[symbol], messageHash)
|
280
469
|
|
281
470
|
def handle_trades(self, client: Client, message):
|
471
|
+
#
|
472
|
+
# spot
|
282
473
|
#
|
283
474
|
# {
|
284
475
|
# "method": "deals.update",
|
285
|
-
# "
|
286
|
-
# "
|
287
|
-
# [
|
288
|
-
#
|
289
|
-
#
|
290
|
-
#
|
291
|
-
#
|
292
|
-
#
|
293
|
-
#
|
294
|
-
#
|
476
|
+
# "data": {
|
477
|
+
# "market": "BTCUSDT",
|
478
|
+
# "deal_list": [
|
479
|
+
# {
|
480
|
+
# "deal_id": 3514376759,
|
481
|
+
# "created_at": 1689152421692,
|
482
|
+
# "side": "buy",
|
483
|
+
# "price": "30718.42",
|
484
|
+
# "amount": "0.00000325"
|
485
|
+
# },
|
486
|
+
# ]
|
487
|
+
# },
|
295
488
|
# "id": null
|
296
489
|
# }
|
297
490
|
#
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
491
|
+
# swap
|
492
|
+
#
|
493
|
+
# {
|
494
|
+
# "method": "deals.update",
|
495
|
+
# "data": {
|
496
|
+
# "market": "BTCUSDT",
|
497
|
+
# "deal_list": [
|
498
|
+
# {
|
499
|
+
# "deal_id": 3514376759,
|
500
|
+
# "created_at": 1689152421692,
|
501
|
+
# "side": "buy",
|
502
|
+
# "price": "30718.42",
|
503
|
+
# "amount": "0.00000325"
|
504
|
+
# },
|
505
|
+
# ]
|
506
|
+
# },
|
507
|
+
# "id": null
|
508
|
+
# }
|
509
|
+
#
|
510
|
+
data = self.safe_dict(message, 'data', {})
|
511
|
+
trades = self.safe_list(data, 'deal_list', [])
|
512
|
+
marketId = self.safe_string(data, 'market')
|
513
|
+
isSpot = client.url.find('spot') > -1
|
514
|
+
defaultType = 'spot' if isSpot else 'swap'
|
302
515
|
market = self.safe_market(marketId, None, None, defaultType)
|
303
516
|
symbol = market['symbol']
|
304
517
|
messageHash = 'trades:' + symbol
|
@@ -315,289 +528,239 @@ class coinex(ccxt.async_support.coinex):
|
|
315
528
|
client.resolve(self.trades[symbol], messageHash)
|
316
529
|
|
317
530
|
def parse_ws_trade(self, trade, market=None):
|
531
|
+
#
|
532
|
+
# spot watchTrades
|
318
533
|
#
|
319
534
|
# {
|
320
|
-
# "
|
321
|
-
# "
|
322
|
-
# "
|
323
|
-
# "
|
324
|
-
# "amount": "0.
|
535
|
+
# "deal_id": 3514376759,
|
536
|
+
# "created_at": 1689152421692,
|
537
|
+
# "side": "buy",
|
538
|
+
# "price": "30718.42",
|
539
|
+
# "amount": "0.00000325"
|
325
540
|
# }
|
326
541
|
#
|
327
|
-
|
328
|
-
|
542
|
+
# swap watchTrades
|
543
|
+
#
|
544
|
+
# {
|
545
|
+
# "deal_id": 3514376759,
|
546
|
+
# "created_at": 1689152421692,
|
547
|
+
# "side": "buy",
|
548
|
+
# "price": "30718.42",
|
549
|
+
# "amount": "0.00000325"
|
550
|
+
# }
|
551
|
+
#
|
552
|
+
# spot and swap watchMyTrades
|
553
|
+
#
|
554
|
+
# {
|
555
|
+
# "deal_id": 3514376759,
|
556
|
+
# "created_at": 1689152421692,
|
557
|
+
# "market": "BTCUSDT",
|
558
|
+
# "side": "buy",
|
559
|
+
# "order_id": 8678890,
|
560
|
+
# "margin_market": "BTCUSDT",
|
561
|
+
# "price": "30718.42",
|
562
|
+
# "amount": "0.00000325",
|
563
|
+
# "role": "taker",
|
564
|
+
# "fee": "0.0299",
|
565
|
+
# "fee_ccy": "USDT"
|
566
|
+
# }
|
567
|
+
#
|
568
|
+
timestamp = self.safe_integer(trade, 'created_at')
|
569
|
+
isSpot = ('margin_market' in trade)
|
570
|
+
defaultType = 'spot' if isSpot else 'swap'
|
571
|
+
marketId = self.safe_string(trade, 'market')
|
572
|
+
market = self.safe_market(marketId, market, None, defaultType)
|
573
|
+
fee: dict = {}
|
574
|
+
feeCost = self.omit_zero(self.safe_string(trade, 'fee'))
|
575
|
+
if feeCost is not None:
|
576
|
+
feeCurrencyId = self.safe_string(trade, 'fee_ccy', market['quote'])
|
577
|
+
fee = {
|
578
|
+
'currency': self.safe_currency_code(feeCurrencyId),
|
579
|
+
'cost': feeCost,
|
580
|
+
}
|
329
581
|
return self.safe_trade({
|
330
|
-
'id': self.safe_string(trade, '
|
582
|
+
'id': self.safe_string(trade, 'deal_id'),
|
331
583
|
'info': trade,
|
332
584
|
'timestamp': timestamp,
|
333
585
|
'datetime': self.iso8601(timestamp),
|
334
|
-
'symbol': self.safe_symbol(
|
335
|
-
'order':
|
586
|
+
'symbol': self.safe_symbol(marketId, market, None, defaultType),
|
587
|
+
'order': self.safe_string(trade, 'order_id'),
|
336
588
|
'type': None,
|
337
|
-
'side': self.safe_string(trade, '
|
338
|
-
'takerOrMaker':
|
589
|
+
'side': self.safe_string(trade, 'side'),
|
590
|
+
'takerOrMaker': self.safe_string(trade, 'role'),
|
339
591
|
'price': self.safe_string(trade, 'price'),
|
340
592
|
'amount': self.safe_string(trade, 'amount'),
|
341
593
|
'cost': None,
|
342
|
-
'fee':
|
594
|
+
'fee': fee,
|
343
595
|
}, market)
|
344
596
|
|
345
|
-
def handle_ohlcv(self, client: Client, message):
|
346
|
-
#
|
347
|
-
# spot
|
348
|
-
# {
|
349
|
-
# "error": null,
|
350
|
-
# "result": [
|
351
|
-
# [
|
352
|
-
# 1673846940,
|
353
|
-
# "21148.74",
|
354
|
-
# "21148.38",
|
355
|
-
# "21148.75",
|
356
|
-
# "21138.66",
|
357
|
-
# "1.57060173",
|
358
|
-
# "33214.9138778914"
|
359
|
-
# ],
|
360
|
-
# ]
|
361
|
-
# "id": 1,
|
362
|
-
# }
|
363
|
-
# swap
|
364
|
-
# {
|
365
|
-
# "method": "kline.update",
|
366
|
-
# "params": [
|
367
|
-
# [
|
368
|
-
# 1654019640, # timestamp
|
369
|
-
# "32061.99", # open
|
370
|
-
# "32061.28", # close
|
371
|
-
# "32061.99", # high
|
372
|
-
# "32061.28", # low
|
373
|
-
# "0.1285", # amount base
|
374
|
-
# "4119.943736" # amount quote
|
375
|
-
# ]
|
376
|
-
# ],
|
377
|
-
# "id": null
|
378
|
-
# }
|
379
|
-
#
|
380
|
-
candles = self.safe_value_2(message, 'params', 'result', [])
|
381
|
-
messageHash = 'ohlcv'
|
382
|
-
id = self.safe_string(message, 'id')
|
383
|
-
ohlcvs = self.parse_ohlcvs(candles)
|
384
|
-
if id is not None:
|
385
|
-
# spot subscription response
|
386
|
-
client.resolve(ohlcvs, messageHash)
|
387
|
-
return
|
388
|
-
keys = list(self.ohlcvs.keys())
|
389
|
-
keysLength = len(keys)
|
390
|
-
if keysLength == 0:
|
391
|
-
self.ohlcvs['unknown'] = {}
|
392
|
-
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
|
393
|
-
stored = ArrayCacheByTimestamp(limit)
|
394
|
-
self.ohlcvs['unknown']['unknown'] = stored
|
395
|
-
ohlcv = self.ohlcvs['unknown']['unknown']
|
396
|
-
for i in range(0, len(ohlcvs)):
|
397
|
-
candle = ohlcvs[i]
|
398
|
-
ohlcv.append(candle)
|
399
|
-
client.resolve(ohlcv, messageHash)
|
400
|
-
|
401
597
|
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
|
402
598
|
"""
|
403
|
-
:see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket007_state_subscribe
|
404
599
|
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
600
|
+
:see: https://docs.coinex.com/api/v2/spot/market/ws/market
|
601
|
+
:see: https://docs.coinex.com/api/v2/futures/market/ws/market-state
|
405
602
|
:param str symbol: unified symbol of the market to fetch the ticker for
|
406
603
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
407
604
|
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
408
605
|
"""
|
606
|
+
await self.load_markets()
|
607
|
+
market = self.market(symbol)
|
409
608
|
tickers = await self.watch_tickers([symbol], params)
|
410
|
-
return
|
609
|
+
return tickers[market['symbol']]
|
411
610
|
|
412
611
|
async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
413
612
|
"""
|
414
|
-
:see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket007_state_subscribe
|
415
613
|
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
|
614
|
+
:see: https://docs.coinex.com/api/v2/spot/market/ws/market
|
615
|
+
:see: https://docs.coinex.com/api/v2/futures/market/ws/market-state
|
416
616
|
:param str[] symbols: unified symbol of the market to fetch the ticker for
|
417
617
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
418
618
|
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
419
619
|
"""
|
420
620
|
await self.load_markets()
|
421
|
-
|
621
|
+
marketIds = self.market_ids(symbols)
|
622
|
+
market = None
|
623
|
+
messageHashes = []
|
624
|
+
symbolsDefined = (symbols is not None)
|
625
|
+
if symbolsDefined:
|
626
|
+
for i in range(0, len(symbols)):
|
627
|
+
symbol = symbols[i]
|
628
|
+
market = self.market(symbol)
|
629
|
+
messageHashes.append('tickers::' + market['symbol'])
|
630
|
+
else:
|
631
|
+
messageHashes.append('tickers')
|
422
632
|
type = None
|
423
|
-
type, params = self.handle_market_type_and_params('watchTickers',
|
633
|
+
type, params = self.handle_market_type_and_params('watchTickers', market, params)
|
424
634
|
url = self.urls['api']['ws'][type]
|
425
|
-
|
426
|
-
if symbols is not None:
|
427
|
-
messageHash = 'tickers::' + ','.join(symbols)
|
635
|
+
subscriptionHashes = ['all@ticker']
|
428
636
|
subscribe: dict = {
|
429
637
|
'method': 'state.subscribe',
|
638
|
+
'params': {'market_list': marketIds},
|
430
639
|
'id': self.request_id(),
|
431
|
-
'params': [],
|
432
640
|
}
|
433
|
-
|
434
|
-
newTickers = await self.watch(url, messageHash, request, messageHash)
|
641
|
+
result = await self.watch_multiple(url, messageHashes, self.deep_extend(subscribe, params), subscriptionHashes)
|
435
642
|
if self.newUpdates:
|
436
|
-
return
|
643
|
+
return result
|
437
644
|
return self.filter_by_array(self.tickers, 'symbol', symbols)
|
438
645
|
|
439
646
|
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
440
647
|
"""
|
441
|
-
:see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket012_deal_subcribe
|
442
|
-
:see: https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket019_deal_subcribe
|
443
648
|
get the list of most recent trades for a particular symbol
|
649
|
+
:see: https://docs.coinex.com/api/v2/spot/market/ws/market-deals
|
650
|
+
:see: https://docs.coinex.com/api/v2/futures/market/ws/market-deals
|
444
651
|
:param str symbol: unified symbol of the market to fetch trades for
|
445
652
|
:param int [since]: timestamp in ms of the earliest trade to fetch
|
446
653
|
:param int [limit]: the maximum amount of trades to fetch
|
447
654
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
448
655
|
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
449
656
|
"""
|
657
|
+
params['callerMethodName'] = 'watchTrades'
|
658
|
+
return await self.watch_trades_for_symbols([symbol], since, limit, params)
|
659
|
+
|
660
|
+
async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
661
|
+
"""
|
662
|
+
watch the most recent trades for a list of symbols
|
663
|
+
:see: https://docs.coinex.com/api/v2/spot/market/ws/market-deals
|
664
|
+
:see: https://docs.coinex.com/api/v2/futures/market/ws/market-deals
|
665
|
+
:param str[] symbols: unified symbols of the markets to fetch trades for
|
666
|
+
:param int [since]: timestamp in ms of the earliest trade to fetch
|
667
|
+
:param int [limit]: the maximum amount of trades to fetch
|
668
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
669
|
+
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
670
|
+
"""
|
450
671
|
await self.load_markets()
|
451
|
-
|
672
|
+
subscribedSymbols = []
|
673
|
+
messageHashes = []
|
674
|
+
market = None
|
675
|
+
callerMethodName = None
|
676
|
+
callerMethodName, params = self.handle_param_string(params, 'callerMethodName', 'watchTradesForSymbols')
|
677
|
+
symbolsDefined = (symbols is not None)
|
678
|
+
if symbolsDefined:
|
679
|
+
for i in range(0, len(symbols)):
|
680
|
+
symbol = symbols[i]
|
681
|
+
market = self.market(symbol)
|
682
|
+
subscribedSymbols.append(market['id'])
|
683
|
+
messageHashes.append('trades:' + market['symbol'])
|
684
|
+
else:
|
685
|
+
messageHashes.append('trades')
|
452
686
|
type = None
|
453
|
-
type, params = self.handle_market_type_and_params(
|
687
|
+
type, params = self.handle_market_type_and_params(callerMethodName, market, params)
|
454
688
|
url = self.urls['api']['ws'][type]
|
455
|
-
|
456
|
-
|
457
|
-
subscribedSymbols = self.safe_value(self.options, 'watchTradesSubscriptions', [])
|
458
|
-
subscribedSymbols.append(market['id'])
|
459
|
-
message: dict = {
|
689
|
+
subscriptionHashes = ['trades']
|
690
|
+
subscribe: dict = {
|
460
691
|
'method': 'deals.subscribe',
|
461
|
-
'params': subscribedSymbols,
|
692
|
+
'params': {'market_list': subscribedSymbols},
|
462
693
|
'id': self.request_id(),
|
463
694
|
}
|
464
|
-
self.
|
465
|
-
|
466
|
-
|
695
|
+
trades = await self.watch_multiple(url, messageHashes, self.deep_extend(subscribe, params), subscriptionHashes)
|
696
|
+
if self.newUpdates:
|
697
|
+
return trades
|
467
698
|
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
468
699
|
|
469
|
-
async def
|
700
|
+
async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
|
470
701
|
"""
|
471
|
-
:see: https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket017_depth_subscribe_multi
|
472
|
-
:see: https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket011_depth_subscribe_multi
|
473
702
|
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
474
|
-
:
|
703
|
+
:see: https://docs.coinex.com/api/v2/spot/market/ws/market-depth
|
704
|
+
:see: https://docs.coinex.com/api/v2/futures/market/ws/market-depth
|
705
|
+
:param str[] symbols: unified array of symbols
|
475
706
|
:param int [limit]: the maximum amount of order book entries to return
|
476
707
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
477
708
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
478
709
|
"""
|
479
710
|
await self.load_markets()
|
480
|
-
|
481
|
-
|
711
|
+
watchOrderBookSubscriptions: dict = {}
|
712
|
+
messageHashes = []
|
713
|
+
market = None
|
482
714
|
type = None
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
limits = self.safe_value(options, 'limits', [])
|
715
|
+
callerMethodName = None
|
716
|
+
callerMethodName, params = self.handle_param_string(params, 'callerMethodName', 'watchOrderBookForSymbols')
|
717
|
+
type, params = self.handle_market_type_and_params(callerMethodName, None, params)
|
718
|
+
options = self.safe_dict(self.options, 'watchOrderBook', {})
|
719
|
+
limits = self.safe_list(options, 'limits', [])
|
489
720
|
if limit is None:
|
490
|
-
limit = self.
|
721
|
+
limit = self.safe_integer(options, 'defaultLimit', 50)
|
491
722
|
if not self.in_array(limit, limits):
|
492
|
-
raise NotSupported(self.id + '
|
723
|
+
raise NotSupported(self.id + ' watchOrderBookForSymbols() limit must be one of ' + ', '.join(limits))
|
493
724
|
defaultAggregation = self.safe_string(options, 'defaultAggregation', '0')
|
494
|
-
aggregations = self.
|
725
|
+
aggregations = self.safe_list(options, 'aggregations', [])
|
495
726
|
aggregation = self.safe_string(params, 'aggregation', defaultAggregation)
|
496
727
|
if not self.in_array(aggregation, aggregations):
|
497
|
-
raise NotSupported(self.id + '
|
728
|
+
raise NotSupported(self.id + ' watchOrderBookForSymbols() aggregation must be one of ' + ', '.join(aggregations))
|
498
729
|
params = self.omit(params, 'aggregation')
|
499
|
-
|
500
|
-
|
730
|
+
symbolsDefined = (symbols is not None)
|
731
|
+
if symbolsDefined:
|
732
|
+
for i in range(0, len(symbols)):
|
733
|
+
symbol = symbols[i]
|
734
|
+
market = self.market(symbol)
|
735
|
+
messageHashes.append('orderbook:' + market['symbol'])
|
736
|
+
watchOrderBookSubscriptions[symbol] = [market['id'], limit, aggregation, True]
|
737
|
+
else:
|
738
|
+
messageHashes.append('orderbook')
|
739
|
+
marketList = list(watchOrderBookSubscriptions.values())
|
501
740
|
subscribe: dict = {
|
502
|
-
'method': 'depth.
|
741
|
+
'method': 'depth.subscribe',
|
742
|
+
'params': {'market_list': marketList},
|
503
743
|
'id': self.request_id(),
|
504
|
-
'params': list(watchOrderBookSubscriptions.values()),
|
505
744
|
}
|
506
|
-
self.
|
507
|
-
subscriptionHash = self.hash(self.encode(self.json(watchOrderBookSubscriptions)), 'sha256')
|
508
|
-
request = self.deep_extend(subscribe, params)
|
509
|
-
orderbook = await self.watch(url, messageHash, request, subscriptionHash, request)
|
510
|
-
return orderbook.limit()
|
511
|
-
|
512
|
-
async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
513
|
-
"""
|
514
|
-
:see: https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket023_kline_subscribe
|
515
|
-
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
516
|
-
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
517
|
-
:param str timeframe: the length of time each candle represents
|
518
|
-
:param int [since]: timestamp in ms of the earliest candle to fetch
|
519
|
-
:param int [limit]: the maximum amount of candles to fetch
|
520
|
-
:param dict [params]: extra parameters specific to the exchange API endpoint
|
521
|
-
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
522
|
-
"""
|
523
|
-
await self.load_markets()
|
524
|
-
market = self.market(symbol)
|
525
|
-
symbol = market['symbol']
|
526
|
-
type = None
|
527
|
-
type, params = self.handle_market_type_and_params('watchOHLCV', market, params)
|
528
|
-
if type != 'swap':
|
529
|
-
raise NotSupported(self.id + ' watchOHLCV() is only supported for swap markets. Try using fetchOHLCV() instead')
|
745
|
+
subscriptionHashes = self.hash(self.encode(self.json(watchOrderBookSubscriptions)), 'sha256')
|
530
746
|
url = self.urls['api']['ws'][type]
|
531
|
-
|
532
|
-
watchOHLCVWarning = self.safe_bool(self.options, 'watchOHLCVWarning', True)
|
533
|
-
client = self.safe_value(self.clients, url, {})
|
534
|
-
clientSub = self.safe_value(client, 'subscriptions', {})
|
535
|
-
existingSubscription = self.safe_value(clientSub, messageHash)
|
536
|
-
subSymbol = self.safe_string(existingSubscription, 'symbol')
|
537
|
-
subTimeframe = self.safe_string(existingSubscription, 'timeframe')
|
538
|
-
# due to nature of coinex response can only watch one symbol at a time
|
539
|
-
if watchOHLCVWarning and existingSubscription is not None and (subSymbol != symbol or subTimeframe != timeframe):
|
540
|
-
raise ExchangeError(self.id + ' watchOHLCV() can only watch one symbol and timeframe at a time. To supress self warning set watchOHLCVWarning to False in options')
|
541
|
-
timeframes = self.safe_value(self.options, 'timeframes', {})
|
542
|
-
subscribe: dict = {
|
543
|
-
'method': 'kline.subscribe',
|
544
|
-
'id': self.request_id(),
|
545
|
-
'params': [
|
546
|
-
market['id'],
|
547
|
-
self.safe_integer(timeframes, timeframe),
|
548
|
-
],
|
549
|
-
}
|
550
|
-
subscription: dict = {
|
551
|
-
'symbol': symbol,
|
552
|
-
'timeframe': timeframe,
|
553
|
-
}
|
554
|
-
request = self.deep_extend(subscribe, params)
|
555
|
-
ohlcvs = await self.watch(url, messageHash, request, messageHash, subscription)
|
747
|
+
orderbooks = await self.watch_multiple(url, messageHashes, self.deep_extend(subscribe, params), subscriptionHashes)
|
556
748
|
if self.newUpdates:
|
557
|
-
|
558
|
-
return
|
749
|
+
return orderbooks
|
750
|
+
return orderbooks.limit()
|
559
751
|
|
560
|
-
async def
|
752
|
+
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
561
753
|
"""
|
562
|
-
|
563
|
-
|
564
|
-
:
|
565
|
-
:param str
|
566
|
-
:param int
|
567
|
-
:param
|
568
|
-
:
|
569
|
-
:param int|None params['end']: the end time for spot markets, self.seconds() is set
|
570
|
-
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
754
|
+
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
755
|
+
:see: https://docs.coinex.com/api/v2/spot/market/ws/market-depth
|
756
|
+
:see: https://docs.coinex.com/api/v2/futures/market/ws/market-depth
|
757
|
+
:param str symbol: unified symbol of the market to fetch the order book for
|
758
|
+
:param int [limit]: the maximum amount of order book entries to return
|
759
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
760
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
571
761
|
"""
|
572
|
-
|
573
|
-
|
574
|
-
type, query = self.handle_market_type_and_params('fetchOHLCV', market, params)
|
575
|
-
url = self.urls['api']['ws'][type]
|
576
|
-
symbol = market['symbol']
|
577
|
-
messageHash = 'ohlcv'
|
578
|
-
timeframes = self.safe_value(self.options, 'timeframes', {})
|
579
|
-
timeframe = self.safe_string(timeframes, timeframe, timeframe)
|
580
|
-
if since is None:
|
581
|
-
since = 1640995200 # January 1, 2022
|
582
|
-
id = self.request_id()
|
583
|
-
subscribe: dict = {
|
584
|
-
'method': 'kline.query',
|
585
|
-
'params': [
|
586
|
-
market['id'],
|
587
|
-
self.parse_to_int(since / 1000),
|
588
|
-
self.safe_integer(params, 'end', self.seconds()),
|
589
|
-
self.parse_to_int(timeframe),
|
590
|
-
],
|
591
|
-
'id': id,
|
592
|
-
}
|
593
|
-
subscription: dict = {
|
594
|
-
'id': id,
|
595
|
-
'future': messageHash,
|
596
|
-
}
|
597
|
-
subscriptionHash = id
|
598
|
-
request = self.deep_extend(subscribe, query)
|
599
|
-
ohlcvs = await self.watch(url, messageHash, request, subscriptionHash, subscription)
|
600
|
-
return self.filter_by_since_limit(ohlcvs, since, limit, 0)
|
762
|
+
params['callerMethodName'] = 'watchOrderBook'
|
763
|
+
return await self.watch_order_book_for_symbols([symbol], limit, params)
|
601
764
|
|
602
765
|
def handle_delta(self, bookside, delta):
|
603
766
|
bidAsk = self.parse_bid_ask(delta, 0, 1)
|
@@ -611,49 +774,51 @@ class coinex(ccxt.async_support.coinex):
|
|
611
774
|
#
|
612
775
|
# {
|
613
776
|
# "method": "depth.update",
|
614
|
-
# "
|
615
|
-
#
|
616
|
-
#
|
777
|
+
# "data": {
|
778
|
+
# "market": "BTCUSDT",
|
779
|
+
# "is_full": True,
|
780
|
+
# "depth": {
|
617
781
|
# "asks": [
|
618
|
-
# [
|
619
|
-
#
|
782
|
+
# [
|
783
|
+
# "30740.00",
|
784
|
+
# "0.31763545"
|
785
|
+
# ],
|
620
786
|
# ],
|
621
787
|
# "bids": [
|
622
|
-
# [
|
623
|
-
#
|
788
|
+
# [
|
789
|
+
# "30736.00",
|
790
|
+
# "0.04857373"
|
791
|
+
# ],
|
624
792
|
# ],
|
625
|
-
# "last": "
|
626
|
-
# "
|
627
|
-
# "checksum":
|
628
|
-
# }
|
629
|
-
#
|
630
|
-
# ],
|
793
|
+
# "last": "30746.28",
|
794
|
+
# "updated_at": 1689152421692,
|
795
|
+
# "checksum": 2578768879
|
796
|
+
# }
|
797
|
+
# },
|
631
798
|
# "id": null
|
632
799
|
# }
|
633
800
|
#
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
marketId = self.safe_string(params, 2)
|
640
|
-
market = self.safe_market(marketId, None, None, marketType)
|
801
|
+
defaultType = self.safe_string(self.options, 'defaultType')
|
802
|
+
data = self.safe_dict(message, 'data', {})
|
803
|
+
depth = self.safe_dict(data, 'depth', {})
|
804
|
+
marketId = self.safe_string(data, 'market')
|
805
|
+
market = self.safe_market(marketId, None, None, defaultType)
|
641
806
|
symbol = market['symbol']
|
642
807
|
name = 'orderbook'
|
643
808
|
messageHash = name + ':' + symbol
|
644
|
-
timestamp = self.safe_integer(
|
809
|
+
timestamp = self.safe_integer(depth, 'updated_at')
|
645
810
|
currentOrderBook = self.safe_value(self.orderbooks, symbol)
|
811
|
+
fullOrderBook = self.safe_bool(data, 'is_full', False)
|
646
812
|
if fullOrderBook:
|
647
|
-
snapshot = self.parse_order_book(
|
813
|
+
snapshot = self.parse_order_book(depth, symbol, timestamp)
|
648
814
|
if currentOrderBook is None:
|
649
|
-
|
650
|
-
self.orderbooks[symbol] = orderbook
|
815
|
+
self.orderbooks[symbol] = self.order_book(snapshot)
|
651
816
|
else:
|
652
817
|
orderbook = self.orderbooks[symbol]
|
653
818
|
orderbook.reset(snapshot)
|
654
819
|
else:
|
655
|
-
asks = self.
|
656
|
-
bids = self.
|
820
|
+
asks = self.safe_list(depth, 'asks', [])
|
821
|
+
bids = self.safe_list(depth, 'bids', [])
|
657
822
|
self.handle_deltas(currentOrderBook['asks'], asks)
|
658
823
|
self.handle_deltas(currentOrderBook['bids'], bids)
|
659
824
|
currentOrderBook['nonce'] = timestamp
|
@@ -664,24 +829,50 @@ class coinex(ccxt.async_support.coinex):
|
|
664
829
|
client.resolve(self.orderbooks[symbol], messageHash)
|
665
830
|
|
666
831
|
async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
832
|
+
"""
|
833
|
+
watches information on multiple orders made by the user
|
834
|
+
:see: https://docs.coinex.com/api/v2/spot/order/ws/user-order
|
835
|
+
:see: https://docs.coinex.com/api/v2/futures/order/ws/user-order
|
836
|
+
:param str symbol: unified market symbol of the market orders were made in
|
837
|
+
:param int [since]: the earliest time in ms to fetch orders for
|
838
|
+
:param int [limit]: the maximum number of order structures to retrieve
|
839
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
840
|
+
:param bool [params.trigger]: if the orders to watch are trigger orders or not
|
841
|
+
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
842
|
+
"""
|
667
843
|
await self.load_markets()
|
668
|
-
|
844
|
+
stop = self.safe_bool_2(params, 'trigger', 'stop')
|
845
|
+
params = self.omit(params, ['trigger', 'stop'])
|
669
846
|
messageHash = 'orders'
|
670
847
|
market = None
|
671
|
-
|
672
|
-
message: dict = {
|
673
|
-
'method': 'order.subscribe',
|
674
|
-
'id': self.request_id(),
|
675
|
-
}
|
848
|
+
marketList = None
|
676
849
|
if symbol is not None:
|
677
850
|
market = self.market(symbol)
|
678
851
|
symbol = market['symbol']
|
679
|
-
|
852
|
+
type = None
|
853
|
+
type, params = self.handle_market_type_and_params('watchOrders', market, params, 'spot')
|
854
|
+
await self.authenticate(type)
|
855
|
+
if symbol is not None:
|
856
|
+
marketList = [market['id']]
|
680
857
|
messageHash += ':' + symbol
|
681
858
|
else:
|
682
|
-
|
859
|
+
marketList = []
|
860
|
+
if type == 'spot':
|
861
|
+
messageHash += ':spot'
|
862
|
+
else:
|
863
|
+
messageHash += ':swap'
|
864
|
+
method = None
|
865
|
+
if stop:
|
866
|
+
method = 'stop.subscribe'
|
867
|
+
else:
|
868
|
+
method = 'order.subscribe'
|
869
|
+
message: dict = {
|
870
|
+
'method': method,
|
871
|
+
'params': {'market_list': marketList},
|
872
|
+
'id': self.request_id(),
|
873
|
+
}
|
683
874
|
url = self.urls['api']['ws'][type]
|
684
|
-
request = self.deep_extend(message,
|
875
|
+
request = self.deep_extend(message, params)
|
685
876
|
orders = await self.watch(url, messageHash, request, messageHash, request)
|
686
877
|
if self.newUpdates:
|
687
878
|
limit = orders.getLimit(symbol, limit)
|
@@ -689,255 +880,260 @@ class coinex(ccxt.async_support.coinex):
|
|
689
880
|
|
690
881
|
def handle_orders(self, client: Client, message):
|
691
882
|
#
|
692
|
-
#
|
883
|
+
# spot
|
693
884
|
#
|
694
|
-
#
|
695
|
-
#
|
696
|
-
#
|
697
|
-
#
|
698
|
-
#
|
699
|
-
#
|
700
|
-
#
|
701
|
-
#
|
702
|
-
#
|
703
|
-
#
|
704
|
-
#
|
705
|
-
#
|
706
|
-
#
|
707
|
-
#
|
708
|
-
#
|
709
|
-
#
|
710
|
-
#
|
711
|
-
#
|
712
|
-
#
|
713
|
-
#
|
714
|
-
#
|
715
|
-
#
|
716
|
-
#
|
717
|
-
#
|
718
|
-
#
|
719
|
-
#
|
720
|
-
#
|
721
|
-
#
|
722
|
-
# "last_deal_price": "0",
|
723
|
-
# "last_deal_time": 0,
|
724
|
-
# "last_deal_id": 0,
|
725
|
-
# "last_role": 0,
|
726
|
-
# "fee_asset": null,
|
727
|
-
# "stop_id": 0
|
728
|
-
# }
|
729
|
-
# ],
|
730
|
-
# "id": null
|
731
|
-
# }
|
885
|
+
# {
|
886
|
+
# "method": "order.update",
|
887
|
+
# "data": {
|
888
|
+
# "event": "put",
|
889
|
+
# "order": {
|
890
|
+
# "order_id": 12750,
|
891
|
+
# "market": "BTCUSDT",
|
892
|
+
# "margin_market": "BTCUSDT",
|
893
|
+
# "type": "limit",
|
894
|
+
# "side": "buy",
|
895
|
+
# "price": "5999.00",
|
896
|
+
# "amount": "1.50000000",
|
897
|
+
# "unfill_amount": "1.50000000",
|
898
|
+
# "fill_value": "1.50000000",
|
899
|
+
# "taker_fee_rate": "0.0001",
|
900
|
+
# "maker_fee_rate": "0.0001",
|
901
|
+
# "base_ccy_fee": "0.0001",
|
902
|
+
# "quote_ccy_fee": "0.0001",
|
903
|
+
# "discount_ccy_fee": "0.0001",
|
904
|
+
# "last_fill_amount": "0",
|
905
|
+
# "last_fill_price": "0",
|
906
|
+
# "client_id": "buy1_1234",
|
907
|
+
# "created_at": 1689152421692,
|
908
|
+
# "updated_at": 1689152421692,
|
909
|
+
# }
|
910
|
+
# },
|
911
|
+
# "id": null
|
912
|
+
# }
|
732
913
|
#
|
733
|
-
#
|
914
|
+
# spot stop
|
734
915
|
#
|
735
|
-
#
|
736
|
-
#
|
737
|
-
#
|
738
|
-
#
|
739
|
-
#
|
740
|
-
#
|
741
|
-
#
|
742
|
-
#
|
743
|
-
#
|
744
|
-
#
|
745
|
-
#
|
746
|
-
#
|
747
|
-
#
|
748
|
-
#
|
749
|
-
#
|
750
|
-
#
|
751
|
-
#
|
752
|
-
#
|
753
|
-
#
|
754
|
-
#
|
755
|
-
#
|
756
|
-
#
|
757
|
-
#
|
758
|
-
#
|
759
|
-
#
|
760
|
-
#
|
761
|
-
#
|
762
|
-
#
|
763
|
-
#
|
764
|
-
#
|
765
|
-
#
|
766
|
-
#
|
767
|
-
#
|
768
|
-
#
|
769
|
-
#
|
770
|
-
#
|
771
|
-
#
|
772
|
-
#
|
773
|
-
#
|
774
|
-
#
|
775
|
-
#
|
776
|
-
#
|
777
|
-
|
778
|
-
|
916
|
+
# {
|
917
|
+
# "method": "stop.update",
|
918
|
+
# "data": {
|
919
|
+
# "event": 1,
|
920
|
+
# "stop": {
|
921
|
+
# "stop_id": 102067022299,
|
922
|
+
# "market": "BTCUSDT",
|
923
|
+
# "margin_market": "BTCUSDT",
|
924
|
+
# "type": "limit",
|
925
|
+
# "side": "buy",
|
926
|
+
# "price": "20000.00",
|
927
|
+
# "amount": "0.10000000",
|
928
|
+
# "trigger_price": "20000.00",
|
929
|
+
# "trigger_direction": "lower",
|
930
|
+
# "taker_fee_rate": "0.0016",
|
931
|
+
# "maker_fee_rate": "0.0016",
|
932
|
+
# "status": "active_success",
|
933
|
+
# "client_id": "",
|
934
|
+
# "created_at": 1689152996689,
|
935
|
+
# "updated_at": 1689152996689,
|
936
|
+
# }
|
937
|
+
# },
|
938
|
+
# "id": null
|
939
|
+
# }
|
940
|
+
#
|
941
|
+
# swap
|
942
|
+
#
|
943
|
+
# {
|
944
|
+
# "method": "order.update",
|
945
|
+
# "data": {
|
946
|
+
# "event": "put",
|
947
|
+
# "order": {
|
948
|
+
# "order_id": 98388656341,
|
949
|
+
# "stop_id": 0,
|
950
|
+
# "market": "BTCUSDT",
|
951
|
+
# "side": "buy",
|
952
|
+
# "type": "limit",
|
953
|
+
# "amount": "0.0010",
|
954
|
+
# "price": "50000.00",
|
955
|
+
# "unfilled_amount": "0.0010",
|
956
|
+
# "filled_amount": "0",
|
957
|
+
# "filled_value": "0",
|
958
|
+
# "fee": "0",
|
959
|
+
# "fee_ccy": "USDT",
|
960
|
+
# "taker_fee_rate": "0.00046",
|
961
|
+
# "maker_fee_rate": "0.00000000000000000000",
|
962
|
+
# "client_id": "",
|
963
|
+
# "last_filled_amount": "0.0010",
|
964
|
+
# "last_filled_price": "30721.35",
|
965
|
+
# "created_at": 1689145715129,
|
966
|
+
# "updated_at": 1689145715129
|
967
|
+
# }
|
968
|
+
# },
|
969
|
+
# "id": null
|
970
|
+
# }
|
971
|
+
#
|
972
|
+
# swap stop
|
973
|
+
#
|
974
|
+
# {
|
975
|
+
# "method": "stop.update",
|
976
|
+
# "data": {
|
977
|
+
# "event": "put",
|
978
|
+
# "stop": {
|
979
|
+
# "stop_id": 98389557871,
|
980
|
+
# "market": "BTCUSDT",
|
981
|
+
# "side": "sell",
|
982
|
+
# "type": "limit",
|
983
|
+
# "price": "20000.00",
|
984
|
+
# "amount": "0.0100",
|
985
|
+
# "trigger_price": "20000.00",
|
986
|
+
# "trigger_direction": "higer",
|
987
|
+
# "trigger_price_type": "index_price",
|
988
|
+
# "taker_fee_rate": "0.00046",
|
989
|
+
# "maker_fee_rate": "0.00026",
|
990
|
+
# "client_id": "",
|
991
|
+
# "created_at": 1689146382674,
|
992
|
+
# "updated_at": 1689146382674
|
993
|
+
# }
|
994
|
+
# },
|
995
|
+
# "id": null
|
996
|
+
# }
|
997
|
+
#
|
998
|
+
data = self.safe_dict(message, 'data', {})
|
999
|
+
order = self.safe_dict_2(data, 'order', 'stop', {})
|
779
1000
|
parsedOrder = self.parse_ws_order(order)
|
1001
|
+
symbol = parsedOrder['symbol']
|
1002
|
+
market = self.market(symbol)
|
780
1003
|
if self.orders is None:
|
781
1004
|
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
782
1005
|
self.orders = ArrayCacheBySymbolById(limit)
|
783
1006
|
orders = self.orders
|
784
1007
|
orders.append(parsedOrder)
|
785
1008
|
messageHash = 'orders'
|
786
|
-
|
787
|
-
|
1009
|
+
messageWithType = messageHash + ':' + market['type']
|
1010
|
+
client.resolve(self.orders, messageWithType)
|
1011
|
+
messageHash += ':' + symbol
|
788
1012
|
client.resolve(self.orders, messageHash)
|
789
1013
|
|
790
1014
|
def parse_ws_order(self, order, market=None):
|
791
1015
|
#
|
792
|
-
#
|
1016
|
+
# spot
|
793
1017
|
#
|
794
|
-
#
|
795
|
-
#
|
796
|
-
#
|
797
|
-
#
|
798
|
-
#
|
799
|
-
#
|
800
|
-
#
|
801
|
-
#
|
802
|
-
#
|
803
|
-
#
|
804
|
-
#
|
805
|
-
#
|
806
|
-
#
|
807
|
-
#
|
808
|
-
#
|
809
|
-
#
|
810
|
-
#
|
811
|
-
#
|
812
|
-
#
|
813
|
-
#
|
814
|
-
#
|
815
|
-
# "asset_fee": "0",
|
816
|
-
# "fee_discount": "1",
|
817
|
-
# "last_deal_amount": "0",
|
818
|
-
# "last_deal_price": "0",
|
819
|
-
# "last_deal_time": 0,
|
820
|
-
# "last_deal_id": 0,
|
821
|
-
# "last_role": 0,
|
822
|
-
# "fee_asset": null,
|
823
|
-
# "stop_id": 0
|
824
|
-
# }
|
1018
|
+
# {
|
1019
|
+
# "order_id": 12750,
|
1020
|
+
# "market": "BTCUSDT",
|
1021
|
+
# "margin_market": "BTCUSDT",
|
1022
|
+
# "type": "limit",
|
1023
|
+
# "side": "buy",
|
1024
|
+
# "price": "5999.00",
|
1025
|
+
# "amount": "1.50000000",
|
1026
|
+
# "unfill_amount": "1.50000000",
|
1027
|
+
# "fill_value": "1.50000000",
|
1028
|
+
# "taker_fee_rate": "0.0001",
|
1029
|
+
# "maker_fee_rate": "0.0001",
|
1030
|
+
# "base_ccy_fee": "0.0001",
|
1031
|
+
# "quote_ccy_fee": "0.0001",
|
1032
|
+
# "discount_ccy_fee": "0.0001",
|
1033
|
+
# "last_fill_amount": "0",
|
1034
|
+
# "last_fill_price": "0",
|
1035
|
+
# "client_id": "buy1_1234",
|
1036
|
+
# "created_at": 1689152421692,
|
1037
|
+
# "updated_at": 1689152421692,
|
1038
|
+
# }
|
825
1039
|
#
|
826
|
-
#
|
1040
|
+
# spot stop
|
827
1041
|
#
|
828
|
-
#
|
829
|
-
#
|
830
|
-
#
|
831
|
-
#
|
832
|
-
#
|
833
|
-
#
|
834
|
-
#
|
835
|
-
#
|
836
|
-
#
|
837
|
-
#
|
838
|
-
#
|
839
|
-
#
|
840
|
-
#
|
841
|
-
#
|
842
|
-
#
|
843
|
-
#
|
844
|
-
#
|
845
|
-
#
|
846
|
-
#
|
847
|
-
#
|
848
|
-
#
|
849
|
-
#
|
850
|
-
#
|
851
|
-
#
|
852
|
-
#
|
853
|
-
#
|
854
|
-
#
|
855
|
-
#
|
856
|
-
#
|
857
|
-
#
|
858
|
-
#
|
859
|
-
#
|
860
|
-
#
|
861
|
-
#
|
862
|
-
#
|
863
|
-
#
|
864
|
-
#
|
865
|
-
#
|
866
|
-
#
|
867
|
-
#
|
868
|
-
#
|
869
|
-
#
|
870
|
-
#
|
871
|
-
#
|
872
|
-
#
|
873
|
-
#
|
874
|
-
#
|
875
|
-
#
|
876
|
-
#
|
877
|
-
#
|
878
|
-
#
|
879
|
-
#
|
880
|
-
#
|
881
|
-
#
|
882
|
-
#
|
883
|
-
#
|
884
|
-
#
|
885
|
-
#
|
886
|
-
#
|
887
|
-
#
|
888
|
-
|
1042
|
+
# {
|
1043
|
+
# "stop_id": 102067022299,
|
1044
|
+
# "market": "BTCUSDT",
|
1045
|
+
# "margin_market": "BTCUSDT",
|
1046
|
+
# "type": "limit",
|
1047
|
+
# "side": "buy",
|
1048
|
+
# "price": "20000.00",
|
1049
|
+
# "amount": "0.10000000",
|
1050
|
+
# "trigger_price": "20000.00",
|
1051
|
+
# "trigger_direction": "lower",
|
1052
|
+
# "taker_fee_rate": "0.0016",
|
1053
|
+
# "maker_fee_rate": "0.0016",
|
1054
|
+
# "status": "active_success",
|
1055
|
+
# "client_id": "",
|
1056
|
+
# "created_at": 1689152996689,
|
1057
|
+
# "updated_at": 1689152996689,
|
1058
|
+
# }
|
1059
|
+
#
|
1060
|
+
# swap
|
1061
|
+
#
|
1062
|
+
# {
|
1063
|
+
# "order_id": 98388656341,
|
1064
|
+
# "stop_id": 0,
|
1065
|
+
# "market": "BTCUSDT",
|
1066
|
+
# "side": "buy",
|
1067
|
+
# "type": "limit",
|
1068
|
+
# "amount": "0.0010",
|
1069
|
+
# "price": "50000.00",
|
1070
|
+
# "unfilled_amount": "0.0010",
|
1071
|
+
# "filled_amount": "0",
|
1072
|
+
# "filled_value": "0",
|
1073
|
+
# "fee": "0",
|
1074
|
+
# "fee_ccy": "USDT",
|
1075
|
+
# "taker_fee_rate": "0.00046",
|
1076
|
+
# "maker_fee_rate": "0.00000000000000000000",
|
1077
|
+
# "client_id": "",
|
1078
|
+
# "last_filled_amount": "0.0010",
|
1079
|
+
# "last_filled_price": "30721.35",
|
1080
|
+
# "created_at": 1689145715129,
|
1081
|
+
# "updated_at": 1689145715129
|
1082
|
+
# }
|
1083
|
+
#
|
1084
|
+
# swap stop
|
1085
|
+
#
|
1086
|
+
# {
|
1087
|
+
# "stop_id": 98389557871,
|
1088
|
+
# "market": "BTCUSDT",
|
1089
|
+
# "side": "sell",
|
1090
|
+
# "type": "limit",
|
1091
|
+
# "price": "20000.00",
|
1092
|
+
# "amount": "0.0100",
|
1093
|
+
# "trigger_price": "20000.00",
|
1094
|
+
# "trigger_direction": "higer",
|
1095
|
+
# "trigger_price_type": "index_price",
|
1096
|
+
# "taker_fee_rate": "0.00046",
|
1097
|
+
# "maker_fee_rate": "0.00026",
|
1098
|
+
# "client_id": "",
|
1099
|
+
# "created_at": 1689146382674,
|
1100
|
+
# "updated_at": 1689146382674
|
1101
|
+
# }
|
1102
|
+
#
|
1103
|
+
timestamp = self.safe_integer(order, 'created_at')
|
889
1104
|
marketId = self.safe_string(order, 'market')
|
890
|
-
typeCode = self.safe_string(order, 'type')
|
891
|
-
type = self.safe_string({
|
892
|
-
'1': 'limit',
|
893
|
-
'2': 'market',
|
894
|
-
}, typeCode)
|
895
|
-
sideCode = self.safe_string(order, 'side')
|
896
|
-
side = self.safe_string({
|
897
|
-
'1': 'sell',
|
898
|
-
'2': 'buy',
|
899
|
-
}, sideCode)
|
900
|
-
remaining = self.safe_string(order, 'left')
|
901
|
-
amount = self.safe_string(order, 'amount')
|
902
1105
|
status = self.safe_string(order, 'status')
|
903
|
-
|
1106
|
+
isSpot = ('margin_market' in order)
|
1107
|
+
defaultType = 'spot' if isSpot else 'swap'
|
904
1108
|
market = self.safe_market(marketId, market, None, defaultType)
|
905
|
-
cost = self.safe_string(order, 'deal_money')
|
906
|
-
filled = self.safe_string(order, 'deal_stock')
|
907
|
-
average = None
|
908
|
-
if market['swap']:
|
909
|
-
leverage = self.safe_string(order, 'leverage')
|
910
|
-
cost = Precise.string_div(filled, leverage)
|
911
|
-
average = Precise.string_div(filled, amount)
|
912
|
-
filled = None
|
913
1109
|
fee = None
|
914
|
-
feeCost = self.omit_zero(self.
|
1110
|
+
feeCost = self.omit_zero(self.safe_string_2(order, 'fee', 'quote_ccy_fee'))
|
915
1111
|
if feeCost is not None:
|
916
|
-
feeCurrencyId = self.safe_string(order, '
|
1112
|
+
feeCurrencyId = self.safe_string(order, 'fee_ccy', market['quote'])
|
917
1113
|
fee = {
|
918
1114
|
'currency': self.safe_currency_code(feeCurrencyId),
|
919
1115
|
'cost': feeCost,
|
920
1116
|
}
|
921
1117
|
return self.safe_order({
|
922
1118
|
'info': order,
|
923
|
-
'id': self.safe_string_2(order, 'order_id', '
|
1119
|
+
'id': self.safe_string_2(order, 'order_id', 'stop_id'),
|
924
1120
|
'clientOrderId': self.safe_string(order, 'client_id'),
|
925
1121
|
'datetime': self.iso8601(timestamp),
|
926
1122
|
'timestamp': timestamp,
|
927
|
-
'lastTradeTimestamp': self.
|
1123
|
+
'lastTradeTimestamp': self.safe_integer(order, 'updated_at'),
|
928
1124
|
'symbol': market['symbol'],
|
929
|
-
'type': type,
|
1125
|
+
'type': self.safe_string(order, 'type'),
|
930
1126
|
'timeInForce': None,
|
931
1127
|
'postOnly': None,
|
932
|
-
'side': side,
|
1128
|
+
'side': self.safe_string(order, 'side'),
|
933
1129
|
'price': self.safe_string(order, 'price'),
|
934
|
-
'stopPrice': self.safe_string(order, '
|
935
|
-
'triggerPrice': self.safe_string(order, '
|
936
|
-
'amount': amount,
|
937
|
-
'filled':
|
938
|
-
'remaining':
|
939
|
-
'cost':
|
940
|
-
'average':
|
1130
|
+
'stopPrice': self.safe_string(order, 'trigger_price'),
|
1131
|
+
'triggerPrice': self.safe_string(order, 'trigger_price'),
|
1132
|
+
'amount': self.safe_string(order, 'amount'),
|
1133
|
+
'filled': self.safe_string_2(order, 'filled_amount', 'fill_value'),
|
1134
|
+
'remaining': self.safe_string_2(order, 'unfilled_amount', 'unfill_amount'),
|
1135
|
+
'cost': None,
|
1136
|
+
'average': None,
|
941
1137
|
'status': self.parse_ws_order_status(status),
|
942
1138
|
'fee': fee,
|
943
1139
|
'trades': None,
|
@@ -945,24 +1141,109 @@ class coinex(ccxt.async_support.coinex):
|
|
945
1141
|
|
946
1142
|
def parse_ws_order_status(self, status):
|
947
1143
|
statuses: dict = {
|
948
|
-
'
|
949
|
-
'
|
1144
|
+
'active_success': 'open',
|
1145
|
+
'active_fail': 'canceled',
|
1146
|
+
'cancel': 'canceled',
|
950
1147
|
}
|
951
1148
|
return self.safe_string(statuses, status, status)
|
952
1149
|
|
1150
|
+
async def watch_bids_asks(self, symbols: Strings = None, params={}) -> Tickers:
|
1151
|
+
"""
|
1152
|
+
watches best bid & ask for symbols
|
1153
|
+
:see: https://docs.coinex.com/api/v2/spot/market/ws/market-bbo
|
1154
|
+
:see: https://docs.coinex.com/api/v2/futures/market/ws/market-bbo
|
1155
|
+
:param str[] [symbols]: unified symbol of the market to fetch the ticker for
|
1156
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
1157
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
1158
|
+
"""
|
1159
|
+
await self.load_markets()
|
1160
|
+
marketIds = self.market_ids(symbols)
|
1161
|
+
messageHashes = []
|
1162
|
+
market = None
|
1163
|
+
symbolsDefined = (symbols is not None)
|
1164
|
+
if symbolsDefined:
|
1165
|
+
for i in range(0, len(symbols)):
|
1166
|
+
symbol = symbols[i]
|
1167
|
+
market = self.market(symbol)
|
1168
|
+
messageHashes.append('bidsasks:' + market['symbol'])
|
1169
|
+
else:
|
1170
|
+
messageHashes.append('bidsasks')
|
1171
|
+
type = None
|
1172
|
+
type, params = self.handle_market_type_and_params('watchBidsAsks', market, params)
|
1173
|
+
url = self.urls['api']['ws'][type]
|
1174
|
+
subscriptionHashes = ['all@bidsasks']
|
1175
|
+
subscribe: dict = {
|
1176
|
+
'method': 'bbo.subscribe',
|
1177
|
+
'params': {'market_list': marketIds},
|
1178
|
+
'id': self.request_id(),
|
1179
|
+
}
|
1180
|
+
result = await self.watch_multiple(url, messageHashes, self.deep_extend(subscribe, params), subscriptionHashes)
|
1181
|
+
if self.newUpdates:
|
1182
|
+
return result
|
1183
|
+
return self.filter_by_array(self.bidsasks, 'symbol', symbols)
|
1184
|
+
|
1185
|
+
def handle_bid_ask(self, client: Client, message):
|
1186
|
+
#
|
1187
|
+
# {
|
1188
|
+
# "method": "bbo.update",
|
1189
|
+
# "data": {
|
1190
|
+
# "market": "BTCUSDT",
|
1191
|
+
# "updated_at": 1656660154,
|
1192
|
+
# "best_bid_price": "20000",
|
1193
|
+
# "best_bid_size": "0.1",
|
1194
|
+
# "best_ask_price": "20001",
|
1195
|
+
# "best_ask_size": "0.15"
|
1196
|
+
# },
|
1197
|
+
# "id": null
|
1198
|
+
# }
|
1199
|
+
#
|
1200
|
+
data = self.safe_dict(message, 'data', {})
|
1201
|
+
parsedTicker = self.parse_ws_bid_ask(data)
|
1202
|
+
symbol = parsedTicker['symbol']
|
1203
|
+
self.bidsasks[symbol] = parsedTicker
|
1204
|
+
messageHash = 'bidsasks:' + symbol
|
1205
|
+
client.resolve(parsedTicker, messageHash)
|
1206
|
+
|
1207
|
+
def parse_ws_bid_ask(self, ticker, market=None):
|
1208
|
+
#
|
1209
|
+
# {
|
1210
|
+
# "market": "BTCUSDT",
|
1211
|
+
# "updated_at": 1656660154,
|
1212
|
+
# "best_bid_price": "20000",
|
1213
|
+
# "best_bid_size": "0.1",
|
1214
|
+
# "best_ask_price": "20001",
|
1215
|
+
# "best_ask_size": "0.15"
|
1216
|
+
# }
|
1217
|
+
#
|
1218
|
+
defaultType = self.safe_string(self.options, 'defaultType')
|
1219
|
+
marketId = self.safe_string(ticker, 'market')
|
1220
|
+
market = self.safe_market(marketId, market, None, defaultType)
|
1221
|
+
timestamp = self.safe_timestamp(ticker, 'updated_at')
|
1222
|
+
return self.safe_ticker({
|
1223
|
+
'symbol': self.safe_symbol(marketId, market, None, defaultType),
|
1224
|
+
'timestamp': timestamp,
|
1225
|
+
'datetime': self.iso8601(timestamp),
|
1226
|
+
'ask': self.safe_number(ticker, 'best_ask_price'),
|
1227
|
+
'askVolume': self.safe_number(ticker, 'best_ask_size'),
|
1228
|
+
'bid': self.safe_number(ticker, 'best_bid_price'),
|
1229
|
+
'bidVolume': self.safe_number(ticker, 'best_bid_size'),
|
1230
|
+
'info': ticker,
|
1231
|
+
}, market)
|
1232
|
+
|
953
1233
|
def handle_message(self, client: Client, message):
|
954
|
-
error = self.safe_value(message, 'error')
|
955
|
-
if error is not None:
|
956
|
-
raise ExchangeError(self.id + ' ' + self.json(error))
|
957
1234
|
method = self.safe_string(message, 'method')
|
1235
|
+
error = self.safe_string(message, 'message')
|
1236
|
+
if error is not None:
|
1237
|
+
self.handle_errors(None, None, client.url, method, None, self.json(error), message, None, None)
|
958
1238
|
handlers: dict = {
|
959
1239
|
'state.update': self.handle_ticker,
|
960
|
-
'
|
1240
|
+
'balance.update': self.handle_balance,
|
961
1241
|
'deals.update': self.handle_trades,
|
1242
|
+
'user_deals.update': self.handle_my_trades,
|
962
1243
|
'depth.update': self.handle_order_book,
|
963
1244
|
'order.update': self.handle_orders,
|
964
|
-
'
|
965
|
-
'
|
1245
|
+
'stop.update': self.handle_orders,
|
1246
|
+
'bbo.update': self.handle_bid_ask,
|
966
1247
|
}
|
967
1248
|
handler = self.safe_value(handlers, method)
|
968
1249
|
if handler is not None:
|
@@ -970,92 +1251,90 @@ class coinex(ccxt.async_support.coinex):
|
|
970
1251
|
return
|
971
1252
|
self.handle_subscription_status(client, message)
|
972
1253
|
|
1254
|
+
def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
|
1255
|
+
if response is None:
|
1256
|
+
return None
|
1257
|
+
#
|
1258
|
+
# {"id": 1, "code": 20001, "message": "invalid argument"}
|
1259
|
+
# {"id": 2, "code": 21001, "message": "require auth"}
|
1260
|
+
# {"id": 1, "code": 21002, "message": "Signature Incorrect"}
|
1261
|
+
#
|
1262
|
+
message = self.safe_string_lower(response, 'message')
|
1263
|
+
isErrorMessage = (message is not None) and (message != 'ok')
|
1264
|
+
errorCode = self.safe_string(response, 'code')
|
1265
|
+
isErrorCode = (errorCode is not None) and (errorCode != '0')
|
1266
|
+
if isErrorCode or isErrorMessage:
|
1267
|
+
feedback = self.id + ' ' + body
|
1268
|
+
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
|
1269
|
+
self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
|
1270
|
+
raise ExchangeError(feedback)
|
1271
|
+
return None
|
1272
|
+
|
973
1273
|
def handle_authentication_message(self, client: Client, message):
|
1274
|
+
#
|
1275
|
+
# success
|
974
1276
|
#
|
975
1277
|
# {
|
976
|
-
# "
|
977
|
-
# "
|
978
|
-
#
|
979
|
-
# },
|
980
|
-
# "id": 1
|
1278
|
+
# "id": 1,
|
1279
|
+
# "code": 0,
|
1280
|
+
# "message": "OK"
|
981
1281
|
# }
|
982
1282
|
#
|
983
|
-
|
984
|
-
|
985
|
-
#
|
986
|
-
#
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
1283
|
+
# fail
|
1284
|
+
#
|
1285
|
+
# {
|
1286
|
+
# "id": 1,
|
1287
|
+
# "code": 21002,
|
1288
|
+
# "message": ""
|
1289
|
+
# }
|
1290
|
+
#
|
1291
|
+
status = self.safe_string_lower(message, 'message')
|
1292
|
+
errorCode = self.safe_string(message, 'code')
|
1293
|
+
messageHash = 'authenticated'
|
1294
|
+
if (status == 'ok') or (errorCode == '0'):
|
1295
|
+
future = self.safe_value(client.futures, messageHash)
|
1296
|
+
future.resolve(True)
|
1297
|
+
else:
|
1298
|
+
error = AuthenticationError(self.json(message))
|
1299
|
+
client.reject(error, messageHash)
|
1300
|
+
if messageHash in client.subscriptions:
|
1301
|
+
del client.subscriptions[messageHash]
|
992
1302
|
|
993
1303
|
def handle_subscription_status(self, client: Client, message):
|
994
1304
|
id = self.safe_integer(message, 'id')
|
995
1305
|
subscription = self.safe_value(client.subscriptions, id)
|
996
1306
|
if subscription is not None:
|
997
1307
|
futureIndex = self.safe_string(subscription, 'future')
|
998
|
-
if futureIndex == 'ohlcv':
|
999
|
-
self.handle_ohlcv(client, message)
|
1000
|
-
return
|
1001
1308
|
future = self.safe_value(client.futures, futureIndex)
|
1002
1309
|
if future is not None:
|
1003
1310
|
future.resolve(True)
|
1004
1311
|
del client.subscriptions[id]
|
1005
1312
|
|
1006
|
-
async def authenticate(self,
|
1007
|
-
type = None
|
1008
|
-
type, params = self.handle_market_type_and_params('authenticate', None, params)
|
1313
|
+
async def authenticate(self, type: str):
|
1009
1314
|
url = self.urls['api']['ws'][type]
|
1010
1315
|
client = self.client(url)
|
1011
1316
|
time = self.milliseconds()
|
1012
|
-
|
1013
|
-
|
1014
|
-
swapMessageHash = 'authenticated:swap'
|
1015
|
-
messageHash = spotMessageHash if isSpot else swapMessageHash
|
1317
|
+
timestamp = str(time)
|
1318
|
+
messageHash = 'authenticated'
|
1016
1319
|
future = client.future(messageHash)
|
1017
1320
|
authenticated = self.safe_value(client.subscriptions, messageHash)
|
1018
|
-
if
|
1019
|
-
if authenticated is not None:
|
1020
|
-
return await future
|
1021
|
-
requestId = self.request_id()
|
1022
|
-
subscribe: dict = {
|
1023
|
-
'id': requestId,
|
1024
|
-
'future': spotMessageHash,
|
1025
|
-
}
|
1026
|
-
signData = 'access_id=' + self.apiKey + '&tonce=' + self.number_to_string(time) + '&secret_key=' + self.secret
|
1027
|
-
hash = self.hash(self.encode(signData), 'md5')
|
1028
|
-
request: dict = {
|
1029
|
-
'method': 'server.sign',
|
1030
|
-
'params': [
|
1031
|
-
self.apiKey,
|
1032
|
-
hash.upper(),
|
1033
|
-
time,
|
1034
|
-
],
|
1035
|
-
'id': requestId,
|
1036
|
-
}
|
1037
|
-
self.watch(url, messageHash, request, requestId, subscribe)
|
1038
|
-
client.subscriptions[messageHash] = True
|
1039
|
-
return await future
|
1040
|
-
else:
|
1041
|
-
if authenticated is not None:
|
1042
|
-
return await future
|
1043
|
-
requestId = self.request_id()
|
1044
|
-
subscribe: dict = {
|
1045
|
-
'id': requestId,
|
1046
|
-
'future': swapMessageHash,
|
1047
|
-
}
|
1048
|
-
signData = 'access_id=' + self.apiKey + '×tamp=' + self.number_to_string(time) + '&secret_key=' + self.secret
|
1049
|
-
hash = self.hash(self.encode(signData), 'sha256', 'hex')
|
1050
|
-
request: dict = {
|
1051
|
-
'method': 'server.sign',
|
1052
|
-
'params': [
|
1053
|
-
self.apiKey,
|
1054
|
-
hash.lower(),
|
1055
|
-
time,
|
1056
|
-
],
|
1057
|
-
'id': requestId,
|
1058
|
-
}
|
1059
|
-
self.watch(url, messageHash, request, requestId, subscribe)
|
1060
|
-
client.subscriptions[messageHash] = True
|
1321
|
+
if authenticated is not None:
|
1061
1322
|
return await future
|
1323
|
+
requestId = self.request_id()
|
1324
|
+
subscribe: dict = {
|
1325
|
+
'id': requestId,
|
1326
|
+
'future': messageHash,
|
1327
|
+
}
|
1328
|
+
hmac = self.hmac(self.encode(timestamp), self.encode(self.secret), hashlib.sha256, 'hex')
|
1329
|
+
request: dict = {
|
1330
|
+
'id': requestId,
|
1331
|
+
'method': 'server.sign',
|
1332
|
+
'params': {
|
1333
|
+
'access_id': self.apiKey,
|
1334
|
+
'signed_str': hmac.lower(),
|
1335
|
+
'timestamp': time,
|
1336
|
+
},
|
1337
|
+
}
|
1338
|
+
self.watch(url, messageHash, request, requestId, subscribe)
|
1339
|
+
client.subscriptions[messageHash] = True
|
1340
|
+
return await future
|