mex api progress
This commit is contained in:
parent
ae62d893bc
commit
2d2db276f3
@ -23,15 +23,15 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
"""
|
"""
|
||||||
super().__init__(api_key, api_secret, test_mode)
|
super().__init__(api_key, api_secret, test_mode)
|
||||||
self.base_url = "https://api.mexc.com"
|
self.base_url = "https://api.mexc.com"
|
||||||
self.api_version = "v3"
|
self.api_version = "api/v3"
|
||||||
|
|
||||||
def connect(self) -> bool:
|
def connect(self) -> bool:
|
||||||
"""Connect to MEXC API."""
|
"""Connect to MEXC API."""
|
||||||
if not self.api_key or not self.api_secret:
|
if not self.api_key or not self.api_secret:
|
||||||
logger.warning("MEXC API credentials not provided. Running in read-only mode.")
|
logger.warning("MEXC API credentials not provided. Running in read-only mode.")
|
||||||
try:
|
try:
|
||||||
# Test public API connection by getting ticker data for BTC/USDT
|
# Test public API connection by getting server time (ping)
|
||||||
self.get_ticker("BTC/USDT")
|
self.get_server_time()
|
||||||
logger.info("Successfully connected to MEXC API in read-only mode")
|
logger.info("Successfully connected to MEXC API in read-only mode")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -41,7 +41,7 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
try:
|
try:
|
||||||
# Test connection by getting account info
|
# Test connection by getting account info
|
||||||
self.get_account_info()
|
self.get_account_info()
|
||||||
logger.info("Successfully connected to MEXC API")
|
logger.info("Successfully connected to MEXC API with authentication")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to connect to MEXC API: {str(e)}")
|
logger.error(f"Failed to connect to MEXC API: {str(e)}")
|
||||||
@ -49,7 +49,9 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
|
|
||||||
def _generate_signature(self, params: Dict[str, Any]) -> str:
|
def _generate_signature(self, params: Dict[str, Any]) -> str:
|
||||||
"""Generate signature for authenticated requests."""
|
"""Generate signature for authenticated requests."""
|
||||||
query_string = urlencode(params)
|
# Sort parameters by key for consistent signature generation
|
||||||
|
sorted_params = sorted(params.items())
|
||||||
|
query_string = urlencode(sorted_params)
|
||||||
signature = hmac.new(
|
signature = hmac.new(
|
||||||
self.api_secret.encode('utf-8'),
|
self.api_secret.encode('utf-8'),
|
||||||
query_string.encode('utf-8'),
|
query_string.encode('utf-8'),
|
||||||
@ -81,26 +83,32 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
if params is None:
|
if params is None:
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
# Add timestamp
|
# Add timestamp and recvWindow as required by MEXC
|
||||||
params['timestamp'] = int(time.time() * 1000)
|
params['timestamp'] = int(time.time() * 1000)
|
||||||
|
if 'recvWindow' not in params:
|
||||||
|
params['recvWindow'] = 5000
|
||||||
|
|
||||||
# Generate signature
|
# Generate signature using the correct MEXC format
|
||||||
signature = self._generate_signature(params)
|
signature = self._generate_signature(params)
|
||||||
params['signature'] = signature
|
params['signature'] = signature
|
||||||
|
|
||||||
# Set headers
|
# Set headers as required by MEXC documentation
|
||||||
headers = {
|
headers = {
|
||||||
'X-MEXC-APIKEY': self.api_key
|
'X-MEXC-APIKEY': self.api_key,
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
}
|
}
|
||||||
|
|
||||||
url = f"{self.base_url}/{self.api_version}/{endpoint}"
|
url = f"{self.base_url}/{self.api_version}/{endpoint}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if method.upper() == 'GET':
|
if method.upper() == 'GET':
|
||||||
|
# For GET requests, send parameters as query string
|
||||||
response = requests.get(url, params=params, headers=headers)
|
response = requests.get(url, params=params, headers=headers)
|
||||||
elif method.upper() == 'POST':
|
elif method.upper() == 'POST':
|
||||||
response = requests.post(url, json=params, headers=headers)
|
# For POST requests, send as form data in request body per MEXC documentation
|
||||||
|
response = requests.post(url, data=params, headers=headers)
|
||||||
elif method.upper() == 'DELETE':
|
elif method.upper() == 'DELETE':
|
||||||
|
# For DELETE requests, send parameters as query string
|
||||||
response = requests.delete(url, params=params, headers=headers)
|
response = requests.delete(url, params=params, headers=headers)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported HTTP method: {method}")
|
raise ValueError(f"Unsupported HTTP method: {method}")
|
||||||
@ -109,11 +117,23 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
return response.json()
|
return response.json()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in private request to {endpoint}: {str(e)}")
|
logger.error(f"Error in private request to {endpoint}: {str(e)}")
|
||||||
|
if hasattr(e, 'response') and e.response is not None:
|
||||||
|
logger.error(f"Response status: {e.response.status_code}")
|
||||||
|
logger.error(f"Response content: {e.response.text}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def get_server_time(self) -> Dict[str, Any]:
|
||||||
|
"""Get server time (ping test)."""
|
||||||
|
return self._send_public_request('GET', 'time')
|
||||||
|
|
||||||
|
def ping(self) -> Dict[str, Any]:
|
||||||
|
"""Test connectivity to the Rest API."""
|
||||||
|
return self._send_public_request('GET', 'ping')
|
||||||
|
|
||||||
def get_account_info(self) -> Dict[str, Any]:
|
def get_account_info(self) -> Dict[str, Any]:
|
||||||
"""Get account information."""
|
"""Get account information."""
|
||||||
return self._send_private_request('GET', 'account')
|
params = {'recvWindow': 5000}
|
||||||
|
return self._send_private_request('GET', 'account', params)
|
||||||
|
|
||||||
def get_balance(self, asset: str) -> float:
|
def get_balance(self, asset: str) -> float:
|
||||||
"""Get balance of a specific asset.
|
"""Get balance of a specific asset.
|
||||||
@ -125,7 +145,8 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
float: Available balance of the asset
|
float: Available balance of the asset
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
account_info = self._send_private_request('GET', 'account')
|
params = {'recvWindow': 5000}
|
||||||
|
account_info = self._send_private_request('GET', 'account', params)
|
||||||
balances = account_info.get('balances', [])
|
balances = account_info.get('balances', [])
|
||||||
|
|
||||||
for balance in balances:
|
for balance in balances:
|
||||||
@ -148,61 +169,86 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
dict: Ticker data including price information
|
dict: Ticker data including price information
|
||||||
"""
|
"""
|
||||||
mexc_symbol = symbol.replace('/', '')
|
mexc_symbol = symbol.replace('/', '')
|
||||||
|
|
||||||
|
# Use official MEXC API endpoints from documentation
|
||||||
endpoints_to_try = [
|
endpoints_to_try = [
|
||||||
('ticker/price', {'symbol': mexc_symbol}),
|
('ticker/price', {'symbol': mexc_symbol}), # Symbol Price Ticker
|
||||||
('ticker', {'symbol': mexc_symbol}),
|
('ticker/24hr', {'symbol': mexc_symbol}), # 24hr Ticker Price Change Statistics
|
||||||
('ticker/24hr', {'symbol': mexc_symbol}),
|
('ticker/bookTicker', {'symbol': mexc_symbol}), # Symbol Order Book Ticker
|
||||||
('ticker/bookTicker', {'symbol': mexc_symbol}),
|
|
||||||
('market/ticker', {'symbol': mexc_symbol})
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for endpoint, params in endpoints_to_try:
|
for endpoint, params in endpoints_to_try:
|
||||||
try:
|
try:
|
||||||
logger.info(f"Trying to get ticker from endpoint: {endpoint}")
|
logger.debug(f"Trying MEXC endpoint: {endpoint} for {mexc_symbol}")
|
||||||
response = self._send_public_request('GET', endpoint, params)
|
response = self._send_public_request('GET', endpoint, params)
|
||||||
|
|
||||||
|
if not response:
|
||||||
|
continue
|
||||||
|
|
||||||
# Handle the response based on structure
|
# Handle the response based on structure
|
||||||
if isinstance(response, dict):
|
if isinstance(response, dict):
|
||||||
# Single ticker response
|
|
||||||
ticker = response
|
ticker = response
|
||||||
elif isinstance(response, list) and len(response) > 0:
|
elif isinstance(response, list) and len(response) > 0:
|
||||||
# List of tickers, find the one we want
|
# Find the specific symbol in list response
|
||||||
ticker = None
|
ticker = None
|
||||||
for t in response:
|
for t in response:
|
||||||
if t.get('symbol') == mexc_symbol:
|
if t.get('symbol') == mexc_symbol:
|
||||||
ticker = t
|
ticker = t
|
||||||
break
|
break
|
||||||
if ticker is None:
|
if ticker is None:
|
||||||
continue # Try next endpoint if not found
|
continue
|
||||||
else:
|
else:
|
||||||
continue # Try next endpoint if unexpected response
|
continue
|
||||||
|
|
||||||
# Convert to a standardized format with defaults for missing fields
|
# Convert to standardized format based on MEXC API response
|
||||||
current_time = int(time.time() * 1000)
|
current_time = int(time.time() * 1000)
|
||||||
|
|
||||||
|
# Handle different response formats from different endpoints
|
||||||
|
if 'price' in ticker:
|
||||||
|
# ticker/price endpoint
|
||||||
|
price = float(ticker['price'])
|
||||||
result = {
|
result = {
|
||||||
'symbol': symbol,
|
'symbol': symbol,
|
||||||
'bid': float(ticker.get('bidPrice', ticker.get('bid', 0))),
|
'bid': price, # Use price as fallback
|
||||||
'ask': float(ticker.get('askPrice', ticker.get('ask', 0))),
|
'ask': price, # Use price as fallback
|
||||||
'last': float(ticker.get('price', ticker.get('lastPrice', ticker.get('last', 0)))),
|
'last': price,
|
||||||
'volume': float(ticker.get('volume', ticker.get('quoteVolume', 0))),
|
'volume': 0, # Not available in price endpoint
|
||||||
'timestamp': int(ticker.get('time', ticker.get('closeTime', current_time)))
|
'timestamp': current_time
|
||||||
}
|
}
|
||||||
|
elif 'lastPrice' in ticker:
|
||||||
|
# ticker/24hr endpoint
|
||||||
|
result = {
|
||||||
|
'symbol': symbol,
|
||||||
|
'bid': float(ticker.get('bidPrice', ticker.get('lastPrice', 0))),
|
||||||
|
'ask': float(ticker.get('askPrice', ticker.get('lastPrice', 0))),
|
||||||
|
'last': float(ticker.get('lastPrice', 0)),
|
||||||
|
'volume': float(ticker.get('volume', ticker.get('quoteVolume', 0))),
|
||||||
|
'timestamp': int(ticker.get('closeTime', current_time))
|
||||||
|
}
|
||||||
|
elif 'bidPrice' in ticker:
|
||||||
|
# ticker/bookTicker endpoint
|
||||||
|
result = {
|
||||||
|
'symbol': symbol,
|
||||||
|
'bid': float(ticker.get('bidPrice', 0)),
|
||||||
|
'ask': float(ticker.get('askPrice', 0)),
|
||||||
|
'last': float(ticker.get('bidPrice', 0)), # Use bid as fallback for last
|
||||||
|
'volume': 0, # Not available in book ticker
|
||||||
|
'timestamp': current_time
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
# Ensure we have at least a price
|
# Validate we have a valid price
|
||||||
if result['last'] > 0:
|
if result['last'] > 0:
|
||||||
logger.info(f"Successfully got ticker from {endpoint} for {symbol}: {result['last']}")
|
logger.info(f"✅ MEXC: Got ticker from {endpoint} for {symbol}: ${result['last']:.2f}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ CRITICAL: Failed to get ticker for {symbol}: {e}")
|
logger.warning(f"MEXC endpoint {endpoint} failed for {symbol}: {e}")
|
||||||
logger.error("❌ NO DUMMY DATA FALLBACK - Real market data required")
|
continue
|
||||||
# Return None instead of dummy data - let calling code handle the failure
|
|
||||||
return None
|
|
||||||
|
|
||||||
# If we get here, all endpoints failed
|
# All endpoints failed
|
||||||
logger.error(f"All ticker endpoints failed for {symbol}")
|
logger.error(f"❌ MEXC: All ticker endpoints failed for {symbol}")
|
||||||
|
|
||||||
# Return None instead of dummy data - let calling code handle the failure
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def place_order(self, symbol: str, side: str, order_type: str,
|
def place_order(self, symbol: str, side: str, order_type: str,
|
||||||
@ -211,8 +257,8 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
symbol: Trading symbol (e.g., 'BTC/USDT')
|
symbol: Trading symbol (e.g., 'BTC/USDT')
|
||||||
side: Order side ('buy' or 'sell')
|
side: Order side ('BUY' or 'SELL')
|
||||||
order_type: Order type ('market', 'limit', etc.)
|
order_type: Order type ('MARKET', 'LIMIT', etc.)
|
||||||
quantity: Order quantity
|
quantity: Order quantity
|
||||||
price: Order price (for limit orders)
|
price: Order price (for limit orders)
|
||||||
|
|
||||||
@ -220,22 +266,30 @@ class MEXCInterface(ExchangeInterface):
|
|||||||
dict: Order information including order ID
|
dict: Order information including order ID
|
||||||
"""
|
"""
|
||||||
mexc_symbol = symbol.replace('/', '')
|
mexc_symbol = symbol.replace('/', '')
|
||||||
|
|
||||||
|
# Prepare order parameters according to MEXC API
|
||||||
params = {
|
params = {
|
||||||
'symbol': mexc_symbol,
|
'symbol': mexc_symbol,
|
||||||
'side': side.upper(),
|
'side': side.upper(),
|
||||||
'type': order_type.upper(),
|
'type': order_type.upper(),
|
||||||
'quantity': quantity,
|
'quantity': str(quantity), # MEXC expects string format
|
||||||
|
'recvWindow': 5000
|
||||||
}
|
}
|
||||||
|
|
||||||
if order_type.lower() == 'limit' and price is not None:
|
# Add price and timeInForce for limit orders
|
||||||
params['price'] = price
|
if order_type.upper() == 'LIMIT':
|
||||||
|
if price is None:
|
||||||
|
raise ValueError("Price is required for LIMIT orders")
|
||||||
|
params['price'] = str(price)
|
||||||
params['timeInForce'] = 'GTC' # Good Till Cancelled
|
params['timeInForce'] = 'GTC' # Good Till Cancelled
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
logger.info(f"MEXC: Placing {side} {order_type} order for {symbol}: {quantity} @ {price}")
|
||||||
order_result = self._send_private_request('POST', 'order', params)
|
order_result = self._send_private_request('POST', 'order', params)
|
||||||
|
logger.info(f"MEXC: Order placed successfully: {order_result.get('orderId', 'N/A')}")
|
||||||
return order_result
|
return order_result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error placing {side} {order_type} order for {symbol}: {str(e)}")
|
logger.error(f"MEXC: Error placing {side} {order_type} order for {symbol}: {str(e)}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def cancel_order(self, symbol: str, order_id: str) -> bool:
|
def cancel_order(self, symbol: str, order_id: str) -> bool:
|
||||||
|
16
config.yaml
16
config.yaml
@ -151,18 +151,18 @@ mexc_trading:
|
|||||||
|
|
||||||
# Risk management
|
# Risk management
|
||||||
max_daily_loss_usd: 5.0 # Stop trading if daily loss exceeds $5
|
max_daily_loss_usd: 5.0 # Stop trading if daily loss exceeds $5
|
||||||
max_concurrent_positions: 1 # Only 1 position at a time for testing
|
max_concurrent_positions: 3 # Only 1 position at a time for testing
|
||||||
max_trades_per_hour: 2 # Maximum 2 trades per hour
|
max_trades_per_hour: 60 # Maximum 60 trades per hour
|
||||||
min_trade_interval_seconds: 300 # Minimum 5 minutes between trades
|
min_trade_interval_seconds: 30 Minimum between trades
|
||||||
|
|
||||||
# Order configuration
|
# Order configuration
|
||||||
order_type: "market" # Use market orders for immediate execution
|
order_type: "market" # Use market orders for immediate execution
|
||||||
timeout_seconds: 30 # Order timeout
|
timeout_seconds: 30 # Order timeout
|
||||||
retry_attempts: 3 # Number of retry attempts for failed orders
|
retry_attempts: 0 # Number of retry attempts for failed orders
|
||||||
|
|
||||||
# Safety features
|
# Safety features
|
||||||
dry_run_mode: true # Log trades but don't execute (for testing)
|
dry_run_mode: false # Execute real trades (was true for testing)
|
||||||
require_confirmation: true # Require manual confirmation for trades
|
require_confirmation: false # No manual confirmation for live trading
|
||||||
emergency_stop: false # Emergency stop all trading
|
emergency_stop: false # Emergency stop all trading
|
||||||
|
|
||||||
# Supported symbols for live trading
|
# Supported symbols for live trading
|
||||||
@ -178,8 +178,8 @@ mexc_trading:
|
|||||||
|
|
||||||
# Memory Management
|
# Memory Management
|
||||||
memory:
|
memory:
|
||||||
total_limit_gb: 8.0 # Total system memory limit
|
total_limit_gb: 28.0 # Total system memory limit
|
||||||
model_limit_gb: 2.0 # Per-model memory limit
|
model_limit_gb: 4.0 # Per-model memory limit
|
||||||
cleanup_interval: 1800 # Memory cleanup every 30 minutes
|
cleanup_interval: 1800 # Memory cleanup every 30 minutes
|
||||||
|
|
||||||
# Web Dashboard
|
# Web Dashboard
|
||||||
|
71
test_mexc_public_api.py
Normal file
71
test_mexc_public_api.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test script for MEXC public API endpoints
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Add project root to path
|
||||||
|
sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
from NN.exchanges.mexc_interface import MEXCInterface
|
||||||
|
|
||||||
|
# Setup logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def test_mexc_public_api():
|
||||||
|
"""Test MEXC public API endpoints"""
|
||||||
|
print("="*60)
|
||||||
|
print("TESTING MEXC PUBLIC API")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Initialize MEXC interface without API keys (public access only)
|
||||||
|
mexc = MEXCInterface()
|
||||||
|
|
||||||
|
print("\n1. Testing server connectivity...")
|
||||||
|
try:
|
||||||
|
# Test ping
|
||||||
|
ping_result = mexc.ping()
|
||||||
|
print(f"✅ Ping successful: {ping_result}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Ping failed: {e}")
|
||||||
|
|
||||||
|
print("\n2. Testing server time...")
|
||||||
|
try:
|
||||||
|
# Test server time
|
||||||
|
time_result = mexc.get_server_time()
|
||||||
|
print(f"✅ Server time: {time_result}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Server time failed: {e}")
|
||||||
|
|
||||||
|
print("\n3. Testing ticker data...")
|
||||||
|
symbols_to_test = ['BTC/USDT', 'ETH/USDT']
|
||||||
|
|
||||||
|
for symbol in symbols_to_test:
|
||||||
|
try:
|
||||||
|
ticker = mexc.get_ticker(symbol)
|
||||||
|
if ticker:
|
||||||
|
print(f"✅ {symbol}: ${ticker['last']:.2f} (bid: ${ticker['bid']:.2f}, ask: ${ticker['ask']:.2f})")
|
||||||
|
else:
|
||||||
|
print(f"❌ {symbol}: No data returned")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ {symbol}: Error - {e}")
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("PUBLIC API TEST COMPLETED")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error initializing MEXC interface: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_mexc_public_api()
|
@ -149,6 +149,12 @@ class TradingDashboard:
|
|||||||
if self.trading_executor and hasattr(self.trading_executor, 'get_account_balance'):
|
if self.trading_executor and hasattr(self.trading_executor, 'get_account_balance'):
|
||||||
logger.info("Fetching initial balance from MEXC...")
|
logger.info("Fetching initial balance from MEXC...")
|
||||||
|
|
||||||
|
# Check if trading is enabled and not in dry run mode
|
||||||
|
if not self.trading_executor.trading_enabled:
|
||||||
|
logger.warning("MEXC: Trading not enabled - using default balance")
|
||||||
|
elif self.trading_executor.dry_run:
|
||||||
|
logger.warning("MEXC: Dry run mode enabled - using default balance")
|
||||||
|
else:
|
||||||
# Get USDT balance from MEXC
|
# Get USDT balance from MEXC
|
||||||
balance_info = self.trading_executor.get_account_balance()
|
balance_info = self.trading_executor.get_account_balance()
|
||||||
if balance_info and 'USDT' in balance_info:
|
if balance_info and 'USDT' in balance_info:
|
||||||
@ -157,18 +163,20 @@ class TradingDashboard:
|
|||||||
logger.info(f"MEXC: Retrieved USDT balance: ${usdt_balance:.2f}")
|
logger.info(f"MEXC: Retrieved USDT balance: ${usdt_balance:.2f}")
|
||||||
return usdt_balance
|
return usdt_balance
|
||||||
else:
|
else:
|
||||||
logger.warning("MEXC: No USDT balance found")
|
logger.warning("MEXC: No USDT balance found in account")
|
||||||
else:
|
else:
|
||||||
logger.warning("MEXC: Failed to retrieve balance info")
|
logger.error("MEXC: Failed to retrieve balance info from API")
|
||||||
else:
|
else:
|
||||||
logger.info("MEXC: Trading executor not available for balance retrieval")
|
logger.info("MEXC: Trading executor not available for balance retrieval")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting MEXC balance: {e}")
|
logger.error(f"Error getting MEXC balance: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
# Fallback to default
|
# Fallback to default
|
||||||
default_balance = 100.0
|
default_balance = 100.0
|
||||||
logger.info(f"Using default starting balance: ${default_balance:.2f}")
|
logger.warning(f"Using default starting balance: ${default_balance:.2f}")
|
||||||
return default_balance
|
return default_balance
|
||||||
|
|
||||||
def _setup_layout(self):
|
def _setup_layout(self):
|
||||||
@ -180,7 +188,7 @@ class TradingDashboard:
|
|||||||
html.I(className="fas fa-chart-line me-2"),
|
html.I(className="fas fa-chart-line me-2"),
|
||||||
"Live Trading Dashboard"
|
"Live Trading Dashboard"
|
||||||
], className="text-white mb-1"),
|
], className="text-white mb-1"),
|
||||||
html.P(f"Ultra-Fast Updates • Starting Balance: ${self.starting_balance:,.0f}",
|
html.P(f"Ultra-Fast Updates • Portfolio: ${self.starting_balance:,.0f} • {'MEXC Live' if (self.trading_executor and self.trading_executor.trading_enabled and not self.trading_executor.dry_run) else 'Demo Mode'}",
|
||||||
className="text-light mb-0 opacity-75 small")
|
className="text-light mb-0 opacity-75 small")
|
||||||
], className="bg-dark p-2 mb-2"),
|
], className="bg-dark p-2 mb-2"),
|
||||||
|
|
||||||
@ -530,11 +538,12 @@ class TradingDashboard:
|
|||||||
trade_count_text = f"{len(self.session_trades)}"
|
trade_count_text = f"{len(self.session_trades)}"
|
||||||
portfolio_text = f"${portfolio_value:,.2f}"
|
portfolio_text = f"${portfolio_value:,.2f}"
|
||||||
|
|
||||||
# MEXC status
|
# MEXC status with detailed information
|
||||||
if self.trading_executor and self.trading_executor.trading_enabled:
|
if self.trading_executor and self.trading_executor.trading_enabled:
|
||||||
mexc_status = "LIVE"
|
if self.trading_executor.dry_run:
|
||||||
elif self.trading_executor and self.trading_executor.dry_run:
|
|
||||||
mexc_status = "DRY RUN"
|
mexc_status = "DRY RUN"
|
||||||
|
else:
|
||||||
|
mexc_status = "LIVE"
|
||||||
else:
|
else:
|
||||||
mexc_status = "OFFLINE"
|
mexc_status = "OFFLINE"
|
||||||
|
|
||||||
@ -1404,16 +1413,24 @@ class TradingDashboard:
|
|||||||
# Add MEXC execution status to decision record
|
# Add MEXC execution status to decision record
|
||||||
decision['mexc_executed'] = mexc_success
|
decision['mexc_executed'] = mexc_success
|
||||||
|
|
||||||
# Calculate USD-based position size for testing ($1 orders)
|
# Calculate position size based on confidence and configuration
|
||||||
if current_price and current_price > 0:
|
if current_price and current_price > 0:
|
||||||
usd_size = 1.0 # $1 per trade for testing
|
# Get position sizing from trading executor configuration
|
||||||
|
if self.trading_executor:
|
||||||
|
usd_size = self.trading_executor._calculate_position_size(decision['confidence'], current_price)
|
||||||
|
else:
|
||||||
|
# Fallback calculation based on confidence
|
||||||
|
max_usd = 1.0 # Default max position
|
||||||
|
min_usd = 0.1 # Default min position
|
||||||
|
usd_size = max(min_usd, min(max_usd * decision['confidence'], max_usd))
|
||||||
|
|
||||||
position_size = usd_size / current_price # Convert USD to crypto amount
|
position_size = usd_size / current_price # Convert USD to crypto amount
|
||||||
decision['size'] = round(position_size, 6) # Update decision with calculated size
|
decision['size'] = round(position_size, 6) # Update decision with calculated size
|
||||||
decision['usd_size'] = usd_size # Track USD amount for logging
|
decision['usd_size'] = usd_size # Track USD amount for logging
|
||||||
else:
|
else:
|
||||||
# Fallback if no price available
|
# Fallback if no price available
|
||||||
decision['size'] = 0.001
|
decision['size'] = 0.001
|
||||||
decision['usd_size'] = 1.0
|
decision['usd_size'] = 0.1
|
||||||
|
|
||||||
if decision['action'] == 'BUY':
|
if decision['action'] == 'BUY':
|
||||||
# First, close any existing SHORT position
|
# First, close any existing SHORT position
|
||||||
@ -1485,7 +1502,7 @@ class TradingDashboard:
|
|||||||
trade_record['fees'] = fee
|
trade_record['fees'] = fee
|
||||||
self.session_trades.append(trade_record)
|
self.session_trades.append(trade_record)
|
||||||
|
|
||||||
logger.info(f"[TRADE] OPENED LONG: {decision['size']:.6f} (~${decision.get('usd_size', 1.0):.2f}) @ ${decision['price']:.2f} (confidence: {decision['confidence']:.1%})")
|
logger.info(f"[TRADE] OPENED LONG: {decision['size']:.6f} (${decision.get('usd_size', 0.1):.2f}) @ ${decision['price']:.2f} (confidence: {decision['confidence']:.1%})")
|
||||||
|
|
||||||
elif self.current_position['side'] == 'LONG':
|
elif self.current_position['side'] == 'LONG':
|
||||||
# Already have a long position - could add to it or replace it
|
# Already have a long position - could add to it or replace it
|
||||||
@ -1610,7 +1627,7 @@ class TradingDashboard:
|
|||||||
trade_record['fees'] = fee
|
trade_record['fees'] = fee
|
||||||
self.session_trades.append(trade_record)
|
self.session_trades.append(trade_record)
|
||||||
|
|
||||||
logger.info(f"[TRADE] OPENED SHORT: {decision['size']:.6f} (~${decision.get('usd_size', 1.0):.2f}) @ ${decision['price']:.2f} (confidence: {decision['confidence']:.1%})")
|
logger.info(f"[TRADE] OPENED SHORT: {decision['size']:.6f} (${decision.get('usd_size', 0.1):.2f}) @ ${decision['price']:.2f} (confidence: {decision['confidence']:.1%})")
|
||||||
|
|
||||||
elif self.current_position['side'] == 'SHORT':
|
elif self.current_position['side'] == 'SHORT':
|
||||||
# Already have a short position - could add to it or replace it
|
# Already have a short position - could add to it or replace it
|
||||||
|
Loading…
x
Reference in New Issue
Block a user