freqai 尝试+5
This commit is contained in:
parent
30b2e3f46f
commit
42ec194bbd
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user