freqai 尝试+5

This commit is contained in:
zhangkun9038@dingtalk.com 2025-11-24 14:32:51 +08:00
parent 30b2e3f46f
commit 42ec194bbd
2 changed files with 102 additions and 66 deletions

View File

@ -67,47 +67,24 @@
],
"freqai": {
"enabled": true,
"identifier": "freqai_dual_model",
"model": "LightGBMClassifier",
"identifier": "freqai_primer",
"feature_parameters": {
"include_timeframes": ["3m", "15m", "1h"],
"include_corr_pairlist": ["BTC/USDT", "SOL/USDT"],
"include_shifted_candles": 3,
"label_period_candles": 6,
"indicator_periods_candles": [10, 20, 50],
"use_strategy_to_train": true
"label_period_candles": 6
},
"data_split_parameters": {
"test_size": 0.2,
"shuffle": false
},
"model_training_parameters": {
"entry_signal": {
"model": "LightGBMClassifier",
"model_params": {
"n_estimators": 300,
"learning_rate": 0.08,
"num_leaves": 31,
"max_depth": 10,
"min_child_samples": 5,
"class_weight": "balanced",
"verbose": -1
}
},
"exit_signal": {
"model": "LightGBMClassifier",
"model_params": {
"n_estimators": 300,
"learning_rate": 0.08,
"num_leaves": 31,
"max_depth": 10,
"min_child_samples": 5,
"verbose": -1
}
}
"n_estimators": 200,
"learning_rate": 0.05,
"num_leaves": 31,
"verbose": -1
},
"fit_live_predictions_candles": 200,
"live_retrain_candles": 200
"fit_live_predictions_candles": 100,
"live_retrain_candles": 100
},
"api_server": {
"enabled": true,

View File

@ -14,6 +14,29 @@ import math
logger = logging.getLogger(__name__)
class FreqaiPrimer(IStrategy):
# FreqAI 配置 - 直接在类中定义
freqai_info = {
"identifier": "freqai_primer",
"model": "LightGBMRegressor",
"feature_parameters": {
"include_timeframes": ["3m", "15m", "1h"],
"include_shifted_candles": 3,
"label_period_candles": 6,
},
"data_split_parameters": {
"test_size": 0.2,
"shuffle": False,
},
"model_training_parameters": {
"n_estimators": 200,
"learning_rate": 0.05,
"num_leaves": 31,
"verbose": -1,
},
"fit_live_predictions_candles": 100,
"live_retrain_candles": 100,
}
# 策略参数 - 使用custom_roi替代minimal_roi字典
loglevel = "warning"
@ -171,7 +194,53 @@ class FreqaiPrimer(IStrategy):
desired_stake = math.floor(desired_stake) # 取整,去掉小数点后的数字
return max(min(desired_stake, max_stake), min_stake)
def feature_engineering_expand_all(self, dataframe: DataFrame, period, metadata, **kwargs) -> DataFrame:
"""
FreqAI 生成扩展特征
"""
# RSI 特征
dataframe[f"%-rsi-{period}"] = ta.rsi(dataframe["close"], length=period)
# 成交量特征
dataframe[f"%-volume_ratio-{period}"] = dataframe["volume"] / dataframe["volume"].rolling(period).mean()
# ADX 趋势强度
adx_result = ta.adx(dataframe["high"], dataframe["low"], dataframe["close"], length=period)
dataframe[f"%-adx-{period}"] = adx_result[f"ADX_{period}"]
# 价格位置特征
low = dataframe["low"].rolling(period).min()
high = dataframe["high"].rolling(period).max()
dataframe[f"%-price_position-{period}"] = (dataframe["close"] - low) / (high - low + 1e-8)
return dataframe
def feature_engineering_standard(self, dataframe: DataFrame, metadata, **kwargs) -> DataFrame:
"""
标准特征不需要自动扩展
"""
# 小时时间特征
dataframe["%-hour_of_day"] = (dataframe["date"].dt.hour + 1) / 25
# ATR 波动性
atr_14 = ta.atr(dataframe["high"], dataframe["low"], dataframe["close"], length=14)
dataframe["%-atr_14"] = atr_14 / dataframe["close"]
# EMA 比率
ema_5 = ta.ema(dataframe["close"], length=5)
ema_10 = ta.ema(dataframe["close"], length=10)
dataframe["%-ema_ratio"] = ema_5 / (ema_10 + 1e-8)
return dataframe
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 🔧 第一步: 调用 FreqAI 进行预测和训练
if hasattr(self, 'freqai') and self.freqai is not None:
dataframe = self.freqai.start(dataframe, metadata, self)
logger.debug(f"[{metadata['pair']}] FreqAI 已调用")
else:
logger.warning(f"[{metadata['pair']}] FreqAI 未初始化")
# 计算 3m 周期的指标
bb_length_value = self.bb_length.value
bb_std_value = self.bb_std.value
@ -406,46 +475,36 @@ class FreqaiPrimer(IStrategy):
return dataframe
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
def set_freqai_targets(self, dataframe: DataFrame, metadata, **kwargs) -> DataFrame:
"""
FreqAI 模型生成训练标签
entry_signal: 1 = 良好的入场机会0 = 非入场点
exit_signal: 1 = 需要出场0 = 继续持有
FreqAI 生成回归目标: 预测未来价格上升幅度
&-price_rise: 未来 label_period K 线内的最大上升幅度的预测
"""
# ======================== 入场标签生成 ========================
# 定义良好的入场条件(参考 populate_entry_trend 中的逻辑)
close_to_bb_lower = (dataframe['close'] <= dataframe['bb_lower_1h'] * self.bb_lower_deviation.value)
rsi_oversold = dataframe['rsi_1h'] < self.rsi_oversold.value
stochrsi_oversold = (dataframe['stochrsi_k_1h'] < self.stochrsi_neutral_threshold.value) & \
(dataframe['stochrsi_d_1h'] < self.stochrsi_neutral_threshold.value)
macd_uptrend = dataframe['macd_1h'] > dataframe['macd_signal_1h']
volume_spike = dataframe['volume'] > dataframe['volume_ma'] * self.volume_multiplier.value
pair = metadata.get('pair', 'Unknown')
# 综合条件:至少满足 4 个条件即为良好入场点
entry_score = (
close_to_bb_lower.astype(int) +
rsi_oversold.astype(int) +
stochrsi_oversold.astype(int) +
macd_uptrend.astype(int) +
volume_spike.astype(int)
)
dataframe['&-entry_signal'] = (entry_score >= 4).astype(int)
# 取得 label_period
try:
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
except:
label_period = 6 # 默认值
# ======================== 出场标签生成 ========================
# 定义需要出场的条件(参考 populate_exit_trend 中的逻辑)
breakout_upper = dataframe['close'] >= dataframe['bb_upper_1h'] * self.exit_bb_upper_deviation.value
volume_expansion = dataframe['volume'] > dataframe['volume_ma'] * self.exit_volume_multiplier.value
macd_downtrend = dataframe['macd_1h'] < dataframe['macd_signal_1h']
rsi_overbought = dataframe['rsi_1h'] > self.exit_rsi_threshold.value
# 计算未来 label_period 根 K 线内的最大上升幅度
dataframe["&-price_rise"] = 0.0
# 综合条件:满足 2 个或以上条件即需要出场
exit_score = (
breakout_upper.astype(int) +
volume_expansion.astype(int) +
macd_downtrend.astype(int) +
rsi_overbought.astype(int)
)
dataframe['&-exit_signal'] = (exit_score >= 2).astype(int)
for i in range(len(dataframe) - label_period):
future_high = dataframe["high"].iloc[i:i+label_period].max()
current_price = dataframe["close"].iloc[i]
# 计算上升幅度(百分比)
max_rise_pct = (future_high - current_price) / current_price if current_price != 0 else 0
dataframe.loc[i, "&-price_rise"] = max(0, max_rise_pct) # 只记录上升下跌记为0
# 平滑处理
dataframe["&-price_rise"] = dataframe["&-price_rise"].rolling(3).mean().fillna(0)
logger.debug(f"[{pair}] 目标标签统计: 平均={dataframe['&-price_rise'].mean():.6f}, "
f"最小={dataframe['&-price_rise'].min():.6f}, "
f"最大={dataframe['&-price_rise'].max():.6f}")
return dataframe