Files
mt5_python_ea_suite/main.py
T
songkunling 21ce1831ec add files
2025-08-11 18:06:53 +08:00

249 lines
10 KiB
Python

import pandas as pd
from utils import initialize, shutdown, get_rates, close_all, send_order
from backtest import BacktestEngine
from logger import logger
from config import INITIAL_CAPITAL, SYMBOL, TIMEFRAME, BACKTEST_COUNT, BACKTEST_START_DATE, BACKTEST_END_DATE, USE_DATE_RANGE
from risk_management import RiskController
from dynamic_weights import DynamicWeightManager
from trade_logger import trade_logger
from logger import logger
# 导入优化器
from optimizer import run_optimizer
def run_realtime():
if not initialize():
logger.error("MT5初始化失败")
return
# 初始化风险管理和动态权重
risk_controller = RiskController()
weight_manager = DynamicWeightManager(risk_controller)
# 获取动态策略配置
strategies_with_weights = weight_manager.get_current_strategies_and_weights()
weight_info = weight_manager.get_weight_info()
logger.info(f"市场状态: {weight_info['market_state']}, 置信度: {weight_info['confidence']:.2f}")
# 执行策略信号生成
signals = []
weights = []
for strat, weight in strategies_with_weights:
try:
logger.info(f"执行策略:{strat.__class__.__module__}, 权重: {weight:.2f}")
# 特殊处理风险管理策略
if strat.__class__.__module__ == 'risk_management':
signal = strat.generate_signal_with_sync(risk_controller)
else:
signal = strat.generate_signal()
signals.append(signal)
weights.append(weight)
except Exception as e:
logger.exception(f"运行策略 {strat.__class__.__module__} 时出错:{e}")
# 计算加权信号
weighted_signal_sum = sum(s * w for s, w in zip(signals, weights))
logger.info(f"加权信号总和: {weighted_signal_sum:.2f}")
# 获取当前价格用于风险管理
current_price = get_current_price(SYMBOL)
current_time = pd.Timestamp.now()
# 检查风险管理条件
if current_price:
risk_action, risk_reason = risk_controller.check_risk_management(current_price)
if risk_action != "none":
logger.info(f"触发风险管理: {risk_action}, 原因: {risk_reason}")
# 记录平仓交易
if trade_logger.current_position:
trade_logger.close_position(SYMBOL, current_price, current_time, f"risk_management_{risk_action}")
risk_controller.execute_risk_action(risk_action, risk_reason)
shutdown()
return
# 获取当前持仓状态,取消交易间隔限制
current_position = trade_logger.current_position
min_trade_interval_minutes = 0 # 取消最小交易间隔限制
# 取消交易间隔限制检查
can_trade = True
# 调试信息:打印当前状态
logger.info(f"当前持仓状态: {current_position is not None}")
if current_position:
time_since_open = (current_time - current_position['open_time']).total_seconds() / 60
logger.info(f"持仓信息: {current_position['direction']} @ {current_position['open_price']:.2f}, 持仓时间: {time_since_open:.1f}分钟")
# 执行交易决策
logger.info(f"加权信号总和: {weighted_signal_sum:.2f}, 买入阈值: 0, 卖出阈值: 0")
logger.info(f"风险管理允许买入: {risk_controller.should_allow_trade('buy')}, 允许卖出: {risk_controller.should_allow_trade('sell')}")
logger.info(f"允许交易: {can_trade}")
if weighted_signal_sum > 0:
logger.info(f"检测到买入信号: {weighted_signal_sum:.2f} > 0")
if risk_controller.should_allow_trade("buy") and can_trade:
# 检查是否已经有相同方向的持仓
if current_position and current_position['direction'] == 'buy':
logger.info(f"已持有多头仓位,信号强度: {weighted_signal_sum:.2f},不重复开仓")
else:
logger.info(f"=== 准备执行买入交易 ===")
logger.info(f"买入信号: 加权总和({weighted_signal_sum:.2f}) > 0")
logger.info(f"当前价格: {current_price:.2f}")
logger.info(f"风险管理允许: {risk_controller.should_allow_trade('buy')}")
logger.info(f"交易间隔检查通过: {can_trade}")
# 先平掉现有仓位(如果有)
if current_position:
close_all(SYMBOL)
trade_logger.close_position(SYMBOL, current_price, current_time, "close_before_buy")
# 开新仓
send_order(SYMBOL, 'buy')
trade_logger.open_position(SYMBOL, 'buy', current_price, current_time, f"weighted_signal_{weighted_signal_sum:.2f}")
if current_price:
risk_controller.update_position_entry(current_price, "long")
else:
if not can_trade:
logger.info(f"买入信号被阻止: 交易间隔限制")
else:
logger.info(f"买入信号被风险管理阻止: risk_controller.should_allow_trade('buy') = {risk_controller.should_allow_trade('buy')}")
elif weighted_signal_sum < 0:
logger.info(f"检测到卖出信号: {weighted_signal_sum:.2f} < 0")
if risk_controller.should_allow_trade("sell") and can_trade:
# 检查是否已经有相同方向的持仓
if current_position and current_position['direction'] == 'sell':
logger.info(f"已持有空头仓位,信号强度: {weighted_signal_sum:.2f},不重复开仓")
else:
logger.info(f"=== 准备执行卖出交易 ===")
logger.info(f"卖出信号: 加权总和({weighted_signal_sum:.2f}) < 0")
logger.info(f"当前价格: {current_price:.2f}")
logger.info(f"风险管理允许: {risk_controller.should_allow_trade('sell')}")
logger.info(f"交易间隔检查通过: {can_trade}")
# 先平掉现有仓位(如果有)
if current_position:
close_all(SYMBOL)
trade_logger.close_position(SYMBOL, current_price, current_time, "close_before_sell")
# 开新仓
send_order(SYMBOL, 'sell')
trade_logger.open_position(SYMBOL, 'sell', current_price, current_time, f"weighted_signal_{weighted_signal_sum:.2f}")
if current_price:
risk_controller.update_position_entry(current_price, "short")
else:
logger.info(f"卖出信号被阻止")
else:
# 信号在中间区域,使用RiskController决定是否平仓
if current_position:
# 使用RiskController检查风险管理条件
risk_action, risk_reason = risk_controller.check_risk_management(current_price)
if risk_action != "none":
close_all(SYMBOL)
trade_logger.close_position(SYMBOL, current_price, current_time, f"signal_neutral_{risk_action}")
risk_controller.execute_risk_action(risk_action, risk_reason)
shutdown()
def get_current_price(symbol):
"""获取当前价格"""
try:
import MetaTrader5 as mt5
tick = mt5.symbol_info_tick(symbol)
return tick.bid if tick else None
except Exception as e:
logger.error(f"获取当前价格失败: {e}")
return None
def run_backtest():
if not initialize():
logger.error("MT5初始化失败")
return
# 重置交易日志记录器
trade_logger.__init__()
# 根据配置选择获取数据的方式
if USE_DATE_RANGE:
rates = get_rates(SYMBOL, TIMEFRAME, BACKTEST_COUNT, BACKTEST_START_DATE, BACKTEST_END_DATE)
else:
rates = get_rates(SYMBOL, TIMEFRAME, BACKTEST_COUNT)
if rates is None:
logger.error("获取历史数据失败")
shutdown()
return
logger.info(f"初始资金: {INITIAL_CAPITAL}")
df = pd.DataFrame(rates)
engine = BacktestEngine(df)
# 使用动态权重管理器(需要在MT5连接状态下获取市场状态)
risk_controller = RiskController()
weight_manager = DynamicWeightManager(risk_controller)
# 获取当前策略配置(回测时使用固定权重或模拟动态权重)
strategies_with_weights = weight_manager.get_current_strategies_and_weights()
weight_info = weight_manager.get_weight_info()
logger.info(f"回测使用市场状态: {weight_info['market_state']}")
# 获取市场状态后关闭MT5连接
shutdown()
signals_list = []
weights = []
strategy_names = []
for strat, weight in strategies_with_weights:
try:
logger.info(f"回测策略:{strat.__class__.__module__}, 权重: {weight:.2f}")
signals = engine.run_strategy(strat)
signals_list.append(signals)
weights.append(weight)
strategy_names.append(strat.__class__.__module__)
except Exception as e:
logger.exception(f"回测策略 {strat.__class__.__module__} 时出错:{e}")
combined_signal = engine.combine_signals(signals_list, weights)
# 使用带交易记录的收益率计算
cum_ret = engine.calc_returns_with_trades(combined_signal, SYMBOL)
final_capital = INITIAL_CAPITAL * (1 + cum_ret.iloc[-1])
logger.info("策略组合回测完成")
logger.info(f"最终资金: {final_capital:.2f}")
logger.info(f"总收益率: {(final_capital/INITIAL_CAPITAL - 1):.2%}")
logger.info("使用的策略权重:")
for name, weight in zip(strategy_names, weights):
logger.info(f" {name}: {weight:.2f}")
logger.info("最近收益率:")
logger.info(cum_ret.tail())
# 打印详细的交易记录
logger.info("\n" + "="*50)
logger.info("详细交易记录:")
trade_logger.print_all_trades()
# 保存交易记录到文件
trade_logger.save_to_csv("backtest_trades.csv")
trade_logger.save_to_json("backtest_trades.json")
if __name__ == "__main__":
# --- 选择运行模式 ---
# 1. 运行一次回测 (使用config.py中的默认权重)
run_backtest()
# 2. 运行实盘交易 (使用config.py中的默认权重)
# run_realtime()
# 3. 运行遗传算法优化,寻找最佳权重
# run_optimizer()