DEV Community

Henry Lin
Henry Lin

Posted on

第 28 课:Freqtrade高频交易与网格策略

第 28 课:高频交易与网格策略

⏱ 课时:2 小时
🎯 学习目标:学习特殊策略类型——网格交易和高频交易的原理与实现


课程概述

之前我们学习的都是趋势跟随或均值回归策略。本课将介绍两种特殊类型的策略:

  • 网格交易:在震荡市场中获利
  • 高频交易:捕捉短期价格波动

警告
⚠️ 这两种策略都有较高的风险和复杂度
⚠️ 需要更深入的理解和更严格的风险控制
⚠️ 建议先在 Dry-run 中充分测试


第一部分:网格交易策略

1.1 网格交易原理

核心理念
在价格区间内设置多个买入和卖出网格,通过频繁的低买高卖获利。

基本概念

价格网格示例:
────────────────────────────────────
 $32,000  卖出网格 5  →  卖出
 $31,500  卖出网格 4  →  卖出
 $31,000  卖出网格 3  →  卖出
────────────────────────────────────
 $30,500  中心价格
────────────────────────────────────
 $30,000  买入网格 1  ←  买入
 $29,500  买入网格 2  ←  买入
 $29,000  买入网格 3  ←  买入
────────────────────────────────────

工作原理:
1. 在 $30,000 买入
2. 价格涨到 $31,000,卖出(盈利 $1,000)
3. 价格跌回 $30,000,再次买入
4. 重复循环
Enter fullscreen mode Exit fullscreen mode

网格交易的优势和劣势

✅ 优势:
1. 适合震荡市场
   - 价格来回波动就能盈利
   - 不依赖趋势判断

2. 操作简单
   - 规则明确
   - 易于自动化

3. 频繁获利
   - 每次小幅波动都能捕捉
   - 心理满足感强

4. 无需预测方向
   - 不需要判断涨跌
   - 只要有波动就行

❌ 劣势:
1. 单边市场风险
   - 持续上涨:卖飞后踏空
   - 持续下跌:接飞刀,越跌越买

2. 资金要求高
   - 需要分多次买入
   - 预留足够资金

3. 手续费侵蚀
   - 交易频繁
   - 手续费累计高

4. 资金利用率
   - 震荡不强时,收益低
   - 资金被大量占用
Enter fullscreen mode Exit fullscreen mode

1.2 简单网格策略实现

创建 user_data/strategies/SimpleGridStrategy.py

from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta

