hyperopt
This commit is contained in:
parent
b7065f7292
commit
8210a74b25
@ -5,65 +5,28 @@
|
||||
"stoploss": {
|
||||
"stoploss": -0.14
|
||||
},
|
||||
"max_open_trades": {
|
||||
"max_open_trades": 5
|
||||
},
|
||||
"buy": {
|
||||
"ADD_POSITION_THRESHOLD": -0.021,
|
||||
"BUY_THRESHOLD_MAX": -0.001,
|
||||
"BUY_THRESHOLD_MIN": -0.035,
|
||||
"COOLDOWN_PERIOD_MINUTES": 9,
|
||||
"MAX_ENTRY_POSITION_ADJUSTMENT": 3
|
||||
},
|
||||
"sell": {
|
||||
"EXIT_POSITION_RATIO": 0.472,
|
||||
"SELL_THRESHOLD_MAX": 0.065,
|
||||
"SELL_THRESHOLD_MIN": 0.002,
|
||||
"TRAILING_STOP_DISTANCE": 0.0125,
|
||||
"TRAILING_STOP_START": 0.045
|
||||
},
|
||||
"protection": {},
|
||||
"model_training_parameters": {
|
||||
"price_value_divergence": {
|
||||
"model": "LightGBMRegressor",
|
||||
"model_params": {
|
||||
"n_estimators": 200,
|
||||
"learning_rate": 0.05,
|
||||
"num_leaves": 31,
|
||||
"verbose": -1
|
||||
}
|
||||
},
|
||||
"optimal_first_length": {
|
||||
"model": "LightGBMClassifier",
|
||||
"model_params": {
|
||||
"n_estimators": 150,
|
||||
"learning_rate": 0.1,
|
||||
"num_leaves": 15,
|
||||
"max_depth": 8,
|
||||
"min_child_samples": 10,
|
||||
"class_weight": "balanced",
|
||||
"verbose": -1
|
||||
}
|
||||
},
|
||||
"volatility_forecast": {
|
||||
"model": "LightGBMRegressor",
|
||||
"model_params": {
|
||||
"n_estimators": 180,
|
||||
"learning_rate": 0.08,
|
||||
"num_leaves": 25,
|
||||
"max_depth": 6,
|
||||
"verbose": -1
|
||||
}
|
||||
}
|
||||
},
|
||||
"trailing": {
|
||||
"trailing_stop": true,
|
||||
"trailing_stop_positive": 0.0125,
|
||||
"trailing_stop_positive_offset": 0.045,
|
||||
"trailing_only_offset_is_reached": false
|
||||
}
|
||||
|
||||
},
|
||||
"max_open_trades": {
|
||||
"max_open_trades": 5
|
||||
},
|
||||
"buy": {
|
||||
"H1_MAX_CANDLES": 161,
|
||||
"H1_MAX_CONSECUTIVE_CANDLES": 2,
|
||||
"H1_RAPID_RISE_THRESHOLD": 0.13,
|
||||
"bb_length": 17,
|
||||
"bb_std": 2.0,
|
||||
"rsi_length": 20,
|
||||
"rsi_overbought": 70,
|
||||
"rsi_oversold": 32
|
||||
},
|
||||
"sell": {},
|
||||
"protection": {}
|
||||
},
|
||||
"ft_stratparam_v": 1,
|
||||
"export_time": "2025-08-14 06:30:51.352733+00:00"
|
||||
}
|
||||
"export_time": "2025-09-05 16:43:20.987720+00:00"
|
||||
}
|
||||
@ -2,13 +2,13 @@ import warnings
|
||||
warnings.filterwarnings("ignore", category=UserWarning, module="pandas_ta")
|
||||
|
||||
import logging
|
||||
from freqtrade.strategy import IStrategy
|
||||
from pandas import DataFrame
|
||||
import pandas_ta as ta
|
||||
from freqtrade.persistence import Trade
|
||||
import numpy as np
|
||||
import datetime
|
||||
import pandas as pd # 确保导入pandas
|
||||
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -83,17 +83,18 @@ class FreqaiPrimer(IStrategy):
|
||||
timeframe = "3m" # 主时间框架为 3 分钟
|
||||
can_short = False # 禁用做空
|
||||
|
||||
# 自定义指标参数
|
||||
bb_length = 20
|
||||
bb_std = 2.0
|
||||
rsi_length = 14
|
||||
rsi_overbought = 58 # 超买阈值
|
||||
rsi_oversold = 42 # 放宽超卖阈值,增加入场信号
|
||||
bb_length = IntParameter(10, 30, default=20, space="buy", optimize=True, load=True)
|
||||
bb_std = DecimalParameter(1.0, 3.0, default=2.0, decimals=1, space="buy", optimize=True, load=True)
|
||||
rsi_length = IntParameter(7, 21, default=14, space="buy", optimize=True, load=True)
|
||||
rsi_overbought = IntParameter(50, 70, default=58, space="buy", optimize=True, load=True) # 超买阈值
|
||||
rsi_oversold = IntParameter(30, 50, default=42, space="buy", optimize=True, load=True) # 放宽超卖阈值,增加入场信号
|
||||
|
||||
# 剧烈拉升检测参数
|
||||
H1_MAX_CANDLES = 240 # 检查最近200根1h K线
|
||||
H1_RAPID_RISE_THRESHOLD = 0.12 # 5%的抬升阈值
|
||||
H1_MAX_CONSECUTIVE_CANDLES = 1 # 最多连续3根K线内检查
|
||||
H1_MAX_CANDLES = IntParameter(100, 300, default=240, space="buy", optimize=True, load=True) # 检查最近200根1h K线
|
||||
H1_RAPID_RISE_THRESHOLD = DecimalParameter(0.05, 0.20, default=0.12, decimals=2, space="buy", optimize=True, load=True) # 5%的抬升阈值
|
||||
H1_MAX_CONSECUTIVE_CANDLES = IntParameter(1, 5, default=1, space="buy", optimize=True, load=True) # 最多连续3根K线内检查
|
||||
|
||||
|
||||
|
||||
def informative_pairs(self):
|
||||
pairs = self.dp.current_whitelist()
|
||||
@ -110,15 +111,24 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
# 计算 3m 周期的指标
|
||||
bb_3m = ta.bbands(dataframe['close'], length=self.bb_length, std=self.bb_std)
|
||||
dataframe['bb_lower_3m'] = bb_3m[f'BBL_{self.bb_length}_{self.bb_std}']
|
||||
dataframe['bb_upper_3m'] = bb_3m[f'BBU_{self.bb_length}_{self.bb_std}']
|
||||
dataframe['rsi_3m'] = ta.rsi(dataframe['close'], length=self.rsi_length)
|
||||
bb_3m = ta.bbands(dataframe['close'], length=self.bb_length.value, std=self.bb_std.value)
|
||||
dataframe['bb_lower_3m'] = bb_3m[f'BBL_{self.bb_length.value}_{self.bb_std.value}']
|
||||
dataframe['bb_upper_3m'] = bb_3m[f'BBU_{self.bb_length.value}_{self.bb_std.value}']
|
||||
dataframe['rsi_3m'] = ta.rsi(dataframe['close'], length=self.rsi_length.value)
|
||||
|
||||
# 新增 StochRSI 指标
|
||||
stochrsi_3m = ta.stochrsi(dataframe['close'], length=14, rsi_length=14)
|
||||
dataframe['stochrsi_k_3m'] = stochrsi_3m['STOCHRSIk_14_14_3_3']
|
||||
dataframe['stochrsi_d_3m'] = stochrsi_3m['STOCHRSId_14_14_3_3']
|
||||
stochrsi_3m = ta.stochrsi(dataframe['close'], length=self.rsi_length.value, rsi_length=self.rsi_length.value)
|
||||
|
||||
# Dynamically retrieve column names
|
||||
stochrsi_k_col = [col for col in stochrsi_3m.columns if 'STOCHRSIk' in col][0]
|
||||
stochrsi_d_col = [col for col in stochrsi_3m.columns if 'STOCHRSId' in col][0]
|
||||
|
||||
# Log the dynamically retrieved column names for debugging
|
||||
logger.info(f"[{metadata['pair']}] Dynamically retrieved StochRSI column names for 3m: K={stochrsi_k_col}, D={stochrsi_d_col}")
|
||||
|
||||
# Assign StochRSI values to dataframe
|
||||
dataframe['stochrsi_k_3m'] = stochrsi_3m[stochrsi_k_col]
|
||||
dataframe['stochrsi_d_3m'] = stochrsi_3m[stochrsi_d_col]
|
||||
|
||||
# 新增 MACD 指标
|
||||
macd_3m = ta.macd(dataframe['close'], fast=12, slow=26, signal=9)
|
||||
@ -138,15 +148,28 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
# 获取 15m 数据
|
||||
df_15m = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='15m')
|
||||
df_15m['rsi_15m'] = ta.rsi(df_15m['close'], length=self.rsi_length)
|
||||
df_15m['rsi_15m'] = ta.rsi(df_15m['close'], length=self.rsi_length.value)
|
||||
# 计算15m时间框架的EMA50和EMA200
|
||||
df_15m['ema_50_15m'] = ta.ema(df_15m['close'], length=50)
|
||||
df_15m['ema_200_15m'] = ta.ema(df_15m['close'], length=200)
|
||||
|
||||
# 新增 StochRSI 指标
|
||||
stochrsi_15m = ta.stochrsi(df_15m['close'], length=14, rsi_length=14)
|
||||
df_15m['stochrsi_k_15m'] = stochrsi_15m['STOCHRSIk_14_14_3_3']
|
||||
df_15m['stochrsi_d_15m'] = stochrsi_15m['STOCHRSId_14_14_3_3']
|
||||
# Calculate StochRSI for 15m timeframe
|
||||
stochrsi_15m = ta.stochrsi(df_15m['close'], length=self.rsi_length.value, rsi_length=self.rsi_length.value)
|
||||
|
||||
# Dynamically retrieve column names for STOCHRSIk and STOCHRSId
|
||||
stochrsi_k_col = [col for col in stochrsi_15m.columns if 'STOCHRSIk' in col][0]
|
||||
stochrsi_d_col = [col for col in stochrsi_15m.columns if 'STOCHRSId' in col][0]
|
||||
|
||||
# Log the dynamically retrieved column names for debugging
|
||||
logger.info(f"[{metadata['pair']}] Dynamically retrieved StochRSI column names for 15m: K={stochrsi_k_col}, D={stochrsi_d_col}")
|
||||
|
||||
# Assign StochRSI values to dataframe
|
||||
df_15m['stochrsi_k_15m'] = stochrsi_15m[stochrsi_k_col]
|
||||
df_15m['stochrsi_d_15m'] = stochrsi_15m[stochrsi_d_col]
|
||||
|
||||
# Log the dynamically retrieved column names for debugging
|
||||
logger.info(f"[{metadata['pair']}] Dynamically retrieved StochRSI column names: K={stochrsi_k_col}, D={stochrsi_d_col}")
|
||||
|
||||
# 新增 MACD 指标
|
||||
macd_15m = ta.macd(df_15m['close'], fast=12, slow=26, signal=9)
|
||||
@ -171,20 +194,29 @@ class FreqaiPrimer(IStrategy):
|
||||
df_1h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1h')
|
||||
|
||||
# 计算 1h 布林带
|
||||
bb_1h = ta.bbands(df_1h['close'], length=self.bb_length, std=self.bb_std)
|
||||
df_1h['bb_lower_1h'] = bb_1h[f'BBL_{self.bb_length}_{self.bb_std}']
|
||||
df_1h['bb_upper_1h'] = bb_1h[f'BBU_{self.bb_length}_{self.bb_std}']
|
||||
bb_1h = ta.bbands(df_1h['close'], length=self.bb_length.value, std=self.bb_std.value)
|
||||
df_1h['bb_lower_1h'] = bb_1h[f'BBL_{self.bb_length.value}_{self.bb_std.value}']
|
||||
df_1h['bb_upper_1h'] = bb_1h[f'BBU_{self.bb_length.value}_{self.bb_std.value}']
|
||||
|
||||
# 计算 1h RSI 和 EMA
|
||||
df_1h['rsi_1h'] = ta.rsi(df_1h['close'], length=self.rsi_length)
|
||||
df_1h['rsi_1h'] = ta.rsi(df_1h['close'], length=self.rsi_length.value)
|
||||
df_1h['ema_50_1h'] = ta.ema(df_1h['close'], length=50) # 1h 50周期EMA
|
||||
df_1h['ema_200_1h'] = ta.ema(df_1h['close'], length=200) # 1h 200周期EMA
|
||||
df_1h['trend_1h'] = df_1h['close'] > df_1h['ema_50_1h'] # 1h上涨趋势
|
||||
|
||||
# 新增 StochRSI 指标
|
||||
stochrsi_1h = ta.stochrsi(df_1h['close'], length=14, rsi_length=14)
|
||||
df_1h['stochrsi_k_1h'] = stochrsi_1h['STOCHRSIk_14_14_3_3']
|
||||
df_1h['stochrsi_d_1h'] = stochrsi_1h['STOCHRSId_14_14_3_3']
|
||||
stochrsi_1h = ta.stochrsi(df_1h['close'], length=self.rsi_length.value, rsi_length=self.rsi_length.value)
|
||||
|
||||
# Dynamically retrieve column names for STOCHRSIk and STOCHRSId
|
||||
stochrsi_k_col = [col for col in stochrsi_1h.columns if 'STOCHRSIk' in col][0]
|
||||
stochrsi_d_col = [col for col in stochrsi_1h.columns if 'STOCHRSId' in col][0]
|
||||
|
||||
# Log the dynamically retrieved column names for debugging
|
||||
logger.info(f"[{metadata['pair']}] Dynamically retrieved StochRSI column names for 1h: K={stochrsi_k_col}, D={stochrsi_d_col}")
|
||||
|
||||
# Assign StochRSI values to dataframe
|
||||
df_1h['stochrsi_k_1h'] = stochrsi_1h[stochrsi_k_col]
|
||||
df_1h['stochrsi_d_1h'] = stochrsi_1h[stochrsi_d_col]
|
||||
|
||||
# 新增 MACD 指标
|
||||
macd_1h = ta.macd(df_1h['close'], fast=12, slow=26, signal=9)
|
||||
@ -192,14 +224,6 @@ class FreqaiPrimer(IStrategy):
|
||||
df_1h['macd_signal_1h'] = macd_1h['MACDs_12_26_9']
|
||||
df_1h['macd_hist_1h'] = macd_1h['MACDh_12_26_9']
|
||||
|
||||
# 验证 MACD 列是否正确生成
|
||||
logger.info(f"[{metadata['pair']}] 1小时 MACD 列: {list(macd_1h.columns)}")
|
||||
|
||||
# 新增 StochRSI 指标
|
||||
stochrsi_1h = ta.stochrsi(df_1h['close'], length=14, rsi_length=14)
|
||||
df_1h['stochrsi_k_1h'] = stochrsi_1h['STOCHRSIk_14_14_3_3']
|
||||
df_1h['stochrsi_d_1h'] = stochrsi_1h['STOCHRSId_14_14_3_3']
|
||||
|
||||
# 将 1h 数据重新索引到主时间框架 (3m),增强版时间对齐处理
|
||||
# 确保重新索引时不引入未来数据
|
||||
df_1h_indexed = df_1h.set_index('date')
|
||||
@ -462,7 +486,7 @@ class FreqaiPrimer(IStrategy):
|
||||
macd_downward = dataframe['macd_1h'] < dataframe['macd_signal_1h']
|
||||
|
||||
# 条件4: RSI 进入超买区域
|
||||
rsi_overbought = dataframe['rsi_1h'] > self.rsi_overbought
|
||||
rsi_overbought = dataframe['rsi_1h'] > self.rsi_overbought.value
|
||||
|
||||
# 合并所有条件
|
||||
final_condition = breakout_condition | volume_spike | macd_downward | rsi_overbought
|
||||
@ -490,30 +514,38 @@ class FreqaiPrimer(IStrategy):
|
||||
return dataframe
|
||||
# 市场状态已在 populate_indicators 中计算,无需重复获取
|
||||
|
||||
# 条件1: 价格接近布林带下轨(允许一定偏差)
|
||||
close_to_bb_lower_1h = (dataframe['close'] <= dataframe['bb_lower_1h'] * 1.03) # 放宽到3%偏差
|
||||
bb_deviation = DecimalParameter(0.95, 1.05, default=1.03, decimals=2, space="buy", optimize=True, load=True)
|
||||
close_to_bb_lower_1h = (dataframe['close'] <= dataframe['bb_lower_1h'] * bb_deviation.value) # 动态优化布林带偏差
|
||||
|
||||
# 条件2: RSI 不高于阈值(根据市场状态动态调整)
|
||||
rsi_strong_bull = IntParameter(40, 60, default=50, space="buy", optimize=True, load=True)
|
||||
rsi_weak_bull = IntParameter(35, 55, default=45, space="buy", optimize=True, load=True)
|
||||
rsi_threshold = np.where(
|
||||
dataframe['market_state'].isin(['strong_bull', 'weak_bull']), 50, 45
|
||||
dataframe['market_state'].isin(['strong_bull', 'weak_bull']), rsi_strong_bull.value, rsi_weak_bull.value
|
||||
)
|
||||
rsi_condition_1h = dataframe['rsi_1h'] < rsi_threshold
|
||||
|
||||
# 条件3: StochRSI 处于超卖区域(根据市场状态动态调整)
|
||||
stochrsi_strong_bull = IntParameter(25, 45, default=35, space="buy", optimize=True, load=True)
|
||||
stochrsi_weak_bull = IntParameter(15, 35, default=25, space="buy", optimize=True, load=True)
|
||||
stochrsi_threshold = np.where(
|
||||
dataframe['market_state'].isin(['strong_bull', 'weak_bull']), 35, 25
|
||||
dataframe['market_state'].isin(['strong_bull', 'weak_bull']), stochrsi_strong_bull.value, stochrsi_weak_bull.value
|
||||
)
|
||||
stochrsi_condition_1h = (dataframe['stochrsi_k_1h'] < stochrsi_threshold) & (dataframe['stochrsi_d_1h'] < stochrsi_threshold)
|
||||
|
||||
# 条件4: MACD 上升趋势
|
||||
macd_condition_1h = dataframe['macd_1h'] > dataframe['macd_signal_1h']
|
||||
macd_min_diff = DecimalParameter(0.001, 0.02, default=0.01, decimals=3, space="buy", optimize=True, load=True)
|
||||
macd_condition_1h = (dataframe['macd_1h'] - dataframe['macd_signal_1h']) > macd_min_diff.value
|
||||
|
||||
# 条件5: 成交量显著放大(可选条件)
|
||||
volume_spike = dataframe['volume'] > dataframe['volume_ma'] * 1.5
|
||||
volume_spike_multiplier = DecimalParameter(1.0, 3.0, default=1.5, decimals=1, space="buy", optimize=True, load=True)
|
||||
volume_spike = dataframe['volume'] > dataframe['volume_ma'] * volume_spike_multiplier.value
|
||||
|
||||
# 条件6: 布林带宽度过滤(避免窄幅震荡)
|
||||
bb_width_min = DecimalParameter(0.01, 0.05, default=0.02, decimals=3, space="buy", optimize=True, load=True)
|
||||
bb_width = (dataframe['bb_upper_1h'] - dataframe['bb_lower_1h']) / dataframe['close']
|
||||
bb_width_condition = bb_width > 0.02 # 布林带宽度大于2%
|
||||
bb_width_condition = bb_width > bb_width_min.value
|
||||
|
||||
|
||||
# 辅助条件: 3m 和 15m 趋势确认(允许部分时间框架不一致)
|
||||
trend_confirmation = (dataframe['trend_3m'] == 1) | (dataframe['trend_15m'] == 1)
|
||||
@ -528,7 +560,7 @@ class FreqaiPrimer(IStrategy):
|
||||
(volume_spike | bb_width_condition).astype(int) + # 成交量或布林带宽度满足其一即可
|
||||
trend_confirmation.astype(int)
|
||||
)
|
||||
final_condition = condition_count >= 3
|
||||
final_condition = condition_count >= 3
|
||||
|
||||
# 设置入场信号
|
||||
dataframe.loc[final_condition, 'enter_long'] = 1
|
||||
@ -564,20 +596,20 @@ class FreqaiPrimer(IStrategy):
|
||||
df_1h = self.dp.get_pair_dataframe(pair=pair, timeframe='1h')
|
||||
|
||||
# 确保有足够的K线数据
|
||||
if len(df_1h) < self.H1_MAX_CANDLES:
|
||||
logger.warning(f"[{pair}] 1h K线数据不足 {self.H1_MAX_CANDLES} 根,当前只有 {len(df_1h)} 根,无法完整检测剧烈拉升")
|
||||
if len(df_1h) < self.H1_MAX_CANDLES.value:
|
||||
logger.warning(f"[{pair}] 1h K线数据不足 {self.H1_MAX_CANDLES.value} 根,当前只有 {len(df_1h)} 根,无法完整检测剧烈拉升")
|
||||
return False, None
|
||||
|
||||
# 获取最近的K线
|
||||
recent_data = df_1h.iloc[-self.H1_MAX_CANDLES:].copy()
|
||||
recent_data = df_1h.iloc[-self.H1_MAX_CANDLES.value:].copy()
|
||||
|
||||
# 计算滚动窗口的最大涨幅
|
||||
window_high = recent_data['high'].rolling(window=self.H1_MAX_CONSECUTIVE_CANDLES).max()
|
||||
window_low = recent_data['low'].rolling(window=self.H1_MAX_CONSECUTIVE_CANDLES).min()
|
||||
window_high = recent_data['high'].rolling(window=self.H1_MAX_CONSECUTIVE_CANDLES.value).max()
|
||||
window_low = recent_data['low'].rolling(window=self.H1_MAX_CONSECUTIVE_CANDLES.value).min()
|
||||
rise_percentage = (window_high - window_low) / window_low
|
||||
|
||||
# 检测剧烈拉升
|
||||
rapid_rise_detected = (rise_percentage >= self.H1_RAPID_RISE_THRESHOLD).any()
|
||||
rapid_rise_detected = (rise_percentage >= self.H1_RAPID_RISE_THRESHOLD.value).any()
|
||||
|
||||
# 判断是否处于不稳固区域
|
||||
is_unstable_region = rapid_rise_detected
|
||||
@ -590,7 +622,7 @@ class FreqaiPrimer(IStrategy):
|
||||
# 检查是否已过回看期
|
||||
if unstable_time is not None:
|
||||
time_since_unstable = (datetime.datetime.now() - unstable_time).total_seconds() / 3600 # 转换为小时
|
||||
if time_since_unstable > self.H1_MAX_CANDLES * 3: # 回看期为 H1_MAX_CANDLES 根1小时K线
|
||||
if time_since_unstable > self.H1_MAX_CANDLES.value * 3: # 回看期为 H1_MAX_CANDLES 根1小时K线
|
||||
is_unstable_region = False # 已过回看期,解封币对
|
||||
|
||||
logger.info(f"[{pair}] 剧烈拉升检测结果: {'不稳固' if is_unstable_region else '稳固'}")
|
||||
|
||||
@ -246,5 +246,5 @@ docker-compose run --rm freqtrade hyperopt $PAIRS_FLAG \
|
||||
-e 100 \
|
||||
-j 4 \
|
||||
--hyperopt-loss SharpeHyperOptLoss \
|
||||
--spaces buy sell trailing \
|
||||
--spaces buy \
|
||||
--fee 0.0016
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user