mexc-exchange-api 0.0.81__py3-none-any.whl → 0.0.83__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.
mexc/ccxt/pro/mexc.py CHANGED
@@ -52,13 +52,14 @@ class mexc(mexcAsync):
52
52
  'urls': {
53
53
  'api': {
54
54
  'ws': {
55
- 'spot': 'wss://wbs.mexc.com/ws',
55
+ 'spot': 'wss://wbs-api.mexc.com/ws',
56
56
  'swap': 'wss://contract.mexc.com/edge',
57
57
  },
58
58
  },
59
59
  },
60
60
  'options': {
61
61
  'listenKeyRefreshRate': 1200000,
62
+ 'decompressBinary': False,
62
63
  # TODO add reset connection after #16754 is merged
63
64
  'timeframes': {
64
65
  '1m': 'Min1',
@@ -103,13 +104,7 @@ class mexc(mexcAsync):
103
104
  market = self.market(symbol)
104
105
  messageHash = 'ticker:' + market['symbol']
105
106
  if market['spot']:
106
- miniTicker = False
107
- miniTicker, params = self.handle_option_and_params(params, 'watchTicker', 'miniTicker')
108
- channel = None
109
- if miniTicker:
110
- channel = 'spot@public.miniTicker.v3.api@' + market['id'] + '@UTC+8'
111
- else:
112
- channel = 'spot@public.bookTicker.v3.api@' + market['id']
107
+ channel = 'spot@public.aggre.bookTicker.v3.api.pb@100ms@' + market['id']
113
108
  return await self.watch_spot_public(channel, messageHash, params)
114
109
  else:
115
110
  channel = 'sub.ticker'
@@ -187,9 +182,9 @@ class mexc(mexcAsync):
187
182
  # }
188
183
  #
189
184
  self.handle_bid_ask(client, message)
190
- rawTicker = self.safe_dict_2(message, 'd', 'data')
185
+ rawTicker = self.safe_dict_n(message, ['d', 'data', 'publicAggreBookTicker'])
191
186
  marketId = self.safe_string_2(message, 's', 'symbol')
192
- timestamp = self.safe_integer(message, 't')
187
+ timestamp = self.safe_integer_2(message, 't', 'sendtime')
193
188
  market = self.safe_market(marketId)
194
189
  symbol = market['symbol']
195
190
  ticker = None
@@ -229,27 +224,33 @@ class mexc(mexcAsync):
229
224
  url = self.urls['api']['ws']['spot'] if (isSpot) else self.urls['api']['ws']['swap']
230
225
  request: dict = {}
231
226
  if isSpot:
232
- miniTicker = False
233
- miniTicker, params = self.handle_option_and_params(params, 'watchTickers', 'miniTicker')
234
- topics = []
235
- if not miniTicker:
236
- if symbols is None:
237
- raise ArgumentsRequired(self.id + ' watchTickers required symbols argument for the bookTicker channel')
238
- marketIds = self.market_ids(symbols)
239
- for i in range(0, len(marketIds)):
240
- marketId = marketIds[i]
241
- messageHashes.append('ticker:' + symbols[i])
242
- channel = 'spot@public.bookTicker.v3.api@' + marketId
243
- topics.append(channel)
244
- else:
245
- topics.append('spot@public.miniTickers.v3.api@UTC+8')
246
- if symbols is None:
247
- messageHashes.append('spot:ticker')
248
- else:
249
- for i in range(0, len(symbols)):
250
- messageHashes.append('ticker:' + symbols[i])
251
- request['method'] = 'SUBSCRIPTION'
252
- request['params'] = topics
227
+ raise NotSupported(self.id + ' watchTickers does not support spot markets')
228
+ # miniTicker = False
229
+ # miniTicker, params = self.handle_option_and_params(params, 'watchTickers', 'miniTicker')
230
+ # topics = []
231
+ # if not miniTicker:
232
+ # if symbols is None:
233
+ # raise ArgumentsRequired(self.id + ' watchTickers required symbols argument for the bookTicker channel')
234
+ # }
235
+ # marketIds = self.market_ids(symbols)
236
+ # for i in range(0, len(marketIds)):
237
+ # marketId = marketIds[i]
238
+ # messageHashes.append('ticker:' + symbols[i])
239
+ # channel = 'spot@public.bookTicker.v3.api@' + marketId
240
+ # topics.append(channel)
241
+ # }
242
+ # else:
243
+ # topics.append('spot@public.miniTickers.v3.api@UTC+8')
244
+ # if symbols is None:
245
+ # messageHashes.append('spot:ticker')
246
+ # else:
247
+ # for i in range(0, len(symbols)):
248
+ # messageHashes.append('ticker:' + symbols[i])
249
+ # }
250
+ # }
251
+ # }
252
+ # request['method'] = 'SUBSCRIPTION'
253
+ # request['params'] = topics
253
254
  else:
254
255
  request['method'] = 'sub.tickers'
255
256
  request['params'] = {}
@@ -348,6 +349,11 @@ class mexc(mexcAsync):
348
349
  client.resolve(result, topic)
349
350
 
350
351
  def parse_ws_ticker(self, ticker, market=None):
352
+ # protobuf ticker
353
+ # "bidprice": "93387.28", # Best bid price
354
+ # "bidquantity": "3.73485", # Best bid quantity
355
+ # "askprice": "93387.29", # Best ask price
356
+ # "askquantity": "7.669875" # Best ask quantity
351
357
  #
352
358
  # spot
353
359
  #
@@ -362,7 +368,7 @@ class mexc(mexcAsync):
362
368
  #
363
369
  # {
364
370
  # "s": "BTCUSDT",
365
- # "p": "76522",
371
+ # "p": "76521",
366
372
  # "r": "0.0012",
367
373
  # "tr": "0.0012",
368
374
  # "h": "77196.3",
@@ -388,10 +394,10 @@ class mexc(mexcAsync):
388
394
  'low': self.safe_number(ticker, 'l'),
389
395
  'close': price,
390
396
  'last': price,
391
- 'bid': self.safe_number(ticker, 'b'),
392
- 'bidVolume': self.safe_number(ticker, 'B'),
393
- 'ask': self.safe_number(ticker, 'a'),
394
- 'askVolume': self.safe_number(ticker, 'A'),
397
+ 'bid': self.safe_number_2(ticker, 'b', 'bidPrice'),
398
+ 'bidVolume': self.safe_number_2(ticker, 'B', 'bidQuantity'),
399
+ 'ask': self.safe_number_2(ticker, 'a', 'askPrice'),
400
+ 'askVolume': self.safe_number_2(ticker, 'A', 'askQuantity'),
395
401
  'vwap': None,
396
402
  'previousClose': None,
397
403
  'change': None,
@@ -426,7 +432,7 @@ class mexc(mexcAsync):
426
432
  for i in range(0, len(symbols)):
427
433
  if isSpot:
428
434
  market = self.market(symbols[i])
429
- topics.append('spot@public.bookTicker.v3.api@' + market['id'])
435
+ topics.append('spot@public.aggre.bookTicker.v3.api.pb@100ms@' + market['id'])
430
436
  messageHashes.append('bidask:' + symbols[i])
431
437
  url = self.urls['api']['ws']['spot']
432
438
  request: dict = {
@@ -530,7 +536,7 @@ class mexc(mexcAsync):
530
536
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
531
537
  """
532
538
 
533
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#kline-streams
539
+ https://www.mexc.com/api-docs/spot-v3/websocket-market-streams#trade-streams
534
540
 
535
541
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
536
542
  :param str symbol: unified symbol of the market to fetch OHLCV data for
@@ -548,7 +554,7 @@ class mexc(mexcAsync):
548
554
  messageHash = 'candles:' + symbol + ':' + timeframe
549
555
  ohlcv = None
550
556
  if market['spot']:
551
- channel = 'spot@public.kline.v3.api@' + market['id'] + '@' + timeframeId
557
+ channel = 'spot@public.kline.v3.api.pb@' + market['id'] + '@' + timeframeId
552
558
  ohlcv = await self.watch_spot_public(channel, messageHash, params)
553
559
  else:
554
560
  channel = 'sub.kline'
@@ -607,17 +613,45 @@ class mexc(mexcAsync):
607
613
  # "symbol": "BTC_USDT",
608
614
  # "ts": 1651230713067
609
615
  # }
616
+ # protobuf
617
+ # {
618
+ # "channel":"spot@public.kline.v3.api.pb@BTCUSDT@Min1",
619
+ # "symbol":"BTCUSDT",
620
+ # "symbolId":"2fb942154ef44a4ab2ef98c8afb6a4a7",
621
+ # "createTime":"1754737941062",
622
+ # "publicSpotKline":{
623
+ # "interval":"Min1",
624
+ # "windowStart":"1754737920",
625
+ # "openingPrice":"117317.31",
626
+ # "closingPrice":"117325.26",
627
+ # "highestPrice":"117341",
628
+ # "lowestPrice":"117317.3",
629
+ # "volume":"3.12599854",
630
+ # "amount":"366804.43",
631
+ # "windowEnd":"1754737980"
632
+ # }
633
+ # }
610
634
  #
611
- d = self.safe_value_2(message, 'd', 'data', {})
612
- rawOhlcv = self.safe_value(d, 'k', d)
613
- timeframeId = self.safe_string_2(rawOhlcv, 'i', 'interval')
614
- timeframes = self.safe_value(self.options, 'timeframes', {})
615
- timeframe = self.find_timeframe(timeframeId, timeframes)
616
- marketId = self.safe_string_2(message, 's', 'symbol')
617
- market = self.safe_market(marketId)
618
- symbol = market['symbol']
635
+ parsed: dict = None
636
+ symbol: Str = None
637
+ timeframe: Str = None
638
+ if 'publicSpotKline' in message:
639
+ symbol = self.symbol(self.safe_string(message, 'symbol'))
640
+ data = self.safe_dict(message, 'publicSpotKline', {})
641
+ timeframeId = self.safe_string(data, 'interval')
642
+ timeframe = self.find_timeframe(timeframeId, self.options['timeframes'])
643
+ parsed = self.parse_ws_ohlcv(data, self.safe_market(symbol))
644
+ else:
645
+ d = self.safe_value_2(message, 'd', 'data', {})
646
+ rawOhlcv = self.safe_value(d, 'k', d)
647
+ timeframeId = self.safe_string_2(rawOhlcv, 'i', 'interval')
648
+ timeframes = self.safe_value(self.options, 'timeframes', {})
649
+ timeframe = self.find_timeframe(timeframeId, timeframes)
650
+ marketId = self.safe_string_2(message, 's', 'symbol')
651
+ market = self.safe_market(marketId)
652
+ symbol = market['symbol']
653
+ parsed = self.parse_ws_ohlcv(rawOhlcv, market)
619
654
  messageHash = 'candles:' + symbol + ':' + timeframe
620
- parsed = self.parse_ws_ohlcv(rawOhlcv, market)
621
655
  self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
622
656
  stored = self.safe_value(self.ohlcvs[symbol], timeframe)
623
657
  if stored is None:
@@ -659,26 +693,38 @@ class mexc(mexcAsync):
659
693
  # "rh": 27301.8,
660
694
  # "rl": 27301.8
661
695
  # }
696
+ # protobuf
697
+ #
698
+ # "interval":"Min1",
699
+ # "windowStart":"1754737920",
700
+ # "openingPrice":"117317.31",
701
+ # "closingPrice":"117325.26",
702
+ # "highestPrice":"117341",
703
+ # "lowestPrice":"117317.3",
704
+ # "volume":"3.12599854",
705
+ # "amount":"366804.43",
706
+ # "windowEnd":"1754737980"
662
707
  #
663
708
  return [
664
- self.safe_timestamp(ohlcv, 't'),
665
- self.safe_number(ohlcv, 'o'),
666
- self.safe_number(ohlcv, 'h'),
667
- self.safe_number(ohlcv, 'l'),
668
- self.safe_number(ohlcv, 'c'),
669
- self.safe_number_2(ohlcv, 'v', 'q'),
709
+ self.safe_timestamp_2(ohlcv, 't', 'windowStart'),
710
+ self.safe_number_2(ohlcv, 'o', 'openingPrice'),
711
+ self.safe_number_2(ohlcv, 'h', 'highestPrice'),
712
+ self.safe_number_2(ohlcv, 'l', 'lowestPrice'),
713
+ self.safe_number_2(ohlcv, 'c', 'closingPrice'),
714
+ self.safe_number_2(ohlcv, 'v', 'volume'),
670
715
  ]
671
716
 
672
717
  async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
673
718
  """
674
719
 
675
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#diff-depth-stream
720
+ https://www.mexc.com/api-docs/spot-v3/websocket-market-streams#trade-streams
676
721
  https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
677
722
 
678
723
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
679
724
  :param str symbol: unified symbol of the market to fetch the order book for
680
725
  :param int [limit]: the maximum amount of order book entries to return
681
726
  :param dict [params]: extra parameters specific to the exchange API endpoint
727
+ :param str [params.frequency]: the frequency of the order book updates, default is '10ms', can be '100ms' or '10ms
682
728
  :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
683
729
  """
684
730
  await self.load_markets()
@@ -687,7 +733,9 @@ class mexc(mexcAsync):
687
733
  messageHash = 'orderbook:' + symbol
688
734
  orderbook = None
689
735
  if market['spot']:
690
- channel = 'spot@public.increase.depth.v3.api@' + market['id']
736
+ frequency = None
737
+ frequency, params = self.handle_option_and_params(params, 'watchOrderBook', 'frequency', '100ms')
738
+ channel = 'spot@public.aggre.depth.v3.api.pb@' + frequency + '@' + market['id']
691
739
  orderbook = await self.watch_spot_public(channel, messageHash, params)
692
740
  else:
693
741
  channel = 'sub.depth'
@@ -711,12 +759,12 @@ class mexc(mexcAsync):
711
759
  # return the first index of the cache that can be applied to the orderbook or -1 if not possible
712
760
  nonce = self.safe_integer(orderbook, 'nonce')
713
761
  firstDelta = self.safe_value(cache, 0)
714
- firstDeltaNonce = self.safe_integer_2(firstDelta, 'r', 'version')
762
+ firstDeltaNonce = self.safe_integer_n(firstDelta, ['r', 'version', 'fromVersion'])
715
763
  if nonce < firstDeltaNonce - 1:
716
764
  return -1
717
765
  for i in range(0, len(cache)):
718
766
  delta = cache[i]
719
- deltaNonce = self.safe_integer_2(delta, 'r', 'version')
767
+ deltaNonce = self.safe_integer_n(delta, ['r', 'version', 'fromVersion'])
720
768
  if deltaNonce >= nonce:
721
769
  return i
722
770
  return len(cache)
@@ -764,8 +812,31 @@ class mexc(mexcAsync):
764
812
  # "symbol":"BTC_USDT",
765
813
  # "ts":1651239652372
766
814
  # }
815
+ # protofbuf
816
+ # {
817
+ # "channel":"spot@public.aggre.depth.v3.api.pb@100ms@BTCUSDT",
818
+ # "symbol":"BTCUSDT",
819
+ # "sendTime":"1754741322152",
820
+ # "publicAggreDepths":{
821
+ # "asks":[
822
+ # {
823
+ # "price":"117145.49",
824
+ # "quantity":"0"
825
+ # }
826
+ # ],
827
+ # "bids":[
828
+ # {
829
+ # "price":"117053.41",
830
+ # "quantity":"1.86837271"
831
+ # }
832
+ # ],
833
+ # "eventType":"spot@public.aggre.depth.v3.api.pb@100ms",
834
+ # "fromVersion":"43296363236",
835
+ # "toVersion":"43296363255"
836
+ # }
837
+ # }
767
838
  #
768
- data = self.safe_value_2(message, 'd', 'data')
839
+ data = self.safe_dict_n(message, ['d', 'data', 'publicAggreDepths'])
769
840
  marketId = self.safe_string_2(message, 's', 'symbol')
770
841
  symbol = self.safe_symbol(marketId)
771
842
  messageHash = 'orderbook:' + symbol
@@ -784,7 +855,7 @@ class mexc(mexcAsync):
784
855
  return
785
856
  try:
786
857
  self.handle_delta(storedOrderBook, data)
787
- timestamp = self.safe_integer_2(message, 't', 'ts')
858
+ timestamp = self.safe_integer_n(message, ['t', 'ts', 'sendTime'])
788
859
  storedOrderBook['timestamp'] = timestamp
789
860
  storedOrderBook['datetime'] = self.iso8601(timestamp)
790
861
  except Exception as e:
@@ -804,13 +875,13 @@ class mexc(mexcAsync):
804
875
  if isinstance(bidask, list):
805
876
  bookside.storeArray(bidask)
806
877
  else:
807
- price = self.safe_float(bidask, 'p')
808
- amount = self.safe_float(bidask, 'v')
878
+ price = self.safe_float_2(bidask, 'p', 'price')
879
+ amount = self.safe_float_2(bidask, 'v', 'quantity')
809
880
  bookside.store(price, amount)
810
881
 
811
882
  def handle_delta(self, orderbook, delta):
812
883
  existingNonce = self.safe_integer(orderbook, 'nonce')
813
- deltaNonce = self.safe_integer_2(delta, 'r', 'version')
884
+ deltaNonce = self.safe_integer_n(delta, ['r', 'version', 'fromVersion'])
814
885
  if deltaNonce < existingNonce:
815
886
  # even when doing < comparison, self happens: https://app.travis-ci.com/github/ccxt/ccxt/builds/269234741#L1809
816
887
  # so, we just skip old updates
@@ -826,7 +897,7 @@ class mexc(mexcAsync):
826
897
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
827
898
  """
828
899
 
829
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#trade-streams
900
+ https://www.mexc.com/api-docs/spot-v3/websocket-market-streams#trade-streams
830
901
  https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
831
902
 
832
903
  get the list of most recent trades for a particular symbol
@@ -842,7 +913,7 @@ class mexc(mexcAsync):
842
913
  messageHash = 'trades:' + symbol
843
914
  trades = None
844
915
  if market['spot']:
845
- channel = 'spot@public.deals.v3.api@' + market['id']
916
+ channel = 'spot@public.aggre.deals.v3.api.pb@100ms@' + market['id']
846
917
  trades = await self.watch_spot_public(channel, messageHash, params)
847
918
  else:
848
919
  channel = 'sub.deal'
@@ -855,6 +926,23 @@ class mexc(mexcAsync):
855
926
  return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
856
927
 
857
928
  def handle_trades(self, client: Client, message):
929
+ # protobuf
930
+ # {
931
+ # "channel": "spot@public.aggre.deals.v3.api.pb@100ms@BTCUSDT",
932
+ # "publicdeals": {
933
+ # "dealsList": [
934
+ # {
935
+ # "price": "93220.00", # Trade price
936
+ # "quantity": "0.04438243", # Trade quantity
937
+ # "tradetype": 2, # Trade type(1: Buy, 2: Sell)
938
+ # "time": 1736409765051 # Trade time
939
+ # }
940
+ # ],
941
+ # "eventtype": "spot@public.aggre.deals.v3.api.pb@100ms" # Event type
942
+ # },
943
+ # "symbol": "BTCUSDT", # Trading pair
944
+ # "sendtime": 1736409765052 # Event time
945
+ # }
858
946
  #
859
947
  # {
860
948
  # "c": "spot@public.deals.v3.api@BTCUSDT",
@@ -895,8 +983,8 @@ class mexc(mexcAsync):
895
983
  limit = self.safe_integer(self.options, 'tradesLimit', 1000)
896
984
  stored = ArrayCache(limit)
897
985
  self.trades[symbol] = stored
898
- d = self.safe_value_2(message, 'd', 'data')
899
- trades = self.safe_value(d, 'deals', [d])
986
+ d = self.safe_dict_n(message, ['d', 'data', 'publicAggreDeals'])
987
+ trades = self.safe_list_2(d, 'deals', 'dealsList', [d])
900
988
  for j in range(0, len(trades)):
901
989
  parsedTrade = None
902
990
  if market['spot']:
@@ -909,7 +997,7 @@ class mexc(mexcAsync):
909
997
  async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
910
998
  """
911
999
 
912
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#spot-account-deals
1000
+ https://www.mexc.com/api-docs/spot-v3/websocket-user-data-streams#spot-account-deals
913
1001
  https://mexcdevelop.github.io/apidocs/contract_v1_en/#private-channels
914
1002
 
915
1003
  watches information on multiple trades made by the user
@@ -930,7 +1018,7 @@ class mexc(mexcAsync):
930
1018
  type, params = self.handle_market_type_and_params('watchMyTrades', market, params)
931
1019
  trades = None
932
1020
  if type == 'spot':
933
- channel = 'spot@private.deals.v3.api'
1021
+ channel = 'spot@private.deals.v3.api.pb'
934
1022
  trades = await self.watch_spot_private(channel, messageHash, params)
935
1023
  else:
936
1024
  trades = await self.watch_swap_private(messageHash, params)
@@ -956,11 +1044,27 @@ class mexc(mexcAsync):
956
1044
  # "s": "BTCUSDT",
957
1045
  # "t": 1678670940700
958
1046
  # }
1047
+ # {
1048
+ # channel: "spot@private.deals.v3.api.pb",
1049
+ # symbol: "MXUSDT",
1050
+ # sendTime: 1736417034332,
1051
+ # privateDeals {
1052
+ # price: "3.6962",
1053
+ # quantity: "1",
1054
+ # amount: "3.6962",
1055
+ # tradeType: 2,
1056
+ # tradeId: "505979017439002624X1",
1057
+ # orderId: "C02__505979017439002624115",
1058
+ # feeAmount: "0.0003998377369698171",
1059
+ # feeCurrency: "MX",
1060
+ # time: 1736417034280
1061
+ # }
1062
+ # }
959
1063
  #
960
1064
  messageHash = 'myTrades'
961
- data = self.safe_value_2(message, 'd', 'data')
1065
+ data = self.safe_dict_n(message, ['d', 'data', 'privateDeals'])
962
1066
  futuresMarketId = self.safe_string(data, 'symbol')
963
- marketId = self.safe_string(message, 's', futuresMarketId)
1067
+ marketId = self.safe_string_2(message, 's', 'symbol', futuresMarketId)
964
1068
  market = self.safe_market(marketId)
965
1069
  symbol = market['symbol']
966
1070
  trade = None
@@ -980,7 +1084,7 @@ class mexc(mexcAsync):
980
1084
 
981
1085
  def parse_ws_trade(self, trade, market=None):
982
1086
  #
983
- # public trade
1087
+ # public trade(protobuf)
984
1088
  # {
985
1089
  # "p": "20382.70",
986
1090
  # "v": "0.043800",
@@ -1000,7 +1104,6 @@ class mexc(mexcAsync):
1000
1104
  # "v": "5"
1001
1105
  # }
1002
1106
  #
1003
- #
1004
1107
  # d: {
1005
1108
  # p: '1.0005',
1006
1109
  # v: '5.71',
@@ -1015,22 +1118,36 @@ class mexc(mexcAsync):
1015
1118
  # n: '0.005712855',
1016
1119
  # N: 'USDT'
1017
1120
  # }
1018
- timestamp = self.safe_integer(trade, 'T')
1019
- tradeId = self.safe_string(trade, 't')
1121
+ # protobuf
1122
+ #
1123
+ # {
1124
+ # price: "3.6962",
1125
+ # quantity: "1",
1126
+ # amount: "3.6962",
1127
+ # tradeType: 2,
1128
+ # tradeId: "505979017439002624X1",
1129
+ # orderId: "C02__505979017439002624115",
1130
+ # feeAmount: "0.0003998377369698171",
1131
+ # feeCurrency: "MX",
1132
+ # time: 1736417034280
1133
+ # }
1134
+ #
1135
+ timestamp = self.safe_integer_2(trade, 'T', 'time')
1136
+ tradeId = self.safe_string_2(trade, 't', 'tradeId')
1020
1137
  if timestamp is None:
1021
1138
  timestamp = self.safe_integer(trade, 't')
1022
1139
  tradeId = None
1023
- priceString = self.safe_string(trade, 'p')
1024
- amountString = self.safe_string(trade, 'v')
1025
- rawSide = self.safe_string(trade, 'S')
1140
+ priceString = self.safe_string_2(trade, 'p', 'price')
1141
+ amountString = self.safe_string_2(trade, 'v', 'quantity')
1142
+ rawSide = self.safe_string_2(trade, 'S', 'tradeType')
1026
1143
  side = 'buy' if (rawSide == '1') else 'sell'
1027
1144
  isMaker = self.safe_integer(trade, 'm')
1028
- feeAmount = self.safe_number(trade, 'n')
1029
- feeCurrencyId = self.safe_string(trade, 'N')
1145
+ feeAmount = self.safe_string_2(trade, 'n', 'feeAmount')
1146
+ feeCurrencyId = self.safe_string_2(trade, 'N', 'feeCurrency')
1030
1147
  return self.safe_trade({
1031
1148
  'info': trade,
1032
1149
  'id': tradeId,
1033
- 'order': self.safe_string(trade, 'i'),
1150
+ 'order': self.safe_string_2(trade, 'i', 'orderId'),
1034
1151
  'timestamp': timestamp,
1035
1152
  'datetime': self.iso8601(timestamp),
1036
1153
  'symbol': self.safe_symbol(None, market),
@@ -1039,7 +1156,7 @@ class mexc(mexcAsync):
1039
1156
  'takerOrMaker': 'maker' if (isMaker) else 'taker',
1040
1157
  'price': priceString,
1041
1158
  'amount': amountString,
1042
- 'cost': None,
1159
+ 'cost': self.safe_string(trade, 'amount'),
1043
1160
  'fee': {
1044
1161
  'cost': feeAmount,
1045
1162
  'currency': self.safe_currency_code(feeCurrencyId),
@@ -1049,7 +1166,7 @@ class mexc(mexcAsync):
1049
1166
  async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1050
1167
  """
1051
1168
 
1052
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#spot-account-orders
1169
+ https://www.mexc.com/api-docs/spot-v3/websocket-user-data-streams#spot-account-orders
1053
1170
  https://mexcdevelop.github.io/apidocs/spot_v3_en/#margin-account-orders
1054
1171
 
1055
1172
  watches information on multiple orders made by the user
@@ -1071,7 +1188,7 @@ class mexc(mexcAsync):
1071
1188
  type, params = self.handle_market_type_and_params('watchOrders', market, params)
1072
1189
  orders = None
1073
1190
  if type == 'spot':
1074
- channel = type + '@private.orders.v3.api'
1191
+ channel = 'spot@private.orders.v3.api.pb'
1075
1192
  orders = await self.watch_spot_private(channel, messageHash, params)
1076
1193
  else:
1077
1194
  orders = await self.watch_swap_private(messageHash, params)
@@ -1144,11 +1261,18 @@ class mexc(mexcAsync):
1144
1261
  # "s": "MXUSDT",
1145
1262
  # "t":1661938138193
1146
1263
  # }
1264
+ # protobuf
1265
+ # {
1266
+ # channel: "spot@private.orders.v3.api.pb",
1267
+ # symbol: "MXUSDT",
1268
+ # sendTime: 1736417034281,
1269
+ # privateOrders {}
1270
+ # }
1147
1271
  #
1148
1272
  messageHash = 'orders'
1149
- data = self.safe_value_2(message, 'd', 'data')
1273
+ data = self.safe_dict_n(message, ['d', 'data', 'privateOrders'])
1150
1274
  futuresMarketId = self.safe_string(data, 'symbol')
1151
- marketId = self.safe_string(message, 's', futuresMarketId)
1275
+ marketId = self.safe_string_2(message, 's', 'symbol', futuresMarketId)
1152
1276
  market = self.safe_market(marketId)
1153
1277
  symbol = market['symbol']
1154
1278
  parsed = None
@@ -1216,11 +1340,28 @@ class mexc(mexcAsync):
1216
1340
  # "s":1,
1217
1341
  # "i":"e03a5c7441e44ed899466a7140b71391",
1218
1342
  # }
1343
+ # protofbuf spot order
1344
+ # {
1345
+ # "id":"C02__583905164440776704043",
1346
+ # "price":"0.001053",
1347
+ # "quantity":"2000",
1348
+ # "amount":"0",
1349
+ # "avgPrice":"0.001007",
1350
+ # "orderType":5,
1351
+ # "tradeType":1,
1352
+ # "remainAmount":"0.092",
1353
+ # "remainQuantity":"0",
1354
+ # "lastDealQuantity":"2000",
1355
+ # "cumulativeQuantity":"2000",
1356
+ # "cumulativeAmount":"2.014",
1357
+ # "status":2,
1358
+ # "createTime":"1754996075502"
1359
+ # }
1219
1360
  #
1220
- timestamp = self.safe_integer(order, 'O')
1221
- side = self.safe_string(order, 'S')
1222
- status = self.safe_string(order, 's')
1223
- type = self.safe_string(order, 'o')
1361
+ timestamp = self.safe_integer(order, 'createTime')
1362
+ side = self.safe_string(order, 'tradeType')
1363
+ status = self.safe_string(order, 'status')
1364
+ type = self.safe_string(order, 'orderType')
1224
1365
  fee = None
1225
1366
  feeCurrency = self.safe_string(order, 'N')
1226
1367
  if feeCurrency is not None:
@@ -1229,8 +1370,8 @@ class mexc(mexcAsync):
1229
1370
  'cost': None,
1230
1371
  }
1231
1372
  return self.safe_order({
1232
- 'id': self.safe_string(order, 'i'),
1233
- 'clientOrderId': self.safe_string(order, 'c'),
1373
+ 'id': self.safe_string(order, 'id'),
1374
+ 'clientOrderId': self.safe_string(order, 'clientOrderId'),
1234
1375
  'timestamp': timestamp,
1235
1376
  'datetime': self.iso8601(timestamp),
1236
1377
  'lastTradeTimestamp': None,
@@ -1239,14 +1380,14 @@ class mexc(mexcAsync):
1239
1380
  'type': self.parse_ws_order_type(type),
1240
1381
  'timeInForce': self.parse_ws_time_in_force(type),
1241
1382
  'side': 'buy' if (side == '1') else 'sell',
1242
- 'price': self.safe_string(order, 'p'),
1383
+ 'price': self.safe_string(order, 'price'),
1243
1384
  'stopPrice': None,
1244
- 'triggerPrice': self.safe_number(order, 'P'),
1245
- 'average': self.safe_string(order, 'ap'),
1246
- 'amount': self.safe_string(order, 'v'),
1247
- 'cost': self.safe_string(order, 'a'),
1248
- 'filled': self.safe_string(order, 'cv'),
1249
- 'remaining': self.safe_string(order, 'V'),
1385
+ 'triggerPrice': None,
1386
+ 'average': self.safe_string(order, 'avgPrice'),
1387
+ 'amount': self.safe_string(order, 'quantity'),
1388
+ 'cost': self.safe_string(order, 'amount'),
1389
+ 'filled': self.safe_string(order, 'cumulativeQuantity'),
1390
+ 'remaining': self.safe_string(order, 'remainQuantity'),
1250
1391
  'fee': fee,
1251
1392
  'trades': None,
1252
1393
  'info': order,
@@ -1269,7 +1410,7 @@ class mexc(mexcAsync):
1269
1410
  def parse_ws_order_type(self, type):
1270
1411
  types: dict = {
1271
1412
  '1': 'limit', # LIMIT_ORDER
1272
- '2': None, # POST_ONLY
1413
+ '2': 'limit', # POST_ONLY
1273
1414
  '3': None, # IMMEDIATE_OR_CANCEL
1274
1415
  '4': None, # FILL_OR_KILL
1275
1416
  '5': 'market', # MARKET_ORDER
@@ -1291,7 +1432,7 @@ class mexc(mexcAsync):
1291
1432
  async def watch_balance(self, params={}) -> Balances:
1292
1433
  """
1293
1434
 
1294
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#spot-account-upadte
1435
+ https://www.mexc.com/api-docs/spot-v3/websocket-user-data-streams#spot-account-update
1295
1436
 
1296
1437
  watch balance and get the amount of funds available for trading or funds locked in orders
1297
1438
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -1302,7 +1443,7 @@ class mexc(mexcAsync):
1302
1443
  type, params = self.handle_market_type_and_params('watchBalance', None, params)
1303
1444
  messageHash = 'balance:' + type
1304
1445
  if type == 'spot':
1305
- channel = 'spot@private.account.v3.api'
1446
+ channel = 'spot@private.account.v3.api.pb'
1306
1447
  return await self.watch_spot_private(channel, messageHash, params)
1307
1448
  else:
1308
1449
  return await self.watch_swap_private(messageHash, params)
@@ -1339,22 +1480,22 @@ class mexc(mexcAsync):
1339
1480
  # "ts": 1680059188190
1340
1481
  # }
1341
1482
  #
1342
- c = self.safe_string(message, 'c')
1483
+ c = self.safe_string_2(message, 'c', 'channel')
1343
1484
  type = 'swap' if (c is None) else 'spot'
1344
1485
  messageHash = 'balance:' + type
1345
- data = self.safe_value_2(message, 'd', 'data')
1486
+ data = self.safe_dict_n(message, ['d', 'data', 'privateAccount'])
1346
1487
  futuresTimestamp = self.safe_integer(message, 'ts')
1347
- timestamp = self.safe_integer(data, 'c', futuresTimestamp)
1488
+ timestamp = self.safe_integer_2(data, 'c', 'time', futuresTimestamp)
1348
1489
  if not (type in self.balance):
1349
1490
  self.balance[type] = {}
1350
1491
  self.balance[type]['info'] = data
1351
1492
  self.balance[type]['timestamp'] = timestamp
1352
1493
  self.balance[type]['datetime'] = self.iso8601(timestamp)
1353
- currencyId = self.safe_string_2(data, 'a', 'currency')
1494
+ currencyId = self.safe_string_n(data, ['a', 'currency', 'vcoinName'])
1354
1495
  code = self.safe_currency_code(currencyId)
1355
1496
  account = self.account()
1356
- account['free'] = self.safe_string_2(data, 'f', 'availableBalance')
1357
- account['used'] = self.safe_string_2(data, 'l', 'frozenBalance')
1497
+ account['total'] = self.safe_string_n(data, ['f', 'availableBalance', 'balanceAmount'])
1498
+ account['used'] = self.safe_string_n(data, ['l', 'frozenBalance', 'frozenAmount'])
1358
1499
  self.balance[type][code] = account
1359
1500
  self.balance[type] = self.safe_balance(self.balance[type])
1360
1501
  client.resolve(self.balance[type], messageHash)
@@ -1372,22 +1513,17 @@ class mexc(mexcAsync):
1372
1513
  url = None
1373
1514
  channel = None
1374
1515
  if market['spot']:
1375
- miniTicker = False
1376
- miniTicker, params = self.handle_option_and_params(params, 'watchTicker', 'miniTicker')
1377
- if miniTicker:
1378
- channel = 'spot@public.miniTicker.v3.api@' + market['id'] + '@UTC+8'
1379
- else:
1380
- channel = 'spot@public.bookTicker.v3.api@' + market['id']
1516
+ channel = 'spot@public.aggre.bookTicker.v3.api.pb@100ms@' + market['id']
1381
1517
  url = self.urls['api']['ws']['spot']
1382
1518
  params['unsubscribed'] = True
1383
- await self.watch_spot_public(channel, messageHash, params)
1519
+ self.watch_spot_public(channel, messageHash, params)
1384
1520
  else:
1385
1521
  channel = 'unsub.ticker'
1386
1522
  requestParams: dict = {
1387
1523
  'symbol': market['id'],
1388
1524
  }
1389
1525
  url = self.urls['api']['ws']['swap']
1390
- await self.watch_swap_public(channel, messageHash, requestParams, params)
1526
+ self.watch_swap_public(channel, messageHash, requestParams, params)
1391
1527
  client = self.client(url)
1392
1528
  self.handle_unsubscriptions(client, [messageHash])
1393
1529
  return None
@@ -1412,33 +1548,39 @@ class mexc(mexcAsync):
1412
1548
  url = self.urls['api']['ws']['spot'] if (isSpot) else self.urls['api']['ws']['swap']
1413
1549
  request: dict = {}
1414
1550
  if isSpot:
1415
- miniTicker = False
1416
- miniTicker, params = self.handle_option_and_params(params, 'watchTickers', 'miniTicker')
1417
- topics = []
1418
- if not miniTicker:
1419
- if symbols is None:
1420
- raise ArgumentsRequired(self.id + ' watchTickers required symbols argument for the bookTicker channel')
1421
- marketIds = self.market_ids(symbols)
1422
- for i in range(0, len(marketIds)):
1423
- marketId = marketIds[i]
1424
- messageHashes.append('unsubscribe:ticker:' + symbols[i])
1425
- channel = 'spot@public.bookTicker.v3.api@' + marketId
1426
- topics.append(channel)
1427
- else:
1428
- topics.append('spot@public.miniTickers.v3.api@UTC+8')
1429
- if symbols is None:
1430
- messageHashes.append('unsubscribe:spot:ticker')
1431
- else:
1432
- for i in range(0, len(symbols)):
1433
- messageHashes.append('unsubscribe:ticker:' + symbols[i])
1434
- request['method'] = 'UNSUBSCRIPTION'
1435
- request['params'] = topics
1551
+ raise NotSupported(self.id + ' watchTickers does not support spot markets')
1552
+ # miniTicker = False
1553
+ # miniTicker, params = self.handle_option_and_params(params, 'watchTickers', 'miniTicker')
1554
+ # topics = []
1555
+ # if not miniTicker:
1556
+ # if symbols is None:
1557
+ # raise ArgumentsRequired(self.id + ' watchTickers required symbols argument for the bookTicker channel')
1558
+ # }
1559
+ # marketIds = self.market_ids(symbols)
1560
+ # for i in range(0, len(marketIds)):
1561
+ # marketId = marketIds[i]
1562
+ # messageHashes.append('unsubscribe:ticker:' + symbols[i])
1563
+ # channel = 'spot@public.bookTicker.v3.api@' + marketId
1564
+ # topics.append(channel)
1565
+ # }
1566
+ # else:
1567
+ # topics.append('spot@public.miniTickers.v3.api@UTC+8')
1568
+ # if symbols is None:
1569
+ # messageHashes.append('unsubscribe:spot:ticker')
1570
+ # else:
1571
+ # for i in range(0, len(symbols)):
1572
+ # messageHashes.append('unsubscribe:ticker:' + symbols[i])
1573
+ # }
1574
+ # }
1575
+ # }
1576
+ # request['method'] = 'UNSUBSCRIPTION'
1577
+ # request['params'] = topics
1436
1578
  else:
1437
1579
  request['method'] = 'unsub.tickers'
1438
1580
  request['params'] = {}
1439
1581
  messageHashes.append('unsubscribe:ticker')
1440
1582
  client = self.client(url)
1441
- await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes)
1583
+ self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes)
1442
1584
  self.handle_unsubscriptions(client, messageHashes)
1443
1585
  return None
1444
1586
 
@@ -1464,7 +1606,7 @@ class mexc(mexcAsync):
1464
1606
  for i in range(0, len(symbols)):
1465
1607
  if isSpot:
1466
1608
  market = self.market(symbols[i])
1467
- topics.append('spot@public.bookTicker.v3.api@' + market['id'])
1609
+ topics.append('spot@public.aggre.bookTicker.v3.api.pb@100ms@' + market['id'])
1468
1610
  messageHashes.append('unsubscribe:bidask:' + symbols[i])
1469
1611
  url = self.urls['api']['ws']['spot']
1470
1612
  request: dict = {
@@ -1472,7 +1614,7 @@ class mexc(mexcAsync):
1472
1614
  'params': topics,
1473
1615
  }
1474
1616
  client = self.client(url)
1475
- await self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes)
1617
+ self.watch_multiple(url, messageHashes, self.extend(request, params), messageHashes)
1476
1618
  self.handle_unsubscriptions(client, messageHashes)
1477
1619
  return None
1478
1620
 
@@ -1494,9 +1636,9 @@ class mexc(mexcAsync):
1494
1636
  url = None
1495
1637
  if market['spot']:
1496
1638
  url = self.urls['api']['ws']['spot']
1497
- channel = 'spot@public.kline.v3.api@' + market['id'] + '@' + timeframeId
1639
+ channel = 'spot@public.kline.v3.api.pb@' + market['id'] + '@' + timeframeId
1498
1640
  params['unsubscribed'] = True
1499
- await self.watch_spot_public(channel, messageHash, params)
1641
+ self.watch_spot_public(channel, messageHash, params)
1500
1642
  else:
1501
1643
  url = self.urls['api']['ws']['swap']
1502
1644
  channel = 'unsub.kline'
@@ -1504,7 +1646,7 @@ class mexc(mexcAsync):
1504
1646
  'symbol': market['id'],
1505
1647
  'interval': timeframeId,
1506
1648
  }
1507
- await self.watch_swap_public(channel, messageHash, requestParams, params)
1649
+ self.watch_swap_public(channel, messageHash, requestParams, params)
1508
1650
  client = self.client(url)
1509
1651
  self.handle_unsubscriptions(client, [messageHash])
1510
1652
  return None
@@ -1514,6 +1656,7 @@ class mexc(mexcAsync):
1514
1656
  unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
1515
1657
  :param str symbol: unified array of symbols
1516
1658
  :param dict [params]: extra parameters specific to the exchange API endpoint
1659
+ :param str [params.frequency]: the frequency of the order book updates, default is '10ms', can be '100ms' or '10ms
1517
1660
  :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
1518
1661
  """
1519
1662
  await self.load_markets()
@@ -1523,16 +1666,18 @@ class mexc(mexcAsync):
1523
1666
  url = None
1524
1667
  if market['spot']:
1525
1668
  url = self.urls['api']['ws']['spot']
1526
- channel = 'spot@public.increase.depth.v3.api@' + market['id']
1669
+ frequency = None
1670
+ frequency, params = self.handle_option_and_params(params, 'watchOrderBook', 'frequency', '100ms')
1671
+ channel = 'spot@public.aggre.depth.v3.api.pb@' + frequency + '@' + market['id']
1527
1672
  params['unsubscribed'] = True
1528
- await self.watch_spot_public(channel, messageHash, params)
1673
+ self.watch_spot_public(channel, messageHash, params)
1529
1674
  else:
1530
1675
  url = self.urls['api']['ws']['swap']
1531
1676
  channel = 'unsub.depth'
1532
1677
  requestParams: dict = {
1533
1678
  'symbol': market['id'],
1534
1679
  }
1535
- await self.watch_swap_public(channel, messageHash, requestParams, params)
1680
+ self.watch_swap_public(channel, messageHash, requestParams, params)
1536
1681
  client = self.client(url)
1537
1682
  self.handle_unsubscriptions(client, [messageHash])
1538
1683
  return None
@@ -1552,16 +1697,16 @@ class mexc(mexcAsync):
1552
1697
  url = None
1553
1698
  if market['spot']:
1554
1699
  url = self.urls['api']['ws']['spot']
1555
- channel = 'spot@public.deals.v3.api@' + market['id']
1700
+ channel = 'spot@public.aggre.deals.v3.api.pb@100ms@' + market['id']
1556
1701
  params['unsubscribed'] = True
1557
- await self.watch_spot_public(channel, messageHash, params)
1702
+ self.watch_spot_public(channel, messageHash, params)
1558
1703
  else:
1559
1704
  url = self.urls['api']['ws']['swap']
1560
1705
  channel = 'unsub.deal'
1561
1706
  requestParams: dict = {
1562
1707
  'symbol': market['id'],
1563
1708
  }
1564
- await self.watch_swap_public(channel, messageHash, requestParams, params)
1709
+ self.watch_swap_public(channel, messageHash, requestParams, params)
1565
1710
  client = self.client(url)
1566
1711
  self.handle_unsubscriptions(client, [messageHash])
1567
1712
  return None
@@ -1654,16 +1799,59 @@ class mexc(mexcAsync):
1654
1799
  channel = self.safe_string(parts, 1)
1655
1800
  methods: dict = {
1656
1801
  'public.increase.depth.v3.api': self.handle_order_book_subscription,
1802
+ 'public.aggre.depth.v3.api.pb': self.handle_order_book_subscription,
1657
1803
  }
1658
1804
  method = self.safe_value(methods, channel)
1659
1805
  if method is not None:
1660
1806
  method(client, message)
1661
1807
 
1808
+ def handle_protobuf_message(self, client: Client, message):
1809
+ # protobuf message decoded
1810
+ # {
1811
+ # "channel":"spot@public.kline.v3.api.pb@BTCUSDT@Min1",
1812
+ # "symbol":"BTCUSDT",
1813
+ # "symbolId":"2fb942154ef44a4ab2ef98c8afb6a4a7",
1814
+ # "createTime":"1754737941062",
1815
+ # "publicSpotKline":{
1816
+ # "interval":"Min1",
1817
+ # "windowStart":"1754737920",
1818
+ # "openingPrice":"117317.31",
1819
+ # "closingPrice":"117325.26",
1820
+ # "highestPrice":"117341",
1821
+ # "lowestPrice":"117317.3",
1822
+ # "volume":"3.12599854",
1823
+ # "amount":"366804.43",
1824
+ # "windowEnd":"1754737980"
1825
+ # }
1826
+ # }
1827
+ channel = self.safe_string(message, 'channel')
1828
+ channelParts = channel.split('@')
1829
+ channelId = self.safe_string(channelParts, 1)
1830
+ if channelId == 'public.kline.v3.api.pb':
1831
+ self.handle_ohlcv(client, message)
1832
+ elif channelId == 'public.aggre.deals.v3.api.pb':
1833
+ self.handle_trades(client, message)
1834
+ elif channelId == 'public.aggre.bookTicker.v3.api.pb':
1835
+ self.handle_ticker(client, message)
1836
+ elif channelId == 'public.aggre.depth.v3.api.pb':
1837
+ self.handle_order_book(client, message)
1838
+ elif channelId == 'private.account.v3.api.pb':
1839
+ self.handle_balance(client, message)
1840
+ elif channelId == 'private.deals.v3.api.pb':
1841
+ self.handle_my_trade(client, message)
1842
+ elif channelId == 'private.orders.v3.api.pb':
1843
+ self.handle_order(client, message)
1844
+ return True
1845
+
1662
1846
  def handle_message(self, client: Client, message):
1663
1847
  if isinstance(message, str):
1664
1848
  if message == 'Invalid listen key':
1665
1849
  error = AuthenticationError(self.id + ' invalid listen key')
1666
1850
  client.reject(error)
1851
+ return
1852
+ if self.is_binary_message(message):
1853
+ message = self.decode_proto_msg(message)
1854
+ self.handle_protobuf_message(client, message)
1667
1855
  return
1668
1856
  if 'msg' in message:
1669
1857
  self.handle_subscription_status(client, message)