fix merge

This commit is contained in:
Dobromir Popov
2025-10-02 23:50:08 +03:00
parent 8654e08028
commit a468c75c47
13 changed files with 150 additions and 14309 deletions

View File

@@ -84,64 +84,6 @@ class MEXCInterface(ExchangeInterface):
# This method is included for completeness but should not be used for spot trading
return symbol.replace('/', '_').upper()
<<<<<<< HEAD:NN/exchanges/mexc_interface.py
def _generate_signature(self, timestamp: str, method: str, endpoint: str, params: Dict[str, Any]) -> str:
"""Generate signature for private API calls using MEXC's official method"""
# MEXC signature format varies by method:
# For GET/DELETE: URL-encoded query string of alphabetically sorted parameters.
# For POST: JSON string of parameters (no sorting needed).
# The API-Secret is used as the HMAC SHA256 key.
# Remove signature from params to avoid circular inclusion
clean_params = {k: v for k, v in params.items() if k != 'signature'}
parameter_string: str
if method.upper() == "POST":
# For POST requests, the signature parameter is a JSON string
# Ensure sorting keys for consistent JSON string generation across runs
# even though MEXC says sorting is not required for POST params, it's good practice.
parameter_string = json.dumps(clean_params, sort_keys=True, separators=(',', ':'))
else:
# For GET/DELETE requests, parameters are spliced in dictionary order with & interval
sorted_params = sorted(clean_params.items())
parameter_string = '&'.join(f"{key}={str(value)}" for key, value in sorted_params)
# The string to be signed is: accessKey + timestamp + obtained parameter string.
string_to_sign = f"{self.api_key}{timestamp}{parameter_string}"
logger.debug(f"MEXC string to sign (method {method}): {string_to_sign}")
=======
def _generate_signature(self, params: Dict[str, Any]) -> str:
"""Generate signature for private API calls using MEXC's parameter ordering"""
# MEXC uses specific parameter ordering for signature generation
# Based on working Postman collection: symbol, side, type, quantity, price, timestamp, recvWindow, then others
# Remove signature if present
clean_params = {k: v for k, v in params.items() if k != 'signature'}
# MEXC parameter order (from working Postman collection)
mexc_order = ['symbol', 'side', 'type', 'quantity', 'price', 'timestamp', 'recvWindow']
ordered_params = []
# Add parameters in MEXC's expected order
for param_name in mexc_order:
if param_name in clean_params:
ordered_params.append(f"{param_name}={clean_params[param_name]}")
del clean_params[param_name]
# Add any remaining parameters in alphabetical order
for key in sorted(clean_params.keys()):
ordered_params.append(f"{key}={clean_params[key]}")
# Create query string
query_string = '&'.join(ordered_params)
logger.debug(f"MEXC signature query string: {query_string}")
>>>>>>> d49a473ed6f4aef55bfdd47d6370e53582be6b7b:core/exchanges/mexc_interface.py
# Generate HMAC SHA256 signature
signature = hmac.new(
self.api_secret.encode('utf-8'),
@@ -180,11 +122,6 @@ class MEXCInterface(ExchangeInterface):
return {}
def _send_private_request(self, method: str, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
<<<<<<< HEAD:NN/exchanges/mexc_interface.py
"""Send a private request to the exchange with proper signature"""
=======
"""Send a private request to the exchange with proper signature and MEXC error handling"""
>>>>>>> d49a473ed6f4aef55bfdd47d6370e53582be6b7b:core/exchanges/mexc_interface.py
if params is None:
params = {}
@@ -211,19 +148,7 @@ class MEXCInterface(ExchangeInterface):
if method.upper() == "GET":
response = self.session.get(url, headers=headers, params=params, timeout=10)
elif method.upper() == "POST":
<<<<<<< HEAD:NN/exchanges/mexc_interface.py
# MEXC expects POST parameters as JSON in the request body, not as query string
# The signature is generated from the JSON string of parameters.
# We need to exclude 'signature' from the JSON body sent, as it's for the header.
params_for_body = {k: v for k, v in params.items() if k != 'signature'}
response = self.session.post(url, headers=headers, json=params_for_body, timeout=10)
=======
# For POST requests, MEXC expects parameters as query parameters, not form data
# Based on Postman collection: Content-Type header is disabled
response = self.session.post(url, headers=headers, params=params, timeout=10)
elif method.upper() == "DELETE":
response = self.session.delete(url, headers=headers, params=params, timeout=10)
>>>>>>> d49a473ed6f4aef55bfdd47d6370e53582be6b7b:core/exchanges/mexc_interface.py
else:
logger.error(f"Unsupported method: {method}")
return None
@@ -312,31 +237,6 @@ class MEXCInterface(ExchangeInterface):
response = self._send_public_request('GET', endpoint, params)
<<<<<<< HEAD:NN/exchanges/mexc_interface.py
if isinstance(response, dict):
ticker_data: Dict[str, Any] = response
elif isinstance(response, list) and len(response) > 0:
found_ticker = next((item for item in response if item.get('symbol') == formatted_symbol), None)
if found_ticker:
ticker_data = found_ticker
=======
if response:
# MEXC ticker returns a dictionary if single symbol, list if all symbols
if isinstance(response, dict):
ticker_data = response
elif isinstance(response, list) and len(response) > 0:
# If the response is a list, try to find the specific symbol
found_ticker = None
for item in response:
if isinstance(item, dict) and item.get('symbol') == formatted_symbol:
found_ticker = item
break
if found_ticker:
ticker_data = found_ticker
else:
logger.error(f"Ticker data for {formatted_symbol} not found in response list.")
return None
>>>>>>> d49a473ed6f4aef55bfdd47d6370e53582be6b7b:core/exchanges/mexc_interface.py
else:
logger.error(f"Ticker data for {formatted_symbol} not found in response list.")
return None
@@ -396,71 +296,6 @@ class MEXCInterface(ExchangeInterface):
def place_order(self, symbol: str, side: str, order_type: str, quantity: float, price: Optional[float] = None) -> Dict[str, Any]:
"""Place a new order on MEXC."""
<<<<<<< HEAD:NN/exchanges/mexc_interface.py
formatted_symbol = self._format_spot_symbol(symbol)
# Check if symbol is supported for API trading
if not self.is_symbol_supported(symbol):
supported_symbols = self.get_api_symbols()
logger.error(f"Symbol {formatted_symbol} is not supported for API trading")
logger.info(f"Supported symbols include: {supported_symbols[:10]}...") # Show first 10
return {}
# Format quantity according to symbol precision requirements
formatted_quantity = self._format_quantity_for_symbol(formatted_symbol, quantity)
if formatted_quantity is None:
logger.error(f"MEXC: Failed to format quantity {quantity} for {formatted_symbol}")
return {}
# Handle order type restrictions for specific symbols
final_order_type = self._adjust_order_type_for_symbol(formatted_symbol, order_type.upper())
# Get price for limit orders
final_price = price
if final_order_type == 'LIMIT' and price is None:
# Get current market price
ticker = self.get_ticker(symbol)
if ticker and 'last' in ticker:
final_price = ticker['last']
logger.info(f"MEXC: Using market price ${final_price:.2f} for LIMIT order")
else:
logger.error(f"MEXC: Could not get market price for LIMIT order on {formatted_symbol}")
return {}
endpoint = "order"
params: Dict[str, Any] = {
'symbol': formatted_symbol,
'side': side.upper(),
'type': final_order_type,
'quantity': str(formatted_quantity) # Quantity must be a string
}
if final_price is not None:
params['price'] = str(final_price) # Price must be a string for limit orders
logger.info(f"MEXC: Placing {side.upper()} {final_order_type} order for {formatted_quantity} {formatted_symbol} at price {final_price}")
try:
# MEXC API endpoint for placing orders is /api/v3/order (POST)
order_result = self._send_private_request('POST', endpoint, params)
if order_result is not None:
logger.info(f"MEXC: Order placed successfully: {order_result}")
return order_result
else:
logger.error(f"MEXC: Error placing order: request returned None")
=======
try:
logger.info(f"MEXC: place_order called with symbol={symbol}, side={side}, order_type={order_type}, quantity={quantity}, price={price}")
formatted_symbol = self._format_spot_symbol(symbol)
logger.info(f"MEXC: Formatted symbol: {symbol} -> {formatted_symbol}")
# Check if symbol is supported for API trading
if not self.is_symbol_supported(symbol):
supported_symbols = self.get_api_symbols()
logger.error(f"Symbol {formatted_symbol} is not supported for API trading")
logger.info(f"Supported symbols include: {supported_symbols[:10]}...") # Show first 10
>>>>>>> d49a473ed6f4aef55bfdd47d6370e53582be6b7b:core/exchanges/mexc_interface.py
return {}
# Round quantity to MEXC precision requirements and ensure minimum order value