mexc-exchange-api 0.0.80__py3-none-any.whl → 0.0.82__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,9 +855,10 @@ 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)
861
+ storedOrderBook['nonce'] = self.safe_integer(data, 'fromVersion')
790
862
  except Exception as e:
791
863
  del client.subscriptions[messageHash]
792
864
  client.reject(e, messageHash)
@@ -804,13 +876,13 @@ class mexc(mexcAsync):
804
876
  if isinstance(bidask, list):
805
877
  bookside.storeArray(bidask)
806
878
  else:
807
- price = self.safe_float(bidask, 'p')
808
- amount = self.safe_float(bidask, 'v')
879
+ price = self.safe_float_2(bidask, 'p', 'price')
880
+ amount = self.safe_float_2(bidask, 'v', 'quantity')
809
881
  bookside.store(price, amount)
810
882
 
811
883
  def handle_delta(self, orderbook, delta):
812
884
  existingNonce = self.safe_integer(orderbook, 'nonce')
813
- deltaNonce = self.safe_integer_2(delta, 'r', 'version')
885
+ deltaNonce = self.safe_integer_n(delta, ['r', 'version', 'fromVersion'])
814
886
  if deltaNonce < existingNonce:
815
887
  # even when doing < comparison, self happens: https://app.travis-ci.com/github/ccxt/ccxt/builds/269234741#L1809
816
888
  # so, we just skip old updates
@@ -826,7 +898,7 @@ class mexc(mexcAsync):
826
898
  async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
827
899
  """
828
900
 
829
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#trade-streams
901
+ https://www.mexc.com/api-docs/spot-v3/websocket-market-streams#trade-streams
830
902
  https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
831
903
 
832
904
  get the list of most recent trades for a particular symbol
@@ -842,7 +914,7 @@ class mexc(mexcAsync):
842
914
  messageHash = 'trades:' + symbol
843
915
  trades = None
844
916
  if market['spot']:
845
- channel = 'spot@public.deals.v3.api@' + market['id']
917
+ channel = 'spot@public.aggre.deals.v3.api.pb@100ms@' + market['id']
846
918
  trades = await self.watch_spot_public(channel, messageHash, params)
847
919
  else:
848
920
  channel = 'sub.deal'
@@ -855,6 +927,23 @@ class mexc(mexcAsync):
855
927
  return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
856
928
 
857
929
  def handle_trades(self, client: Client, message):
930
+ # protobuf
931
+ # {
932
+ # "channel": "spot@public.aggre.deals.v3.api.pb@100ms@BTCUSDT",
933
+ # "publicdeals": {
934
+ # "dealsList": [
935
+ # {
936
+ # "price": "93220.00", # Trade price
937
+ # "quantity": "0.04438243", # Trade quantity
938
+ # "tradetype": 2, # Trade type(1: Buy, 2: Sell)
939
+ # "time": 1736409765051 # Trade time
940
+ # }
941
+ # ],
942
+ # "eventtype": "spot@public.aggre.deals.v3.api.pb@100ms" # Event type
943
+ # },
944
+ # "symbol": "BTCUSDT", # Trading pair
945
+ # "sendtime": 1736409765052 # Event time
946
+ # }
858
947
  #
859
948
  # {
860
949
  # "c": "spot@public.deals.v3.api@BTCUSDT",
@@ -895,8 +984,8 @@ class mexc(mexcAsync):
895
984
  limit = self.safe_integer(self.options, 'tradesLimit', 1000)
896
985
  stored = ArrayCache(limit)
897
986
  self.trades[symbol] = stored
898
- d = self.safe_value_2(message, 'd', 'data')
899
- trades = self.safe_value(d, 'deals', [d])
987
+ d = self.safe_dict_n(message, ['d', 'data', 'publicAggreDeals'])
988
+ trades = self.safe_list_2(d, 'deals', 'dealsList', [d])
900
989
  for j in range(0, len(trades)):
901
990
  parsedTrade = None
902
991
  if market['spot']:
@@ -909,7 +998,7 @@ class mexc(mexcAsync):
909
998
  async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
910
999
  """
911
1000
 
912
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#spot-account-deals
1001
+ https://www.mexc.com/api-docs/spot-v3/websocket-user-data-streams#spot-account-deals
913
1002
  https://mexcdevelop.github.io/apidocs/contract_v1_en/#private-channels
914
1003
 
915
1004
  watches information on multiple trades made by the user
