164 lines
5.9 KiB
Python
164 lines
5.9 KiB
Python
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() |