class SimpleGridStrategy(IStrategy):
    """
    简单网格交易策略
    在固定价格网格上低买高卖
    """

    INTERFACE_VERSION = 3

    # 关闭 ROI(网格策略自己控制卖出)
    minimal_roi = {
        "0": 100  # 很高的值,实际上不会触发
    }

    # 关闭全局止损(网格策略有自己的止损逻辑)
    stoploss = -0.99

    timeframe = '5m'
    startup_candle_count: int = 50

    # === 网格参数 ===
    # 网格间距(百分比)
    grid_spacing = 0.01  # 1%

    # 网格数量
    num_grids = 5

    # 中心价格(动态计算,使用最近 N 根 K 线的均价)
    center_price_period = 100

    # 是否启用动态中心价格
    dynamic_center = True

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        计算中心价格
        """
        # 动态中心价格(使用 SMA)
        dataframe['center_price'] = ta.SMA(dataframe['close'], timeperiod=self.center_price_period)

        # 计算价格与中心价格的偏离
        dataframe['price_deviation'] = (
            (dataframe['close'] - dataframe['center_price']) /
            dataframe['center_price'] * 100
        )

        # 标记当前处于哪个网格
        dataframe['grid_level'] = (
            dataframe['price_deviation'] / self.grid_spacing
        ).round()

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        买入信号:价格低于中心价格的网格
        """
        dataframe.loc[
            (
                # 价格低于中心价格
                (dataframe['close'] < dataframe['center_price']) &

                # 至少偏离 1 个网格
                (dataframe['grid_level'] <= -1) &

                # 最多偏离 num_grids 个网格(避免过度下跌时买入)
                (dataframe['grid_level'] >= -self.num_grids) &

                (dataframe['volume'] > 0)
            ),
            'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        卖出信号:价格高于买入价格 1 个网格以上
        """
        dataframe.loc[
            (
                # 价格高于中心价格
                (dataframe['close'] > dataframe['center_price']) &

                # 至少偏离 1 个网格
                (dataframe['grid_level'] >= 1) &

                (dataframe['volume'] > 0)
            ),
            'exit_long'] = 1

        return dataframe

    def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime',
                    current_rate: float, current_profit: float, **kwargs) -> bool:
        """
        自定义卖出:达到 1 个网格间距就卖出
        """
        # 如果盈利达到网格间距,卖出
        if current_profit >= self.grid_spacing:
            return True

        return False
Enter fullscreen mode Exit fullscreen mode

1.3 高级网格策略

增强版本,包含动态网格和风险控制:

创建 user_data/strategies/AdvancedGridStrategy.py

from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta
import numpy as np

class AdvancedGridStrategy(IStrategy):
    """
    高级网格策略
    - 动态调整网格间距(根据波动率)
    - 趋势过滤(避免单边市场)
    - 资金管理(限制最大持仓)
    """

    INTERFACE_VERSION = 3

    minimal_roi = {"0": 100}
    stoploss = -0.15  # 设置一个安全止损(防止极端情况)

    timeframe = '5m'
    startup_candle_count: int = 200

    # 最大同时持仓网格数
    max_open_grids = 3

    # 基础网格间距
    base_grid_spacing = 0.01  # 1%

    # 网格范围(上下各几个网格)
    grid_range = 5

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        计算指标
        """
        # 1. 中心价格(EMA)
        dataframe['center_price'] = ta.EMA(dataframe['close'], timeperiod=100)

        # 2. ATR(用于动态调整网格间距)
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['atr_pct'] = (dataframe['atr'] / dataframe['close']) * 100

        # 3. ADX(判断趋势强度,避免单边市场)
        dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)

        # 4. 动态网格间距(基于 ATR)
        dataframe['grid_spacing'] = dataframe['atr_pct'].clip(
            lower=self.base_grid_spacing,  # 最小 1%
            upper=self.base_grid_spacing * 3  # 最大 3%
        )

        # 5. 价格偏离百分比
        dataframe['price_deviation_pct'] = (
            (dataframe['close'] - dataframe['center_price']) /
            dataframe['center_price'] * 100
        )

        # 6. 当前网格层级
        dataframe['grid_level'] = (
            dataframe['price_deviation_pct'] / dataframe['grid_spacing']
        ).round()

        # 7. 成交量
        dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean()

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        买入信号:
        1. 价格低于中心价格
        2. 无强趋势(ADX < 25,避免下跌趋势)
        3. 在网格范围内
        """
        dataframe.loc[
            (
                # 条件 1:价格低于中心价格
                (dataframe['close'] < dataframe['center_price']) &

                # 条件 2:趋势不要太强(避免单边市场)
                (dataframe['adx'] < 30) &

                # 条件 3:在买入网格范围内
                (dataframe['grid_level'] >= -self.grid_range) &
                (dataframe['grid_level'] <= -1) &

                # 条件 4:成交量正常
                (dataframe['volume'] > dataframe['volume_mean'] * 0.5) &

                (dataframe['volume'] > 0)
            ),
            'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        卖出信号:价格回到中心价格以上
        """
        dataframe.loc[
            (
                # 价格高于中心价格
                (dataframe['close'] > dataframe['center_price']) &

                # 至少 1 个网格
                (dataframe['grid_level'] >= 1) &

                (dataframe['volume'] > 0)
            ),
            'exit_long'] = 1

        return dataframe

    def custom_stake_amount(self, pair: str, current_time: 'datetime',
                            current_rate: float, proposed_stake: float,
                            min_stake: float, max_stake: float, **kwargs) -> float:
        """
        自定义仓位:根据网格层级调整
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

        if len(dataframe) == 0:
            return proposed_stake

        last_candle = dataframe.iloc[-1].squeeze()
        grid_level = last_candle['grid_level']

        # 越低的网格,仓位越大(但要控制总量)
        # 例如:
        # 网格 -1: 100% 仓位
        # 网格 -2: 120% 仓位
        # 网格 -3: 150% 仓位

        multiplier = 1.0 + (abs(grid_level) - 1) * 0.2
        multiplier = min(multiplier, 1.5)  # 最大 150%

        return proposed_stake * multiplier

    def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime',
                    current_rate: float, current_profit: float, **kwargs) -> bool:
        """
        自定义卖出:达到 1 个网格间距即卖出
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

        if len(dataframe) == 0:
            return False

        last_candle = dataframe.iloc[-1].squeeze()
        grid_spacing = last_candle['grid_spacing'] / 100  # 转为小数

        # 盈利超过当前网格间距,卖出
        if current_profit >= grid_spacing:
            return True

        return False
Enter fullscreen mode Exit fullscreen mode

1.4 网格策略测试

# 1. 下载数据
freqtrade download-data -c config.json --days 90 --timeframe 5m