@@ -930,7 +1019,7 @@ class mexc(mexcAsync):
930
1019
  type, params = self.handle_market_type_and_params('watchMyTrades', market, params)
931
1020
  trades = None
932
1021
  if type == 'spot':
933
- channel = 'spot@private.deals.v3.api'
1022
+ channel = 'spot@private.deals.v3.api.pb'
934
1023
  trades = await self.watch_spot_private(channel, messageHash, params)
935
1024
  else:
936
1025
  trades = await self.watch_swap_private(messageHash, params)
@@ -956,11 +1045,27 @@ class mexc(mexcAsync):
956
1045
  # "s": "BTCUSDT",
957
1046
  # "t": 1678670940700
958
1047
  # }
1048
+ # {
1049
+ # channel: "spot@private.deals.v3.api.pb",
1050
+ # symbol: "MXUSDT",
1051
+ # sendTime: 1736417034332,
1052
+ # privateDeals {
1053
+ # price: "3.6962",
1054
+ # quantity: "1",
1055
+ # amount: "3.6962",
1056
+ # tradeType: 2,
1057
+ # tradeId: "505979017439002624X1",
1058
+ # orderId: "C02__505979017439002624115",
1059
+ # feeAmount: "0.0003998377369698171",
1060
+ # feeCurrency: "MX",
1061
+ # time: 1736417034280
1062
+ # }
1063
+ # }
959
1064
  #
960
1065
  messageHash = 'myTrades'
961
- data = self.safe_value_2(message, 'd', 'data')
1066
+ data = self.safe_dict_n(message, ['d', 'data', 'privateDeals'])
962
1067
  futuresMarketId = self.safe_string(data, 'symbol')
963
- marketId = self.safe_string(message, 's', futuresMarketId)
1068
+ marketId = self.safe_string_2(message, 's', 'symbol', futuresMarketId)
964
1069
  market = self.safe_market(marketId)
965
1070
  symbol = market['symbol']
966
1071
  trade = None
@@ -980,7 +1085,7 @@ class mexc(mexcAsync):
980
1085
 
981
1086
  def parse_ws_trade(self, trade, market=None):
982
1087
  #
983
- # public trade
1088
+ # public trade(protobuf)
984
1089
  # {
985
1090
  # "p": "20382.70",
986
1091
  # "v": "0.043800",
@@ -1000,7 +1105,6 @@ class mexc(mexcAsync):
1000
1105
  # "v": "5"
1001
1106
  # }
1002
1107
  #
1003
- #
1004
1108
  # d: {
1005
1109
  # p: '1.0005',
1006
1110
  # v: '5.71',
@@ -1015,22 +1119,36 @@ class mexc(mexcAsync):
1015
1119
  # n: '0.005712855',
1016
1120
  # N: 'USDT'
1017
1121
  # }
1018
- timestamp = self.safe_integer(trade, 'T')
1019
- tradeId = self.safe_string(trade, 't')
1122
+ # protobuf
1123
+ #
1124
+ # {
1125
+ # price: "3.6962",
1126
+ # quantity: "1",
1127
+ # amount: "3.6962",
1128
+ # tradeType: 2,
1129
+ # tradeId: "505979017439002624X1",
1130
+ # orderId: "C02__505979017439002624115",
1131
+ # feeAmount: "0.0003998377369698171",
1132
+ # feeCurrency: "MX",
1133
+ # time: 1736417034280
1134
+ # }
1135
+ #
1136
+ timestamp = self.safe_integer_2(trade, 'T', 'time')
1137
+ tradeId = self.safe_string_2(trade, 't', 'tradeId')
1020
1138
  if timestamp is None:
1021
1139
  timestamp = self.safe_integer(trade, 't')
1022
1140
  tradeId = None
1023
- priceString = self.safe_string(trade, 'p')
1024
- amountString = self.safe_string(trade, 'v')
1025
- rawSide = self.safe_string(trade, 'S')
1141
+ priceString = self.safe_string_2(trade, 'p', 'price')
1142
+ amountString = self.safe_string_2(trade, 'v', 'quantity')
1143
+ rawSide = self.safe_string_2(trade, 'S', 'tradeType')
1026
1144
  side = 'buy' if (rawSide == '1') else 'sell'
1027
1145
  isMaker = self.safe_integer(trade, 'm')
