包含4份核心文档: - 轮动策略系统架构分析报告 - 量化策略通用框架抽象设计 - Freqtrade架构调研与对比分析 - ETF轮动策略通用化重构方案 调研结论:三种策略可抽象通用框架 设计决策:因子注册器风格 + 5个核心回调钩子 + TopN/Trend/Reversal信号生成器
21 KiB
21 KiB
Freqtrade架构调研与对比分析报告
一、Freqtrade架构概览
1.1 项目定位
Freqtrade是一个开源的加密货币量化交易框架,核心特点:
- 成熟度高:2017年开源,持续维护,社区活跃
- 生产就绪:支持回测、模拟盘、实盘三种模式
- 配置驱动:策略、参数、风控均可通过配置控制
- 扩展性强:支持自定义因子、策略、损失函数、交易所
1.2 核心架构图
┌─────────────────────────────────────────────────────────────────────┐
│ FreqtradeBot (主控制器) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Strategy │ │ Exchange │ │ DataProvider │ │
│ │ (IStrategy)│ │ (CCXT) │ │ │ │
│ │ │ │ │ │ │ │
│ │ - 指标计算 │ │ - OHLCV获取 │ │ - 数据缓存 │ │
│ │ - 入场信号 │ │ - 交易执行 │ │ - 实时推送 │ │
│ │ - 出场信号 │ │ - 余额查询 │ │ │ │
│ │ - 回调函数 │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Portfolio │ │ RPC/Notify│ │ Hyperopt │ │
│ │ Manager │ │ │ │ (Optuna) │ │
│ │ │ │ - Telegram │ │ │ │
│ │ - 持仓跟踪 │ │ - Discord │ │ - 参数优化 │ │
│ │ - 余额管理 │ │ - WebUI │ │ - 损失函数 │ │
│ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ Configuration (JSON/YAML) │
└─────────────────────────────────────────────────────────────────────┘
二、核心模块设计
2.1 策略抽象层(IStrategy)
设计理念:策略是核心,一切围绕策略接口设计
class IStrategy:
"""策略抽象接口"""
# ===== 类属性(配置化参数) =====
INTERFACE_VERSION = 3 # 接口版本控制
timeframe = '5m' # K线周期
can_short = False # 是否允许做空
minimal_roi = { # 最小ROI目标
"0": 0.10, # 立即获利10%
"60": 0.05, # 60分钟后获利5%
}
stoploss = -0.10 # 止损比例
# ===== 核心抽象方法(必须实现) =====
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""计算技术指标"""
# 用户自定义指标逻辑
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
dataframe['macd'] = ta.MACD(dataframe)['macd']
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""生成入场信号"""
dataframe.loc[
(dataframe['rsi'] < 30) & # 条件组合
(dataframe['macd'] > 0),
'enter_long'
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""生成出场信号"""
dataframe.loc[
(dataframe['rsi'] > 70),
'exit_long'
] = 1
return dataframe
# ===== 回调函数(可选实现) =====
def confirm_trade_entry(self, pair, order_type, amount, rate, **kwargs) -> bool:
"""入场前确认回调 - 可拒绝交易"""
return True
def custom_stake_amount(self, pair, current_time, current_rate, **kwargs) -> float:
"""动态仓位调整"""
return self.stake_amount
def custom_stoploss(self, pair, trade, current_time, current_rate, **kwargs) -> float:
"""动态止损"""
return self.stoploss
def custom_exit(self, pair, trade, current_time, current_rate, **kwargs) -> bool:
"""自定义出场条件"""
return False
def adjust_trade_position(self, pair, trade, current_time, current_rate, **kwargs) -> float:
"""动态仓位调整(DCA/部分止盈)"""
return None
关键设计点:
| 设计点 | 说明 | 优势 |
|---|---|---|
| 接口版本控制 | INTERFACE_VERSION |
向后兼容,平滑升级 |
| 类属性配置 | timeframe, stoploss |
参数可从配置文件覆盖 |
| DataFrame驱动 | 所有计算基于DataFrame | 向量化计算,高性能 |
| 回调钩子 | 多生命周期钩子 | 灵活控制交易细节 |
| 信号列名约定 | enter_long, exit_long |
统一信号格式 |
2.2 数据层抽象(Exchange + DataProvider)
Exchange接口:基于CCXT库,统一封装100+交易所
class Exchange:
"""交易所抽象(基于CCXT)"""
def __init__(self, config: dict):
# 初始化CCXT交易所实例
self._api = ccxt.binance({
'apiKey': config['key'],
'secret': config['secret'],
'enableRateLimit': True,
})
# ===== 数据获取 =====
def fetch_ohlcv(self, pair: str, timeframe: str, since: int) -> List:
"""获取K线数据"""
return self._api.fetch_ohlcv(pair, timeframe, since)
def fetch_ticker(self, pair: str) -> dict:
"""获取行情"""
return self._api.fetch_ticker(pair)
def fetch_order_book(self, pair: str, limit: int) -> dict:
"""获取订单簿"""
return self._api.fetch_order_book(pair, limit)
# ===== 交易执行 =====
def create_order(self, pair: str, type: str, side: str, amount: float, price: float) -> dict:
"""创建订单"""
return self._api.create_order(pair, type, side, amount, price)
def cancel_order(self, order_id: str, pair: str) -> dict:
"""取消订单"""
return self._api.cancel_order(order_id, pair)
# ===== 账户查询 =====
def fetch_balance(self) -> dict:
"""查询余额"""
return self._api.fetch_balance()
def fetch_orders(self, pair: str) -> List:
"""查询订单"""
return self._api.fetch_orders(pair)
DataProvider:数据缓存与统一访问
class DataProvider:
"""数据提供者 - 缓存管理"""
def __init__(self, exchange: Exchange, config: dict):
self._exchange = exchange
self._cached_pairs = {}
def ohlcv(self, pair: str, timeframe: str, since: int) -> DataFrame:
"""获取K线数据(带缓存)"""
cache_key = f"{pair}_{timeframe}"
if cache_key not in self._cached_pairs:
# 从交易所获取
data = self._exchange.fetch_ohlcv(pair, timeframe, since)
# 转换为DataFrame
df = pd.DataFrame(data, columns=['date', 'open', 'high', 'low', 'close', 'volume'])
self._cached_pairs[cache_key] = df
return self._cached_pairs[cache_key]
def refresh(self, pair: str, timeframe: str):
"""刷新缓存"""
# 只更新最新数据,不重新下载全部
pass
2.3 配置驱动设计
配置层级:
{
// 系统配置
"max_open_trades": 3,
"stake_currency": "USDT",
"stake_amount": "unlimited",
"dry_run": true,
"dry_run_wallet": 1000,
// 交易所配置
"exchange": {
"name": "binance",
"key": "your_key",
"secret": "your_secret",
"ccxt_config": {
"enableRateLimit": true
},
"pair_whitelist": ["BTC/USDT", "ETH/USDT"],
"pair_blacklist": ["BNB/USDT"]
},
// 策略配置(可覆盖策略类属性)
"strategy": "SampleStrategy",
"timeframe": "5m",
"minimal_roi": {
"0": 0.10
},
"stoploss": -0.10,
// 通知配置
"telegram": {
"enabled": true,
"token": "your_token",
"chat_id": "your_chat_id"
}
}
配置优先级:
命令行参数 > 环境变量 > 配置文件 > 策略默认值
2.4 Hyperopt参数优化
参数定义(在策略中):
class MyStrategy(IStrategy):
# 定义可优化参数
buy_rsi = IntParameter(10, 40, default=30, space='buy')
sell_rsi = IntParameter(60, 90, default=70, space='sell')
stoploss = DecimalParameter(-0.05, -0.20, default=-0.10, space='stoploss')
def populate_entry_trend(self, dataframe, metadata):
dataframe.loc[
(dataframe['rsi'] < self.buy_rsi.value), # 使用参数
'enter_long'
] = 1
return dataframe
优化空间:
buy/enter: 入场参数sell/exit: 出场参数roi: ROI表优化stoploss: 止损优化trailing: 跟踪止损优化protection: 保护策略优化
损失函数:
class SharpeHyperOptLoss(IHyperOptLoss):
"""Sharpe比率损失函数"""
@staticmethod
def hyperopt_loss_function(results, trade_count, min_date, max_date, *args) -> float:
# 计算Sharpe比率
returns = results['profit_ratio']
sharpe = returns.mean() / returns.std() * np.sqrt(365)
return -sharpe # 返回负值(优化最大化)
三、Freqtrade设计优势
3.1 核心设计亮点
| 设计点 | Freqtrade实现 | 优势 |
|---|---|---|
| 策略接口 | IStrategy抽象类 + 回调钩子 |
灵活度高,生命周期完整 |
| 数据抽象 | CCXT统一封装100+交易所 | 数据源可插拔 |
| 配置驱动 | JSON配置 + 参数覆盖 | 无需修改代码即可调整 |
| 参数优化 | Optuna集成 + 参数装饰器 | 自动化参数搜索 |
| 执行模式 | backtest/dry/live统一接口 | 平滑切换 |
| 风控回调 | custom_stoploss, custom_exit |
灵活风控逻辑 |
3.2 向量化设计
DataFrame驱动:
- 所有指标计算基于DataFrame
- 避免
iloc[-1],使用shift() - 批量计算,高性能
- 防止前视偏差(lookahead bias)
# 错误:使用iloc(可能引入前视偏差)
if dataframe['rsi'].iloc[-1] > 70:
dataframe['exit_long'] = 1
# 正确:使用shift()
dataframe.loc[
(dataframe['rsi'].shift(1) > 70), # 使用前一周期数据
'exit_long'
] = 1
四、与我们的框架对比
4.1 设计差异对比
| 维度 | Freqtrade | 我们的框架 | 建议 |
|---|---|---|---|
| 策略接口 | IStrategy类 + 回调钩子 |
配置驱动策略模式 | 借鉴回调钩子设计 |
| 因子层 | populate_indicators方法内定义 |
FactorBase抽象 + 注册器 |
我们的设计更模块化 |
| 信号层 | populate_entry_trend/exit_trend |
SignalGenerator抽象 |
两者设计相似 |
| 数据层 | CCXT统一封装 | 混合数据源(Tushare + YFinance) | 借鉴交易所抽象接口 |
| 风控层 | 回调函数(custom_stoploss) |
风控组件库 | 回调更灵活,组件更复用 |
| 配置驱动 | JSON配置 + 类属性覆盖 | YAML配置 | 两者相似 |
| 参数优化 | Optuna集成 + 参数装饰器 | 无 | 需要添加Hyperopt模块 |
4.2 可借鉴设计
(1)回调钩子机制
# Freqtrade的回调设计
class IStrategy:
def confirm_trade_entry(self, pair, order_type, amount, rate, **kwargs) -> bool:
"""入场前确认 - 可拒绝交易"""
# 检查溢价率、市场状态等
if self.check_premium(pair) > 0.10:
return False # 拒绝交易
return True
def custom_stoploss(self, pair, trade, current_time, current_rate, **kwargs) -> float:
"""动态止损"""
# 根据持仓时间调整止损
if trade.open_date < current_time - timedelta(days=5):
return -0.05 # 5天后收紧止损
return -0.10
def custom_exit(self, pair, trade, current_time, current_rate, **kwargs) -> bool:
"""自定义出场条件"""
# 检查市场反转信号
if self.detect_reversal(pair):
return True # 强制出场
return False
借鉴方案:
class StrategyBase:
"""我们的策略基类 + 回调钩子"""
# 入场回调
def before_entry(self, code: str, price: float, **kwargs) -> bool:
"""入场前检查(溢价率、崩盘检测)"""
return True
def after_entry(self, trade: Trade, **kwargs):
"""入场后记录"""
pass
# 出场回调
def before_exit(self, trade: Trade, **kwargs) -> bool:
"""出场前检查"""
return True
def after_exit(self, trade: Trade, **kwargs):
"""出场后记录"""
pass
# 风控回调
def dynamic_stoploss(self, position: Position, **kwargs) -> float:
"""动态止损"""
return self.config.get('stoploss', -0.05)
def position_adjust(self, portfolio: Portfolio, **kwargs) -> dict:
"""仓位调整(DCA/部分止盈)"""
return {}
(2)参数装饰器
# Freqtrade的参数装饰器
from freqtrade.strategy import IntParameter, DecimalParameter, CategoricalParameter
class MyStrategy(IStrategy):
# 可优化参数定义
buy_rsi_threshold = IntParameter(10, 40, default=30, space='buy', optimize=True)
sell_rsi_threshold = IntParameter(60, 90, default=70, space='sell', optimize=True)
timeframe = CategoricalParameter(['1m', '5m', '15m'], default='5m', space='buy')
借鉴方案:
# 我们的参数装饰器设计
class Parameter:
"""参数基类"""
def __init__(self, low, high, default, space='default', optimize=True):
self.low = low
self.high = high
self.default = default
self.space = space
self.optimize = optimize
self._value = default
def __get__(self, obj, objtype):
return self._value
def __set__(self, obj, value):
self._value = value
class IntParameter(Parameter):
"""整数参数"""
pass
class FloatParameter(Parameter):
"""浮点参数"""
pass
# 使用示例
class RotationStrategy(StrategyBase):
n_days = IntParameter(10, 60, default=25, space='factor')
select_num = IntParameter(1, 5, default=3, space='signal')
stoploss = FloatParameter(0.01, 0.10, default=0.05, space='risk')
(3)接口版本控制
# Freqtrade的接口版本
class IStrategy:
INTERFACE_VERSION = 3 # 当前版本
# 不同版本的参数命名不同
# v2: buy, sell
# v3: enter_long, exit_long
借鉴方案:
class StrategyBase:
"""策略基类 + 版本控制"""
INTERFACE_VERSION = 1
@classmethod
def validate_version(cls, config: dict) -> bool:
"""验证配置与版本匹配"""
if config.get('version', 1) != cls.INTERFACE_VERSION:
raise ValueError(f"Strategy version mismatch")
return True
五、融合设计方案
5.1 最佳实践融合
融合Freqtrade优势 + 我们的模块化设计:
class QuantStrategy(StrategyBase):
"""量化策略基类 - 融合设计"""
INTERFACE_VERSION = 1
# ===== 类属性(配置可覆盖) =====
timeframe = '1d'
select_num = 3
stoploss = -0.05
# ===== 可优化参数(装饰器) =====
n_days = IntParameter(10, 60, default=25, space='factor')
# ===== 因子模块(模块化) =====
def init_factors(self) -> FactorCombiner:
"""初始化因子组合"""
factors = [
FactorRegistry.get('momentum', n_days=self.n_days.value),
FactorRegistry.get('volatility', period=20),
]
return FactorCombiner(factors, weights=[0.7, 0.3])
# ===== 核心方法(必须实现) =====
def populate_indicators(self, data: DataFrame, metadata: dict) -> DataFrame:
"""计算因子"""
factor_values = self._factors.compute(data)
data = pd.concat([data, factor_values], axis=1)
return data
def generate_signals(self, data: DataFrame, metadata: dict) -> DataFrame:
"""生成信号"""
return self._signal_gen.generate(data)
# ===== 回调钩子(可选实现) =====
def before_entry(self, code: str, price: float, **kwargs) -> bool:
"""入场前检查"""
# 溢价率过滤
if kwargs.get('premium', 0) > 0.10:
print(f"溢价过高,拒绝入场: {code}")
return False
# 崩盘检测
if self._detect_crash(code):
return False
return True
def dynamic_stoploss(self, position: Position, **kwargs) -> float:
"""动态止损"""
# 持仓时间越长,止损越紧
holding_days = position.holding_days
if holding_days > 10:
return -0.03 # 10天后收紧止损
elif holding_days > 5:
return -0.05
return -0.10
def custom_exit(self, position: Position, **kwargs) -> bool:
"""自定义出场"""
# 反转信号检测
if self._detect_reversal(position.code):
return True
return False
5.2 配置驱动融合
# config/strategies/rotation.yaml(融合设计)
strategy:
name: "rotation"
version: 1
# 参数(可覆盖类属性)
parameters:
n_days: 25 # 可优化参数
select_num: 3
stoploss: -0.05
# 因子配置
factors:
- name: "momentum"
weight: 0.7
params:
n_days: "${n_days}" # 引用参数
- name: "volatility"
weight: 0.3
# 信号配置
signal:
mode: "top_n"
select_num: "${select_num}"
group_by: "market"
# 回调配置
callbacks:
before_entry:
- type: "premium_filter"
threshold: 0.10
- type: "crash_filter"
dynamic_stoploss:
enabled: true
schedule:
- days: 5
stoploss: -0.05
- days: 10
stoploss: -0.03
# Hyperopt配置
hyperopt:
enabled: true
spaces: ["factor", "signal"]
loss: "SharpeLoss"
epochs: 100
六、总结与建议
6.1 Freqtrade设计精髓
| 设计精髓 | 说明 |
|---|---|
| 策略为中心 | 所有模块围绕IStrategy接口设计 |
| 回调钩子 | 生命周期完整控制,灵活度高 |
| 配置覆盖 | 类属性 → 配置文件 → 命令行 |
| 参数装饰器 | 声明式参数定义,自动优化 |
| 向量化计算 | DataFrame驱动,高性能 + 防前视偏差 |
| 接口版本 | 平滑升级,向后兼容 |
6.2 建议借鉴内容
| 优先级 | 借鉴内容 | 预估工作量 |
|---|---|---|
| P1 | 回调钩子机制(before_entry, custom_stoploss) |
1天 |
| P1 | 参数装饰器(IntParameter, FloatParameter) |
1天 |
| P2 | 接口版本控制 | 0.5天 |
| P2 | Hyperopt模块(Optuna集成) | 2天 |
| P3 | DataFrame向量化检查工具 | 1天 |
6.3 保持我们优势
| 我们的设计优势 | 说明 |
|---|---|
| 因子模块化 | FactorBase + FactorRegistry 更易于复用 |
| 信号模式 | SignalGenerator 抽象更清晰 |
| 风控组件库 | 组件化设计更易于组合 |
| 混合数据源 | Tushare + YFinance 跨市场支持 |
文档版本:V1.0 调研时间:2026-05-08 参考项目:Freqtrade (freqtrade/freqtrade)