mirror of
https://github.com/silencesdg/mt5_python_ea_suite.git
synced 2026-07-29 03:37:43 +00:00
基本完毕
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import pandas as pd
|
||||
from tqdm import tqdm
|
||||
from core.risk import RiskController
|
||||
from config import SIGNAL_THRESHOLDS, BACKTEST_CONFIG, SPREAD
|
||||
from logger import logger
|
||||
|
||||
class BacktestEngine:
|
||||
def __init__(self, df, trade_direction="both"):
|
||||
self.df = df
|
||||
self.trade_direction = trade_direction
|
||||
self.spread = BACKTEST_CONFIG.get("spread", SPREAD)
|
||||
|
||||
def run_strategy(self, strategy):
|
||||
signals = strategy.run_backtest(self.df)
|
||||
buy_count = (signals == 1).sum()
|
||||
sell_count = (signals == -1).sum()
|
||||
logger.info(f"策略 {strategy.__class__.__module__} 信号统计 - 买入: {buy_count}, 卖出: {sell_count}")
|
||||
return signals
|
||||
|
||||
def combine_signals(self, signals_list, weights):
|
||||
df_signals = pd.concat(signals_list, axis=1).fillna(0)
|
||||
weighted_signals = df_signals * weights
|
||||
combined = weighted_signals.sum(axis=1)
|
||||
|
||||
def apply_threshold(score):
|
||||
if score > SIGNAL_THRESHOLDS["buy_threshold"]:
|
||||
return 1
|
||||
elif score < SIGNAL_THRESHOLDS["sell_threshold"]:
|
||||
return -1
|
||||
else:
|
||||
return 0
|
||||
|
||||
combined_signal = combined.apply(apply_threshold)
|
||||
buy_signals = (combined_signal == 1).sum()
|
||||
sell_signals = (combined_signal == -1).sum()
|
||||
logger.info(f"组合信号统计 - 买入: {buy_signals}, 卖出: {sell_signals}")
|
||||
return combined_signal
|
||||
|
||||
def run_backtest(self, signals, symbol="XAUUSD"):
|
||||
df = self.df.copy()
|
||||
df['signal'] = signals.shift(1).fillna(0)
|
||||
|
||||
risk_controller = RiskController(self.trade_direction)
|
||||
logger.info(f"回测开始: 交易方向={self.trade_direction}")
|
||||
|
||||
# 使用tqdm创建进度条
|
||||
for i in tqdm(range(1, len(df)), desc=f"Backtesting ({len(df)} bars)"):
|
||||
current_signal = df['signal'].iloc[i]
|
||||
# 计算考虑双向点差的买卖价格
|
||||
close_price = df['close'].iloc[i]
|
||||
spread_points = self.spread
|
||||
spread_half = spread_points * 0.01 / 2 # XAUUSD: 1点 = 0.01,双向点差各一半
|
||||
|
||||
current_price = {
|
||||
'bid': close_price - spread_half, # 卖出价格(中间价 - 点差/2)
|
||||
'ask': close_price + spread_half, # 买入价格(中间价 + 点差/2)
|
||||
'last': close_price # 最后成交价(中间价)
|
||||
}
|
||||
|
||||
direction = None
|
||||
if current_signal == 1:
|
||||
direction = "buy"
|
||||
elif current_signal == -1:
|
||||
direction = "sell"
|
||||
|
||||
if direction:
|
||||
risk_controller.process_trading_signal(
|
||||
direction, current_price, abs(current_signal), dry_run=True
|
||||
)
|
||||
|
||||
risk_controller.monitor_positions(current_price, dry_run=True)
|
||||
|
||||
risk_controller.position_manager.force_close_all_positions(df['close'].iloc[-1], dry_run=True)
|
||||
|
||||
logger.info("回测完成,生成性能报告...")
|
||||
summary = risk_controller.position_manager.get_trade_summary()
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info("回测性能报告")
|
||||
logger.info("=" * 80)
|
||||
logger.info(f" 总交易次数: {summary['total_trades']}")
|
||||
logger.info(f" 盈利次数: {summary['winning_trades']}")
|
||||
logger.info(f" 亏损次数: {summary['losing_trades']}")
|
||||
logger.info(f" 胜率: {summary['win_rate']:.2f}%")
|
||||
logger.info("-" * 40)
|
||||
logger.info(f" 总盈亏: ${summary['total_profit_loss']:.2f}")
|
||||
logger.info(f" 平均每笔交易盈亏: ${summary['avg_profit_loss']:.2f}")
|
||||
logger.info(f" 最大盈利: ${summary['max_profit']:.2f}")
|
||||
logger.info(f" 最大亏损: ${summary['max_loss']:.2f}")
|
||||
logger.info("=" * 80)
|
||||
|
||||
risk_controller.position_manager.save_to_csv("backtest_trades.csv")
|
||||
risk_controller.position_manager.save_to_json("backtest_trades.json")
|
||||
|
||||
return summary
|
||||
@@ -0,0 +1,67 @@
|
||||
from strategies import ma_cross, rsi, bollinger, mean_reversion, momentum_breakout, macd, kdj, turtle, daily_breakout, wave_theory
|
||||
from config import DEFAULT_WEIGHTS, SYMBOL, TIMEFRAME
|
||||
from logger import logger
|
||||
from core.risk.market_state import MarketStateAnalyzer
|
||||
|
||||
class DynamicWeightManager:
|
||||
"""
|
||||
动态权重管理器
|
||||
"""
|
||||
|
||||
def __init__(self, data_provider, market_state_params=None, trend_weights=None, trend_thresholds=None, confidence_thresholds=None):
|
||||
self.data_provider = data_provider
|
||||
self.market_state_analyzer = MarketStateAnalyzer(
|
||||
data_provider,
|
||||
market_state_params=market_state_params,
|
||||
trend_weights=trend_weights,
|
||||
trend_thresholds=trend_thresholds,
|
||||
confidence_thresholds=confidence_thresholds
|
||||
)
|
||||
|
||||
# 策略类和它们的初始化参数的映射
|
||||
self.strategy_blueprints = {
|
||||
'ma_cross': (ma_cross.MACrossStrategy, {}),
|
||||
'rsi': (rsi.RSIStrategy, {}),
|
||||
'bollinger': (bollinger.BollingerStrategy, {}),
|
||||
'mean_reversion': (mean_reversion.MeanReversionStrategy, {}),
|
||||
'momentum_breakout': (momentum_breakout.MomentumBreakoutStrategy, {}),
|
||||
'macd': (macd.MACDStrategy, {}),
|
||||
'kdj': (kdj.KDJStrategy, {}),
|
||||
'turtle': (turtle.TurtleStrategy, {}),
|
||||
'daily_breakout': (daily_breakout.DailyBreakoutStrategy, {}),
|
||||
'wave_theory': (wave_theory.WaveTheoryStrategy, {})
|
||||
}
|
||||
|
||||
self.strategy_instances = self._create_strategy_instances()
|
||||
|
||||
self.current_weights = None
|
||||
self.current_market_state = "none"
|
||||
self.current_confidence = 0.0
|
||||
|
||||
def _create_strategy_instances(self):
|
||||
instances = {}
|
||||
for name, (strategy_class, params) in self.strategy_blueprints.items():
|
||||
instances[name] = strategy_class(self.data_provider, SYMBOL, TIMEFRAME, **params)
|
||||
return instances
|
||||
|
||||
def get_current_strategies_and_weights(self):
|
||||
market_state, confidence = self.market_state_analyzer.get_market_state()
|
||||
dynamic_weights = self.market_state_analyzer.get_strategy_weights(market_state, confidence)
|
||||
|
||||
self.current_weights = dynamic_weights
|
||||
self.current_market_state = market_state
|
||||
self.current_confidence = confidence
|
||||
|
||||
strategies_with_weights = []
|
||||
for name, weight in dynamic_weights.items():
|
||||
if name in self.strategy_instances:
|
||||
strategies_with_weights.append((self.strategy_instances[name], weight))
|
||||
|
||||
return strategies_with_weights
|
||||
|
||||
def get_weight_info(self):
|
||||
return {
|
||||
'market_state': self.current_market_state,
|
||||
'confidence': self.current_confidence,
|
||||
'weights': self.current_weights
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import time
|
||||
import signal
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from logger import logger
|
||||
from config import SYMBOL, TIMEFRAME, REALTIME_CONFIG, SIGNAL_THRESHOLDS
|
||||
from core.risk import RiskController
|
||||
from execution.dynamic_weights import DynamicWeightManager
|
||||
|
||||
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.weight_manager = None
|
||||
|
||||
def _initialize(self):
|
||||
if not self.data_provider.initialize():
|
||||
return False
|
||||
|
||||
self.risk_controller = RiskController(self.data_provider)
|
||||
self.weight_manager = DynamicWeightManager(self.data_provider)
|
||||
|
||||
self.risk_controller.sync_state()
|
||||
|
||||
signal.signal(signal.SIGINT, self._signal_handler)
|
||||
signal.signal(signal.SIGTERM, self._signal_handler)
|
||||
logger.info("实时交易系统初始化完成")
|
||||
return True
|
||||
|
||||
def _signal_handler(self, signum, frame):
|
||||
logger.info(f"接收到信号 {signum},准备退出...")
|
||||
self.stop()
|
||||
|
||||
def _run_cycle(self):
|
||||
try:
|
||||
self.risk_controller.sync_state()
|
||||
|
||||
current_price = self.data_provider.get_current_price(SYMBOL)
|
||||
if not current_price:
|
||||
logger.warning("无法获取当前价格,跳过本次循环")
|
||||
return
|
||||
|
||||
strategies_with_weights = self.weight_manager.get_current_strategies_and_weights()
|
||||
if not strategies_with_weights: return
|
||||
|
||||
signals, weights = [], []
|
||||
for strat, weight in strategies_with_weights:
|
||||
signals.append(strat.generate_signal())
|
||||
weights.append(weight)
|
||||
|
||||
# 打印详细信号日志
|
||||
logger.info("--- 信号计算详情 ---")
|
||||
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 = SIGNAL_THRESHOLDS.get('buy_threshold', 1.5)
|
||||
sell_threshold = SIGNAL_THRESHOLDS.get('sell_threshold', -1.5)
|
||||
logger.info(f"加权信号: {weighted_signal_sum:.2f} (买入阈值: {buy_threshold}, 卖出阈值: {sell_threshold})")
|
||||
# logger.info(f"信号比较: {weighted_signal_sum} > {buy_threshold} = {weighted_signal_sum > buy_threshold}")
|
||||
# logger.info(f"信号比较: {weighted_signal_sum} < {sell_threshold} = {weighted_signal_sum < 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 = 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)
|
||||
|
||||
Reference in New Issue
Block a user