Files
mt5_python_ea_suite/execution/realtime_trader.py
T
songkunling 2ad610d8fd add ml
2025-08-14 23:17:12 +08:00

280 lines
12 KiB
Python

import time
import signal
import sys
import json
import pandas as pd
from datetime import datetime
from logger import logger
from config import SYMBOL, TIMEFRAME, REALTIME_CONFIG
from core.risk import RiskController
from strategies.wave_theory import WaveTheoryStrategy
from strategies.ma_cross import MACrossStrategy
from strategies.rsi import RSIStrategy
from strategies.bollinger import BollingerStrategy
from strategies.mean_reversion import MeanReversionStrategy
from strategies.momentum_breakout import MomentumBreakoutStrategy
from strategies.macd import MACDStrategy
from strategies.kdj import KDJStrategy
from strategies.turtle import TurtleStrategy
from strategies.daily_breakout import DailyBreakoutStrategy
class RealtimeTrader:
"""实时交易器 (已重构为依赖注入和市场状态参数切换)"""
def __init__(self, data_provider, update_interval=60):
self.data_provider = data_provider
self.update_interval = update_interval
self.running = False
self.risk_controller = None
self.regime_params = {}
self.regime_detector = None
self.current_regime = 'Ranging' # Default regime
self.strategy_instances = {}
self.current_weights = {}
self.signal_thresholds = {}
self.strategy_config_key_to_class = {
"ma_cross": MACrossStrategy,
"rsi": RSIStrategy,
"bollinger": BollingerStrategy,
"mean_reversion": MeanReversionStrategy,
"momentum_breakout": MomentumBreakoutStrategy,
"macd": MACDStrategy,
"kdj": KDJStrategy,
"turtle": TurtleStrategy,
"daily_breakout": DailyBreakoutStrategy,
"wave_theory": WaveTheoryStrategy
}
def _initialize(self):
if not self.data_provider.initialize():
return False
self.risk_controller = RiskController(self.data_provider)
# Load regime parameters
try:
with open('regime_optimal_params.json', 'r') as f:
self.regime_params = json.load(f)
logger.info("成功加载市场状态参数文件: regime_optimal_params.json")
except FileNotFoundError:
logger.error("错误: 未找到 regime_optimal_params.json。请先运行 regime_optimizer.py。")
return False
except json.JSONDecodeError:
logger.error("错误: regime_optimal_params.json 文件格式不正确。")
return False
# Instantiate regime detector
self.regime_detector = WaveTheoryStrategy(self.data_provider, SYMBOL, TIMEFRAME)
# Create strategy instances
self._create_strategy_instances()
self.risk_controller.sync_state()
signal.signal(signal.SIGINT, self._signal_handler)
signal.signal(signal.SIGTERM, self._signal_handler)
logger.info("实时交易系统初始化完成")
return True
def _create_strategy_instances(self):
for name, strategy_class in self.strategy_config_key_to_class.items():
# Use the name that corresponds to the keys in the parameter file (e.g., 'MACrossStrategy')
class_name = strategy_class.__name__
self.strategy_instances[class_name] = strategy_class(self.data_provider, SYMBOL, TIMEFRAME)
logger.info(f"创建了 {len(self.strategy_instances)} 个策略实例")
def _update_parameters_for_regime(self):
# 1. Determine current regime
df_history = self.data_provider.get_historical_data(SYMBOL, TIMEFRAME, count=200)
if df_history is None or df_history.empty:
logger.warning("无法获取历史数据来判断市场状态,将使用上一个状态。")
return
df_history = pd.DataFrame(df_history)
df_history.set_index(pd.to_datetime(df_history['time'], unit='s'), inplace=True)
df_with_indicators = self.regime_detector._calculate_indicators(df_history.copy())
last_row = df_with_indicators.iloc[-1]
new_regime = "Ranging" # Default
if not pd.isna(last_row['adx']):
if last_row['adx'] < self.regime_detector.adx_threshold:
new_regime = "Ranging"
elif last_row['ema_short'] > last_row['ema_medium'] > last_row['ema_long']:
new_regime = "Uptrend"
else:
new_regime = "Downtrend"
# 2. If regime changed, update parameters
if new_regime != self.current_regime:
self.current_regime = new_regime
logger.info(f"市场状态已切换为: {self.current_regime}")
regime_config = self.regime_params.get(self.current_regime)
if not regime_config:
logger.error(f"在参数文件中未找到状态 {self.current_regime} 的配置,将使用默认参数。")
return
params = regime_config.get('best_parameters', {})
# Group parameters by component (strategy, risk, etc.)
strategy_params = {strat_name: {} for strat_name in self.strategy_instances.keys()}
risk_params = {}
temp_weights = {}
prefix_map = {
'MACrossStrategy': 'ma_cross_',
'RSIStrategy': 'rsi_',
'BollingerStrategy': 'bollinger_',
'MACDStrategy': 'macd_',
'MeanReversionStrategy': 'mean_reversion_',
'MomentumBreakoutStrategy': 'momentum_breakout_',
'KDJStrategy': 'kdj_',
'TurtleStrategy': 'turtle_',
'DailyBreakoutStrategy': 'daily_breakout_',
'WaveTheoryStrategy': 'wave_'
}
for key, value in params.items():
if key.startswith('weight_'):
strategy_name = key.replace('weight_', '')
temp_weights[strategy_name] = value
elif key in ['buy_threshold', 'sell_threshold']:
self.signal_thresholds[key] = value
elif hasattr(self.risk_controller, key):
risk_params[key] = value
else:
for strat_name, prefix in prefix_map.items():
if key.startswith(prefix):
param_name = key.replace(prefix, '')
# BUG FIX: Handle the 'wave_period' special case
if strat_name == 'WaveTheoryStrategy' and key == 'wave_period':
param_name = 'wave_period'
strategy_params[strat_name][param_name] = value
break
# Update parameters in a batch
for strat_name, params_dict in strategy_params.items():
if params_dict:
self.strategy_instances[strat_name].set_params(params_dict)
if risk_params:
for key, value in risk_params.items():
setattr(self.risk_controller, key, value)
self.current_weights = temp_weights
logger.info(f"已为 {self.current_regime} 状态加载新参数和权重。")
def _signal_handler(self, signum, frame):
logger.info(f"接收到信号 {signum},准备退出...")
self.stop()
def _run_cycle(self):
try:
self.risk_controller.sync_state()
# Update parameters based on current market regime
self._update_parameters_for_regime()
current_price = self.data_provider.get_current_price(SYMBOL)
if not current_price:
logger.warning("无法获取当前价格,跳过本次循环")
return
signals, weights = [], []
strategies_with_weights = []
for strat_name, strat_instance in self.strategy_instances.items():
weight = self.current_weights.get(strat_name, 0.0)
if weight > 0: # Only calculate signal if weight is positive
strategies_with_weights.append((strat_instance, weight))
signals.append(strat_instance.generate_signal())
weights.append(weight)
if not strategies_with_weights: return
# 打印详细信号日志
logger.info(f"--- 信号计算详情 (状态: {self.current_regime}) ---")
for i, (strat, weight) in enumerate(strategies_with_weights):
signal = signals[i]
weighted_signal = signal * weight
strat_name = strat.name
logger.info(f" 策略: {strat_name:<25} | 信号: {signal:6.2f} | 权重: {weight:6.2f} | 加权信号: {weighted_signal:6.2f}")
logger.info("--------------------")
weighted_signal_sum = sum(s * w for s, w in zip(signals, weights))
buy_threshold = self.signal_thresholds.get('buy_threshold', 1.5)
sell_threshold = self.signal_thresholds.get('sell_threshold', -1.5)
logger.info(f"加权信号: {weighted_signal_sum:.2f} (买入阈值: {buy_threshold}, 卖出阈值: {sell_threshold})")
direction = None
if weighted_signal_sum > buy_threshold:
direction = "buy"
elif weighted_signal_sum < sell_threshold:
direction = "sell"
if direction:
logger.info(f"准备执行{direction}交易,信号强度: {weighted_signal_sum:.2f}")
success = self.risk_controller.process_trading_signal(direction, current_price, weighted_signal_sum)
if not success:
logger.warning(f"{direction}交易执行失败")
else:
logger.info(f"{direction}交易执行成功")
self.risk_controller.monitor_positions(current_price)
# --- 状态汇总日志 ---
logger.info("--- 财务状况更新 ---")
open_positions = self.risk_controller.position_manager.positions
if not open_positions:
logger.info(" 当前无持仓")
else:
logger.info(f" 当前持仓: {len(open_positions)} 个")
for pos in open_positions:
pnl_pct = self.risk_controller.position_manager._calculate_pnl_pct(pos, current_price['last'])
holding_time = datetime.fromtimestamp(current_price['time']) - pos['entry_time']
holding_minutes = holding_time.total_seconds() / 60
logger.info(f" - Ticket {pos['ticket']}: {pos['position_type']} {pos['symbol']} @ {pos['entry_price']:.2f} | 持仓时间: {holding_minutes:.1f}分钟 | 浮动盈亏: {pnl_pct:.2%}")
trade_summary = self.risk_controller.position_manager.get_trade_summary()
if trade_summary and trade_summary['total_trades'] > 0:
logger.info(" 已平仓交易摘要:")
logger.info(f" - 总交易: {trade_summary['total_trades']}, 盈利: {trade_summary['winning_trades']}, 亏损: {trade_summary['losing_trades']}, 胜率: {trade_summary['win_rate']:.2f}%")
logger.info(f" - 总净盈亏: ${trade_summary['total_profit_loss']:.2f}")
logger.info(f" 总权益: ${self.risk_controller.position_manager.total_equity:.2f}")
logger.info("----------------------")
except Exception as e:
import traceback
logger.error(f"交易周期执行失败: {e}\n{traceback.format_exc()}")
def start(self):
if not self._initialize(): return
logger.info("=== 启动实时交易系统 ===")
self.running = True
while self.running:
cycle_start = time.time()
self._run_cycle()
cycle_time = time.time() - cycle_start
wait_time = max(0, self.update_interval - cycle_time)
if wait_time > 0: time.sleep(wait_time)
def stop(self):
logger.info("=== 停止实时交易系统 ===")
self.running = False
try:
if self.risk_controller:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
self.risk_controller.save_trade_history(f"realtime_trades_{timestamp}")
except Exception as e:
logger.error(f"保存交易记录失败: {e}")
finally:
self.data_provider.shutdown()
logger.info("实时交易系统已停止")
sys.exit(0)