第29课:机器学习与策略优化
Source: Dev.to
⏱ Duration: 2.5 hours
🎯 Learning Objectives
- 学会使用机器学习辅助策略开发和参数优化
Course Overview
机器学习(ML)可以帮助我们:
- 🔍 发现数据中的隐藏模式
- 🎯 优化策略参数
- 📊 预测价格趋势
- 🤖 构建自适应策略
Important Reminders
- ⚠️ 机器学习并非“圣杯”,不能保证盈利。
- ⚠️ 需要大量数据和计算资源。
- ⚠️ 容易出现过拟合,必须谨慎验证。
- ⚠️ 本课程侧重实用方法,而非理论深度。
Part 1: Freqtrade’s Hyperopt
1.1 Hyperopt Introduction
Hyperopt 是 Freqtrade 内置的参数优化工具,使用机器学习算法自动寻找最优参数。
Basic Concepts
What is Hyperopt?
- Automated parameter search
- Uses Bayesian optimization algorithms
- Searches for optimal parameters within specified ranges
- Scores based on backtest results
What can be optimized?
- Buy condition parameters (RSI thresholds, EMA periods)
- Sell condition parameters
- ROI configuration
- Stop loss configuration
- Trailing stop loss configuration
Optimization Spaces
Freqtrade supports 5 optimization spaces:
1. buy – Buy condition parameters
2. sell – Sell condition parameters
3. roi – ROI configuration
4. stoploss – Stop loss configuration
5. trailing – Trailing stop loss configuration
Can optimize individually or in combination.
1.2 Preparing Hyperopt‑Compatible Strategies
Create a strategy file, e.g. user_data/strategies/HyperoptableStrategy.py:
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, CategoricalParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from functools import reduce
class HyperoptableStrategy(IStrategy):
"""
Hyperopt‑compatible strategy
Define optimizable parameter ranges
"""
INTERFACE_VERSION = 3
# ===== Optimizable Parameters =====
# Buy parameters
buy_rsi_threshold = IntParameter(20, 40, default=30, space='buy')
buy_rsi_enabled = CategoricalParameter([True, False], default=True, space='buy')
buy_ema_short = IntParameter(5, 20, default=9, space='buy')
buy_ema_long = IntParameter(15, 50, default=21, space='buy')
# Sell parameters
sell_rsi_threshold = IntParameter(60, 80, default=70, space='sell')
sell_rsi_enabled = CategoricalParameter([True, False], default=True, space='sell')
# ROI parameters (static example)
minimal_roi = {
"0": 0.10,
"30": 0.05,
"60": 0.03,
"120": 0.01
}
# Stop loss parameter
stoploss = -0.10
# Trailing stop loss parameters
trailing_stop = True
trailing_stop_positive = 0.01
trailing_stop_positive_offset = 0.02
trailing_only_offset_is_reached = True
timeframe = '5m'
startup_candle_count: int = 50
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# EMA (using optimizable periods)
for val in self.buy_ema_short.range:
dataframe[f'ema_short_{val}'] = ta.EMA(dataframe, timeperiod=val)
for val in self.buy_ema_long.range:
dataframe[f'ema_long_{val}'] = ta.EMA(dataframe, timeperiod=val)
# RSI
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
# Volume mean
dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean()
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
# EMA golden cross
conditions.append(
qtpylib.crossed_above(
dataframe[f'ema_short_{self.buy_ema_short.value}'],
dataframe[f'ema_long_{self.buy_ema_long.value}']
)
)
# RSI (if enabled)
if self.buy_rsi_enabled.value:
conditions.append(dataframe['rsi'] > self.buy_rsi_threshold.value)
conditions.append(dataframe['rsi'] dataframe['volume_mean'])
conditions.append(dataframe['volume'] > 0)
if conditions:
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
# EMA death cross
conditions.append(
qtpylib.crossed_below(
dataframe[f'ema_short_{self.buy_ema_short.value}'],
dataframe[f'ema_long_{self.buy_ema_long.value}']
)
)
# RSI (if enabled)
if self.sell_rsi_enabled.value:
conditions.append(dataframe['rsi'] > self.sell_rsi_threshold.value)
# Volume filter
conditions.append(dataframe['volume'] > 0)
if conditions:
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1
return dataframe
Key Points
# Integer parameter
buy_rsi_threshold = IntParameter(20, 40, default=30, space='buy')
# Search within 20‑40 range, default 30
# Decimal parameter
stoploss = DecimalParameter(-0.15, -0.05, default=-0.10, space='stoploss')
# Search within -0.15 to -0.05 range
# Categorical parameter (True/False)
buy_rsi_enabled = CategoricalParameter([True, False], default=True, space='buy')
1.3 Running Hyperopt
Basic Commands
# Optimize buy parameters only
freqtrade hyperopt \
-c config.json \
--strategy HyperoptableStrategy \
--hyperopt-loss SharpeHyperOptLoss \
--spaces buy \
--epochs 100
--hyperopt-loss– defines the optimization objective.--spaces– which parameter spaces to optimize.--epochs– number of optimization iterations.
Optimizing Multiple Spaces
# Optimize both buy and sell simultaneously
freqtrade hyperopt \
-c config.json \
--strategy HyperoptableStrategy \
--hyperopt-loss SharpeHyperOptLoss \
--spaces buy sell \
--epochs 200
# Optimize all spaces
freqtrade hyperopt \
-c config.json \
--strategy HyperoptableStrategy \
--hyperopt-loss SharpeHyperOptLoss \
--spaces all \
--epochs 500
Sample Output
Best result:
188/500: 145 trades. Avg profit 0.85%. Total profit 0.01234 BTC (123.45%).
Avg duration 234.5 m. Objective: -2.34567
Buy hyperspace params:
{
"buy_ema_short": 12,
"buy_ema_long": 26,
"buy_rsi_threshold": 35,
"buy_rsi_enabled": true
}
Sell hyperspace params:
{
"sell_rsi_threshold": 65,
"sell_rsi_enabled": true
}
ROI table:
{
"0": 0.088,
"25": 0.045,
"51": 0.019,
"139": 0
}
Stoploss: -0.089
1.4 Loss Functions
Loss functions define what “optimal” means for the optimizer.
Common Loss Functions
# Example: Sharpe ratio based loss
from freqtrade.optimize.hyperopt import HyperOptLoss
class SharpeHyperOptLoss(HyperOptLoss):
@staticmethod
def hyperopt_loss_function(results):
# Higher Sharpe → lower loss (since optimizer minimizes)
return -results['sharpe']
Other built‑in options include CalmarHyperOptLoss, SortinoHyperOptLoss, and custom user‑defined functions. Choose the one that aligns with your risk‑adjusted performance goals.