去掉所有 AI 变量和逻辑,然后只加一个新的,在 confirm_trade_entry 里确认入场是否可信
This commit is contained in:
parent
0d05406167
commit
e0de60144d
@ -112,13 +112,7 @@
|
|||||||
"main_plot": {},
|
"main_plot": {},
|
||||||
"subplots": {
|
"subplots": {
|
||||||
"&-predictions": {
|
"&-predictions": {
|
||||||
"&-exit_return": {"color": "red"}
|
"&-entry_confidence": {"color": "green"}
|
||||||
},
|
|
||||||
"&-risk": {
|
|
||||||
"&-max_drawdown": {"color": "orange"}
|
|
||||||
},
|
|
||||||
"&-timing": {
|
|
||||||
"&-hold_duration": {"color": "blue"}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -372,38 +372,19 @@ class FreqaiPrimer(IStrategy):
|
|||||||
|
|
||||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame:
|
def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
设置 FreqAI 训练标签 (3个变量,简化版)
|
设置 FreqAI 训练标签 (1个变量,简化版)
|
||||||
- &-exit_return: 出场回归标签 (未来收益率)
|
- &-entry_confidence: 入场置信度评分 (0-1),仅用于 confirm_trade_entry 最终确认
|
||||||
- &-max_drawdown: 最大回撤预测 (入场风险过滤)
|
|
||||||
- &-hold_duration: 最佳持仓时长 (0-1,出场ROI优化)
|
|
||||||
"""
|
"""
|
||||||
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
|
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
|
||||||
|
|
||||||
# ========== 1. 出场回归标签 ==========
|
# ========== 入场置信度标签 ==========
|
||||||
# 直接使用未来收益率
|
# 基于未来收益率估算入场好坏,使用 S 型函数映射到 0-1
|
||||||
future_return = dataframe["close"].shift(-label_period) / dataframe["close"] - 1
|
future_return = dataframe["close"].shift(-label_period) / dataframe["close"] - 1
|
||||||
dataframe["&-exit_return"] = future_return
|
dataframe["&-entry_confidence"] = 1 / (1 + np.exp(-future_return * 50))
|
||||||
|
|
||||||
# ========== 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
|
|
||||||
|
|
||||||
# ========== 统一处理 NaN,避免训练数据被全部过滤 ==========
|
# ========== 统一处理 NaN,避免训练数据被全部过滤 ==========
|
||||||
# 所有标签列末尾会因为 shift(-label_period) 产生 NaN,用前向填充避免丢失样本
|
if "&-entry_confidence" in dataframe.columns:
|
||||||
label_columns = [
|
dataframe["&-entry_confidence"] = dataframe["&-entry_confidence"].fillna(method='ffill').fillna(0)
|
||||||
"&-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)
|
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
@ -669,54 +650,11 @@ class FreqaiPrimer(IStrategy):
|
|||||||
# 合并所有条件
|
# 合并所有条件
|
||||||
traditional_condition = breakout_condition | volume_spike | macd_downward | rsi_overbought
|
traditional_condition = breakout_condition | volume_spike | macd_downward | rsi_overbought
|
||||||
|
|
||||||
# ==================== FreqAI 出场信号融合 (市场自适应) ====================
|
# ==================== 出场信号:仅使用传统条件 ====================
|
||||||
# 检查 FreqAI 预测结果是否可用
|
final_condition = traditional_condition
|
||||||
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
|
|
||||||
|
|
||||||
# 设置出场信号
|
|
||||||
dataframe.loc[final_condition, 'exit_long'] = 1
|
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:
|
if dataframe['exit_long'].sum() > 0:
|
||||||
logger.info(f"[{metadata['pair']}] 触发出场信号数量: {dataframe['exit_long'].sum()}")
|
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
|
traditional_condition = condition_count >= self.min_condition_count.value
|
||||||
|
|
||||||
# ==================== FreqAI 入场信号融合 (市场自适应) ====================
|
# ==================== 入场信号:仅使用传统条件 ====================
|
||||||
# 检查 FreqAI 预测结果是否可用
|
final_condition = traditional_condition
|
||||||
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
|
|
||||||
|
|
||||||
# 设置入场信号
|
# 设置入场信号
|
||||||
dataframe.loc[final_condition, 'enter_long'] = 1
|
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({
|
conditions = DataFrame({
|
||||||
'close_to_bb': close_to_bb_lower_1h,
|
'close_to_bb': close_to_bb_lower_1h,
|
||||||
'rsi': rsi_condition_1h,
|
'rsi': rsi_condition_1h,
|
||||||
@ -826,6 +722,7 @@ class FreqaiPrimer(IStrategy):
|
|||||||
})
|
})
|
||||||
correlation = conditions.corr().mean().mean()
|
correlation = conditions.corr().mean().mean()
|
||||||
#logger.info(f"[{metadata['pair']}] 条件平均相关性: {correlation:.2f}")
|
#logger.info(f"[{metadata['pair']}] 条件平均相关性: {correlation:.2f}")
|
||||||
|
|
||||||
# 日志记录
|
# 日志记录
|
||||||
if dataframe['enter_long'].sum() > 0:
|
if dataframe['enter_long'].sum() > 0:
|
||||||
logger.info(f"[{metadata['pair']}] 发现入场信号数量: {dataframe['enter_long'].sum()}")
|
logger.info(f"[{metadata['pair']}] 发现入场信号数量: {dataframe['enter_long'].sum()}")
|
||||||
@ -902,20 +799,35 @@ class FreqaiPrimer(IStrategy):
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
交易买入前的确认函数,用于最终决定是否执行交易
|
交易买入前的确认函数,用于最终决定是否执行交易
|
||||||
此处实现剧烈拉升检查逻辑
|
- 步骤1: 剧烈拉升过滤(纯规则)
|
||||||
|
- 步骤2: FreqAI 入场置信度过滤(仅作为最终确认,不影响回测一致性)
|
||||||
"""
|
"""
|
||||||
# 默认允许交易
|
|
||||||
allow_trade = True
|
allow_trade = True
|
||||||
|
|
||||||
# 仅对多头交易进行检查
|
# 仅对多头交易进行检查
|
||||||
if side == 'long':
|
if side == 'long':
|
||||||
# 检查是否处于剧烈拉升的不稳固区域
|
# 步骤1: 检查是否处于剧烈拉升的不稳固区域
|
||||||
is_unstable_region = self.detect_h1_rapid_rise(pair)
|
is_unstable_region = self.detect_h1_rapid_rise(pair)
|
||||||
if is_unstable_region:
|
if is_unstable_region:
|
||||||
#logger.info(f"[{pair}] 由于检测到剧烈拉升,取消入场交易")
|
#logger.info(f"[{pair}] 由于检测到剧烈拉升,取消入场交易")
|
||||||
allow_trade = False
|
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
|
return allow_trade
|
||||||
|
|
||||||
def custom_stoploss(self, pair: str, trade: 'Trade', current_time, current_rate: float,
|
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
|
dynamic_roi_threshold = 0.0
|
||||||
|
|
||||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
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'
|
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
|
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
|
profit_ratio = current_profit / dynamic_roi_threshold if dynamic_roi_threshold > 0 else 0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user