This commit is contained in:
Dobromir Popov
2025-07-14 17:56:09 +03:00
parent d53a2ba75d
commit 4a55c5ff03
11 changed files with 1509 additions and 118 deletions

View File

@ -17,17 +17,17 @@ import time
logger = logging.getLogger(__name__)
class ConfigSynchronizer:
"""Handles automatic synchronization of config parameters with MEXC API"""
"""Handles automatic synchronization of config parameters with exchange APIs"""
def __init__(self, config_path: str = "config.yaml", mexc_interface=None):
"""Initialize the config synchronizer
Args:
config_path: Path to the main config file
mexc_interface: MEXCInterface instance for API calls
mexc_interface: Exchange interface instance for API calls (maintains compatibility)
"""
self.config_path = config_path
self.mexc_interface = mexc_interface
self.exchange_interface = mexc_interface # Generic exchange interface
self.last_sync_time = None
self.sync_interval = 3600 # Sync every hour by default
self.backup_enabled = True
@ -130,15 +130,15 @@ class ConfigSynchronizer:
logger.info(f"CONFIG SYNC: Skipping sync, last sync was recent")
return sync_record
if not self.mexc_interface:
if not self.exchange_interface:
sync_record['status'] = 'error'
sync_record['errors'].append('No MEXC interface available')
logger.error("CONFIG SYNC: No MEXC interface available for fee sync")
sync_record['errors'].append('No exchange interface available')
logger.error("CONFIG SYNC: No exchange interface available for fee sync")
return sync_record
# Get current fees from MEXC API
logger.info("CONFIG SYNC: Fetching trading fees from MEXC API")
api_fees = self.mexc_interface.get_trading_fees()
logger.info("CONFIG SYNC: Fetching trading fees from exchange API")
api_fees = self.exchange_interface.get_trading_fees()
sync_record['api_response'] = api_fees
if api_fees.get('source') == 'fallback':
@ -205,7 +205,7 @@ class ConfigSynchronizer:
config['trading']['fee_sync_metadata'] = {
'last_sync': datetime.now().isoformat(),
'api_source': 'mexc',
'api_source': 'exchange', # Changed from 'mexc' to 'exchange'
'sync_enabled': True,
'api_commission_rates': {
'maker': api_fees.get('maker_commission', 0),
@ -288,7 +288,7 @@ class ConfigSynchronizer:
'sync_interval_seconds': self.sync_interval,
'latest_sync_result': latest_sync,
'total_syncs': len(self.sync_history),
'mexc_interface_available': self.mexc_interface is not None
'mexc_interface_available': self.exchange_interface is not None # Changed from mexc_interface to exchange_interface
}
except Exception as e:

View File

@ -22,7 +22,8 @@ import sys
# Add NN directory to path for exchange interfaces
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'NN'))
from NN.exchanges import MEXCInterface
from NN.exchanges.exchange_factory import ExchangeFactory
from NN.exchanges.exchange_interface import ExchangeInterface
from .config import get_config
from .config_sync import ConfigSynchronizer
@ -63,53 +64,68 @@ class TradeRecord:
hold_time_seconds: float = 0.0 # Hold time in seconds
class TradingExecutor:
"""Handles trade execution through MEXC API with risk management"""
"""Handles trade execution through multiple exchange APIs with risk management"""
def __init__(self, config_path: str = "config.yaml"):
"""Initialize the trading executor"""
self.config = get_config(config_path)
self.mexc_config = self.config.get('mexc_trading', {})
self.exchanges_config = self.config.get('exchanges', {})
self.trading_config = self.config.get('trading', {})
# Initialize MEXC interface
api_key = os.getenv('MEXC_API_KEY', self.mexc_config.get('api_key', ''))
api_secret = os.getenv('MEXC_SECRET_KEY', self.mexc_config.get('api_secret', ''))
# Initialize exchanges using factory
self.primary_exchange = ExchangeFactory.get_primary_exchange(self.exchanges_config)
self.all_exchanges = ExchangeFactory.create_multiple_exchanges(self.exchanges_config)
# Determine trading mode from unified config
trading_mode = self.mexc_config.get('trading_mode', 'simulation')
# Set primary exchange as main interface
self.exchange = self.primary_exchange
# Map trading mode to exchange test_mode and execution mode
if trading_mode == 'simulation':
exchange_test_mode = True
if not self.exchange:
logger.error("Failed to initialize primary exchange")
self.trading_enabled = False
self.simulation_mode = True
elif trading_mode == 'testnet':
exchange_test_mode = True
self.simulation_mode = False
elif trading_mode == 'live':
exchange_test_mode = False
self.simulation_mode = False
else:
logger.warning(f"Unknown trading_mode '{trading_mode}', defaulting to simulation")
exchange_test_mode = True
self.simulation_mode = True
primary_name = self.exchanges_config.get('primary', 'deribit')
primary_config = self.exchanges_config.get(primary_name, {})
# Determine trading and simulation modes
trading_mode = primary_config.get('trading_mode', 'simulation')
self.trading_enabled = self.trading_config.get('enabled', True)
self.simulation_mode = trading_mode == 'simulation'
logger.info(f"Trading Executor initialized with {primary_name} as primary exchange")
logger.info(f"Trading mode: {trading_mode}, Simulation: {self.simulation_mode}")
self.exchange = MEXCInterface(
api_key=api_key,
api_secret=api_secret,
test_mode=exchange_test_mode,
# Initialize config synchronizer with the primary exchange
self.config_sync = ConfigSynchronizer(
config_path=config_path,
mexc_interface=self.exchange if self.trading_enabled else None
)
# Trading state
self.positions: Dict[str, Position] = {}
self.trade_history: List[TradeRecord] = []
# Trading state management
self.current_position = {} # symbol -> position data
self.trade_history = []
self.daily_trades = 0
self.daily_pnl = 0.0
self.daily_loss = 0.0
self.last_trade_time = {}
self.trading_enabled = self.mexc_config.get('enabled', False)
self.trading_mode = trading_mode
self.consecutive_losses = 0 # Track consecutive losing trades
logger.debug(f"TRADING EXECUTOR: Initial trading_enabled state from config: {self.trading_enabled}")
# Store trading mode for compatibility
primary_name = self.exchanges_config.get('primary', 'deribit')
primary_config = self.exchanges_config.get(primary_name, {})
self.trading_mode = primary_config.get('trading_mode', 'simulation')
# Initialize session stats
self.session_start_time = datetime.now()
self.session_trades = 0
self.session_pnl = 0.0
# Position tracking
self.positions = {} # symbol -> Position object
self.trade_records = [] # List of TradeRecord objects
logger.info(f"TradingExecutor initialized - Trading: {self.trading_enabled}, Mode: {self.trading_mode}")
# Legacy compatibility (deprecated)
self.dry_run = self.simulation_mode
@ -155,24 +171,34 @@ class TradingExecutor:
logger.info(f"Trading Executor initialized - Mode: {self.trading_mode}, Enabled: {self.trading_enabled}")
def _connect_exchange(self) -> bool:
"""Connect to the MEXC exchange"""
def _safe_exchange_call(self, method_name: str, *args, **kwargs):
"""Safely call exchange methods with null checking"""
if not self.exchange:
logger.warning(f"No exchange interface available for {method_name}")
return None
try:
logger.debug("TRADING EXECUTOR: Calling self.exchange.connect()...")
connected = self.exchange.connect()
logger.debug(f"TRADING EXECUTOR: self.exchange.connect() returned: {connected}")
if connected:
logger.info("Successfully connected to MEXC exchange")
return True
method = getattr(self.exchange, method_name, None)
if method:
return method(*args, **kwargs)
else:
logger.error("Failed to connect to MEXC exchange: Connection returned False.")
if not self.dry_run:
logger.info("TRADING EXECUTOR: Setting trading_enabled to False due to connection failure.")
self.trading_enabled = False
return False
logger.error(f"Method {method_name} not available on exchange")
return None
except Exception as e:
logger.error(f"Error connecting to MEXC exchange: {e}. Setting trading_enabled to False.")
self.trading_enabled = False
logger.error(f"Error calling {method_name}: {e}")
return None
def _connect_exchange(self) -> bool:
"""Connect to the primary exchange"""
if not self.exchange:
logger.warning("No exchange interface available")
return False
if self.exchange.connect():
logger.info("Successfully connected to exchange")
return True
else:
logger.error("Failed to connect to exchange")
return False
def execute_signal(self, symbol: str, action: str, confidence: float,