1028
- feeAmount = self.safe_number(trade, 'n')
1029
- feeCurrencyId = self.safe_string(trade, 'N')
1146
+ feeAmount = self.safe_string_2(trade, 'n', 'feeAmount')
1147
+ feeCurrencyId = self.safe_string_2(trade, 'N', 'feeCurrency')
1030
1148
  return self.safe_trade({
1031
1149
  'info': trade,
1032
1150
  'id': tradeId,
1033
- 'order': self.safe_string(trade, 'i'),
1151
+ 'order': self.safe_string_2(trade, 'i', 'orderId'),
1034
1152
  'timestamp': timestamp,
1035
1153
  'datetime': self.iso8601(timestamp),
1036
1154
  'symbol': self.safe_symbol(None, market),
@@ -1039,7 +1157,7 @@ class mexc(mexcAsync):
1039
1157
  'takerOrMaker': 'maker' if (isMaker) else 'taker',
1040
1158
  'price': priceString,
1041
1159
  'amount': amountString,
1042
- 'cost': None,
1160
+ 'cost': self.safe_string(trade, 'amount'),
1043
1161
  'fee': {
1044
1162
  'cost': feeAmount,
1045
1163
  'currency': self.safe_currency_code(feeCurrencyId),
@@ -1049,7 +1167,7 @@ class mexc(mexcAsync):
1049
1167
  async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1050
1168
  """
1051
1169
 
1052
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#spot-account-orders
1170
+ https://www.mexc.com/api-docs/spot-v3/websocket-user-data-streams#spot-account-orders
1053
1171
  https://mexcdevelop.github.io/apidocs/spot_v3_en/#margin-account-orders
1054
1172
 
1055
1173
  watches information on multiple orders made by the user
@@ -1071,7 +1189,7 @@ class mexc(mexcAsync):
1071
1189
  type, params = self.handle_market_type_and_params('watchOrders', market, params)
1072
1190
  orders = None
1073
1191
  if type == 'spot':
1074
- channel = type + '@private.orders.v3.api'
1192
+ channel = 'spot@private.orders.v3.api.pb'
1075
1193
  orders = await self.watch_spot_private(channel, messageHash, params)
1076
1194
  else:
1077
1195
  orders = await self.watch_swap_private(messageHash, params)
@@ -1144,11 +1262,18 @@ class mexc(mexcAsync):
1144
1262
  # "s": "MXUSDT",
1145
1263
  # "t":1661938138193
1146
1264
  # }
1265
+ # protobuf
1266
+ # {
1267
+ # channel: "spot@private.orders.v3.api.pb",
1268
+ # symbol: "MXUSDT",
1269
+ # sendTime: 1736417034281,
1270
+ # privateOrders {}
1271
+ # }
1147
1272
  #
1148
1273
  messageHash = 'orders'
1149
- data = self.safe_value_2(message, 'd', 'data')
1274
+ data = self.safe_dict_n(message, ['d', 'data', 'privateOrders'])
1150
1275
  futuresMarketId = self.safe_string(data, 'symbol')
1151
- marketId = self.safe_string(message, 's', futuresMarketId)
1276
+ marketId = self.safe_string_2(message, 's', 'symbol', futuresMarketId)
1152
1277
  market = self.safe_market(marketId)
1153
1278
  symbol = market['symbol']
1154
1279
  parsed = None
@@ -1216,11 +1341,28 @@ class mexc(mexcAsync):
1216
1341
  # "s":1,
1217
1342
  # "i":"e03a5c7441e44ed899466a7140b71391",
1218
1343
  # }
1344
+ # protofbuf spot order
1345
+ # {
1346
+ # "id":"C02__583905164440776704043",
1347
+ # "price":"0.001053",
1348
+ # "quantity":"2000",
1349
+ # "amount":"0",
1350
+ # "avgPrice":"0.001007",
1351
+ # "orderType":5,
1352
+ # "tradeType":1,
1353
+ # "remainAmount":"0.092",
1354
+ # "remainQuantity":"0",
1355
+ # "lastDealQuantity":"2000",
1356
+ # "cumulativeQuantity":"2000",
1357
+ # "cumulativeAmount":"2.014",
1358
+ # "status":2,
1359
+ # "createTime":"1754996075502"
1360
+ # }
1219
1361
  #
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')
1362
+ timestamp = self.safe_integer(order, 'createTime')
1363
+ side = self.safe_string(order, 'tradeType')
1364
+ status = self.safe_string(order, 'status')
1365
+ type = self.safe_string(order, 'orderType')
1224
1366
  fee = None
1225
1367
  feeCurrency = self.safe_string(order, 'N')
1226
1368
  if feeCurrency is not None:
@@ -1229,8 +1371,8 @@ class mexc(mexcAsync):
1229
1371
  'cost': None,
1230
1372
  }
1231
1373
  return self.safe_order({
1232
- 'id': self.safe_string(order, 'i'),
1233
- 'clientOrderId': self.safe_string(order, 'c'),
1374
+ 'id': self.safe_string(order, 'id'),
1375
+ 'clientOrderId': self.safe_string(order, 'clientOrderId'),
1234
1376
  'timestamp': timestamp,
1235
1377
  'datetime': self.iso8601(timestamp),
1236
1378
  'lastTradeTimestamp': None,
@@ -1239,14 +1381,14 @@ class mexc(mexcAsync):
1239
1381
  'type': self.parse_ws_order_type(type),
1240
1382
  'timeInForce': self.parse_ws_time_in_force(type),
1241
1383
  'side': 'buy' if (side == '1') else 'sell',
1242
- 'price': self.safe_string(order, 'p'),
1384
+ 'price': self.safe_string(order, 'price'),
1243
1385
  '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'),
1386
+ 'triggerPrice': None,
1387
+ 'average': self.safe_string(order, 'avgPrice'),
1388
+ 'amount': self.safe_string(order, 'quantity'),
1389
+ 'cost': self.safe_string(order, 'amount'),
1390
+ 'filled': self.safe_string(order, 'cumulativeQuantity'),
1391
+ 'remaining': self.safe_string(order, 'remainQuantity'),
1250
1392
  'fee': fee,
1251
1393
  'trades': None,
1252
1394
  'info': order,
@@ -1269,7 +1411,7 @@ class mexc(mexcAsync):
1269
1411
  def parse_ws_order_type(self, type):
1270
1412
  types: dict = {
1271
1413
  '1': 'limit', # LIMIT_ORDER
1272
- '2': None, # POST_ONLY
1414
+ '2': 'limit', # POST_ONLY
1273
1415
  '3': None, # IMMEDIATE_OR_CANCEL
1274
1416
  '4': None, # FILL_OR_KILL
1275
1417
  '5': 'market', # MARKET_ORDER
@@ -1291,7 +1433,7 @@ class mexc(mexcAsync):
1291
1433
  async def watch_balance(self, params={}) -> Balances:
1292
1434
  """
1293
1435
 
1294
- https://mexcdevelop.github.io/apidocs/spot_v3_en/#spot-account-upadte
1436
+ https://www.mexc.com/api-docs/spot-v3/websocket-user-data-streams#spot-account-update
1295
1437
 
1296
1438
  watch balance and get the amount of funds available for trading or funds locked in orders
1297
1439
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -1302,7 +1444,7 @@ class mexc(mexcAsync):
1302
1444
  type, params = self.handle_market_type_and_params('watchBalance', None, params)
1303
1445
  messageHash = 'balance:' + type
1304
1446
  if type == 'spot':
1305
- channel = 'spot@private.account.v3.api'
1447
+ channel = 'spot@private.account.v3.api.pb'
1306
1448
  return await self.watch_spot_private(channel, messageHash, params)
1307
1449
  else:
1308
1450
  return await self.watch_swap_private(messageHash, params)
@@ -1339,22 +1481,22 @@ class mexc(mexcAsync):
1339
1481
  # "ts": 1680059188190
1340
1482
  # }
1341
1483
  #
1342
- c = self.safe_string(message, 'c')
1484
+ c = self.safe_string_2(message, 'c', 'channel')
1343
1485
  type = 'swap' if (c is None) else 'spot'
1344
1486
  messageHash = 'balance:' + type
1345
- data = self.safe_value_2(message, 'd', 'data')
1487
+ data = self.safe_dict_n(message, ['d', 'data', 'privateAccount'])
1346
1488
  futuresTimestamp = self.safe_integer(message, 'ts')
1347
- timestamp = self.safe_integer(data, 'c', futuresTimestamp)
1489
+ timestamp = self.safe_integer_2(data, 'c', 'time', futuresTimestamp)
1348
1490
  if not (type in self.balance):
1349
1491
  self.balance[type] = {}
1350
1492
  self.balance[type]['info'] = data
1351
1493
  self.balance[type]['timestamp'] = timestamp
1352
1494
  self.balance[type]['datetime'] = self.iso8601(timestamp)
1353
- currencyId = self.safe_string_2(data, 'a', 'currency')
1495
+ currencyId = self.safe_string_n(data, ['a', 'currency', 'vcoinName'])
1354
1496
  code = self.safe_currency_code(currencyId)
1355
1497
  account = self.account()
1356
- account['free'] = self.safe_string_2(data, 'f', 'availableBalance')
1357
- account['used'] = self.safe_string_2(data, 'l', 'frozenBalance')
1498
+ account['total'] = self.safe_string_n(data, ['f', 'availableBalance', 'balanceAmount'])
1499
+ account['used'] = self.safe_string_n(data, ['l', 'frozenBalance', 'frozenAmount'])
1358
1500
  self.balance[type][code] = account
1359
1501
  self.balance[type] = self.safe_balance(self.balance[type])
1360
1502
  client.resolve(self.balance[type], messageHash)
@@ -1372,12 +1514,7 @@ class mexc(mexcAsync):
1372
1514
  url = None
1373
1515
  channel = None
1374
1516
  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']
1517
+ channel = 'spot@public.aggre.bookTicker.v3.api.pb@100ms@' + market['id']
1381
1518
  url = self.urls['api']['ws']['spot']
1382
1519
  params['unsubscribed'] = True
1383
1520
  await self.watch_spot_public(channel, messageHash, params)
@@ -1412,27 +1549,33 @@ class mexc(mexcAsync):
1412
1549
  url = self.urls['api']['ws']['spot'] if (isSpot) else self.urls['api']['ws']['swap']
1413
1550
  request: dict = {}
1414
1551
  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
1552
+ raise NotSupported(self.id + ' watchTickers does not support spot markets')
1553
+ # miniTicker = False
1554
+ # miniTicker, params = self.handle_option_and_params(params, 'watchTickers', 'miniTicker')
1555
+ # topics = []
1556
+ # if not miniTicker:
1557
+ # if symbols is None:
1558
+ # raise ArgumentsRequired(self.id + ' watchTickers required symbols argument for the bookTicker channel')
1559
+ # }
1560
+ # marketIds = self.market_ids(symbols)
1561
+ # for i in range(0, len(marketIds)):
1562
+ # marketId = marketIds[i]
1563
+ # messageHashes.append('unsubscribe:ticker:' + symbols[i])
1564
+ # channel = 'spot@public.bookTicker.v3.api@' + marketId
1565
+ # topics.append(channel)
1566
+ # }
1567
+ # else:
1568
+ # topics.append('spot@public.miniTickers.v3.api@UTC+8')
1569
+ # if symbols is None:
1570
+ # messageHashes.append('unsubscribe:spot:ticker')
1571
+ # else:
1572
+ # for i in range(0, len(symbols)):
1573
+ # messageHashes.append('unsubscribe:ticker:' + symbols[i])
1574
+ # }
1575
+ # }
1576
+ # }
1577
+ # request['method'] = 'UNSUBSCRIPTION'
1578
+ # request['params'] = topics
1436
1579
  else:
1437
1580
  request['method'] = 'unsub.tickers'
1438
1581
  request['params'] = {}
@@ -1464,7 +1607,7 @@ class mexc(mexcAsync):
1464
1607
  for i in range(0, len(symbols)):
1465
1608
  if isSpot:
1466
1609
  market = self.market(symbols[i])
1467
- topics.append('spot@public.bookTicker.v3.api@' + market['id'])
1610
+ topics.append('spot@public.aggre.bookTicker.v3.api.pb@100ms@' + market['id'])
1468
1611
  messageHashes.append('unsubscribe:bidask:' + symbols[i])
1469
1612
  url = self.urls['api']['ws']['spot']
1470
1613
  request: dict = {
@@ -1494,7 +1637,7 @@ class mexc(mexcAsync):
1494
1637
  url = None
1495
1638
  if market['spot']:
1496
1639
  url = self.urls['api']['ws']['spot']
1497
- channel = 'spot@public.kline.v3.api@' + market['id'] + '@' + timeframeId
1640
+ channel = 'spot@public.kline.v3.api.pb@' + market['id'] + '@' + timeframeId
1498
1641
  params['unsubscribed'] = True
1499
1642
  await self.watch_spot_public(channel, messageHash, params)
1500
1643
  else:
@@ -1514,6 +1657,7 @@ class mexc(mexcAsync):
1514
1657
  unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
1515
1658
  :param str symbol: unified array of symbols
1516
1659
  :param dict [params]: extra parameters specific to the exchange API endpoint
1660
+ :param str [params.frequency]: the frequency of the order book updates, default is '10ms', can be '100ms' or '10ms
1517
1661
  :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
1518
1662
  """
1519
1663
  await self.load_markets()
@@ -1523,7 +1667,9 @@ class mexc(mexcAsync):
1523
1667
  url = None
1524
1668
  if market['spot']:
1525
1669
  url = self.urls['api']['ws']['spot']
1526
- channel = 'spot@public.increase.depth.v3.api@' + market['id']
1670
+ frequency = None
1671
+ frequency, params = self.handle_option_and_params(params, 'watchOrderBook', 'frequency', '100ms')
1672
+ channel = 'spot@public.aggre.depth.v3.api.pb@' + frequency + '@' + market['id']
1527
1673
  params['unsubscribed'] = True
1528
1674
  await self.watch_spot_public(channel, messageHash, params)
1529
1675
  else:
@@ -1552,7 +1698,7 @@ class mexc(mexcAsync):
1552
1698
  url = None
1553
1699
  if market['spot']:
1554
1700
  url = self.urls['api']['ws']['spot']
1555
- channel = 'spot@public.deals.v3.api@' + market['id']
1701
+ channel = 'spot@public.aggre.deals.v3.api.pb@100ms@' + market['id']
1556
1702
  params['unsubscribed'] = True
1557
1703
  await self.watch_spot_public(channel, messageHash, params)
1558
1704
  else:
@@ -1654,16 +1800,59 @@ class mexc(mexcAsync):
1654
1800
  channel = self.safe_string(parts, 1)
1655
1801
  methods: dict = {
1656
1802
  'public.increase.depth.v3.api': self.handle_order_book_subscription,
1803
+ 'public.aggre.depth.v3.api.pb': self.handle_order_book_subscription,
1657
1804
  }
1658
1805
  method = self.safe_value(methods, channel)
1659
1806
  if method is not None:
1660
1807
  method(client, message)
1661
1808
 
1809
+ def handle_protobuf_message(self, client: Client, message):
1810
+ # protobuf message decoded
1811
+ # {
1812
+ # "channel":"spot@public.kline.v3.api.pb@BTCUSDT@Min1",
1813
+ # "symbol":"BTCUSDT",
1814
+ # "symbolId":"2fb942154ef44a4ab2ef98c8afb6a4a7",
1815
+ # "createTime":"1754737941062",
1816
+ # "publicSpotKline":{
1817
+ # "interval":"Min1",
1818
+ # "windowStart":"1754737920",
1819
+ # "openingPrice":"117317.31",
1820
+ # "closingPrice":"117325.26",
1821
+ # "highestPrice":"117341",
1822
+ # "lowestPrice":"117317.3",
1823
+ # "volume":"3.12599854",
1824
+ # "amount":"366804.43",
1825
+ # "windowEnd":"1754737980"
1826
+ # }
1827
+ # }
1828
+ channel = self.safe_string(message, 'channel')
1829
+ channelParts = channel.split('@')
1830
+ channelId = self.safe_string(channelParts, 1)
1831
+ if channelId == 'public.kline.v3.api.pb':
1832
+ self.handle_ohlcv(client, message)
1833
+ elif channelId == 'public.aggre.deals.v3.api.pb':
1834
+ self.handle_trades(client, message)
1835
+ elif channelId == 'public.aggre.bookTicker.v3.api.pb':
1836
+ self.handle_ticker(client, message)
1837
+ elif channelId == 'public.aggre.depth.v3.api.pb':
1838
+ self.handle_order_book(client, message)
1839
+ elif channelId == 'private.account.v3.api.pb':
1840
+ self.handle_balance(client, message)
1841
+ elif channelId == 'private.deals.v3.api.pb':
1842
+ self.handle_my_trade(client, message)
1843
+ elif channelId == 'private.orders.v3.api.pb':
1844
+ self.handle_order(client, message)
1845
+ return True
1846
+
1662
1847
  def handle_message(self, client: Client, message):
1663
1848
  if isinstance(message, str):
1664
1849
  if message == 'Invalid listen key':
1665
1850
  error = AuthenticationError(self.id + ' invalid listen key')
1666
1851
  client.reject(error)
1852
+ return
1853
+ if self.is_binary_message(message):
1854
+ message = self.decode_proto_msg(message)
1855
+ self.handle_protobuf_message(client, message)
1667
1856
  return
1668
1857
  if 'msg' in message:
1669
1858
  self.handle_subscription_status(client, message)