From 9b0eeb521eb2b85c709ba3192e62aa0095ad7882 Mon Sep 17 00:00:00 2001 From: silencesdg Date: Thu, 14 May 2026 17:14:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=9B=88=E4=BA=8F=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=B4=A6=E6=88=B7=E4=BD=99=E9=A2=9D%=20+=20?= =?UTF-8?q?=E6=8B=96=E5=B0=BE=E6=AD=A2=E6=8D=9F=E7=BB=9D=E5=AF=B9=E5=80=BC?= =?UTF-8?q?=E6=89=A3=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _calculate_pnl_pct: 从金价涨跌%改为账户余额% - _get_contract_size: 新增缓存合约规格方法 - 硬止损: 从金价%乘法改为账户$→点数→价格转换 - 拖尾止损: peak*(1-retrace) 改为 peak-retrace(绝对值扣除) - 统一 _record_closed_trade/_calculate_unrealized_pnl 使用缓存合约规格 - RISK_CONFIG: 止损-1.5%/止盈+3.0%/拖尾激活+2.0%/回撤1.0%(均为账户%) --- .restart_signal | 1 + config.py | 163 ++++++++++++------------ core/risk/exit_rules.py | 3 +- core/risk/position.py | 50 +++++--- optimization_report_20260514_141914.txt | 75 +++++++++++ 5 files changed, 195 insertions(+), 97 deletions(-) create mode 100644 .restart_signal create mode 100644 optimization_report_20260514_141914.txt diff --git a/.restart_signal b/.restart_signal new file mode 100644 index 0000000..5446aa5 --- /dev/null +++ b/.restart_signal @@ -0,0 +1 @@ +2026-05-14T14:19:14.539006 \ No newline at end of file diff --git a/config.py b/config.py index bdf7f55..8ae035d 100644 --- a/config.py +++ b/config.py @@ -135,54 +135,55 @@ GENETIC_OPTIMIZER_CONFIG = { # 信号阈值配置(优化器结果 2026-05-10,5万根M1数据) SIGNAL_THRESHOLDS = { - "buy_threshold": 1.344, - "sell_threshold": -2.980 + "buy_threshold": 1.0315663601353515, + "sell_threshold": -2.312867704316165 } # 风险管理参数(优化器结果 2026-05-10,5万根M1数据) RISK_CONFIG = { - "stop_loss_pct": -0.046, - "profit_retracement_pct": 0.030, - "min_profit_for_trailing": 0.03, - "take_profit_pct": 0.246, + # ★ 以下百分比均为账户余额%,非金价涨跌% + # 例:余额$1682,-1.5% = -$25 ≈ 25点金价(0.01手) + "stop_loss_pct": -0.015, # -1.5% 账户 + "profit_retracement_pct": 0.01, # 1.0% 账户(拖尾回撤容忍) + "min_profit_for_trailing": 0.02, # +2.0% 后激活拖尾 + "take_profit_pct": 0.03, # +3.0% 账户 "max_daily_loss": -0.3, "max_holding_minutes": 133, "min_profit_for_time_exit": 0.010, "cooldown_bars": 30, # ★ 硬止损倍率:MT5 服务器端 SL/TP = 软止损 × 倍率(兜底,仅 EA 挂掉时触发) - # 硬止损必须比软止损宽(倍率>1),否则会抢先触发导致拖尾失效 - "hard_sl_multiplier": 1.5, # 硬 SL = 软 SL × 1.5(例如软-4.6%→硬-6.9%) - "hard_tp_multiplier": 1.3, # 硬 TP = 软 TP × 1.3(例如软+24.6%→硬+32.0%) + "hard_sl_multiplier": 1.5, # 硬 SL = 软 SL × 1.5(-1.5%→-2.25%账户) + "hard_tp_multiplier": 1.3, # 硬 TP = 软 TP × 1.3(+3.0%→+3.9%账户) } # 市场状态分析参数 MARKET_STATE_CONFIG = { - "trend_period": 24, - "retracement_tolerance": 0.425, - "volume_period": 21, - "volume_ma_period": 12 + "trend_period": 83, + "retracement_tolerance": 0.15567164907853615, + "volume_period": 29, + "volume_ma_period": 23 } # 策略参数配置(优化器结果 2026-05-10,5万根M1数据) STRATEGY_CONFIG = { "ma_cross": { - "short_window": 12, - "long_window": 30 + "short_window": 13, + "long_window": 17 }, "rsi": { - "period": 21, - "overbought": 75, - "oversold": 26 + "period": 30, + "overbought": 68, + "oversold": 32 }, "bollinger": { "period": 20, - "std_dev": 2.162 + "std_dev": 2.4663422649047333 }, "macd": { - "fast_ema": 16, - "slow_ema": 34, - "signal_period": 12 + "fast_ema": 14, + "slow_ema": 24, + "signal_period": 9 }, "mean_reversion": { "period": 29, @@ -190,7 +191,7 @@ STRATEGY_CONFIG = { }, "momentum_breakout": { "period": 15, - "momentum_period": 17 + "momentum_period": 13 }, "kdj": { "period": 21 @@ -199,96 +200,96 @@ STRATEGY_CONFIG = { "period": 42 }, "daily_breakout": { - "bars_count": 746 + "bars_count": 2008 }, "wave_theory": { "ema_short": 3, - "ema_medium": 16, - "ema_long": 26, - "wave_period": 32, - "range_period": 30, - "adx_period": 23, + "ema_medium": 11, + "ema_long": 46, + "wave_period": 11, + "range_period": 35, + "adx_period": 20, "momentum_period": 10, - "range_threshold": 0.002, - "adx_threshold": 23 + "range_threshold": 0.01, + "adx_threshold": 21 } } # 市场趋势判断权重配置 TREND_INDICATOR_WEIGHTS = { - "price_breakout": -0.0949, - "volume_confirmation": 0.5277, + "price_breakout": 0.1504199442999585, + "volume_confirmation": 0.12782205952949638, "momentum oscillator": 0.3677, - "moving_average": 0.1506 + "moving_average": 0.4092273363154768 } # 趋势判断阈值 TREND_THRESHOLDS = { - "strong_trend": 0.4182, - "weak_trend": 0.2164, - "volume_spike": 1.5843, - "oversold": 24, - "overbought": 80 + "strong_trend": 0.7940886082643032, + "weak_trend": 0.4565953163045441, + "volume_spike": 2.7329673335105396, + "oversold": 32, + "overbought": 68 } # 动态权重配置(优化器结果 2026-05-10,5万根M1数据) DEFAULT_WEIGHTS = { - "ma_cross": 0.809, - "rsi": 1.141, - "bollinger": 0.389, - "mean_reversion": 1.106, - "momentum_breakout": 0.147, - "macd": 1.181, - "kdj": 1.525, - "turtle": 0.559, - "daily_breakout": 1.846, - "wave_theory": 1.361 + "ma_cross": 0.32048324544087786, + "rsi": 1.906009222972592, + "bollinger": 0.9232444456662969, + "mean_reversion": 1.1649306516234597, + "momentum_breakout": 0.8769099086178171, + "macd": 0.748930985323352, + "kdj": 1.4647470623067596, + "turtle": 1.637931136008917, + "daily_breakout": 1.478598649389487, + "wave_theory": 0.818283180489803 } # 市场状态策略权重配置 MARKET_STATE_WEIGHTS = { "uptrend": { - "ma_cross": 1.50, - "momentum_breakout": 1.20, - "turtle": 0.25, - "macd": 0.35, - "daily_breakout": 1.50, - "rsi": 1.00, - "bollinger": 1.00, - "kdj": 0.40, - "mean_reversion": 0.80, - "wave_theory": 0.20 + "ma_cross": 0.32048324544087786, + "momentum_breakout": 0.8769099086178171, + "turtle": 1.637931136008917, + "macd": 0.748930985323352, + "daily_breakout": 1.478598649389487, + "rsi": 1.906009222972592, + "bollinger": 0.9232444456662969, + "kdj": 1.4647470623067596, + "mean_reversion": 1.1649306516234597, + "wave_theory": 0.818283180489803 }, "downtrend": { - "ma_cross": 1.50, - "momentum_breakout": 1.20, - "turtle": 0.25, - "macd": 0.35, - "daily_breakout": 1.50, - "rsi": 1.00, - "bollinger": 1.00, - "kdj": 0.40, - "mean_reversion": 0.80, - "wave_theory": 0.20 + "ma_cross": 0.32048324544087786, + "momentum_breakout": 0.8769099086178171, + "turtle": 1.637931136008917, + "macd": 0.748930985323352, + "daily_breakout": 1.478598649389487, + "rsi": 1.906009222972592, + "bollinger": 0.9232444456662969, + "kdj": 1.4647470623067596, + "mean_reversion": 1.1649306516234597, + "wave_theory": 0.818283180489803 }, "ranging": { - "rsi": 1.60, - "bollinger": 1.70, - "mean_reversion": 1.50, - "kdj": 1.00, - "wave_theory": 0.50, - "ma_cross": 0.70, - "macd": 0.20, - "turtle": 0.10, - "momentum_breakout": 0.50, - "daily_breakout": 0.90 + "rsi": 1.906009222972592, + "bollinger": 0.9232444456662969, + "mean_reversion": 1.1649306516234597, + "kdj": 1.4647470623067596, + "wave_theory": 0.818283180489803, + "ma_cross": 0.32048324544087786, + "macd": 0.748930985323352, + "turtle": 1.637931136008917, + "momentum_breakout": 0.8769099086178171, + "daily_breakout": 1.478598649389487 }, "none": DEFAULT_WEIGHTS } # 市场趋势置信度阈值配置 CONFIDENCE_THRESHOLDS = { - "high_confidence": 0.8474, - "medium_confidence": 0.4964 + "high_confidence": 0.6813641209473742, + "medium_confidence": 0.6336441706563001 } diff --git a/core/risk/exit_rules.py b/core/risk/exit_rules.py index 0b176b4..80a19c3 100644 --- a/core/risk/exit_rules.py +++ b/core/risk/exit_rules.py @@ -58,7 +58,8 @@ class TrailingStopRule(BaseExitRule): if ctx.peak_profit_pct <= min_profit: return "none", "" - stop_level = ctx.peak_profit_pct * (1 - retracement_pct) + # ★ 回撤从峰值绝对值扣除(账户%,非相对%):峰值+2.0%回撤1.0%→止损在+1.0% + stop_level = ctx.peak_profit_pct - retracement_pct if ctx.current_profit_pct <= stop_level: return "close", ( f"追踪止损触发 " diff --git a/core/risk/position.py b/core/risk/position.py index b1a4256..d479be8 100644 --- a/core/risk/position.py +++ b/core/risk/position.py @@ -189,18 +189,27 @@ class PositionManager: return False # ★ 计算 MT5 硬止损/硬止盈(兜底安全网) + # 公式:账户% × 余额 × 倍率 → 美元 → 金价点数 → 价格 hard_sl_price = None hard_tp_price = None + contract_size = self._get_contract_size() + points_per_dollar = 1.0 / (position_volume * contract_size) if position_volume > 0 else 0 + + sl_dollars = abs(self.stop_loss_pct) * self.total_equity * self.hard_sl_mult # 正数 + tp_dollars = abs(self.take_profit_pct) * self.total_equity * self.hard_tp_mult + sl_points = sl_dollars * points_per_dollar + tp_points = tp_dollars * points_per_dollar + if direction == 'buy': if self.hard_sl_mult > 0: - hard_sl_price = round(execution_price * (1 + self.stop_loss_pct * self.hard_sl_mult), 2) + hard_sl_price = round(execution_price - sl_points, 2) if self.hard_tp_mult > 0: - hard_tp_price = round(execution_price * (1 + self.take_profit_pct * self.hard_tp_mult), 2) + hard_tp_price = round(execution_price + tp_points, 2) else: if self.hard_sl_mult > 0: - hard_sl_price = round(execution_price * (1 - self.stop_loss_pct * self.hard_sl_mult), 2) + hard_sl_price = round(execution_price + sl_points, 2) if self.hard_tp_mult > 0: - hard_tp_price = round(execution_price * (1 - self.take_profit_pct * self.hard_tp_mult), 2) + hard_tp_price = round(execution_price - tp_points, 2) order_result = self.data_provider.send_order( self.symbol, direction, position_volume, @@ -309,25 +318,39 @@ class PositionManager: # ── 盈亏计算 ── + def _get_contract_size(self) -> float: + """获取合约规格(缓存避免重复API调用)""" + if not hasattr(self, '_cached_contract_size'): + symbol_info = self.data_provider.get_symbol_info(self.symbol) + self._cached_contract_size = ( + symbol_info['trade_contract_size'] + if symbol_info and isinstance(symbol_info, dict) + else 100 + ) if symbol_info else 100 + return self._cached_contract_size + def _calculate_pnl_pct(self, position, current_price_value): + """★ 账户余额百分比盈亏(非金价涨跌%)""" entry_price = position['entry_price'] + quantity = position.get('quantity', 0.01) + contract_size = self._get_contract_size() + if position['position_type'] == 'long': - return (current_price_value - entry_price) / entry_price if entry_price != 0 else 0.0 + dollar_pnl = (current_price_value - entry_price) * quantity * contract_size else: - return (entry_price - current_price_value) / entry_price if entry_price != 0 else 0.0 + dollar_pnl = (entry_price - current_price_value) * quantity * contract_size + + return dollar_pnl / self.total_equity if self.total_equity > 0 else 0.0 def _calculate_unrealized_pnl(self) -> float: - """★ 新增:计算所有持仓的浮动盈亏""" + """计算所有持仓的浮动盈亏""" if not self.positions: return 0.0 current_price_data = self.data_provider.get_current_price(self.symbol) if not current_price_data: return 0.0 current_price = current_price_data['last'] - symbol_info = self.data_provider.get_symbol_info(self.symbol) - contract_size = (symbol_info['trade_contract_size'] - if symbol_info and isinstance(symbol_info, dict) - else 100) if symbol_info else 100 + contract_size = self._get_contract_size() total = 0.0 for pos in self.positions: if pos['position_type'] == 'long': @@ -338,10 +361,7 @@ class PositionManager: return total def _record_closed_trade(self, position, close_price, close_reason): - symbol_info = self.data_provider.get_symbol_info(position['symbol']) - contract_size = (symbol_info['trade_contract_size'] - if symbol_info and isinstance(symbol_info, dict) - else 100) if symbol_info else 100 + contract_size = self._get_contract_size() if position['position_type'] == 'long': pnl = (close_price - position['entry_price']) * position['quantity'] * contract_size else: diff --git a/optimization_report_20260514_141914.txt b/optimization_report_20260514_141914.txt new file mode 100644 index 0000000..6c98e82 --- /dev/null +++ b/optimization_report_20260514_141914.txt @@ -0,0 +1,75 @@ +优化完成时间: 2026-05-14 14:19:14.534049 +最佳适应度: 169.69 + +最佳参数: + ma_cross_short_window: 13 + ma_cross_long_window: 17 + rsi_period: 7 + rsi_overbought: 73 + rsi_oversold: 27 + bollinger_period: 24 + bollinger_std_dev: 1.9616407387901151 + macd_fast_ema: 14 + macd_slow_ema: 24 + macd_signal_period: 9 + mean_reversion_period: 30 + mean_reversion_std_dev: 2.4663422649047333 + momentum_breakout_period: 50 + momentum_breakout_momentum_period: 27 + kdj_period: 12 + turtle_period: 30 + wave_ema_short: 3 + wave_ema_medium: 11 + wave_ema_long: 46 + wave_period: 11 + wave_range_period: 35 + wave_adx_period: 20 + wave_momentum_period: 13 + wave_range_threshold: 0.01 + wave_adx_threshold: 21 + daily_breakout_bars_count: 2008 + buy_threshold: 1.0315663601353515 + sell_threshold: -2.312867704316165 + stop_loss_pct: -0.028495655271807224 + profit_retracement_pct: 0.07523134661548606 + min_profit_for_trailing: 0.005 + take_profit_pct: 0.3970073007719148 + max_holding_minutes: 156 + min_profit_for_time_exit: 0.006397809476304411 + weight_MACrossStrategy: 0.32048324544087786 + weight_RSIStrategy: 1.906009222972592 + weight_BollingerStrategy: 0.9232444456662969 + weight_MeanReversionStrategy: 1.1649306516234597 + weight_MomentumBreakoutStrategy: 0.8769099086178171 + weight_MACDStrategy: 0.748930985323352 + weight_KDJStrategy: 1.4647470623067596 + weight_TurtleStrategy: 1.637931136008917 + weight_DailyBreakoutStrategy: 1.478598649389487 + weight_WaveTheoryStrategy: 0.818283180489803 + market_trend_period: 83 + market_retracement_tolerance: 0.15567164907853615 + market_volume_period: 29 + market_volume_ma_period: 23 + trend_price_breakout_weight: 0.1504199442999585 + trend_volume_confirmation_weight: 0.12782205952949638 + trend_momentum_oscillator_weight: 0.3645053274270841 + trend_moving_average_weight: 0.4092273363154768 + trend_strong_threshold: 0.7940886082643032 + trend_weak_threshold: 0.4565953163045441 + trend_volume_spike: 2.7329673335105396 + trend_oversold: 32 + trend_overbought: 68 + confidence_high: 0.6813641209473742 + confidence_medium: 0.6336441706563001 + +代数统计: + 代数 1: 最佳=166.40, 平均=33.80 + 代数 2: 最佳=116.73, 平均=50.63 + 代数 3: 最佳=104.87, 平均=55.76 + 代数 4: 最佳=147.56, 平均=74.03 + 代数 5: 最佳=141.85, 平均=57.90 + 代数 6: 最佳=141.85, 平均=70.85 + 代数 7: 最佳=154.87, 平均=85.11 + 代数 8: 最佳=169.69, 平均=94.60 + 代数 9: 最佳=169.69, 平均=99.20 + 代数 10: 最佳=169.69, 平均=112.85