mexc-websocket added
All checks were successful
SonarQube Scan / SonarQube Trigger (push) Successful in 1m12s

This commit is contained in:
dongho
2024-12-20 20:48:02 +09:00
parent 1b7cd7960e
commit 9e9cbf3547
6 changed files with 4419 additions and 0 deletions

164
pymexc/base.py Normal file
View File

@ -0,0 +1,164 @@
from abc import ABC, abstractclassmethod
from typing import Union, Literal
import hmac
import hashlib
import requests
from urllib.parse import urlencode
import logging
import time
logger = logging.getLogger(__name__)
class MexcAPIError(Exception):
pass
class MexcSDK(ABC):
"""
Initializes a new instance of the class with the given `api_key` and `api_secret` parameters.
:param api_key: A string representing the API key.
:param api_secret: A string representing the API secret.
:param base_url: A string representing the base URL of the API.
"""
def __init__(self, api_key: str, api_secret: str, base_url: str, proxies: dict = None):
self.api_key = api_key
self.api_secret = api_secret
self.recvWindow = 5000
self.base_url = base_url
self.session = requests.Session()
self.session.headers.update({
"Content-Type": "application/json",
})
if proxies:
self.session.proxies.update(proxies)
@abstractclassmethod
def sign(self, **kwargs) -> str:
...
@abstractclassmethod
def call(self, method: Union[Literal["GET"], Literal["POST"], Literal["PUT"], Literal["DELETE"]], router: str, *args, **kwargs) -> dict:
...
class _SpotHTTP(MexcSDK):
def __init__(self, api_key: str = None, api_secret: str = None, proxies: dict = None):
super().__init__(api_key, api_secret, "https://api.mexc.com", proxies = proxies)
self.session.headers.update({
"X-MEXC-APIKEY": self.api_key
})
def sign(self, query_string: str) -> str:
"""
Generates a signature for an API request using HMAC SHA256 encryption.
Args:
**kwargs: Arbitrary keyword arguments representing request parameters.
Returns:
A hexadecimal string representing the signature of the request.
"""
# Generate signature
signature = hmac.new(self.api_secret.encode('utf-8'), query_string.encode('utf-8'), hashlib.sha256).hexdigest()
return signature
def call(self, method: Union[Literal["GET"], Literal["POST"], Literal["PUT"], Literal["DELETE"]], router: str, auth: bool = True, *args, **kwargs) -> dict:
if not router.startswith("/"):
router = f"/{router}"
# clear None values
kwargs = {k: v for k, v in kwargs.items() if v is not None}
if kwargs.get('params'):
kwargs['params'] = {k: v for k, v in kwargs['params'].items() if v is not None}
else:
kwargs['params'] = {}
timestamp = str(int(time.time() * 1000))
kwargs['params']['timestamp'] = timestamp
kwargs['params']['recvWindow'] = self.recvWindow
kwargs['params'] = {k: v for k, v in sorted(kwargs['params'].items())}
params = urlencode(kwargs.pop('params'), doseq=True).replace('+', '%20')
if self.api_key and self.api_secret and auth:
params += "&signature=" + self.sign(params)
response = self.session.request(method, f"{self.base_url}{router}", params = params, *args, **kwargs)
if not response.ok:
raise MexcAPIError(f'(code={response.json()["code"]}): {response.json()["msg"]}')
return response.json()
class _FuturesHTTP(MexcSDK):
def __init__(self, api_key: str = None, api_secret: str = None, proxies: dict = None):
super().__init__(api_key, api_secret, "https://contract.mexc.com", proxies = proxies)
self.session.headers.update({
"Content-Type": "application/json",
"ApiKey": self.api_key
})
def sign(self, timestamp: str, **kwargs) -> str:
"""
Generates a signature for an API request using HMAC SHA256 encryption.
:param timestamp: A string representing the timestamp of the request.
:type timestamp: str
:param kwargs: Arbitrary keyword arguments representing request parameters.
:type kwargs: dict
:return: A hexadecimal string representing the signature of the request.
:rtype: str
"""
# Generate signature
query_string = "&".join([f"{k}={v}" for k, v in sorted(kwargs.items())])
query_string = self.api_key + timestamp + query_string
signature = hmac.new(self.api_secret.encode('utf-8'), query_string.encode('utf-8'), hashlib.sha256).hexdigest()
return signature
def call(self, method: Union[Literal["GET"], Literal["POST"], Literal["PUT"], Literal["DELETE"]], router: str, *args, **kwargs) -> dict:
"""
Makes a request to the specified HTTP method and router using the provided arguments.
:param method: A string that represents the HTTP method(GET, POST, PUT, or DELETE) to be used.
:type method: str
:param router: A string that represents the API endpoint to be called.
:type router: str
:param *args: Variable length argument list.
:type *args: list
:param **kwargs: Arbitrary keyword arguments.
:type **kwargs: dict
:return: A dictionary containing the JSON response of the request.
"""
if not router.startswith("/"):
router = f"/{router}"
# clear None values
kwargs = {k: v for k, v in kwargs.items() if v is not None}
for variant in ('params', 'json'):
if kwargs.get(variant):
kwargs[variant] = {k: v for k, v in kwargs[variant].items() if v is not None}
if self.api_key and self.api_secret:
# add signature
timestamp = str(int(time.time() * 1000))
kwargs['headers'] = {
"Request-Time": timestamp,
"Signature": self.sign(timestamp, **kwargs[variant])
}
response = self.session.request(method, f"{self.base_url}{router}", *args, **kwargs)
return response.json()