hyperopt & change freqai params
This commit is contained in:
parent
c167bf5df0
commit
998ac0a7b1
128
chat/hyperopt.md
Normal file
128
chat/hyperopt.md
Normal file
@ -0,0 +1,128 @@
|
||||
- Datetime: 2025-06-03 17:30:28
|
||||
|
||||
- Model: _qwen3/qwen-plus-latest
|
||||
|
||||
- Selected files:
|
||||
- freqtrade/templates/freqaiprimer.py
|
||||
|
||||
> https://www.freqtrade.io/en/stable/hyperopt/
|
||||
> 在官方文档中,有没有提到hyperopt多久执行一次, 每次timerange的跨度是几天
|
||||
|
||||
|
||||
非常感谢你的提问!
|
||||
|
||||
你提供的链接是 Freqtrade 的官方 Hyperopt 文档:
|
||||
|
||||
🔗 [https://www.freqtrade.io/en/stable/hyperopt/](https://www.freqtrade.io/en/stable/hyperopt/)
|
||||
|
||||
---
|
||||
|
||||
## ✅ 回答你的问题:
|
||||
|
||||
> **官方文档中没有明确说明“Hyperopt 应该多久执行一次”或“每次 timerange 的跨度应该是几天”**,但我们可以从文档和最佳实践中推导出合理的建议。
|
||||
|
||||
---
|
||||
|
||||
## 📚 官方文档相关提示
|
||||
|
||||
在文档的以下部分提到了一些关键信息:
|
||||
|
||||
### 1. **关于 `--timerange`**
|
||||
> You can use the `--timerange` parameter to specify which date range you want to use for Hyperopt.
|
||||
|
||||
示例:
|
||||
```bash
|
||||
freqtrade hyperopt --timerange 20200101-20210101 ...
|
||||
```
|
||||
|
||||
这意味着你可以自由指定训练数据的时间范围。
|
||||
|
||||
---
|
||||
|
||||
### 2. **关于训练集大小(Training Set Size)**
|
||||
|
||||
> The larger your training set, the more reliable your results will be — but the longer it will take to run.
|
||||
|
||||
也就是说:
|
||||
- 更大的训练集 → 更可靠的结果
|
||||
- 更小的训练集 → 更快完成,但可能过拟合
|
||||
|
||||
---
|
||||
|
||||
### 3. **关于优化目标(Optimization Target)**
|
||||
|
||||
Freqtrade 推荐使用如 `SortinoRatio`, `SharpeRatio`, 或 `Profit` 作为优化目标,以确保策略具有良好的风险收益比。
|
||||
|
||||
---
|
||||
|
||||
## ✅ 实战经验总结:Hyperopt 执行频率 & Timerange 设置建议
|
||||
|
||||
| 问题 | 建议 |
|
||||
|------|------|
|
||||
| ❓ Hyperopt 多久执行一次? | ✅ 每次策略逻辑、参数空间或市场环境发生重大变化时运行一次 |
|
||||
| ❓ 每次 Hyperopt 使用多少历史数据? | ✅ 至少 **30天以上**,推荐 **90~180天** |
|
||||
| ❓ Hyperopt 需要定期重新运行吗? | ✅ 是的,建议每 **1~3个月** 重新运行一次,适应市场变化 |
|
||||
|
||||
---
|
||||
|
||||
## 🧠 推荐配置(基于实战经验)
|
||||
|
||||
### ✅ Hyperopt 运行频率建议:
|
||||
|
||||
| 场景 | 推荐频率 |
|
||||
|------|-----------|
|
||||
| 策略首次开发 | ✅ 必须运行一次 |
|
||||
| 策略小幅调整(如指标、阈值) | ✅ 每次修改后运行 |
|
||||
| 市场风格切换(如牛市→熊市) | ✅ 每季度运行一次 |
|
||||
| 策略部署到实盘前 | ✅ 必须运行并验证 |
|
||||
|
||||
---
|
||||
|
||||
### ✅ `--timerange` 时间跨度建议:
|
||||
|
||||
| 目标 | 推荐时间跨度 |
|
||||
|------|----------------|
|
||||
| 快速测试 | ✅ 7~14天 |
|
||||
| 中期趋势策略 | ✅ 30~60天 |
|
||||
| 长期趋势策略 | ✅ 90~180天 |
|
||||
| 多币种轮动策略 | ✅ 至少覆盖一个完整周期(如牛熊交替) |
|
||||
|
||||
---
|
||||
|
||||
### ✅ 示例命令:
|
||||
|
||||
```bash
|
||||
# 快速测试用(7天)
|
||||
freqtrade hyperopt --strategy MyStrategy --timerange 20250501-20250507 --timeframe 5m --hyperopt-loss SharpeHyperOptLossDaily
|
||||
|
||||
# 正式优化用(30天)
|
||||
freqtrade hyperopt --strategy MyStrategy --timerange 20250401-20250501 --timeframe 5m --hyperopt-loss SortinoHyperOptLoss
|
||||
|
||||
# 多币种轮动策略(90天+)
|
||||
freqtrade hyperopt --strategy MyStrategy --timerange 20250101-20250401 --timeframe 15m --hyperopt-loss ProfitHyperOptLoss --spaces all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 补充建议:Hyperopt 的最佳实践
|
||||
|
||||
| 建议 | 描述 |
|
||||
|------|------|
|
||||
| 🔁 分阶段运行 | 先跑短期(7天)快速验证逻辑,再跑长期(30天)优化参数 |
|
||||
| 📊 使用合适的时间粒度 | 如果你是做日内交易,使用 `1m` 或 `5m`;如果是波段交易,使用 `15m` 或 `1h` |
|
||||
| 🧪 多币种回测 | 使用多个币种进行 Hyperopt,避免单币过拟合 |
|
||||
| 📦 保存结果 | 使用 `--export-filename` 导出最优参数,便于复用 |
|
||||
|
||||
---
|
||||
|
||||
## ✅ 总结
|
||||
|
||||
| 问题 | 推荐做法 |
|
||||
|------|-----------|
|
||||
| Hyperopt 多久执行一次? | ✅ 每次策略更新或市场环境变化时运行 |
|
||||
| 每次 Hyperopt 使用多少数据? | ✅ 最少 7~14天,推荐 30~90天 |
|
||||
| 是否需要定期重新运行? | ✅ 是的,建议每月或每季度重新运行一次 |
|
||||
|
||||
---
|
||||
|
||||
如果你希望我帮你生成一个完整的 Hyperopt 配置文件(含推荐的 timerange、loss 函数、参数空间等),我可以继续为你输出。是否需要?
|
||||
@ -107,11 +107,11 @@
|
||||
"shuffle": false
|
||||
},
|
||||
"model_training_parameters": {
|
||||
"n_estimators": 120,
|
||||
"learning_rate": 0.05,
|
||||
"n_estimators": 60,
|
||||
"learning_rate": 0.1,
|
||||
"num_leaves": 31,
|
||||
"verbose": -1,
|
||||
"max_depth": 8
|
||||
"verbose": 1,
|
||||
"max_depth": 4
|
||||
}
|
||||
},
|
||||
"api_server": {
|
||||
|
||||
@ -2,29 +2,35 @@
|
||||
"strategy_name": "FreqaiPrimer",
|
||||
"params": {
|
||||
"max_open_trades": {
|
||||
"max_open_trades": 2
|
||||
"max_open_trades": 4
|
||||
},
|
||||
"buy": {},
|
||||
"sell": {},
|
||||
"protection": {
|
||||
"cooldown_period": 5
|
||||
"buy": {
|
||||
"BUY_THRESHOLD_MAX": -0.005,
|
||||
"BUY_THRESHOLD_MIN": -0.05
|
||||
},
|
||||
"sell": {
|
||||
"SELL_THRESHOLD_MAX": 0.05,
|
||||
"SELL_THRESHOLD_MIN": 0.005,
|
||||
"TRAILING_STOP_DISTANCE": 0.01,
|
||||
"TRAILING_STOP_START": 0.03
|
||||
},
|
||||
"protection": {},
|
||||
"roi": {
|
||||
"0": 0.03,
|
||||
"15": 0.02,
|
||||
"30": 0.015,
|
||||
"45": 0.01,
|
||||
"60": 0.002
|
||||
"0": 0.135,
|
||||
"21": 0.033,
|
||||
"41": 0.007,
|
||||
"110": 0
|
||||
},
|
||||
"stoploss": {
|
||||
"stoploss": -0.015
|
||||
"stoploss": -0.036
|
||||
},
|
||||
"trailing": {
|
||||
"trailing_stop": true,
|
||||
"trailing_stop_positive": 0.005,
|
||||
"trailing_stop_positive_offset": 0.01
|
||||
"trailing_stop_positive": 0.151,
|
||||
"trailing_stop_positive_offset": 0.237,
|
||||
"trailing_only_offset_is_reached": true
|
||||
}
|
||||
},
|
||||
"ft_stratparam_v": 1,
|
||||
"export_time": "2025-05-31 23:03:00.000000+09:00"
|
||||
}
|
||||
"export_time": "2025-06-03 22:51:36.760022+00:00"
|
||||
}
|
||||
@ -9,21 +9,42 @@ from freqtrade.persistence import Trade
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from typing import Dict
|
||||
from freqtrade.strategy import IStrategy
|
||||
from freqtrade.strategy import (DecimalParameter, IStrategy, IntParameter)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class FreqaiPrimer(IStrategy):
|
||||
"""
|
||||
基于 FreqAI 的动态阈值交易策略
|
||||
"""
|
||||
|
||||
# --- 🧪 Hyperopt Parameters ---
|
||||
|
||||
# ROI 参数(用于收益目标)
|
||||
ROI_T0 = DecimalParameter(0.01, 0.05, default=0.02, space='roi', optimize=True)
|
||||
ROI_T1 = DecimalParameter(0.005, 0.02, default=0.01, space='roi', optimize=True)
|
||||
ROI_T2 = DecimalParameter(0.0, 0.01, default=0.0, space='roi', optimize=True)
|
||||
|
||||
# Trailing Stop 参数(追踪止损)
|
||||
TRAILING_STOP_START = DecimalParameter(0.01, 0.05, default=0.03, space='sell', optimize=True)
|
||||
TRAILING_STOP_DISTANCE = DecimalParameter(0.005, 0.02, default=0.01, space='sell', optimize=True)
|
||||
|
||||
# 买卖阈值边界(你要优化的目标)
|
||||
BUY_THRESHOLD_MIN = DecimalParameter(-0.1, -0.01, default=-0.05, space='buy', optimize=True)
|
||||
BUY_THRESHOLD_MAX = DecimalParameter(-0.02, -0.001, default=-0.005, space='buy', optimize=True)
|
||||
SELL_THRESHOLD_MIN = DecimalParameter(0.001, 0.02, default=0.005, space='sell', optimize=True)
|
||||
SELL_THRESHOLD_MAX = DecimalParameter(0.02, 0.1, default=0.05, space='sell', optimize=True)
|
||||
|
||||
|
||||
# --- 🛠️ 固定配置参数 ---
|
||||
minimal_roi = {
|
||||
"0": 0.02,
|
||||
"30": 0.01,
|
||||
"60": 0
|
||||
"0": ROI_T0.value,
|
||||
"30": ROI_T1.value,
|
||||
"60": ROI_T2.value
|
||||
}
|
||||
|
||||
stoploss = -0.015
|
||||
|
||||
timeframe = "3m"
|
||||
|
||||
use_custom_stoploss = False
|
||||
|
||||
plot_config = {
|
||||
@ -82,6 +103,7 @@ class FreqaiPrimer(IStrategy):
|
||||
self.pair_stats = {}
|
||||
self.stats_logged = False
|
||||
self.fit_live_predictions_candles = self.freqai_info.get("fit_live_predictions_candles", 100)
|
||||
self.last_entry_time = {} # 记录每个币种的最后入场时间
|
||||
|
||||
def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame:
|
||||
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
|
||||
@ -215,7 +237,26 @@ class FreqaiPrimer(IStrategy):
|
||||
labels_std = 0.01
|
||||
logger.warning(f"[{pair}] labels_std 计算异常,使用默认值 0.01")
|
||||
|
||||
self.pair_stats[pair] = {"labels_mean": labels_mean, "labels_std": labels_std}
|
||||
# 根据 labels_std 调整 k 值
|
||||
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
|
||||
|
||||
self.buy_threshold = labels_mean - k_buy * labels_std
|
||||
self.sell_threshold = labels_mean + k_sell * labels_std
|
||||
else:
|
||||
self.buy_threshold = -0.02
|
||||
self.sell_threshold = 0.03
|
||||
self.pair_stats[pair] = {"labels_mean": labels_mean, "labels_std": labels_std}
|
||||
|
||||
if labels_std > 0.015:
|
||||
k_buy = 1.2
|
||||
@ -234,15 +275,17 @@ class FreqaiPrimer(IStrategy):
|
||||
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)
|
||||
# 使用 Hyperopt 参数动态限制阈值
|
||||
self.buy_threshold = max(self.buy_threshold, self.BUY_THRESHOLD_MIN.value)
|
||||
self.buy_threshold = min(self.buy_threshold, self.BUY_THRESHOLD_MAX.value)
|
||||
self.sell_threshold = min(self.sell_threshold, self.SELL_THRESHOLD_MAX.value)
|
||||
self.sell_threshold = max(self.sell_threshold, self.SELL_THRESHOLD_MIN.value)
|
||||
|
||||
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}")
|
||||
logger.info(f"[{pair}] 当前买入阈值:{self.buy_threshold:.6f},卖出阈值:{self.sell_threshold:.6f}")
|
||||
|
||||
if not self.stats_logged:
|
||||
logger.info("===== 所有币对的 labels_mean 和 labels_std 汇总 =====")
|
||||
@ -253,6 +296,27 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
return dataframe
|
||||
|
||||
def generate_roi_table(self, params: Dict) -> Dict[int, float]:
|
||||
roi_table = {
|
||||
"0": params.get("roi_t0", 0.02),
|
||||
"30": params.get("roi_t1", 0.01),
|
||||
"60": params.get("roi_t2", 0.0),
|
||||
}
|
||||
return roi_table
|
||||
|
||||
def roi_space(self):
|
||||
return [
|
||||
DecimalParameter(0.01, 0.05, name="roi_t0"),
|
||||
DecimalParameter(0.005, 0.02, name="roi_t1"),
|
||||
DecimalParameter(0.0, 0.01, name="roi_t2")
|
||||
]
|
||||
|
||||
def trailing_space(self):
|
||||
return [
|
||||
DecimalParameter(0.01, 0.05, name="trailing_stop_start"),
|
||||
DecimalParameter(0.005, 0.02, name="trailing_stop_distance")
|
||||
]
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
pair = metadata.get('pair', 'Unknown')
|
||||
conditions = []
|
||||
@ -345,20 +409,24 @@ class FreqaiPrimer(IStrategy):
|
||||
return None
|
||||
|
||||
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.query.filter(
|
||||
and_(
|
||||
Trade.pair == pair,
|
||||
Trade.is_open == False, # noqa: E712
|
||||
Trade.close_date > current_time - datetime.timedelta(minutes=5)
|
||||
)
|
||||
).all()
|
||||
if len(recent_trades) > 0:
|
||||
logger.info(f"[{pair}] 5分钟内有近期交易({len(recent_trades)} 笔),跳过本次入场")
|
||||
return False
|
||||
time_in_force: str, current_time: datetime, **kwargs) -> bool:
|
||||
# """
|
||||
# 使用内存变量代替数据库查询,防止 Hyperopt 报错
|
||||
# """
|
||||
#
|
||||
cooldown_period_minutes = 5 # 可以改为 DecimalParameter 进行 Hyperopt
|
||||
|
||||
if pair in self.last_entry_time:
|
||||
last_time = self.last_entry_time[pair]
|
||||
if (current_time - last_time).total_seconds() < cooldown_period_minutes * 60:
|
||||
logger.info(f"[{pair}] 冷却期内,跳过本次入场")
|
||||
return False
|
||||
|
||||
# 更新最后入场时间
|
||||
self.last_entry_time[pair] = current_time
|
||||
self.trailing_stop_enabled = False
|
||||
|
||||
# 如果我猜的没错的话, 用了这个方法上述被注释的内容, 反到降低了本策略盈利, 需要更多测试,所以暂时禁用并不删除
|
||||
return True
|
||||
|
||||
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
||||
|
||||
@ -4,10 +4,10 @@
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "⚠️ 本地缺少 .env 文件,请创建并配置。示例内容如下:"
|
||||
echo ""
|
||||
echo "STRATEGY_NAME=TheForceV7"
|
||||
echo "CONFIG_FILE=basic.json"
|
||||
echo "TEST_BRANCH=theforce-noai-test"
|
||||
echo "DRYRUN_BRANCH=theforce-noai-dryrun"
|
||||
echo "STRATEGY_NAME=FreqaiPrimer"
|
||||
echo "CONFIG_FILE=FreqaiPrimer.json"
|
||||
echo "TEST_BRANCH=freqaiprimer-deviation-test"
|
||||
echo "DRYRUN_BRANCH=freqaiprimer-deviation-dryrun"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
@ -15,8 +15,8 @@ fi
|
||||
# 加载 .env 文件中的变量
|
||||
export $(grep -v '^#' .env | xargs)
|
||||
# 设置默认值
|
||||
STRATEGY_NAME=${STRATEGY_NAME:-TheForceV7}
|
||||
CONFIG_FILE=${CONFIG_FILE:-basic.json}
|
||||
STRATEGY_NAME=${STRATEGY_NAME:-FreqaiPrimer}
|
||||
CONFIG_FILE=${CONFIG_FILE:-FreqaiPrimer.json}
|
||||
|
||||
echo "Using strategy: $STRATEGY_NAME"
|
||||
echo "Using config: $CONFIG_FILE"
|
||||
@ -34,7 +34,7 @@ rm -fr ./user_data/dryrun_results/*
|
||||
#docker-compose -f docker-compose_backtest.yml run --rm freqtrade >output.log 2>&1
|
||||
echo "docker-compose run --rm freqtrade hyperopt \
|
||||
--logfile /freqtrade/user_data/logs/freqtrade.log \
|
||||
--freqaimodel LightGBMRegressor \
|
||||
--freqaimodel XGBoostRegressorMultiTarget \
|
||||
--strategy $STRATEGY_NAME \
|
||||
--config /freqtrade/config_examples/$CONFIG_FILE \
|
||||
--strategy-path /freqtrade/templates \
|
||||
@ -45,12 +45,12 @@ echo "docker-compose run --rm freqtrade hyperopt \
|
||||
--fee 0.0016"
|
||||
docker-compose run --rm freqtrade hyperopt \
|
||||
--logfile /freqtrade/user_data/logs/freqtrade.log \
|
||||
--freqaimodel LightGBMRegressorMultiTarget \
|
||||
--freqaimodel XGBoostRegressorMultiTarget \
|
||||
--strategy $STRATEGY_NAME \
|
||||
--config /freqtrade/config_examples/$CONFIG_FILE \
|
||||
--strategy-path /freqtrade/templates \
|
||||
--timerange ${START_DATE}-${END_DATE} \
|
||||
-e 200 \
|
||||
-e 100 \
|
||||
--hyperopt-loss ShortTradeDurHyperOptLoss \
|
||||
--spaces roi stoploss trailing \
|
||||
--fee 0.0016
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user