23 KiB
-
Datetime: 2025-05-29 18:04:45
-
Model: _qwen3/qwen-plus-latest
-
Selected files:
- /home/ubuntu/freqtrade/freqtrade/templates/freqaiprimer.py
无法触发入场信号的原因是: (
df["&-trend_strength"]> 0.01) 这个条件永远无法被满足: 这是一个典型的训练结果 "labels_mean": { "&-trend_strength": 0.00016881476792118572, "&-volatility_forecast": 0.005005756262776747, "&-roi_target": 0.008831010745460291, "&-stoploss_target": -0.006006907515332095, "&-buy_signal": 0.06259223890962758, "&-sell_signal": 0.05226148103133953 }, "labels_std": { "&-trend_strength": 0.0066341052806047325, "&-volatility_forecast": 0.004356863038665235, "&-roi_target": 0.004687195365502718, "&-stoploss_target": 0.005228235646398282, "&-buy_signal": 0.24222809609520457, "&-sell_signal": 0.22255385557601662 },那么根据此结果,阈值该如何判定 ,另外 labels_mean和labels_std有什么区别
你提出的问题非常关键,涉及到 FreqAI 模型输出的标签分布分析 和 如何根据模型预测结果设定合理的交易信号阈值。
🧠 问题总结
你发现:
buy_condition &= (df["&-trend_strength"] > 0.01)
这个条件 永远无法满足,因为从你的 FreqAI 输出中可以看到:
"labels_mean": {
"&-trend_strength": 0.00016881476792118572,
...
}
说明模型预测的趋势强度(&-trend_strength)平均值接近于 0,标准差也很小(0.0066),所以几乎不会出现大于 0.01 的值。
✅ 解决方案:基于 labels_mean 和 labels_std 设置动态阈值
🔍 什么是 labels_mean 和 labels_std?
| 字段 | 含义 | 示例 |
|---|---|---|
labels_mean |
所有预测目标列的均值 | 0.0001688 表示趋势强度平均为 0.0001688 |
labels_std |
所有预测目标列的标准差 | 0.006634 表示趋势强度波动范围约为 ±0.0066 |
✅ 这两个统计量可以帮助我们判断模型预测值的分布情况,从而设置更合理的交易信号阈值。
✅ 如何设置合理的入场信号阈值?
方法一:使用 Z-score 判断显著性
你可以使用如下方式来判断当前趋势是否显著:
# 计算 z-score
mean = 0.0001688
std = 0.0066341
df["z_score_trend"] = (df["&-trend_strength"] - mean) / std
# 只在趋势强度显著高于平均水平时开仓(例如 z-score > 1)
buy_condition &= (df["z_score_trend"] > 1)
这表示只有当趋势强度超过历史平均值 +1 个标准差时才认为是“有效趋势”。
方法二:直接使用分位数过滤
如果你不想用 z-score,也可以使用滚动窗口的分位数来判断:
# 使用过去 20 根 K 线的趋势强度分位数
df["trend_quantile"] = df["&-trend_strength"].rolling(20).quantile(0.8)
# 只在趋势强度处于前 20% 时开仓
buy_condition &= (df["&-trend_strength"] >= df["trend_quantile"])
方法三:结合多个信号综合判断
由于 &-trend_strength 本身变化较小,可以考虑结合其他信号一起判断:
# 结合 buy_signal 和 trend_strength
buy_condition &= (
(df["&-buy_signal"] > df["buy_threshold"]) &
((df["&-trend_strength"] > 0.005) | (df["&-volatility_forecast"] > 0.006))
)
这样即使趋势不强,但波动率较高或买方信号强烈时,也能触发入场。
✅ 推荐修改后的 populate_entry_trend() 片段
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
conditions = []
if "&-buy_signal" in df.columns:
# 基础买入信号
df["buy_threshold"] = df["&-buy_signal"].rolling(20).quantile(0.8)
buy_condition = (df["&-buy_signal"] >= df["buy_threshold"]) & (df["&-buy_signal"] > 0.4)
# 趋势强度过滤(使用 z-score)
mean_trend = 0.0001688
std_trend = 0.0066341
df["z_score_trend"] = (df["&-trend_strength"] - mean_trend) / std_trend
buy_condition &= (df["z_score_trend"] > 1)
# EMA 趋势过滤
df["ema50"] = ta.EMA(df, timeperiod=50)
buy_condition &= (df["close"] > df["ema50"])
# 成交量过滤
df["vol_mean"] = df["volume"].rolling(20).mean()
buy_condition &= (df["volume"] > df["vol_mean"] * 1.2)
# 市场状态过滤
if "&-market_condition" in df.columns:
buy_condition &= ~(
(df["&-market_condition"] == "overbought")
)
conditions.append(buy_condition)
else:
logger.warning("⚠️ &-buy_signal 列缺失,跳过该条件")
if len(conditions) == 0:
return df
df.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1
return df
✅ 总结
| 内容 | 说明 |
|---|---|
❌ 为什么 df["&-trend_strength"] > 0.01 不生效? |
因为模型预测值太小,均值仅 0.0001688,标准差 0.0066 |
| ✅ 如何解决? | 使用 z-score、分位数、多信号组合等方式替代固定阈值 |
📊 labels_mean vs labels_std |
mean 是中心位置,std 是离散程度,两者共同描述分布形态 |
| 🛠 推荐做法 | 使用 z-score 或滚动分位数来判断趋势是否显著,避免硬编码阈值 |
如果你希望我帮你生成完整的可运行策略文件,并根据你的回测数据自动计算最佳阈值,请告诉我你想预测的目标(比如涨跌、波动率等),我可以为你定制这部分逻辑。
-
Datetime: 2025-05-29 18:07:22
-
Model: _qwen3/qwen-plus-latest
-
Selected files:
- /home/ubuntu/freqtrade/freqtrade/templates/freqaiprimer.py
mean_trend和std_trend是否可以从dataframe里动态获取, 而不是写死
是的,mean_trend 和 std_trend 完全可以从 dataframe 中动态获取,而不是写死在代码中。实际上,这也是更推荐的做法,因为:
- 模型预测值会随时间变化(尤其是在模型重新训练后)
- 不同币种可能有不同的分布特性
- 写死数值不利于长期运行和策略适应性
✅ 推荐做法:使用滚动窗口计算趋势强度的均值和标准差
你可以在 populate_entry_trend() 方法中添加如下逻辑:
# 动态计算趋势强度的滚动均值和标准差(例如过去 50 根 K 线)
window = 50
df["trend_mean"] = df["&-trend_strength"].rolling(window).mean()
df["trend_std"] = df["&-trend_strength"].rolling(window).std()
# 使用 z-score 判断当前趋势是否显著
df["z_score_trend"] = (df["&-trend_strength"] - df["trend_mean"]) / df["trend_std"]
# 只在趋势强度显著高于历史平均水平时开仓(例如 z-score > 1)
buy_condition &= (df["z_score_trend"] > 1)
✅ 示例:完整更新后的 populate_entry_trend() 片段
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
conditions = []
if "&-buy_signal" in df.columns:
# 基础买入信号
df["buy_threshold"] = df["&-buy_signal"].rolling(20).quantile(0.8)
buy_condition = (df["&-buy_signal"] >= df["buy_threshold"]) & (df["&-buy_signal"] > 0.4)
# 动态计算趋势强度的滚动均值和标准差
window = 50
df["trend_mean"] = df["&-trend_strength"].rolling(window).mean()
df["trend_std"] = df["&-trend_strength"].rolling(window).std()
# 使用 z-score 判断趋势是否显著
df["z_score_trend"] = (df["&-trend_strength"] - df["trend_mean"]) / df["trend_std"]
buy_condition &= (df["z_score_trend"] > 1)
# EMA 趋势过滤
df["ema50"] = ta.EMA(df, timeperiod=50)
buy_condition &= (df["close"] > df["ema50"])
# 成交量过滤
df["vol_mean"] = df["volume"].rolling(20).mean()
buy_condition &= (df["volume"] > df["vol_mean"] * 1)
# 市场状态过滤(只排除超买)
if "&-market_condition" in df.columns:
buy_condition &= ~(
(df["&-market_condition"] == "overbought")
)
conditions.append(buy_condition)
else:
logger.warning("⚠️ &-buy_signal 列缺失,跳过该条件")
if len(conditions) == 0:
return df
df.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1
return df
✅ 进阶建议:使用 FreqAI 提供的 labels_mean 和 labels_std
FreqAI 在每次训练后都会输出 labels_mean 和 labels_std,你可以通过以下方式访问它们:
if self.freqai and hasattr(self.freqai, "data"):
labels_mean = self.freqai.data.labels_mean
labels_std = self.freqai.data.labels_std
mean_trend = labels_mean.get("&-trend_strength", 0)
std_trend = labels_std.get("&-trend_strength", 1e-6)
⚠️ 注意:这些值在整个回测过程中不会实时更新,仅反映模型训练时的状态。
✅ 总结
| 方法 | 是否推荐 | 说明 |
|---|---|---|
❌ 写死 mean_trend 和 std_trend |
否 | 不灵活,容易失效 |
| ✅ 使用滚动窗口动态计算 | 是 | 更适合实盘和长期运行 |
🧠 使用 self.freqai.data.labels_mean |
可选 | 适用于静态分析或调试 |
如果你希望我帮你生成一个 完全自适应、自动调节阈值的版本,请告诉我你的目标(比如你想预测涨跌、波动率、趋势强度等),我可以为你定制这部分逻辑。
-
Datetime: 2025-05-29 18:11:48
-
Model: _qwen3/qwen-plus-latest
-
Selected files:
- /home/ubuntu/freqtrade/freqtrade/templates/freqaiprimer.py
这些值在整个回测过程中不会实时更新,仅反映模型训练时的状态。
如何理解上面这句话, 在trade模式下会实时更新吗
非常好的问题!
你提到的这句话:
"这些值在整个回测过程中不会实时更新,仅反映模型训练时的状态。"
我们来详细解释它的含义,并回答你在 trade 模式下是否会实时更新 的问题。
✅ 一、在 backtesting(回测)模式下的行为
📌 FreqAI 的 labels_mean 和 labels_std 是什么?
它们是 FreqAI 在每次 模型训练完成后 自动计算并保存的统计信息,用于描述模型预测目标列的分布情况。
例如:
{
"labels_mean": {
"&-trend_strength": 0.0001688,
...
},
"labels_std": {
"&-trend_strength": 0.0066341,
...
}
}
这些值是在模型训练时基于训练数据计算出来的,不是实时滚动更新的统计数据。
🧨 回测中为什么不会“实时”更新?
因为回测是 历史模拟,它必须保证:
- 所有决策只能使用过去的数据(不能偷看未来)
- 所有参数必须在训练阶段就确定下来
所以:
❗ 在回测中,
labels_mean和labels_std只会在模型第一次训练时计算一次,之后不会再更新。
即使你在策略中调用:
self.freqai.data.labels_mean
它返回的也是 模型训练那一刻的历史值,而不是当前 K 线窗口的动态均值/标准差。
✅ 二、在 live/dry-run/trade(实盘/模拟交易)模式下的行为
🚀 实盘运行时会重新训练模型
FreqAI 支持定期重新训练模型(通过配置 live_retrain_hours),这意味着:
✅ 在 trade 模式下,
labels_mean和labels_std是会随着模型重新训练而更新的!
也就是说:
| 模式 | 是否更新 labels_mean/std |
原因 |
|---|---|---|
backtesting |
❌ 否 | 使用固定模型和静态统计量 |
trade / dry_run |
✅ 是 | 模型定期重训练,统计量随之更新 |
🛠 如何获取最新的 labels_mean 和 labels_std?
你可以这样访问:
if self.freqai and hasattr(self.freqai, "data"):
labels_mean = self.freqai.data.labels_mean
labels_std = self.freqai.data.labels_std
mean_trend = labels_mean.get("&-trend_strength", 0)
std_trend = labels_std.get("&-trend_strength", 1e-6)
logger.info(f"Current trend mean: {mean_trend}, std: {std_trend}")
⚠️ 注意:这些值只在模型训练或加载时更新,不是每根 K 线都更新。
✅ 三、如何实现“实时”动态统计?
如果你希望根据最近一段时间的预测值动态计算趋势强度的均值和标准差(比如滚动窗口),可以这样做:
window = 50
df["trend_mean"] = df["&-trend_strength"].rolling(window).mean()
df["trend_std"] = df["&-trend_strength"].rolling(window).std()
# 使用 z-score 判断趋势是否显著
df["z_score_trend"] = (df["&-trend_strength"] - df["trend_mean"]) / df["trend_std"]
buy_condition &= (df["z_score_trend"] > 1)
这种方式适用于所有模式(包括 backtest 和 trade),并且能更灵活地适应市场变化。
✅ 总结
| 场景 | labels_mean/std 是否更新 |
是否推荐使用 |
|---|---|---|
| 回测模式 | ❌ 否(仅训练时一次) | ❌ 不适合长期依赖 |
| 实盘/模拟交易 | ✅ 是(随模型重训练更新) | ✅ 推荐用于辅助分析 |
| 滚动窗口动态统计 | ✅ 是(每根 K 线更新) | ✅ 强烈推荐用于信号判断 |
如果你想我帮你写一个 完全自适应、自动调节阈值的版本,请告诉我你的目标(比如你想预测涨跌、波动率、趋势强度等),我可以为你定制这部分逻辑。
-
Datetime: 2025-05-29 18:18:57
-
Model: _qwen3/qwen-plus-latest
-
Selected files:
- /home/ubuntu/freqtrade/freqtrade/templates/freqaiprimer.py
既然已经: window = 50
df["trend_mean"]=df["&-trend_strength"].rolling(window).mean()df["trend_std"]=df["&-trend_strength"].rolling(window).std()使用 z-score 判断趋势是否显著
df["z_score_trend"]= (df["&-trend_strength"]-df["trend_mean"]) /df["trend_std"]buy_condition &= (df["z_score_trend"]> 1) 那么什么时候需要用到: if self.freqai and hasattr(self.freqai, "data"): labels_mean = self.freqai.data.labels_mean labels_std = self.freqai.data.labels_stdmean_trend = labels_mean.get("&-trend_strength", 0) std_trend = labels_std.get("&-trend_strength", 1e-6) logger.info(f"Current trend mean: {mean_trend}, std: {std_trend}")
非常好的问题!
你已经正确地在 populate_entry_trend() 中使用了 滚动窗口的 z-score 来动态判断趋势是否显著,这在大多数交易场景下是更优的做法。
那么:
🤔 既然我已经用上了滚动窗口 z-score,那 labels_mean 和 labels_std 还有什么用?
✅ 简短回答:
labels_mean和labels_std是 模型训练时的全局统计信息,主要用于:
- 调试和分析
- 辅助设置初始阈值
- 实盘中监控模型输出分布变化
它们不是用于实时信号生成(你已经做得很好),而是用于 理解模型行为、验证数据质量、检测漂移等目的。
✅ 具体用途详解
1. 调试模型输出分布
你可以通过打印 labels_mean["&-trend_strength"] 来确认模型输出的趋势强度是否合理。
例如:
logger.info(f"Model trend mean: {mean_trend}, std: {std_trend}")
如果发现 mean_trend 接近 0,且 std_trend 很小(如你的案例),说明模型预测的趋势不明显,可能需要调整标签定义或特征工程。
2. 作为回测/实盘的一致性检查
如果你在回测中看到某个币种的 z_score_trend > 1 频繁触发,但在实盘中几乎不触发,可以对比:
- 实盘中的
labels_mean/std是否与回测一致? - 模型是否发生了“漂移”?
这有助于判断模型是否仍然有效。
3. 作为自适应策略的一部分
虽然你已经在用滚动窗口 z-score,但也可以结合 labels_mean/std 做一些高级逻辑,比如:
🧠 动态调整窗口大小
if std_trend < 0.001:
window = 100 # 如果标准差太小,说明趋势变化慢,延长窗口
else:
window = 50
📊 判断模型是否“失效”
if abs(mean_trend) < 1e-4 and std_trend < 1e-4:
logger.warning("⚠️ 模型趋势预测无变化,可能需要重新训练")
✅ 总结:你应该什么时候使用它?
| 使用场景 | 是否推荐 | 说明 |
|---|---|---|
| 实时信号生成 | ❌ 否 | 应该使用滚动窗口 z-score |
| 调试模型输出 | ✅ 是 | 查看模型预测趋势是否合理 |
| 监控模型漂移 | ✅ 是 | 对比不同时间点的 labels_mean/std |
| 辅助策略决策 | ✅ 可选 | 如动态调整窗口长度、判断模型有效性 |
✅ 示例:保留 labels_mean/std 用于日志记录
你可以这样写代码:
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
conditions = []
if "&-buy_signal" in df.columns:
# 基础买入信号
df["buy_threshold"] = df["&-buy_signal"].rolling(20).quantile(0.8)
buy_condition = (df["&-buy_signal"] >= df["buy_threshold"]) & (df["&-buy_signal"] > 0.4)
# 动态计算趋势强度的滚动均值和标准差
window = 50
df["trend_mean"] = df["&-trend_strength"].rolling(window).mean()
df["trend_std"] = df["&-trend_strength"].rolling(window).std()
# 使用 z-score 判断趋势是否显著
df["z_score_trend"] = (df["&-trend_strength"] - df["trend_mean"]) / df["trend_std"]
buy_condition &= (df["z_score_trend"] > 1)
# 打印模型统计信息用于调试
if self.freqai and hasattr(self.freqai, "data"):
labels_mean = self.freqai.data.labels_mean
labels_std = self.freqai.data.labels_std
mean_trend = labels_mean.get("&-trend_strength", 0)
std_trend = labels_std.get("&-trend_strength", 1e-6)
logger.info(f"[{metadata['pair']}] Model trend mean: {mean_trend:.6f}, std: {std_trend:.6f}")
# EMA 趋势过滤
df["ema50"] = ta.EMA(df, timeperiod=50)
buy_condition &= (df["close"] > df["ema50"])
# 成交量过滤
df["vol_mean"] = df["volume"].rolling(20).mean()
buy_condition &= (df["volume"] > df["vol_mean"] * 1)
# 市场状态过滤(只排除超买)
if "&-market_condition" in df.columns:
buy_condition &= ~(
(df["&-market_condition"] == "overbought")
)
conditions.append(buy_condition)
else:
logger.warning("⚠️ &-buy_signal 列缺失,跳过该条件")
if len(conditions) == 0:
return df
df.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1
return df
✅ 结论:
你现在的做法非常合理 —— 使用滚动窗口 z-score 来判断趋势是否显著。
而 labels_mean 和 labels_std 更像是“仪表盘上的指示灯”,帮助你了解模型运行状态,而不是直接用于交易信号。
如果你想我帮你加入自动模型漂移检测、动态窗口调整等功能,请告诉我你的目标(比如你想预测涨跌、波动率等),我可以为你定制这部分逻辑。
-
Datetime: 2025-05-29 18:37:43
-
Model: _qwen3/qwen-plus-latest
-
Selected files:
- /home/ubuntu/freqtrade/freqtrade/templates/freqaiprimer.py
当前trailing stop loss的逻辑由哪段代码或配置来决定
在你提供的 FreqaiPrimer 策略中,当前并没有启用 Freqtrade 的 trailing stop loss(追踪止损)功能。
也就是说:
❌ 目前的止损逻辑完全由
custom_stoploss()方法控制,而不是通过 trailing stop loss 配置。
✅ 1. 当前使用的止损机制
📌 custom_stoploss() 方法说明:
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
"""
动态止损回调函数,基于 FreqAI 预测的 &-stoploss_target 值。
"""
# 获取当前币种的 dataframe
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1]
# 获取预测的止损目标
stoploss_target = last_candle.get("&-stoploss_target", -0.01)
# 将 stoploss_target 转换为负数(因为 stoploss 是负值)
dynamic_stoploss = min(-0.005, stoploss_target) # 设置最小止损限制
return dynamic_stoploss
这段代码是你的策略中唯一生效的止损逻辑 —— 它根据 FreqAI 提供的 &-stoploss_target 来动态设置止损点。
✅ 2. Trailing Stop Loss 是什么?
Trailing stop loss 是一种 跟随价格移动的止损机制,例如:
"trailing_stop": true,
"trailing_stop_positive": 0.02,
"trailing_stop_positive_offset": 0.03,
"trailing_only_offset_is_reached": true
它的行为是:
- 开仓后,止损点随着价格上涨“向上移动”
- 可以锁定利润,防止回吐过多盈利
✅ 3. 如何判断是否启用了 trailing stop loss?
在你的策略类中没有看到如下配置或方法:
trailing_stop = True
也没有使用以下任何与 trailing stop 相关的参数。
因此可以确认:
🔍 当前使用的是
custom_stoploss()的动态止损,而不是 trailing stop loss。
✅ 4. 如果你想启用 trailing stop loss,应该怎么做?
你可以选择以下两种方式之一:
✅ 方式一:使用全局配置文件(如 config.json)
{
"max_open_trades": 3,
"stake_currency": "USDT",
"stake_amount": 100,
"tradable_balance_ratio": 0.99,
"minimal_roi": {
"60": 0.01,
"30": 0.02,
"0": 0.03
},
"stoploss": -0.10,
"trailing_stop": true,
"trailing_stop_positive": 0.02,
"trailing_stop_positive_offset": 0.03,
"trailing_only_offset_is_reached": true
}
这样会全局启用 trailing stop loss。
✅ 方式二:在策略中定义(覆盖配置)
# 在策略类中添加如下属性
trailing_stop = True
trailing_stop_positive = 0.02
trailing_stop_positive_offset = 0.03
trailing_only_offset_is_reached = True
这将覆盖配置文件中的设置。
✅ 5. custom_stoploss() vs trailing_stop
| 特性 | custom_stoploss() |
trailing_stop |
|---|---|---|
| 是否可动态计算 | ✅ 是 | ❌ 否(固定百分比) |
| 是否跟随价格移动 | ❌ 否 | ✅ 是 |
| 是否支持 FreqAI 输出 | ✅ 是 | ❌ 否 |
| 是否需要额外配置 | ❌ 否 | ✅ 是 |
✅ 总结
| 问题 | 回答 |
|---|---|
| 当前是否启用了 trailing stop loss? | ❌ 否 |
| 止损逻辑由哪段代码决定? | ✅ custom_stoploss() 方法 |
| 如何启用 trailing stop loss? | ✅ 在配置文件或策略中添加相关参数 |
| 哪个更适合结合 FreqAI 使用? | ✅ custom_stoploss() 更灵活,推荐继续使用它 |
如果你想我帮你实现一个 结合 FreqAI 和 trailing stop 的混合止损系统,请告诉我你的目标(比如你想预测涨跌、波动率等),我可以为你定制这部分逻辑。