# 2. 回测简单网格策略
freqtrade backtesting \
    -c config.json \
    --strategy SimpleGridStrategy \
    --timerange 20230101-20230331

# 3. 回测高级网格策略
freqtrade backtesting \
    -c config.json \
    --strategy AdvancedGridStrategy \
    --timerange 20230101-20230331
Enter fullscreen mode Exit fullscreen mode

注意事项

1. 手续费设置要准确
   - 网格策略交易频繁
   - 手续费影响很大
   - "fee": 0.001  # 0.1%

2. 选择合适的交易对
   - 流动性好(BTC/USDT, ETH/USDT)
   - 波动适中(不要太剧烈)
   - 避免长期单边趋势的币种

3. 资金管理
   - 预留足够资金应对下跌
   - 不要满仓
   - 建议用总资金的 50-70%
Enter fullscreen mode Exit fullscreen mode

第二部分:高频交易策略

2.1 高频交易原理

定义
高频交易(High-Frequency Trading, HFT)是利用极短时间内的价格波动获利的策略。

特点

时间尺度:
- 传统策略:持仓几小时到几天
- 高频策略:持仓几秒到几分钟

交易频率:
- 传统策略:每天 3-5 笔
- 高频策略:每天 50-500 笔

盈利模式:
- 传统策略:捕捉趋势,单笔盈利大
- 高频策略:捕捉微小波动,单笔盈利小但频繁
Enter fullscreen mode Exit fullscreen mode

高频交易的挑战

1. 技术要求
   ✗ 需要极低延迟
   ✗ 需要稳定的网络
   ✗ 需要高性能服务器
   ✗ Freqtrade 不是专业 HFT 系统

2. 成本问题
   ✗ 手续费累计高
   ✗ 滑点影响大
   ✗ API 限流

3. 策略难度
   ✗ 信号稳定性差
   ✗ 假信号多
   ✗ 难以回测验证

结论:
⚠️ Freqtrade 不适合真正的高频交易(毫秒级)
✅ 但可以做中高频交易(分钟级,每天 20-50 笔)
Enter fullscreen mode Exit fullscreen mode

2.2 中高频策略示例

创建 user_data/strategies/ScalpingStrategy.py

from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib

