Files
etf/docs/Freqtrade架构调研与对比分析.md
aszerW c54ba442ad docs: 添加策略框架调研与设计方案
包含4份核心文档:
- 轮动策略系统架构分析报告
- 量化策略通用框架抽象设计
- Freqtrade架构调研与对比分析
- ETF轮动策略通用化重构方案

调研结论:三种策略可抽象通用框架
设计决策:因子注册器风格 + 5个核心回调钩子 + TopN/Trend/Reversal信号生成器
2026-05-11 22:17:41 +08:00

639 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
**设计理念**:策略是核心,一切围绕策略接口设计
```python
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+交易所
```python
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**:数据缓存与统一访问
```python
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 配置驱动设计
**配置层级**
```json
{
// 系统配置
"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参数优化
**参数定义**(在策略中):
```python
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`: 保护策略优化
**损失函数**
```python
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
```python
# 错误使用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回调钩子机制
```python
# 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
```
**借鉴方案**
```python
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参数装饰器
```python
# 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')
```
**借鉴方案**
```python
# 我们的参数装饰器设计
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接口版本控制
```python
# Freqtrade的接口版本
class IStrategy:
INTERFACE_VERSION = 3 # 当前版本
# 不同版本的参数命名不同
# v2: buy, sell
# v3: enter_long, exit_long
```
**借鉴方案**
```python
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优势 + 我们的模块化设计**
```python
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 配置驱动融合
```yaml
# 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)*