├── .gitignore ├── .pylintrc ├── .readthedocs.yaml ├── LICENSE ├── README.md ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── conf.py │ ├── endpoints │ ├── account.md │ ├── etf.md │ ├── market.md │ ├── rebate.md │ ├── sub_account.md │ └── wallet.md │ ├── enums.md │ ├── exceptions.md │ ├── index.md │ ├── spot.md │ └── websocket_stream.md ├── mexc_api ├── __init__.py ├── common │ ├── __init__.py │ ├── api.py │ ├── enums.py │ ├── exceptions.py │ └── utils.py ├── spot │ ├── __init__.py │ └── endpoints │ │ ├── _account.py │ │ ├── _etf.py │ │ ├── _market.py │ │ ├── _rebate.py │ │ ├── _sub_account.py │ │ └── _wallet.py └── websocket │ ├── __init__.py │ ├── mexc_websocket_app.py │ └── websocket_stream.py ├── mypy.ini ├── pyproject.toml └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | build 3 | .venv 4 | dist 5 | *.egg-info 6 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MAIN] 2 | disable= 3 | too-many-public-methods, 4 | too-many-arguments, 5 | use-dict-literal, 6 | too-many-instance-attributes, 7 | no-name-in-module, 8 | too-few-public-methods, -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/source/conf.py 17 | 18 | # Optionally declare the Python requirements required to build your docs 19 | python: 20 | install: 21 | - requirements: docs/requirements.txt 22 | - requirements: requirements.txt 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Floris272 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mexc api 2 | 3 | Python wrapper for the Mexc api 4 | 5 | [![Documentation Status](https://readthedocs.org/projects/py-mexc-api/badge/?version=latest)](https://py-mexc-api.readthedocs.io/en/latest/?badge=latest) 6 | 7 | [mexc docs](https://www.mexc.com/mexc-api) 8 | 9 | ## Usage 10 | 11 | ### Requirements 12 | - Python 3.11 or newer 13 | - requests 14 | - websocket-client 15 | 16 | ### installation 17 | ``` 18 | pip install mexc-api 19 | ``` 20 | 21 | ### api key 22 | generate an api key here: 23 | [https://www.mexc.com/user/openapi](https://www.mexc.com/user/openapi) 24 | 25 | 26 | ### Example 27 | ```python 28 | import time 29 | from mexc_api.spot import Spot 30 | from mexc_api.websocket import SpotWebsocketStreamClient 31 | from mexc_api.common.enums import Side, OrderType, StreamInterval, Action 32 | from mexc_api.common.exceptions import MexcAPIError 33 | 34 | KEY = "" 35 | SECRET = "" 36 | 37 | spot = Spot(KEY, SECRET) 38 | 39 | server_time = spot.market.server_time() 40 | print(server_time) 41 | 42 | order_book = spot.market.order_book("MXUSDT") 43 | print(order_book) 44 | 45 | order = spot.account.test_new_order( 46 | "MXUSDT", 47 | Side.BUY, 48 | OrderType.LIMIT, 49 | '1', 50 | price='1' 51 | ) 52 | print(order) 53 | 54 | ws = SpotWebsocketStreamClient( 55 | KEY, 56 | SECRET, 57 | on_message=print 58 | ) 59 | time.sleep(1) 60 | ws.klines("MXUSDT", StreamInterval.ONE_MIN) 61 | ws.account_orders() 62 | 63 | time.sleep(5) 64 | ws.account_orders(Action.UNSUBSCRIBE) 65 | ws.klines("MXUSDT", StreamInterval.ONE_MIN, Action.UNSUBSCRIBE) 66 | ws.stop() 67 | ``` 68 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx>=6.2.1 2 | sphinx-rtd-theme>=1.2.2 3 | myst-parser>=2.0.0 4 | enum-tools[sphinx] -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | sys.path.insert(0, os.path.abspath('../..')) 4 | # Configuration file for the Sphinx documentation builder. 5 | # 6 | # For the full list of built-in configuration values, see the documentation: 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 8 | 9 | # -- Project information ----------------------------------------------------- 10 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 11 | 12 | project = 'Mexc-api' 13 | copyright = '2023, Floris272' 14 | author = 'Floris272' 15 | release = '0.0.1' 16 | 17 | # -- General configuration --------------------------------------------------- 18 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 19 | 20 | extensions = [] 21 | 22 | templates_path = ['_templates'] 23 | 24 | extensions = ['myst_parser', 'sphinx.ext.autodoc', 'enum_tools.autoenum'] 25 | 26 | 27 | # -- Options for HTML output ------------------------------------------------- 28 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 29 | 30 | html_theme = 'sphinx_rtd_theme' 31 | html_static_path = ['_static'] 32 | -------------------------------------------------------------------------------- /docs/source/endpoints/account.md: -------------------------------------------------------------------------------- 1 | # Account 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.spot.endpoints._account 5 | :members: 6 | :private-members: 7 | :exclude-members: __weakref__ 8 | ``` -------------------------------------------------------------------------------- /docs/source/endpoints/etf.md: -------------------------------------------------------------------------------- 1 | # Etf 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.spot.endpoints._etf 5 | :members: 6 | :private-members: 7 | :exclude-members: __weakref__ 8 | ``` -------------------------------------------------------------------------------- /docs/source/endpoints/market.md: -------------------------------------------------------------------------------- 1 | # Market 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.spot.endpoints._market 5 | :members: 6 | :private-members: 7 | :exclude-members: __weakref__ 8 | ``` -------------------------------------------------------------------------------- /docs/source/endpoints/rebate.md: -------------------------------------------------------------------------------- 1 | # Rebate 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.spot.endpoints._rebate 5 | :members: 6 | :private-members: 7 | :exclude-members: __weakref__ 8 | ``` -------------------------------------------------------------------------------- /docs/source/endpoints/sub_account.md: -------------------------------------------------------------------------------- 1 | # Sub Account 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.spot.endpoints._sub_account 5 | :members: 6 | :private-members: 7 | :exclude-members: __weakref__ 8 | ``` -------------------------------------------------------------------------------- /docs/source/endpoints/wallet.md: -------------------------------------------------------------------------------- 1 | # Wallet 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.spot.endpoints._wallet 5 | :members: 6 | :private-members: 7 | :exclude-members: __weakref__ 8 | ``` -------------------------------------------------------------------------------- /docs/source/enums.md: -------------------------------------------------------------------------------- 1 | # Enums 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.common.enums 5 | :members: 6 | ``` -------------------------------------------------------------------------------- /docs/source/exceptions.md: -------------------------------------------------------------------------------- 1 | # Exceptions 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.common.exceptions 5 | :members: 6 | ``` -------------------------------------------------------------------------------- /docs/source/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to the mexc api docs! 2 | 3 | Python wrapper for the Mexc api 4 | 5 | [mexc docs](https://www.mexc.com/mexc-api) 6 | 7 | ## Usage 8 | 9 | ### Requirements 10 | - Python 3.11 or newer 11 | - requests 12 | - websocket-client 13 | 14 | ### installation 15 | ``` 16 | pip install mexc-api 17 | ``` 18 | 19 | ### api key 20 | generate an api key here: 21 | [https://www.mexc.com/user/openapi](https://www.mexc.com/user/openapi) 22 | 23 | 24 | ### Example 25 | ```python 26 | import time 27 | from mexc_api.spot import Spot 28 | from mexc_api.websocket import SpotWebsocketStreamClient 29 | from mexc_api.common.enums import Side, OrderType, StreamInterval, Action 30 | from mexc_api.common.exceptions import MexcAPIError 31 | 32 | KEY = "" 33 | SECRET = "" 34 | 35 | spot = Spot(KEY, SECRET) 36 | 37 | server_time = spot.market.server_time() 38 | print(server_time) 39 | 40 | order_book = spot.market.order_book("MXUSDT") 41 | print(order_book) 42 | 43 | order = spot.account.test_new_order( 44 | "MXUSDT", 45 | Side.BUY, 46 | OrderType.LIMIT, 47 | '1', 48 | price='1' 49 | ) 50 | print(order) 51 | 52 | ws = SpotWebsocketStreamClient( 53 | KEY, 54 | SECRET, 55 | on_message=print 56 | ) 57 | time.sleep(1) 58 | ws.klines("MXUSDT", StreamInterval.ONE_MIN) 59 | ws.account_orders() 60 | 61 | time.sleep(5) 62 | ws.account_orders(Action.UNSUBSCRIBE) 63 | ws.klines("MXUSDT", StreamInterval.ONE_MIN, Action.UNSUBSCRIBE) 64 | ws.stop() 65 | ``` 66 | 67 | ```{toctree} 68 | --- 69 | hidden: 70 | maxdepth: 2 71 | --- 72 | spot 73 | websocket_stream 74 | enums 75 | exceptions 76 | ``` -------------------------------------------------------------------------------- /docs/source/spot.md: -------------------------------------------------------------------------------- 1 | # Spot 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.spot 5 | :members: 6 | :private-members: 7 | :special-members: 8 | :exclude-members: __weakref__ 9 | ``` 10 | 11 | ```{toctree} 12 | --- 13 | maxdepth: 1 14 | glob: 15 | --- 16 | endpoints/* 17 | ``` -------------------------------------------------------------------------------- /docs/source/websocket_stream.md: -------------------------------------------------------------------------------- 1 | # Websocket stream 2 | 3 | ```{eval-rst} 4 | .. automodule:: mexc_api.websocket.websocket_stream 5 | :members: 6 | :exclude-members: __weakref__ 7 | ``` -------------------------------------------------------------------------------- /mexc_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floris272/py-mexc-api/7e9961bc6aae5e3acd8968af3848b39299d5eb4d/mexc_api/__init__.py -------------------------------------------------------------------------------- /mexc_api/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floris272/py-mexc-api/7e9961bc6aae5e3acd8968af3848b39299d5eb4d/mexc_api/common/__init__.py -------------------------------------------------------------------------------- /mexc_api/common/api.py: -------------------------------------------------------------------------------- 1 | """Defines the Api class.""" 2 | import hashlib 3 | import hmac 4 | from typing import Any 5 | 6 | from requests import Session 7 | 8 | from .enums import Method 9 | from .exceptions import MexcAPIError 10 | from .utils import get_timestamp 11 | 12 | 13 | class Api: 14 | """Defines a base api class.""" 15 | 16 | def __init__( 17 | self, 18 | api_key: str, 19 | api_secret: str, 20 | base_url: str = "https://api.mexc.com", 21 | recv_window: int = 5000, 22 | ) -> None: 23 | self.api_key = api_key 24 | self.api_secret = api_secret 25 | 26 | self.base_url = base_url 27 | self.recv_window = recv_window 28 | 29 | self.session = Session() 30 | self.session.headers.update( 31 | {"Content-Type": "application/json", "X-MEXC-APIKEY": self.api_key} 32 | ) 33 | 34 | def get_query(self, params: dict) -> str: 35 | """Returns a query string of all given parameters.""" 36 | query = "" 37 | for key, value in params.items(): 38 | query += f"{key}={value}&" 39 | return query[:-1] 40 | 41 | def get_signature(self, query: str) -> str: 42 | """Returns the signature based on the api secret and the query.""" 43 | return hmac.new( 44 | self.api_secret.encode("utf-8"), query.encode("utf-8"), hashlib.sha256 45 | ).hexdigest() 46 | 47 | def remove_none_params(self, params: dict) -> dict: 48 | """Returns a dict without empty parameter values.""" 49 | return {k: v for k, v in params.items() if v is not None} 50 | 51 | def send_request( 52 | self, method: Method, endpoint: str, params: dict, sign: bool = False 53 | ) -> Any: 54 | """ 55 | Sends a request with the given method to the given endpoint. 56 | RecvWindow, timestamp and signature are added to the parameters. 57 | 58 | Throws an MexcAPIError if the response has an error. 59 | Returns the json encoded content of the response. 60 | """ 61 | params = self.remove_none_params(params) 62 | 63 | if sign: 64 | params["recvWindow"] = self.recv_window 65 | params["timestamp"] = get_timestamp() 66 | params["signature"] = self.get_signature(self.get_query(params)) 67 | response = self.session.request(method.value, self.base_url + endpoint, params) 68 | 69 | if not response.ok: 70 | raise MexcAPIError(response.status_code, response.json().get("msg")) 71 | 72 | return response.json() 73 | -------------------------------------------------------------------------------- /mexc_api/common/enums.py: -------------------------------------------------------------------------------- 1 | """This module stores the enums used by the mexc_api classes.""" 2 | from enum import Enum 3 | 4 | 5 | class Method(Enum): 6 | """Method enum.""" 7 | 8 | GET = "GET" 9 | POST = "POST" 10 | PUT = "PUT" 11 | DELETE = "DELETE" 12 | 13 | 14 | class StreamInterval(Enum): 15 | """Interval enum used by the websocket kline stream.""" 16 | 17 | ONE_MIN = "Min1" 18 | FIVE_MIN = "Min5" 19 | FIFTEEN_MIN = "Min15" 20 | THIRTY_MIN = "Min30" 21 | SIXTY_MIN = "Min60" 22 | FOUR_HOUR = "Hour4" 23 | EIGHT_HOUR = "Hour8" 24 | ONE_DAY = "Day1" 25 | ONE_WEEK = "Week1" 26 | ONE_MONTH = "Month1" 27 | 28 | 29 | class Interval(Enum): 30 | """Interval used by the spot http kline endpoint.""" 31 | 32 | ONE_MIN = "1m" 33 | FIVE_MIN = "5m" 34 | FIFTEEN_MIN = "15m" 35 | THIRTY_MIN = "30m" 36 | SIXTY_MIN = "60m" 37 | FOUR_HOUR = "4h" 38 | EIGHT_HOUR = "8h" 39 | ONE_DAY = "1d" 40 | ONE_MONTH = "1M" 41 | 42 | 43 | class Side(Enum): 44 | """Side enum.""" 45 | 46 | BUY = "BUY" 47 | SELL = "SELL" 48 | 49 | 50 | class OrderType(Enum): 51 | """Order type enum.""" 52 | 53 | LIMIT = "LIMIT" 54 | MARKET = "MARKET" 55 | LIMIT_MAKER = "LIMIT_MAKER" 56 | # ??? 57 | # IMMEDIATE_OR_CANCEL ="IMMEDIATE_OR_CANCEL" 58 | # FILL_OR_KILL = "FILL_OR_KILL" 59 | 60 | 61 | class Action(Enum): 62 | """Action type enum.""" 63 | 64 | SUBSCRIBE = "SUBSCRIPTION" 65 | UNSUBSCRIBE = "UNSUBSCRIPTION" 66 | 67 | 68 | class AccountType(Enum): 69 | """account type enum.""" 70 | 71 | SPOT = "SPOT" 72 | FUTURES = "FUTURES" 73 | -------------------------------------------------------------------------------- /mexc_api/common/exceptions.py: -------------------------------------------------------------------------------- 1 | """Defines all exceptions.""" 2 | 3 | 4 | class MexcAPIError(Exception): 5 | """Default exception.""" 6 | -------------------------------------------------------------------------------- /mexc_api/common/utils.py: -------------------------------------------------------------------------------- 1 | """Defines utils used in the package.""" 2 | import time 3 | 4 | 5 | def get_timestamp() -> int: 6 | """Returns the current timestamp in milliseconds.""" 7 | return round(time.time() * 1000) 8 | -------------------------------------------------------------------------------- /mexc_api/spot/__init__.py: -------------------------------------------------------------------------------- 1 | """Defines the Spot class.""" 2 | from ..common.api import Api 3 | from .endpoints._account import _Account 4 | from .endpoints._etf import _Etf 5 | from .endpoints._market import _Market 6 | from .endpoints._rebate import _Rebate 7 | from .endpoints._sub_account import _SubAccount 8 | from .endpoints._wallet import _Wallet 9 | 10 | 11 | class Spot: 12 | """Class for handling the MEXC REST API.""" 13 | 14 | def __init__(self, api_key: str, api_secret: str) -> None: 15 | api = Api(api_key, api_secret) 16 | 17 | self.market = _Market(api) 18 | self.account = _Account(api) 19 | self.subaccount = _SubAccount(api) 20 | self.etf = _Etf(api) 21 | self.rebate = _Rebate(api) 22 | self.wallet = _Wallet(api) 23 | -------------------------------------------------------------------------------- /mexc_api/spot/endpoints/_account.py: -------------------------------------------------------------------------------- 1 | """Defines the _Account class.""" 2 | from mexc_api.common.api import Api 3 | from mexc_api.common.enums import Method, OrderType, Side 4 | 5 | 6 | class _Account: 7 | """Defines all Account endpoints.""" 8 | 9 | def __init__(self, api: Api) -> None: 10 | self.api = api 11 | 12 | def test_new_order( 13 | self, 14 | symbol: str, 15 | side: Side, 16 | order_type: OrderType, 17 | quantity: str | None = None, 18 | quote_order_quantity: str | None = None, 19 | price: str | None = None, 20 | client_order_id: str | None = None, 21 | ) -> dict: 22 | """Creates a test order.""" 23 | params = { 24 | "symbol": symbol.upper(), 25 | "side": side.value, 26 | "quantity": quantity, 27 | "price": price, 28 | "type": order_type.value, 29 | "quoteOrderQty": quote_order_quantity, 30 | "newClientOrderId": client_order_id, 31 | } 32 | return self.api.send_request(Method.POST, "/api/v3/order/test", params, True) 33 | 34 | def new_order( 35 | self, 36 | symbol: str, 37 | side: Side, 38 | order_type: OrderType, 39 | quantity: str | None = None, 40 | quote_order_quantity: str | None = None, 41 | price: str | None = None, 42 | client_order_id: str | None = None, 43 | ) -> dict: 44 | """Creates a new order.""" 45 | params = { 46 | "symbol": symbol.upper(), 47 | "side": side.value, 48 | "quantity": quantity, 49 | "price": price, 50 | "type": order_type.value, 51 | "quoteOrderQty": quote_order_quantity, 52 | "newClientOrderId": client_order_id, 53 | } 54 | return self.api.send_request(Method.POST, "/api/v3/order", params, True) 55 | 56 | def batch_order(self) -> dict: 57 | """Creates multiple orders.""" 58 | raise NotImplementedError 59 | 60 | def cancel_order( 61 | self, 62 | symbol: str, 63 | order_id: str | None = None, 64 | client_order_id: str | None = None, 65 | ) -> dict: 66 | """Cancels an order based on the order id or client order id.""" 67 | params = { 68 | "symbol": symbol.upper(), 69 | "orderId": order_id, 70 | "origClientOrderId": client_order_id, 71 | } 72 | 73 | return self.api.send_request(Method.DELETE, "/api/v3/order", params, True) 74 | 75 | def cancel_open_orders(self, symbol: str) -> list: 76 | """Cancels all open orders.""" 77 | params = { 78 | "symbol": symbol.upper(), 79 | } 80 | return self.api.send_request(Method.DELETE, "/api/v3/openOrders", params, True) 81 | 82 | def get_order( 83 | self, 84 | symbol: str, 85 | order_id: str | None = None, 86 | client_order_id: str | None = None, 87 | ) -> dict: 88 | """Returns an order for a symbol based on the order id or client order id.""" 89 | params = { 90 | "symbol": symbol.upper(), 91 | "orderId": order_id, 92 | "origClientOrderId": client_order_id, 93 | } 94 | return self.api.send_request(Method.GET, "/api/v3/order", params, True) 95 | 96 | def get_open_orders(self, symbol: str) -> list: 97 | """Returns all open orders for a symbol.""" 98 | params = { 99 | "symbol": symbol.upper(), 100 | } 101 | return self.api.send_request(Method.GET, "/api/v3/openOrders", params, True) 102 | 103 | def get_orders( 104 | self, 105 | symbol: str, 106 | start_ms: int | None = None, 107 | end_ms: int | None = None, 108 | limit: int | None = None, 109 | ) -> list: 110 | """Returns all orders for a symbol within the optional start and end timestamps.""" 111 | params = { 112 | "symbol": symbol.upper(), 113 | "limit": limit, 114 | "startTime": start_ms, 115 | "endTime": end_ms, 116 | } 117 | return self.api.send_request(Method.GET, "/api/v3/allOrders", params, True) 118 | 119 | def get_account_info(self) -> dict: 120 | """Returns the account info.""" 121 | return self.api.send_request(Method.GET, "/api/v3/account", {}, True) 122 | 123 | def get_trades( 124 | self, 125 | symbol: str, 126 | start_ms: int | None = None, 127 | end_ms: int | None = None, 128 | limit: int | None = None, 129 | ) -> list: 130 | """Returns all trades for a symbol within the optional start and end timestamps.""" 131 | params = { 132 | "symbol": symbol.upper(), 133 | "limit": limit, 134 | "startTime": start_ms, 135 | "endTime": end_ms, 136 | } 137 | return self.api.send_request(Method.GET, "/api/v3/myTrades", params, True) 138 | 139 | def enable_mx_deduct(self, is_enabled: bool) -> dict: 140 | """Enables mx deduct.""" 141 | params = dict(mxDeductEnable=is_enabled) 142 | return self.api.send_request( 143 | Method.POST, "/api/v3/mxDeduct/enable", params, True 144 | ) 145 | 146 | def get_mx_deduct(self) -> dict: 147 | """Returns the mx deduct status.""" 148 | return self.api.send_request(Method.GET, "/api/v3/mxDeduct/enable", {}, True) 149 | 150 | def create_listen_key(self) -> str: 151 | """Returns a listen key""" 152 | response = self.api.send_request( 153 | Method.POST, "/api/v3/userDataStream", {}, True 154 | ) 155 | return response["listenKey"] 156 | 157 | def get_all_listen_keys(self) -> dict: 158 | """Returns a listen key.""" 159 | return self.api.send_request(Method.GET, "/api/v3/userDataStream", {}, True) 160 | 161 | def keep_alive_listen_key(self, listen_key: str) -> None: 162 | """Keeps the listen key alive.""" 163 | params = {"listenKey": listen_key} 164 | self.api.send_request(Method.PUT, "/api/v3/userDataStream", params, True) 165 | 166 | def delete_listen_key(self, listen_key: str) -> None: 167 | """deletes a listen key.""" 168 | params = {"listenKey": listen_key} 169 | self.api.send_request(Method.DELETE, "/api/v3/userDataStream", params, True) 170 | -------------------------------------------------------------------------------- /mexc_api/spot/endpoints/_etf.py: -------------------------------------------------------------------------------- 1 | """Defines the _Etf class.""" 2 | from mexc_api.common.api import Api 3 | from mexc_api.common.enums import Method 4 | 5 | 6 | class _Etf: 7 | """Defines all etf endpoints.""" 8 | 9 | def __init__(self, api: Api) -> None: 10 | self.api = api 11 | 12 | def info(self, etf_symbol: str) -> dict: 13 | """Returns etf info.""" 14 | params = {"symbol": etf_symbol.upper()} 15 | return self.api.send_request(Method.GET, "/api/v3/etf/info", params) 16 | -------------------------------------------------------------------------------- /mexc_api/spot/endpoints/_market.py: -------------------------------------------------------------------------------- 1 | """Defines the _Market class.""" 2 | from mexc_api.common.api import Api 3 | from mexc_api.common.enums import Interval, Method 4 | 5 | 6 | class _Market: 7 | """Defines all Market endpoints.""" 8 | 9 | def __init__(self, api: Api) -> None: 10 | self.api = api 11 | 12 | def test(self) -> None: 13 | """Tests connectivity to the Rest API.""" 14 | self.api.send_request(Method.GET, "/api/v3/ping", {}) 15 | 16 | def server_time(self) -> int: 17 | """Returns the server time.""" 18 | response = self.api.send_request(Method.GET, "/api/v3/time", {}) 19 | return response["serverTime"] 20 | 21 | def default_symbols(self) -> list[str]: 22 | """Returns all symbols.""" 23 | response = self.api.send_request(Method.GET, "/api/v3/defaultSymbols", {}) 24 | return response["data"] 25 | 26 | def exchange_info( 27 | self, symbol: str | None = None, symbols: list[str] | None = None 28 | ) -> dict: 29 | """ 30 | Returns the rules and symbol info of the given symbol(s). 31 | All symbols will be returned when no parameter is given. 32 | """ 33 | 34 | if symbol: 35 | symbol.upper() 36 | elif symbols: 37 | symbols = [symbol.upper() for symbol in symbols] 38 | 39 | params = {"symbol": symbol, "symbols": symbols} 40 | return self.api.send_request(Method.GET, "/api/v3/exchangeInfo", params) 41 | 42 | def order_book(self, symbol: str, limit: int | None = None) -> dict: 43 | """Returns the bids and asks of symbol.""" 44 | params = {"symbol": symbol.upper(), "limit": limit} 45 | return self.api.send_request(Method.GET, "/api/v3/depth", params) 46 | 47 | def trades(self, symbol: str, limit: int | None = None) -> list: 48 | """Returns the the recent of symbol.""" 49 | params = {"symbol": symbol.upper(), "limit": limit} 50 | return self.api.send_request(Method.GET, "/api/v3/trades", params) 51 | 52 | def agg_trades( 53 | self, 54 | symbol: str, 55 | start_ms: int | None = None, 56 | end_ms: int | None = None, 57 | limit: int | None = None, 58 | ) -> list: 59 | """Returns the aggregate trades of symbol.""" 60 | params = { 61 | "symbol": symbol.upper(), 62 | "limit": limit, 63 | "startTime": start_ms, 64 | "endTime": end_ms, 65 | } 66 | return self.api.send_request(Method.GET, "/api/v3/aggTrades", params) 67 | 68 | def klines( 69 | self, 70 | symbol: str, 71 | interval: Interval, 72 | start_ms: int | None = None, 73 | end_ms: int | None = None, 74 | limit: int | None = None, 75 | ) -> list: 76 | """ 77 | Returns the klines of a symbol on the given interval 78 | between the optional start and end timestamps. 79 | """ 80 | params = { 81 | "symbol": symbol.upper(), 82 | "interval": interval.value, 83 | "limit": limit, 84 | "startTime": start_ms, 85 | "endTime": end_ms, 86 | } 87 | return self.api.send_request(Method.GET, "/api/v3/klines", params) 88 | 89 | def avg_price(self, symbol: str) -> dict: 90 | """Returns the average price of a symbol.""" 91 | params = { 92 | "symbol": symbol.upper(), 93 | } 94 | return self.api.send_request(Method.GET, "/api/v3/avgPrice", params) 95 | 96 | def ticker_24h(self, symbol: str | None = None) -> list: 97 | """ 98 | Returns ticker data from the last 24 hours. 99 | Data for all symbols will be sent if symbol was not given. 100 | """ 101 | if symbol: 102 | symbol.upper() 103 | 104 | params = { 105 | "symbol": symbol, 106 | } 107 | response = self.api.send_request(Method.GET, "/api/v3/ticker/24hr", params) 108 | return [response] if isinstance(response, dict) else response 109 | 110 | def ticker_price(self, symbol: str | None = None) -> list: 111 | """ 112 | Returns the ticker price of a symbol. 113 | Prices of all symbols will be send if symbol was not given. 114 | """ 115 | if symbol: 116 | symbol.upper() 117 | 118 | params = { 119 | "symbol": symbol, 120 | } 121 | response = self.api.send_request(Method.GET, "/api/v3/ticker/price", params) 122 | return [response] if isinstance(response, dict) else response 123 | 124 | def ticker_book_price(self, symbol: str | None = None) -> list: 125 | """ 126 | Returns the best price/qty on the order book for a symbol. 127 | Data for all symbols will be sent if symbol was not given. 128 | """ 129 | if symbol: 130 | symbol.upper() 131 | 132 | params = { 133 | "symbol": symbol, 134 | } 135 | response = self.api.send_request( 136 | Method.GET, "/api/v3/ticker/bookTicker", params 137 | ) 138 | return [response] if isinstance(response, dict) else response 139 | -------------------------------------------------------------------------------- /mexc_api/spot/endpoints/_rebate.py: -------------------------------------------------------------------------------- 1 | """Defines the _Rebate class.""" 2 | from mexc_api.common.api import Api 3 | from mexc_api.common.enums import Method 4 | 5 | 6 | class _Rebate: 7 | """Defines all rebate endpoints.""" 8 | 9 | def __init__(self, api: Api) -> None: 10 | self.api = api 11 | 12 | def get_records( 13 | self, 14 | start_ms: int | None = None, 15 | end_ms: int | None = None, 16 | page: int | None = None, 17 | limit: int | None = None, 18 | ) -> dict: 19 | """Returns rebate records.""" 20 | params = { 21 | "startTime": start_ms, 22 | "endTime": end_ms, 23 | "page": page, 24 | "limit": limit, 25 | } 26 | return self.api.send_request( 27 | Method.GET, "/api/v3/rebate/taxQuery", params, True 28 | ) 29 | 30 | def get_record_details( 31 | self, 32 | start_ms: int | None = None, 33 | end_ms: int | None = None, 34 | page: int | None = None, 35 | ) -> dict: 36 | """Returns rebate record details.""" 37 | params = { 38 | "startTime": start_ms, 39 | "endTime": end_ms, 40 | "page": page, 41 | } 42 | return self.api.send_request(Method.GET, "/api/v3/rebate/detail", params, True) 43 | 44 | def get_self_rebate_records( 45 | self, 46 | start_ms: int | None = None, 47 | end_ms: int | None = None, 48 | page: int | None = None, 49 | ) -> dict: 50 | """Returns self rebate records.""" 51 | params = { 52 | "startTime": start_ms, 53 | "endTime": end_ms, 54 | "page": page, 55 | } 56 | return self.api.send_request( 57 | Method.GET, "/api/v3/rebate/detail/kickback", params, True 58 | ) 59 | 60 | def get_refer_code(self) -> str: 61 | """Returns an refer code""" 62 | response = self.api.send_request( 63 | Method.GET, "/api/v3/rebate/referCode", {}, True 64 | ) 65 | return response["referCode"] 66 | -------------------------------------------------------------------------------- /mexc_api/spot/endpoints/_sub_account.py: -------------------------------------------------------------------------------- 1 | """Defines the _SubAccount class.""" 2 | from mexc_api.common.api import Api 3 | from mexc_api.common.enums import AccountType, Method 4 | 5 | 6 | class _SubAccount: 7 | """Defines all sub account endpoints.""" 8 | 9 | def __init__(self, api: Api) -> None: 10 | self.api = api 11 | 12 | def create_sub_account(self, account_name: str, desc: str) -> dict: 13 | """Creates an sub account.""" 14 | params = {"subAccount": account_name, "note": desc} 15 | return self.api.send_request( 16 | Method.POST, "/api/v3/sub-account/virtualSubAccount", params, True 17 | ) 18 | 19 | def get_sub_accounts( 20 | self, 21 | account_name: str | None = None, 22 | frozen: bool | None = None, 23 | page: int | None = None, 24 | limit: int | None = None, 25 | ) -> dict: 26 | """Returns info of a sub account. Returns all subaccounts if name is not given.""" 27 | params = { 28 | "subAccount": account_name, 29 | "isFreeze": frozen, 30 | "page": page, 31 | "limit": limit, 32 | } 33 | return self.api.send_request( 34 | Method.GET, "/api/v3/sub-account/list", params, True 35 | ) 36 | 37 | def create_api_key( 38 | self, 39 | account_name: str, 40 | desc: str, 41 | permissions: list[str] | None = None, 42 | ips: list[str] | None = None, 43 | ) -> dict: 44 | """Creates an api key for a sub account.""" 45 | params = { 46 | "subAccount": account_name, 47 | "note": desc, 48 | "permissions": permissions, 49 | "ip": ips, 50 | } 51 | return self.api.send_request( 52 | Method.POST, "/api/v3/sub-account/apiKey", params, True 53 | ) 54 | 55 | def get_api_key(self, account_name: str) -> dict: 56 | """Returns the api key(s) of a sub account.""" 57 | params = {"subAccount": account_name} 58 | return self.api.send_request( 59 | Method.GET, "/api/v3/sub-account/apiKey", params, True 60 | ) 61 | 62 | def delete_api_key(self, account_name: str, api_key: str) -> None: 63 | """Deletes the api key of a sub account.""" 64 | params = {"subAccount": account_name, "apiKey": api_key} 65 | self.api.send_request(Method.DELETE, "/api/v3/sub-account/apiKey", params, True) 66 | 67 | def transfer( 68 | self, 69 | send_account_type: AccountType, 70 | receive_account_type: AccountType, 71 | asset: str, 72 | amount: str, 73 | sender: str | None = None, 74 | receiver: str | None = None, 75 | ) -> str: 76 | """ 77 | Transfers an asset between accounts. 78 | The default sender/receiver is the master account. 79 | Returns the transfer id. 80 | """ 81 | params = { 82 | "fromAccount": sender, 83 | "toAccount": receiver, 84 | "fromAccountType": send_account_type, 85 | "toAccountType": receive_account_type, 86 | "asset": asset.upper(), 87 | "amount": amount, 88 | } 89 | response = self.api.send_request( 90 | Method.POST, "/api/v3/capital/sub-account/universalTransfer", params, True 91 | ) 92 | return response["tranId"] 93 | 94 | def get_transfers( 95 | self, 96 | send_account_type: AccountType, 97 | receive_account_type: AccountType, 98 | start_ms: int | None = None, 99 | end_ms: int | None = None, 100 | sender: str | None = None, 101 | receiver: str | None = None, 102 | page: int | None = None, 103 | limit: int | None = None, 104 | ) -> dict: 105 | """Returns all transfers for the given parameters. The default sender/receiver is the master account.""" 106 | params = { 107 | "fromAccount": sender, 108 | "toAccount": receiver, 109 | "fromAccountType": send_account_type, 110 | "toAccountType": receive_account_type, 111 | "startTime": start_ms, 112 | "endTime": end_ms, 113 | "page": page, 114 | "limit": limit, 115 | } 116 | return self.api.send_request( 117 | Method.GET, "/api/v3/capital/sub-account/universalTransfer", params, True 118 | ) 119 | -------------------------------------------------------------------------------- /mexc_api/spot/endpoints/_wallet.py: -------------------------------------------------------------------------------- 1 | """Defines the _Wallet class.""" 2 | from mexc_api.common.api import Api 3 | from mexc_api.common.enums import AccountType, Method 4 | 5 | 6 | class _Wallet: 7 | """Defines all wallet endpoints.""" 8 | 9 | def __init__(self, api: Api) -> None: 10 | self.api = api 11 | 12 | def info(self) -> list: 13 | """Returns all currency info.""" 14 | return self.api.send_request( 15 | Method.GET, "/api/v3/capital/config/getall", {}, True 16 | ) 17 | 18 | def withdraw( 19 | self, 20 | asset: str, 21 | network: str | None, 22 | address: str, 23 | amount: str, 24 | order_id: str | None = None, 25 | memo: str | None = None, 26 | remark: str | None = None, 27 | ) -> list: 28 | """Withdraws an asset from mexc to an address.""" 29 | params = { 30 | "coin": asset.upper(), 31 | "network": network, 32 | "address": address, 33 | "amount": amount, 34 | "withdrawOrderId": order_id, 35 | "memo": memo, 36 | "remark": remark, 37 | } 38 | return self.api.send_request( 39 | Method.POST, "/api/v3/capital/withdraw/apply", params, True 40 | ) 41 | 42 | def cancel_withdraw(self, withdraw_id: str) -> str: 43 | """Cancels a withdrawal.""" 44 | params = { 45 | "id": withdraw_id, 46 | } 47 | response = self.api.send_request( 48 | Method.DELETE, "/api/v3/capital/withdraw", params, True 49 | ) 50 | return response["id"] 51 | 52 | def get_withdrawal_history( 53 | self, 54 | asset: str | None = None, 55 | status: int | None = None, 56 | start_ms: int | None = None, 57 | end_ms: int | None = None, 58 | limit: int | None = None, 59 | ) -> list: 60 | """Returns withdrawals based on the parameters.""" 61 | params = { 62 | "coin": asset.upper() if isinstance(asset, str) else None, 63 | "status": status, 64 | "startTime": start_ms, 65 | "endTime": end_ms, 66 | "limit": limit, 67 | } 68 | return self.api.send_request( 69 | Method.GET, "/api/v3/capital/withdraw/history", params, True 70 | ) 71 | 72 | def get_deposit_history( 73 | self, 74 | asset: str | None = None, 75 | status: int | None = None, 76 | start_ms: int | None = None, 77 | end_ms: int | None = None, 78 | limit: int | None = None, 79 | ) -> list: 80 | """Returns deposits based on the parameters.""" 81 | params = { 82 | "coin": asset.upper() if isinstance(asset, str) else None, 83 | "status": status, 84 | "startTime": start_ms, 85 | "endTime": end_ms, 86 | "limit": limit, 87 | } 88 | return self.api.send_request( 89 | Method.GET, "/api/v3/capital/deposit/hisrec", params, True 90 | ) 91 | 92 | def get_create_deposit_address( 93 | self, 94 | asset: str, 95 | network: str, 96 | ) -> list: 97 | """Creates an deposit address.""" 98 | params = { 99 | "coin": asset.upper(), 100 | "network": network, 101 | } 102 | return self.api.send_request( 103 | Method.POST, "/api/v3/capital/deposit/address", params, True 104 | ) 105 | 106 | def get_deposit_address( 107 | self, 108 | asset: str, 109 | network: str | None = None, 110 | ) -> dict: 111 | """Returns the deposit addresses of an asset.""" 112 | params = { 113 | "coin": asset.upper(), 114 | "network": network, 115 | } 116 | return self.api.send_request( 117 | Method.GET, "/api/v3/capital/deposit/address", params, True 118 | ) 119 | 120 | def get_withdrawal_address( 121 | self, 122 | asset: str | None = None, 123 | page: int | None = None, 124 | limit: int | None = None, 125 | ) -> dict: 126 | """Returns the withdrawal addresses of an asset. Returns all withdrawal addresses if no asset is given.""" 127 | params = { 128 | "coin": asset.upper() if isinstance(asset, str) else None, 129 | "page": page, 130 | "limit": limit, 131 | } 132 | return self.api.send_request( 133 | Method.GET, "/api/v3/capital/withdraw/address", params, True 134 | ) 135 | 136 | def transfer( 137 | self, 138 | send_account_type: AccountType, 139 | receive_account_type: AccountType, 140 | asset: str, 141 | amount: str, 142 | ) -> list: 143 | """Tranfer an asset between account types""" 144 | params = { 145 | "fromAccountType": send_account_type, 146 | "toAccountType": receive_account_type, 147 | "asset": asset, 148 | "amount": amount, 149 | } 150 | return self.api.send_request( 151 | Method.POST, "/api/v3/capital/transfer", params, True 152 | ) 153 | 154 | def get_transfers( 155 | self, 156 | send_account_type: AccountType, 157 | receive_account_type: AccountType, 158 | start_ms: int | None = None, 159 | end_ms: int | None = None, 160 | page: int | None = None, 161 | size: int | None = None, 162 | ) -> list: 163 | """Returns transfers baesed on the parameters.""" 164 | params = { 165 | "fromAccountType": send_account_type, 166 | "toAccountType": receive_account_type, 167 | "startTime": start_ms, 168 | "endTime": end_ms, 169 | "page": page, 170 | "size": size, 171 | } 172 | return self.api.send_request( 173 | Method.GET, "/api/v3/capital/transfer", params, True 174 | ) 175 | 176 | def get_transfers_by_id(self, transfer_id: str) -> dict: 177 | """Returns an transfer.""" 178 | params = { 179 | "tranId": transfer_id, 180 | } 181 | return self.api.send_request( 182 | Method.GET, "/api/v3/capital/transfer/tranId", params, True 183 | ) 184 | 185 | def get_mx_convertible_assets(self) -> list: 186 | """Returns assets that can be converted into MX""" 187 | return self.api.send_request( 188 | Method.GET, "/api/v3/capital/convert/list", {}, True 189 | ) 190 | 191 | def dust_transfer(self, asset: str | list[str]) -> dict: 192 | """Converts asset(s) to dust.""" 193 | params = { 194 | "asset": asset, 195 | } 196 | return self.api.send_request( 197 | Method.POST, "/api/v3/capital/convert", params, True 198 | ) 199 | 200 | def dust_log( 201 | self, 202 | start_ms: int | None = None, 203 | end_ms: int | None = None, 204 | page: int | None = None, 205 | limit: int | None = None, 206 | ) -> dict: 207 | """Returns the dust log.""" 208 | params = { 209 | "startTime": start_ms, 210 | "endTime": end_ms, 211 | "page": page, 212 | "limit": limit, 213 | } 214 | return self.api.send_request( 215 | Method.GET, "/api/v3/capital/convert", params, True 216 | ) 217 | -------------------------------------------------------------------------------- /mexc_api/websocket/__init__.py: -------------------------------------------------------------------------------- 1 | """Websocket.""" 2 | from .websocket_stream import SpotWebsocketStreamClient 3 | -------------------------------------------------------------------------------- /mexc_api/websocket/mexc_websocket_app.py: -------------------------------------------------------------------------------- 1 | """Defines the MexcWebsocketApp class.""" 2 | import json 3 | import logging 4 | import time 5 | from threading import Thread 6 | from typing import Callable 7 | 8 | from websocket import WebSocketApp 9 | 10 | from mexc_api.spot import Spot 11 | 12 | 13 | class MexcWebsocketApp(WebSocketApp): # type: ignore[misc] 14 | """ 15 | Implements the WebSocketApp ands starts the run in a thread. 16 | Also creates a second thread for keeping the listen key alive. 17 | """ 18 | 19 | def __init__( 20 | self, 21 | api_key: str, 22 | api_secret: str, 23 | on_open: Callable | None = None, 24 | on_message: Callable | None = None, 25 | on_error: Callable | None = None, 26 | on_close: Callable | None = None, 27 | ) -> None: 28 | self.logger = logging.getLogger(__name__) 29 | spot = Spot(api_key, api_secret) 30 | self.listen_key = spot.account.create_listen_key() 31 | stream_url = f"wss://wbs.mexc.com/ws?listenKey={self.listen_key}" 32 | 33 | super().__init__( 34 | stream_url, 35 | on_open=on_open, 36 | on_message=on_message, 37 | on_error=on_error, 38 | on_close=on_close, 39 | ) 40 | 41 | Thread( 42 | target=self.run_forever, kwargs={"reconnect": 1, "ping_interval": 20} 43 | ).start() 44 | 45 | Thread( 46 | target=lambda: self._keep_alive(spot), 47 | daemon=True, 48 | name="Mexc keep alive listen key", 49 | ).start() 50 | 51 | def _keep_alive(self, spot: Spot) -> None: 52 | """Keeps the listen key alive.""" 53 | while self._keep_alive: # type: ignore[truthy-function] 54 | time.sleep(1800) 55 | spot.account.keep_alive_listen_key(self.listen_key) 56 | 57 | def send_message(self, message: dict) -> None: 58 | """Sends a message to the connected server.""" 59 | self.logger.debug("Sending message to Mexc WebSocket Server: %s", message) 60 | self.send(json.dumps(message)) 61 | -------------------------------------------------------------------------------- /mexc_api/websocket/websocket_stream.py: -------------------------------------------------------------------------------- 1 | """Defines the MexcWebsocketClient.""" 2 | import json 3 | import logging 4 | from typing import Callable, Literal 5 | 6 | from mexc_api.common.enums import Action, StreamInterval 7 | 8 | from .mexc_websocket_app import MexcWebsocketApp 9 | 10 | 11 | class SpotWebsocketStreamClient: 12 | """Handles the stream subscriptions.""" 13 | 14 | def __init__( 15 | self, 16 | api_key: str, 17 | api_secret: str, 18 | on_message: Callable[[dict], None], 19 | on_open: Callable[[], None] | None = None, 20 | on_close: Callable | None = None, 21 | on_error: Callable | None = None, 22 | ): 23 | self.logger = logging.getLogger(__name__) 24 | self.streams: set[str] = set() 25 | self.on_open = on_open 26 | self.on_message = on_message 27 | self.mexc_websocket_app = MexcWebsocketApp( 28 | api_key=api_key, 29 | api_secret=api_secret, 30 | on_message=self._on_message, 31 | on_open=self._on_open, 32 | on_close=on_close, 33 | on_error=on_error, 34 | ) 35 | self.logger.debug("Mexc WebSocket Client started.") 36 | 37 | def _on_open(self, _app: MexcWebsocketApp) -> None: 38 | """Reconnects to all streams and calls on_open callback.""" 39 | for stream in self.streams: 40 | self._change_subscription(stream) 41 | 42 | if self.on_open: 43 | self.on_open() 44 | 45 | def _on_message(self, _app: MexcWebsocketApp, message: str) -> None: 46 | """passes the loaded data to the on_message_callback.""" 47 | self.on_message(json.loads(message)) 48 | 49 | def _change_subscription( 50 | self, stream: str, action: Action = Action.SUBSCRIBE 51 | ) -> None: 52 | """Subscribes to or unsubscribes from stream.""" 53 | self.streams.add(stream) 54 | message = {"method": action.value, "params": [stream]} 55 | self.mexc_websocket_app.send_message(message) 56 | 57 | def stop(self) -> None: 58 | """Stops the websocket connection.""" 59 | self.mexc_websocket_app.close() 60 | 61 | def trades(self, symbol: str, action: Action = Action.SUBSCRIBE) -> None: 62 | """Subscribes to the trade stream of a symbol.""" 63 | self._change_subscription(f"spot@public.deals.v3.api@{symbol.upper()}", action) 64 | 65 | def klines( 66 | self, symbol: str, interval: StreamInterval, action: Action = Action.SUBSCRIBE 67 | ) -> None: 68 | """Subscribes to the kline stream of a symbol.""" 69 | self._change_subscription( 70 | f"spot@public.kline.v3.api@{symbol.upper()}@{interval.value}", action 71 | ) 72 | 73 | def diff_depth(self, symbol: str, action: Action = Action.SUBSCRIBE) -> None: 74 | """Subscribes to the increase depth stream of a symbol.""" 75 | self._change_subscription( 76 | f"spot@public.increase.depth.v3.api@{symbol.upper()}", action 77 | ) 78 | 79 | def partial_depth( 80 | self, symbol: str, level: Literal[5, 10, 20], action: Action = Action.SUBSCRIBE 81 | ) -> None: 82 | """Subscribes to the partial depth stream of a symbol.""" 83 | self._change_subscription( 84 | f"spot@public.limit.depth.v3.api@{symbol.upper()}@{level}", action 85 | ) 86 | 87 | def book_ticker(self, symbol: str, action: Action = Action.SUBSCRIBE) -> None: 88 | """Subscribes to the book ticker stream of a symbol.""" 89 | self._change_subscription( 90 | f"spot@public.bookTicker.v3.api@{symbol.upper()}", action 91 | ) 92 | 93 | def account_updates(self, action: Action = Action.SUBSCRIBE) -> None: 94 | """Subscribes to account updates.""" 95 | self._change_subscription("spot@private.account.v3.api", action) 96 | 97 | def account_deals(self, action: Action = Action.SUBSCRIBE) -> None: 98 | """Subscribes to account deals.""" 99 | self._change_subscription("spot@private.deals.v3.api", action) 100 | 101 | def account_orders(self, action: Action = Action.SUBSCRIBE) -> None: 102 | """Subscribes to account orders.""" 103 | self._change_subscription("spot@private.orders.v3.api", action) 104 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | python_version = 3.11 3 | show_error_codes = true 4 | follow_imports = silent 5 | local_partial_types = true 6 | strict_equality = true 7 | no_implicit_optional = true 8 | warn_incomplete_stub = true 9 | warn_redundant_casts = true 10 | warn_unused_configs = true 11 | warn_unused_ignores = true 12 | check_untyped_defs = true 13 | disallow_incomplete_defs = true 14 | disallow_subclassing_any = true 15 | disallow_untyped_decorators = true 16 | disallow_untyped_defs = true 17 | warn_unreachable = true 18 | ignore_missing_imports = True 19 | 20 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "mexc-api" 7 | version = "0.0.2" 8 | authors = [ 9 | { name="Floris272", email="florispuijk@outlook.com" }, 10 | ] 11 | description = "A wrapper for the Mexc rest and websocket api." 12 | readme = "README.md" 13 | license = {text = "MIT"} 14 | requires-python = ">=3.11" 15 | classifiers = [ 16 | "Programming Language :: Python :: 3", 17 | "License :: OSI Approved :: MIT License", 18 | "Operating System :: OS Independent", 19 | ] 20 | dependencies = [ 21 | "requests >= 2.31.0", 22 | "websocket-client >= 1.6.1" 23 | ] 24 | 25 | [project.urls] 26 | "Homepage" = "https://github.com/Floris272/py-mexc-api" 27 | "Bug Tracker" = "https://github.com/Floris272/py-mexc-api/issues" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | websocket-client 3 | --------------------------------------------------------------------------------