直接从 self.freqai 获取 JSON 文件中的值

This commit is contained in:
zhangkun9038@dingtalk.com 2025-06-01 01:06:49 +00:00
parent 8c5d239f85
commit 2b8ee60ee5

View File

@ -10,30 +10,19 @@ from freqtrade.strategy import IStrategy
logger = logging.getLogger(__name__)
class FreqaiPrimer(IStrategy):
"""
策略说明
- 只使用回归模型预测价值背离&-price_value_divergence
- 动态计算 labels_mean labels_std基于当前数据
- 在日志中打印所有币对的 labels_mean labels_std 汇总
- 优化出场逻辑避免过早卖出
- 使用RSI和布林带作为辅助过滤条件
"""
# 策略参数
class ValueDivergencePrimerRegression(IStrategy):
minimal_roi = {
"0": 0.02, # 固定止盈2%
"0": 0.02,
"30": 0.01,
"60": 0
}
stoploss = -0.015 # 固定止损-1.5%
stoploss = -0.015
timeframe = "3m" # 3分钟K线
timeframe = "3m"
use_custom_stoploss = False # 不使用动态止损,改为固定止损
use_custom_stoploss = False
# 绘图配置
plot_config = {
"main_plot": {
"ema200": {"color": "blue"},
@ -58,7 +47,6 @@ class FreqaiPrimer(IStrategy):
}
}
# FreqAI 配置:回归模型(预测价值背离)
freqai_info = {
"identifier": "divergence_model",
"model": "LightGBMRegressor",
@ -84,18 +72,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.trailing_stop_start = 0.03
self.trailing_stop_distance = 0.01
self.pair_stats = {}
self.stats_logged = False # 标记是否已打印汇总日志
self.stats_logged = False
def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame:
"""
特征工程计算技术指标作为FreqAI的输入特征
"""
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period)
dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)
@ -126,9 +109,6 @@ class FreqaiPrimer(IStrategy):
return dataframe
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
"""
设置FreqAI的训练目标只预测价值背离
"""
if len(dataframe) < 200:
logger.warning(f"[{metadata['pair']}] 数据量不足({len(dataframe)}根K线需要至少200根K线进行训练")
return dataframe
@ -147,9 +127,6 @@ class FreqaiPrimer(IStrategy):
return dataframe
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
计算所有指标包括FreqAI预测结果并动态计算 labels_mean labels_std
"""
logger.info(f"[{metadata['pair']}] 当前可用列调用FreqAI前{list(dataframe.columns)}")
# 计算200周期EMA和历史价值背离
@ -181,71 +158,78 @@ 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
# 直接从 FreqAI 获取 labels_mean 和 labels_std
pair = metadata["pair"]
if "&-price_value_divergence" in dataframe.columns:
recent_data = dataframe["&-price_value_divergence"].tail(1000)
if hasattr(self, 'freqai') and self.freqai is not None and pair in self.freqai.data:
# 从 self.freqai.data 中获取 labels_mean 和 labels_std
if "labels_mean" in self.freqai.data and "&-price_value_divergence" in self.freqai.data["labels_mean"]:
labels_mean = self.freqai.data["labels_mean"]["&-price_value_divergence"]
labels_std = self.freqai.data["labels_std"]["&-price_value_divergence"]
else:
logger.warning(f"[{pair}] 无法从 FreqAI 获取 labels_mean 和 labels_std重新计算")
# 回退:重新计算真实目标值
dataframe["&-price_value_divergence_actual"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"]
dataframe["&-price_value_divergence_actual"] = dataframe["&-price_value_divergence_actual"].replace([np.inf, -np.inf], 0).ffill().fillna(0)
recent_data = dataframe["&-price_value_divergence_actual"].tail(self.freqai_info["fit_live_predictions_candles"])
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")
else:
logger.warning(f"[{pair}] FreqAI 未初始化或数据不可用,重新计算")
# 回退:重新计算真实目标值
dataframe["&-price_value_divergence_actual"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"]
dataframe["&-price_value_divergence_actual"] = dataframe["&-price_value_divergence_actual"].replace([np.inf, -np.inf], 0).ffill().fillna(0)
recent_data = dataframe["&-price_value_divergence_actual"].tail(self.freqai_info["fit_live_predictions_candles"])
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 # 标记已打印,避免重复
# 存储统计信息
self.pair_stats[pair] = {"labels_mean": labels_mean, "labels_std": labels_std}
# 计算动态阈值
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:
self.buy_threshold = -0.015
self.sell_threshold = 0.015
logger.warning(f"[{pair}] 无法计算 labels_mean 和 labels_std使用默认阈值 ±0.015")
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}")
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
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:
@ -264,9 +248,6 @@ class FreqaiPrimer(IStrategy):
return df
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
"""
出场逻辑优化以避免过早卖出
"""
conditions = []
if "&-price_value_divergence" in df.columns:
@ -285,9 +266,6 @@ class FreqaiPrimer(IStrategy):
def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float,
min_roi: Dict[float, float], max_profit: float):
"""
动态调整仓位添加最小持仓时间和 Trailing Stop
"""
hold_time = (current_time - trade.open_date_utc).total_seconds() / 60
if hold_time < 15:
@ -315,9 +293,6 @@ class FreqaiPrimer(IStrategy):
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, **kwargs) -> bool:
"""
交易确认确保没有快速重复交易
"""
recent_trades = Trade.get_trades(
pair=pair,
is_open=False,
@ -332,8 +307,5 @@ class FreqaiPrimer(IStrategy):
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
rate: float, time_in_force: str, exit_reason: str,
current_time: datetime, **kwargs) -> bool:
"""
交易退出确认记录退出原因
"""
logger.info(f"[{pair}] 退出交易,原因:{exit_reason}, 利润:{trade.calc_profit_ratio(rate):.2%}")
return True