class ScalpingStrategy(IStrategy):
    """
    剥头皮策略(Scalping)
    捕捉 1 分钟图上的快速波动
    目标:单笔 0.3-1% 的小利润,快进快出
    """

    INTERFACE_VERSION = 3

    # 快速止盈
    minimal_roi = {
        "0": 0.01,   # 1% 立即止盈
        "5": 0.008,  # 5 分钟后 0.8%
        "10": 0.005, # 10 分钟后 0.5%
        "15": 0.003  # 15 分钟后 0.3%
    }

    # 紧止损
    stoploss = -0.015  # -1.5%

    # 使用 1 分钟图
    timeframe = '1m'
    startup_candle_count: int = 30

    # 订单类型(市价单,快速成交)
    order_types = {
        'entry': 'market',  # 市价买入
        'exit': 'market',   # 市价卖出
        'stoploss': 'market',
        'stoploss_on_exchange': True
    }

    # 不允许持仓超过 30 分钟(强制平仓)
    max_holding_minutes = 30

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        使用快速指标
        """
        # 极短期 EMA
        dataframe['ema_5'] = ta.EMA(dataframe, timeperiod=5)
        dataframe['ema_10'] = ta.EMA(dataframe, timeperiod=10)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=7)  # 更短周期

        # 布林带(窄周期)
        bollinger = qtpylib.bollinger_bands(dataframe['close'], window=10, stds=2)
        dataframe['bb_lower'] = bollinger['lower']
        dataframe['bb_middle'] = bollinger['mid']
        dataframe['bb_upper'] = bollinger['upper']

        # 成交量突增
        dataframe['volume_mean'] = dataframe['volume'].rolling(window=10).mean()
        dataframe['volume_surge'] = dataframe['volume'] / dataframe['volume_mean']

        # 价格动量
        dataframe['price_momentum'] = (
            (dataframe['close'] - dataframe['close'].shift(3)) /
            dataframe['close'].shift(3) * 100
        )

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        买入信号:快速反弹
        """
        dataframe.loc[
            (
                # 条件 1:快速 EMA 金叉
                (qtpylib.crossed_above(dataframe['ema_5'], dataframe['ema_10'])) &

                # 条件 2:RSI 从超卖恢复
                (dataframe['rsi'] > 30) &
                (dataframe['rsi'] < 60) &
                (dataframe['rsi'] > dataframe['rsi'].shift(1)) &  # RSI 上升

                # 条件 3:价格在布林带下半部(有上涨空间)
                (dataframe['close'] < dataframe['bb_middle']) &

                # 条件 4:成交量突增(确认动能)
                (dataframe['volume_surge'] > 1.5) &

                # 条件 5:短期动量向上
                (dataframe['price_momentum'] > 0) &

                (dataframe['volume'] > 0)
            ),
            'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        卖出信号:快速获利或反转
        """
        dataframe.loc[
            (
                (
                    # 条件 1:EMA 死叉
                    (qtpylib.crossed_below(dataframe['ema_5'], dataframe['ema_10'])) |

                    # 或条件 2:RSI 超买
                    (dataframe['rsi'] > 70) |

                    # 或条件 3:价格触及布林带上轨
                    (dataframe['close'] > dataframe['bb_upper'])
                ) &

                (dataframe['volume'] > 0)
            ),
            'exit_long'] = 1

        return dataframe

    def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime',
                    current_rate: float, current_profit: float, **kwargs) -> bool:
        """
        强制平仓:超过最大持仓时间
        """
        # 计算持仓时间(分钟)
        trade_duration = (current_time - trade.open_date_utc).total_seconds() / 60

        # 超过 30 分钟,无论盈亏都平仓
        if trade_duration > self.max_holding_minutes:
            return True

        # 快速止盈:0.5% 就走
        if current_profit >= 0.005:
            return True

        return False
Enter fullscreen mode Exit fullscreen mode

2.3 高频策略优化技巧

技巧 1:使用市价单

# 高频策略必须使用市价单
order_types = {
    'entry': 'market',
    'exit': 'market',
    'stoploss': 'market',
    'stoploss_on_exchange': True
}

# 原因:
# 1. 限价单可能不成交,错过机会
# 2. 高频追求速度,不是最优价格
# 3. 1 秒的延迟可能导致信号失效
Enter fullscreen mode Exit fullscreen mode

技巧 2:严格的持仓时间限制

def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime',
                current_rate: float, current_profit: float, **kwargs) -> bool:
    """
    强制时间止损
    """
    trade_duration = (current_time - trade.open_date_utc).total_seconds() / 60

    # 超过 30 分钟强制平仓
    if trade_duration > 30:
        return True

    return False
Enter fullscreen mode Exit fullscreen mode

技巧 3:快速止盈止损

# 止盈目标小
minimal_roi = {
    "0": 0.01,   # 1%
    "10": 0.005  # 10 分钟后 0.5%
}

# 止损紧
stoploss = -0.015  # -1.5%

# 盈亏比约 1:1.5(可接受,因为频率高)
Enter fullscreen mode Exit fullscreen mode

技巧 4:成交量过滤

# 必须有成交量突增
(dataframe['volume_surge'] > 1.5) &

# 原因:
# 高频需要流动性支持
# 成交量小会导致滑点大
Enter fullscreen mode Exit fullscreen mode

2.4 高频策略风险管理

风险 1:手续费侵蚀
- 问题:交易 100 次,手续费 0.1% × 2 × 100 = 20%
- 对策:
  ✓ 使用 VIP 等级降低手续费
  ✓ 使用交易所代币抵扣(如 BNB)
  ✓ 计算盈亏时扣除手续费
  ✓ 确保平均盈利 > 手续费

风险 2:滑点
- 问题:市价单成交价差于预期
- 对策:
  ✓ 只交易高流动性币种(BTC/ETH)
  ✓ 避免大额订单
  ✓ 避免市场剧烈波动时交易

风险 3:过度交易
- 问题:一天交易 200 次,多数是假信号
- 对策:
  ✓ 设置每日最大交易次数(如 50 次)
  ✓ 提高入场条件
  ✓ 增加成交量过滤

风险 4:系统故障
- 问题:网络延迟、API 限流
- 对策:
  ✓ 使用稳定的服务器
  ✓ 备用网络连接
  ✓ 监控 API 调用频率
  ✓ 设置最大持仓保护
Enter fullscreen mode Exit fullscreen mode

第三部分:策略对比和选择

3.1 三种策略对比

特性 趋势跟随 网格交易 高频交易
时间框架 5m-1h 5m-15m 1m-5m
持仓时间 数小时-数天 数小时 数分钟
交易频率 低(3-5/天) 中(10-20/天) 高(50+/天)
单笔盈利 2-5% 1-2% 0.3-1%
胜率 50-60% 60-70% 55-65%
适合市场 趋势市 震荡市 高波动
资金要求
技术要求
手续费影响
风险等级 中高

3.2 如何选择

选择趋势跟随,如果你:
✓ 是新手
✓ 每天只能监控几次
✓ 不想频繁交易
✓ 追求稳定增长
✓ 资金有限

选择网格交易,如果你:
✓ 有一定经验
✓ 市场处于震荡期
✓ 有足够资金分批买入
✓ 能接受资金占用
✓ 追求频繁小胜

选择高频交易,如果你:
✓ 有丰富经验
✓ 有稳定的技术环境
✓ 能接受高风险
✓ 手续费率很低
✓ 追求刺激和挑战

⚠️ 不推荐新手使用网格或高频策略
Enter fullscreen mode Exit fullscreen mode

3.3 组合策略

可以同时运行多个策略:

配置示例:

账户 1(70% 资金):
- 趋势跟随策略
- 5m 时间框架
- 稳健为主

账户 2(20% 资金):
- 网格策略
- 5m 时间框架
- 震荡市场使用

账户 3(10% 资金):
- 剥头皮策略
- 1m 时间框架
- 实验性质

优势:
✓ 分散风险
✓ 适应不同市场
✓ 平滑收益曲线
Enter fullscreen mode Exit fullscreen mode

📝 实践任务

任务 1:回测网格策略

  1. 复制 SimpleGridStrategy 到你的环境
  2. 回测 3 个月数据
  3. 对比不同网格间距(0.5%, 1%, 2%)的效果
  4. 记录:
    • 交易次数
    • 胜率
    • 手续费占比
    • 最大回撤

任务 2:优化网格参数

修改 AdvancedGridStrategy

  1. 调整 ADX 阈值(20, 25, 30)
  2. 调整网格范围(3, 5, 7)
  3. 测试不同的动态网格计算方法
  4. 找出最优组合

任务 3:高频策略实验(谨慎)

  1. 在 Dry-run 中测试 ScalpingStrategy
  2. 运行 3-7 天
  3. 统计:
    • 每日交易次数
    • 平均持仓时间
    • 平均单笔盈亏
    • 手续费总计
  4. 判断:是否适合实盘?

任务 4:手续费敏感度分析

对同一策略,测试不同手续费率的影响:

# 0.1% 手续费
"fee": 0.001

# 0.05% 手续费(VIP 优惠)
"fee": 0.0005

# 0.2% 手续费(最差情况)
"fee": 0.002
Enter fullscreen mode Exit fullscreen mode

对比回测结果,了解手续费的影响。


📌 核心要点

网格交易要点

1. 适合震荡市
   - ADX < 25
   - 价格在区间内波动
   - 避免单边趋势

2. 资金管理
   - 分批买入
   - 预留资金应对下跌
   - 不要满仓

3. 参数设置
   - 网格间距 1-2%
   - 网格数量 5-10 个
   - 动态调整更好

4. 风险控制
   - 设置最大持仓数
   - 设置止损保护
   - 监控单边趋势
Enter fullscreen mode Exit fullscreen mode

高频交易要点

1. 技术要求
   - 低延迟网络
   - 稳定服务器
   - 使用市价单

2. 成本控制
   - 降低手续费
   - 计算滑点
   - 只交易高流动性币种

3. 快进快出
   - 持仓时间 < 30 分钟
   - 止盈 0.5-1%
   - 止损 1-1.5%

4. 严格纪律
   - 设置交易次数上限
   - 避免过度交易
   - 及时止损
Enter fullscreen mode Exit fullscreen mode

不适合的情况

❌ 不要使用网格,如果:
- 市场处于强趋势
- 资金不足
- 无法接受资金占用

❌ 不要使用高频,如果:
- 是新手
- 网络不稳定
- 手续费率高(> 0.1%)
- 资金少(< $5000)
Enter fullscreen mode Exit fullscreen mode

🎯 总结

本课介绍了两种特殊策略:

  • 网格交易:适合震荡市场,通过频繁的低买高卖获利
  • 高频交易:捕捉短期波动,快进快出

重要提醒
⚠️ 这两种策略都比趋势跟随策略风险更高
⚠️ 需要更多的经验和更严格的风险控制
⚠️ 建议先掌握基础策略,再尝试这些高级策略
⚠️ 务必在 Dry-run 中充分测试

下节课我们将学习如何使用机器学习优化策略。


🎓 学习建议

  1. 循序渐进:先掌握趋势策略,再尝试网格和高频
  2. 充分测试:在 Dry-run 中测试至少 2 周
  3. 小资金起步:即使测试成功,也要小资金开始
  4. 严格风控:这两种策略需要更严格的风险控制
  5. 持续监控:需要更频繁地监控和调整

记住:简单有效 > 复杂华丽。不要为了追求高频而高频,适合自己的才是最好的。

Top comments (0)