动态labels_std和labels_mean
This commit is contained in:
parent
e9e23994f1
commit
c77a1925d8
@ -10,12 +10,13 @@ from freqtrade.strategy import IStrategy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class FreqaiPrimer(IStrategy):
|
||||
class ValueDivergencePrimerRegression(IStrategy):
|
||||
"""
|
||||
策略说明:
|
||||
- 只使用回归模型预测价值背离(&-price_value_divergence)
|
||||
- 动态调整阈值,基于 price_value_divergence 的历史数据
|
||||
- 放宽过滤条件,增加信号触发频率
|
||||
- 动态计算 labels_mean 和 labels_std,基于当前数据
|
||||
- 在日志中打印所有币对的 labels_mean 和 labels_std 汇总
|
||||
- 优化出场逻辑,避免过早卖出
|
||||
- 使用RSI和布林带作为辅助过滤条件
|
||||
"""
|
||||
|
||||
@ -83,6 +84,13 @@ class FreqaiPrimer(IStrategy):
|
||||
super().__init__(config, *args, **kwargs)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.debug("✅ 策略已初始化,日志级别设置为 DEBUG")
|
||||
# 动态止盈参数
|
||||
self.trailing_stop_enabled = False
|
||||
self.trailing_stop_start = 0.03 # 价格上涨 3% 后启动 Trailing Stop
|
||||
self.trailing_stop_distance = 0.01 # 允许价格回落 1% 时卖出
|
||||
# 存储每个币对的 labels_mean 和 labels_std
|
||||
self.pair_stats = {}
|
||||
self.stats_logged = False # 标记是否已打印汇总日志
|
||||
|
||||
def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame:
|
||||
"""
|
||||
@ -103,7 +111,6 @@ class FreqaiPrimer(IStrategy):
|
||||
dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period)
|
||||
dataframe["%-relative_volume-period"] = dataframe["volume"] / dataframe["volume"].rolling(period).mean()
|
||||
|
||||
# 计算价值背离作为特征
|
||||
dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)
|
||||
dataframe["%-price_value_divergence"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"]
|
||||
|
||||
@ -126,17 +133,13 @@ class FreqaiPrimer(IStrategy):
|
||||
logger.warning(f"[{metadata['pair']}] 数据量不足({len(dataframe)}根K线),需要至少200根K线进行训练")
|
||||
return dataframe
|
||||
|
||||
# 计算200周期EMA作为公平价值
|
||||
dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)
|
||||
# 计算价值背离(回归目标)
|
||||
dataframe["&-price_value_divergence"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"]
|
||||
|
||||
# 成交量Z分数
|
||||
dataframe["volume_mean_20"] = dataframe["volume"].rolling(20).mean()
|
||||
dataframe["volume_std_20"] = dataframe["volume"].rolling(20).std()
|
||||
dataframe["volume_z_score"] = (dataframe["volume"] - dataframe["volume_mean_20"]) / dataframe["volume_std_20"]
|
||||
|
||||
# 数据清理
|
||||
dataframe["&-price_value_divergence"] = dataframe["&-price_value_divergence"].replace([np.inf, -np.inf], 0).ffill().fillna(0)
|
||||
dataframe["volume_z_score"] = dataframe["volume_z_score"].replace([np.inf, -np.inf], 0).ffill().fillna(0)
|
||||
|
||||
@ -145,7 +148,7 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
计算所有指标,包括FreqAI预测结果,并基于历史数据动态调整阈值
|
||||
计算所有指标,包括FreqAI预测结果,并动态计算 labels_mean 和 labels_std
|
||||
"""
|
||||
logger.info(f"[{metadata['pair']}] 当前可用列(调用FreqAI前):{list(dataframe.columns)}")
|
||||
|
||||
@ -153,34 +156,13 @@ class FreqaiPrimer(IStrategy):
|
||||
dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)
|
||||
dataframe["price_value_divergence"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"]
|
||||
|
||||
# 动态计算阈值:基于 price_value_divergence 的历史均值和标准差
|
||||
if "price_value_divergence" in dataframe.columns:
|
||||
divergence_mean = dataframe["price_value_divergence"].mean()
|
||||
divergence_std = dataframe["price_value_divergence"].std()
|
||||
k = 1.4 # 标准差倍数,可以调整, 改成1.4,降低入场门槛
|
||||
self.buy_threshold = max(divergence_mean - k * divergence_std, -0.05) # 设置下限,避免过低
|
||||
self.sell_threshold = min(divergence_mean + k * divergence_std, 0.05) # 设置上限,避免过高
|
||||
# 确保阈值至少有一定宽度
|
||||
self.buy_threshold = min(self.buy_threshold, -0.005)
|
||||
self.sell_threshold = max(self.sell_threshold, 0.005)
|
||||
logger.info(f"[{metadata['pair']}] price_value_divergence 历史均值:{divergence_mean:.4f}")
|
||||
logger.info(f"[{metadata['pair']}] price_value_divergence 历史标准差:{divergence_std:.4f}")
|
||||
logger.info(f"[{metadata['pair']}] 动态买入阈值:{self.buy_threshold:.4f}")
|
||||
logger.info(f"[{metadata['pair']}] 动态卖出阈值:{self.sell_threshold:.4f}")
|
||||
else:
|
||||
self.buy_threshold = -0.015 # 回退阈值
|
||||
self.sell_threshold = 0.015
|
||||
logger.warning(f"[{metadata['pair']}] 无法计算动态阈值,使用默认阈值 ±0.015")
|
||||
|
||||
# 调用FreqAI预测价值背离
|
||||
if not hasattr(self, 'freqai') or self.freqai is None:
|
||||
logger.error(f"[{metadata['pair']}] FreqAI 未初始化,请确保回测命令中启用了 --freqai")
|
||||
# 回退到规则计算
|
||||
dataframe["&-price_value_divergence"] = dataframe["price_value_divergence"]
|
||||
else:
|
||||
dataframe = self.freqai.start(dataframe, metadata, self)
|
||||
|
||||
# 检查预测结果
|
||||
if "&-price_value_divergence" not in dataframe.columns:
|
||||
logger.warning(f"[{metadata['pair']}] 回归模型未生成 &-price_value_divergence,回退到规则计算")
|
||||
dataframe["&-price_value_divergence"] = dataframe["price_value_divergence"]
|
||||
@ -199,22 +181,77 @@ class FreqaiPrimer(IStrategy):
|
||||
for col in ["ema200", "bb_upperband", "bb_middleband", "bb_lowerband", "rsi", "volume_z_score", "&-price_value_divergence", "price_value_divergence"]:
|
||||
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], 0).ffill().fillna(0)
|
||||
|
||||
# 动态计算 labels_mean 和 labels_std
|
||||
pair = metadata["pair"]
|
||||
if "&-price_value_divergence" in dataframe.columns:
|
||||
recent_data = dataframe["&-price_value_divergence"].tail(1000)
|
||||
labels_mean = recent_data.mean()
|
||||
labels_std = recent_data.std()
|
||||
|
||||
if np.isnan(labels_std) or labels_std == 0:
|
||||
labels_std = 0.01
|
||||
logger.warning(f"[{pair}] labels_std 计算异常,使用默认值 0.01")
|
||||
|
||||
# 存储到 pair_stats
|
||||
self.pair_stats[pair] = {"labels_mean": labels_mean, "labels_std": labels_std}
|
||||
|
||||
# 根据 labels_std 调整 k_buy 和 k_sell
|
||||
if labels_std > 0.015:
|
||||
k_buy = 1.2
|
||||
k_sell = 1.5
|
||||
elif labels_std < 0.010:
|
||||
k_buy = 0.8
|
||||
k_sell = 1.0
|
||||
else:
|
||||
k_buy = 1.0
|
||||
k_sell = 1.2
|
||||
|
||||
if labels_mean > 0.015:
|
||||
k_sell += 0.5
|
||||
logger.info(f"[{pair}] labels_mean 较高({labels_mean:.4f}),增加 k_sell 到 {k_sell:.2f}")
|
||||
|
||||
# 计算阈值
|
||||
self.buy_threshold = labels_mean - k_buy * labels_std
|
||||
self.sell_threshold = labels_mean + k_sell * labels_std
|
||||
|
||||
# 边界保护
|
||||
self.buy_threshold = max(self.buy_threshold, -0.05)
|
||||
self.buy_threshold = min(self.buy_threshold, -0.005)
|
||||
self.sell_threshold = min(self.sell_threshold, 0.05)
|
||||
self.sell_threshold = max(self.sell_threshold, 0.005)
|
||||
|
||||
# 打印当前币对的详细日志
|
||||
logger.info(f"[{pair}] labels_mean:{labels_mean:.4f}, labels_std:{labels_std:.4f}")
|
||||
logger.info(f"[{pair}] k_buy:{k_buy:.2f}, k_sell:{k_sell:.2f}")
|
||||
logger.info(f"[{pair}] 动态买入阈值:{self.buy_threshold:.4f}")
|
||||
logger.info(f"[{pair}] 动态卖出阈值:{self.sell_threshold:.4f}")
|
||||
|
||||
# 打印所有币对的 labels_mean 和 labels_std 汇总(仅第一次)
|
||||
if not self.stats_logged:
|
||||
logger.info("===== 所有币对的 labels_mean 和 labels_std 汇总 =====")
|
||||
for p, stats in self.pair_stats.items():
|
||||
logger.info(f"[{p}] labels_mean:{stats['labels_mean']:.4f}, labels_std:{stats['labels_std']:.4f}")
|
||||
logger.info("==============================================")
|
||||
self.stats_logged = True # 标记已打印,避免重复
|
||||
|
||||
else:
|
||||
self.buy_threshold = -0.015
|
||||
self.sell_threshold = 0.015
|
||||
logger.warning(f"[{pair}] 无法计算 labels_mean 和 labels_std,使用默认阈值 ±0.015")
|
||||
|
||||
logger.info(f"[{metadata['pair']}] 指标计算完成,列:{list(dataframe.columns)}")
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
入场逻辑:基于动态阈值和放宽的过滤条件
|
||||
入场逻辑:保持不变,基于动态阈值
|
||||
"""
|
||||
conditions = []
|
||||
|
||||
if "&-price_value_divergence" in df.columns:
|
||||
# 买入条件:价格低估且放量
|
||||
buy_condition = (df["&-price_value_divergence"] < self.buy_threshold)
|
||||
buy_condition &= (df["volume_z_score"] > 1.5) # 降低成交量要求
|
||||
# RSI超卖过滤(放宽条件)
|
||||
buy_condition &= (df["rsi"] < 42) #40改成42,降低入场门槛
|
||||
# 价格触及布林带下轨
|
||||
buy_condition &= (df["volume_z_score"] > 1.5)
|
||||
buy_condition &= (df["rsi"] < 40)
|
||||
buy_condition &= (df["close"] <= df["bb_lowerband"])
|
||||
conditions.append(buy_condition)
|
||||
else:
|
||||
@ -228,16 +265,13 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
出场逻辑:基于动态阈值和放宽的过滤条件
|
||||
出场逻辑:优化以避免过早卖出
|
||||
"""
|
||||
conditions = []
|
||||
|
||||
if "&-price_value_divergence" in df.columns:
|
||||
# 卖出条件:价格高估
|
||||
sell_condition = (df["&-price_value_divergence"] > self.sell_threshold)
|
||||
sell_condition &= (df["volume_z_score"] > 1.5) # 降低成交量要求
|
||||
# RSI超买过滤(放宽条件)
|
||||
sell_condition |= (df["rsi"] > 60)
|
||||
sell_condition |= (df["rsi"] > 75)
|
||||
conditions.append(sell_condition)
|
||||
else:
|
||||
logger.warning("⚠️ &-price_value_divergence 列缺失,跳过该条件")
|
||||
@ -252,9 +286,28 @@ class FreqaiPrimer(IStrategy):
|
||||
current_rate: float, current_profit: float,
|
||||
min_roi: Dict[float, float], max_profit: float):
|
||||
"""
|
||||
动态调整仓位:持仓时间超过30分钟后强制平仓
|
||||
动态调整仓位:添加最小持仓时间和 Trailing Stop
|
||||
"""
|
||||
hold_time = (current_time - trade.open_date_utc).total_seconds() / 60
|
||||
|
||||
if hold_time < 15:
|
||||
logger.info(f"[{trade.pair}] 持仓时间 {hold_time:.1f} 分钟,未达到最小持仓时间 15 分钟,暂不退出")
|
||||
return None
|
||||
|
||||
profit_ratio = (current_rate - trade.open_rate) / trade.open_rate
|
||||
if profit_ratio >= self.trailing_stop_start and not self.trailing_stop_enabled:
|
||||
self.trailing_stop_enabled = True
|
||||
trade.adjust_max_rate(current_rate)
|
||||
logger.info(f"[{trade.pair}] 价格上涨超过 {self.trailing_stop_start*100:.1f}%,启动 Trailing Stop")
|
||||
|
||||
if self.trailing_stop_enabled:
|
||||
max_rate = trade.max_rate
|
||||
trailing_stop_price = max_rate * (1 - self.trailing_stop_distance)
|
||||
if current_rate < trailing_stop_price:
|
||||
logger.info(f"[{trade.pair}] 价格回落至 Trailing Stop 点 {trailing_stop_price:.6f},触发卖出")
|
||||
return -1
|
||||
trade.adjust_max_rate(current_rate)
|
||||
|
||||
if hold_time > 30:
|
||||
logger.info(f"[{trade.pair}] 持仓时间超过30分钟,强制平仓")
|
||||
return -1
|
||||
@ -273,6 +326,7 @@ class FreqaiPrimer(IStrategy):
|
||||
if len(recent_trades) > 0:
|
||||
logger.info(f"[{pair}] 5分钟内有近期交易,跳过本次入场")
|
||||
return False
|
||||
self.trailing_stop_enabled = False
|
||||
return True
|
||||
|
||||
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user