去掉所有 AI 变量和逻辑,然后只加一个新的,在 confirm_trade_entry 里确认入场是否可信
This commit is contained in:
parent
0d05406167
commit
e0de60144d
@ -112,13 +112,7 @@
|
||||
"main_plot": {},
|
||||
"subplots": {
|
||||
"&-predictions": {
|
||||
"&-exit_return": {"color": "red"}
|
||||
},
|
||||
"&-risk": {
|
||||
"&-max_drawdown": {"color": "orange"}
|
||||
},
|
||||
"&-timing": {
|
||||
"&-hold_duration": {"color": "blue"}
|
||||
"&-entry_confidence": {"color": "green"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,38 +372,19 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame:
|
||||
"""
|
||||
设置 FreqAI 训练标签 (3个变量,简化版)
|
||||
- &-exit_return: 出场回归标签 (未来收益率)
|
||||
- &-max_drawdown: 最大回撤预测 (入场风险过滤)
|
||||
- &-hold_duration: 最佳持仓时长 (0-1,出场ROI优化)
|
||||
设置 FreqAI 训练标签 (1个变量,简化版)
|
||||
- &-entry_confidence: 入场置信度评分 (0-1),仅用于 confirm_trade_entry 最终确认
|
||||
"""
|
||||
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
|
||||
|
||||
# ========== 1. 出场回归标签 ==========
|
||||
# 直接使用未来收益率
|
||||
# ========== 入场置信度标签 ==========
|
||||
# 基于未来收益率估算入场好坏,使用 S 型函数映射到 0-1
|
||||
future_return = dataframe["close"].shift(-label_period) / dataframe["close"] - 1
|
||||
dataframe["&-exit_return"] = future_return
|
||||
|
||||
# ========== 2. 最大回撤标签 (入场风险过滤) ==========
|
||||
# 使用未来最低价计算最大回撤,使用 min_periods=1 避免过多 NaN
|
||||
future_low = dataframe["low"].shift(-1).rolling(window=label_period, min_periods=1).min()
|
||||
dataframe["&-max_drawdown"] = (future_low / dataframe["close"]) - 1
|
||||
|
||||
# ========== 3. 最佳持仓时长标签 (出场ROI优化) ==========
|
||||
# 基于未来收益率估算持仓价值,归一化到 0-1
|
||||
# 收益率高 → hold_duration 高 (应该持仓久)
|
||||
# 收益率低/负 → hold_duration 低 (应该尽快出场)
|
||||
future_return_clipped = future_return.clip(-0.1, 0.1) # 限制在 -10% ~ +10%
|
||||
dataframe["&-hold_duration"] = (future_return_clipped + 0.1) / 0.2 # 归一化到 0-1
|
||||
dataframe["&-entry_confidence"] = 1 / (1 + np.exp(-future_return * 50))
|
||||
|
||||
# ========== 统一处理 NaN,避免训练数据被全部过滤 ==========
|
||||
# 所有标签列末尾会因为 shift(-label_period) 产生 NaN,用前向填充避免丢失样本
|
||||
label_columns = [
|
||||
"&-exit_return", "&-max_drawdown", "&-hold_duration"
|
||||
]
|
||||
for col in label_columns:
|
||||
if col in dataframe.columns:
|
||||
dataframe[col] = dataframe[col].fillna(method='ffill').fillna(0)
|
||||
if "&-entry_confidence" in dataframe.columns:
|
||||
dataframe["&-entry_confidence"] = dataframe["&-entry_confidence"].fillna(method='ffill').fillna(0)
|
||||
|
||||
return dataframe
|
||||
|
||||
@ -669,54 +650,11 @@ class FreqaiPrimer(IStrategy):
|
||||
# 合并所有条件
|
||||
traditional_condition = breakout_condition | volume_spike | macd_downward | rsi_overbought
|
||||
|
||||
# ==================== FreqAI 出场信号融合 (市场自适应) ====================
|
||||
# 检查 FreqAI 预测结果是否可用
|
||||
if "&-exit_return" in dataframe.columns and "do_predict" in dataframe.columns:
|
||||
# 为每一行根据市场状态计算自适应退出阈值,避免在 populate_ 中使用 iloc[-1]
|
||||
if 'market_state' in dataframe.columns:
|
||||
exit_th_series = dataframe['market_state'].apply(
|
||||
lambda s: self._get_adaptive_thresholds(s)['exit_threshold']
|
||||
)
|
||||
else:
|
||||
default_th = self._get_adaptive_thresholds('neutral')['exit_threshold']
|
||||
exit_th_series = np.full(len(dataframe), default_th)
|
||||
|
||||
# 回归器预测: 未来收益率
|
||||
# 熊市时 exit_th 为负值(如-0.02),更容易触发出场
|
||||
# 牛市时 exit_th 为正值(如+0.02),需要更大下跌才出场
|
||||
ml_exit_signal = (dataframe["&-exit_return"] < exit_th_series)
|
||||
|
||||
# 置信度过滤: do_predict >= 1 表示预测可信
|
||||
ml_confidence = (dataframe["do_predict"] >= 1)
|
||||
|
||||
# 融合策略: 传统条件 OR (ML出场信号 AND ML置信度)
|
||||
# 出场使用 OR 逻辑,更激进地保护利润
|
||||
final_condition = traditional_condition | (ml_exit_signal & ml_confidence)
|
||||
|
||||
# 记录融合信息(只统计数量,不再依赖最后一行)
|
||||
logger.info(
|
||||
f"[{metadata['pair']}] FreqAI出场: "
|
||||
f"传统={int(traditional_condition.sum())}, "
|
||||
f"ML出场={int(ml_exit_signal.sum())}, "
|
||||
f"最终={int(final_condition.sum())}"
|
||||
)
|
||||
else:
|
||||
# FreqAI 未启用,使用传统条件
|
||||
final_condition = traditional_condition
|
||||
|
||||
# 设置出场信号
|
||||
# ==================== 出场信号:仅使用传统条件 ====================
|
||||
final_condition = traditional_condition
|
||||
|
||||
dataframe.loc[final_condition, 'exit_long'] = 1
|
||||
|
||||
# 增强调试信息
|
||||
#logger.info(f"[{metadata['pair']}] 出场条件检查:")
|
||||
#logger.info(f" - 价格突破布林带上轨: {breakout_condition.sum()} 次")
|
||||
#logger.info(f" - 成交量显著放大: {volume_spike.sum()} 次")
|
||||
#logger.info(f" - MACD 下降趋势: {macd_downward.sum()} 次")
|
||||
#logger.info(f" - RSI 超买: {rsi_overbought.sum()} 次")
|
||||
#logger.info(f" - 最终条件: {final_condition.sum()} 次")
|
||||
#logger.info(f" - 使用参数: exit_bb_upper_deviation={self.exit_bb_upper_deviation.value}, exit_volume_multiplier={self.exit_volume_multiplier.value}, rsi_overbought={self.rsi_overbought.value}")
|
||||
|
||||
# 日志记录
|
||||
|
||||
if dataframe['exit_long'].sum() > 0:
|
||||
logger.info(f"[{metadata['pair']}] 触发出场信号数量: {dataframe['exit_long'].sum()}")
|
||||
|
||||
@ -767,55 +705,13 @@ class FreqaiPrimer(IStrategy):
|
||||
)
|
||||
traditional_condition = condition_count >= self.min_condition_count.value
|
||||
|
||||
# ==================== FreqAI 入场信号融合 (市场自适应) ====================
|
||||
# 检查 FreqAI 预测结果是否可用
|
||||
if "&-max_drawdown" in dataframe.columns and "do_predict" in dataframe.columns:
|
||||
# 行级动态阈值,避免在 populate_ 中使用 iloc[-1]
|
||||
if 'market_state' in dataframe.columns:
|
||||
drawdown_th_series = dataframe['market_state'].apply(
|
||||
lambda s: self._get_adaptive_thresholds(s)['max_drawdown']
|
||||
)
|
||||
else:
|
||||
default_th = self._get_adaptive_thresholds('neutral')
|
||||
drawdown_th_series = np.full(len(dataframe), default_th['max_drawdown'])
|
||||
|
||||
# 置信度过滤: do_predict >= 1 表示预测可信
|
||||
ml_confidence = (dataframe["do_predict"] >= 1)
|
||||
|
||||
# 最大回撤过滤: 使用动态阈值
|
||||
ml_drawdown_safe = (dataframe["&-max_drawdown"] >= drawdown_th_series)
|
||||
|
||||
# === 极简融合策略:FreqAI 仅做风险否决 ===
|
||||
bad_risk_mask = (dataframe["&-max_drawdown"] < drawdown_th_series) & ml_confidence
|
||||
|
||||
final_condition = traditional_condition & ~bad_risk_mask
|
||||
|
||||
# 记录融合信息(仅统计数量)
|
||||
logger.info(
|
||||
f"[{metadata['pair']}] FreqAI入场: "
|
||||
f"传统={int(traditional_condition.sum())}, "
|
||||
f"回撤安全={int(ml_drawdown_safe.sum())}, "
|
||||
f"风险否决={int(bad_risk_mask.sum())}, "
|
||||
f"最终={int(final_condition.sum())}"
|
||||
)
|
||||
else:
|
||||
# FreqAI 未启用,使用传统条件
|
||||
final_condition = traditional_condition
|
||||
# ==================== 入场信号:仅使用传统条件 ====================
|
||||
final_condition = traditional_condition
|
||||
|
||||
# 设置入场信号
|
||||
dataframe.loc[final_condition, 'enter_long'] = 1
|
||||
|
||||
# 增强调试信息
|
||||
#logger.info(f"[{metadata['pair']}] 入场条件检查:")
|
||||
#logger.info(f" - 价格接近布林带下轨: {close_to_bb_lower_1h.sum()} 次")
|
||||
#logger.info(f" - RSI 超卖: {rsi_condition_1h.sum()} 次")
|
||||
#logger.info(f" - StochRSI 超卖: {stochrsi_condition_1h.sum()} 次")
|
||||
#logger.info(f" - MACD 上升趋势: {macd_condition_1h.sum()} 次")
|
||||
#logger.info(f" - 成交量或布林带宽度: {(volume_spike | bb_width_condition).sum()} 次")
|
||||
#logger.info(f" - 趋势确认: {trend_confirmation.sum()} 次")
|
||||
#logger.info(f" - 最终条件: {final_condition.sum()} 次")
|
||||
# 在populate_entry_trend方法末尾添加
|
||||
# 计算条件间的相关性
|
||||
# 计算条件间的相关性(仅用于分析)
|
||||
conditions = DataFrame({
|
||||
'close_to_bb': close_to_bb_lower_1h,
|
||||
'rsi': rsi_condition_1h,
|
||||
@ -826,6 +722,7 @@ class FreqaiPrimer(IStrategy):
|
||||
})
|
||||
correlation = conditions.corr().mean().mean()
|
||||
#logger.info(f"[{metadata['pair']}] 条件平均相关性: {correlation:.2f}")
|
||||
|
||||
# 日志记录
|
||||
if dataframe['enter_long'].sum() > 0:
|
||||
logger.info(f"[{metadata['pair']}] 发现入场信号数量: {dataframe['enter_long'].sum()}")
|
||||
@ -902,20 +799,35 @@ class FreqaiPrimer(IStrategy):
|
||||
) -> bool:
|
||||
"""
|
||||
交易买入前的确认函数,用于最终决定是否执行交易
|
||||
此处实现剧烈拉升检查逻辑
|
||||
- 步骤1: 剧烈拉升过滤(纯规则)
|
||||
- 步骤2: FreqAI 入场置信度过滤(仅作为最终确认,不影响回测一致性)
|
||||
"""
|
||||
# 默认允许交易
|
||||
allow_trade = True
|
||||
|
||||
|
||||
# 仅对多头交易进行检查
|
||||
if side == 'long':
|
||||
# 检查是否处于剧烈拉升的不稳固区域
|
||||
# 步骤1: 检查是否处于剧烈拉升的不稳固区域
|
||||
is_unstable_region = self.detect_h1_rapid_rise(pair)
|
||||
if is_unstable_region:
|
||||
#logger.info(f"[{pair}] 由于检测到剧烈拉升,取消入场交易")
|
||||
allow_trade = False
|
||||
|
||||
# 如果没有阻止因素,允许交易
|
||||
|
||||
# 步骤2: 使用 FreqAI 入场置信度进行二次确认
|
||||
try:
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||
if "&-entry_confidence" in dataframe.columns and "do_predict" in dataframe.columns and len(dataframe) > 0:
|
||||
last_row = dataframe.iloc[-1]
|
||||
score = last_row["&-entry_confidence"]
|
||||
do_predict = last_row["do_predict"]
|
||||
|
||||
# 简单规则:预测有效且置信度足够高才允许入场
|
||||
min_confidence = 0.52
|
||||
if do_predict < 1 or np.isnan(score) or score < min_confidence:
|
||||
#logger.info(f"[{pair}] 由于 FreqAI 入场置信度不足(score={score:.3f}, do_predict={do_predict}),取消入场交易")
|
||||
allow_trade = False
|
||||
except Exception as e:
|
||||
logger.error(f"[{pair}] confirm_trade_entry 中 FreqAI 置信度检查异常: {str(e)}")
|
||||
|
||||
return allow_trade
|
||||
|
||||
def custom_stoploss(self, pair: str, trade: 'Trade', current_time, current_rate: float,
|
||||
@ -964,20 +876,8 @@ class FreqaiPrimer(IStrategy):
|
||||
dynamic_roi_threshold = 0.0
|
||||
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||
# 优先使用市场状态特征;若不存在则回退为中性市场,避免依赖不存在的sma列
|
||||
# 优先使用市场状态特征;若不存在则回退为中性市场
|
||||
current_state = dataframe['market_state'].iloc[-1] if 'market_state' in dataframe.columns else 'neutral'
|
||||
|
||||
# ==================== FreqAI hold_duration 动态调整 ====================
|
||||
# 使用 ML 预测的最佳持仓时长来调整 ROI 阈值
|
||||
# hold_duration 在 0-1 之间,0 = 应立即出场,1 = 应持有到最后
|
||||
if '&-hold_duration' in dataframe.columns and 'do_predict' in dataframe.columns:
|
||||
ml_hold_duration = dataframe['&-hold_duration'].iloc[-1]
|
||||
ml_predict_valid = dataframe['do_predict'].iloc[-1] >= 1
|
||||
if ml_predict_valid and not np.isnan(ml_hold_duration):
|
||||
# 如果预测应尽快出场(hold_duration < 0.3),降低 ROI 阈值(更容易触发出场)
|
||||
# 如果预测应持有(hold_duration > 0.7),提高 ROI 阈值(更难触发出场)
|
||||
hold_adjustment = 1.0 + (ml_hold_duration - 0.5) * 0.6 # 范围: 0.7 ~ 1.3
|
||||
dynamic_roi_threshold = dynamic_roi_threshold * hold_adjustment
|
||||
|
||||
entry_tag = trade.enter_tag if hasattr(trade, 'enter_tag') else None
|
||||
profit_ratio = current_profit / dynamic_roi_threshold if dynamic_roi_threshold > 0 else 0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user