AlphaFeed Blog

网格交易怎么用 Python 准备数据并回测?(含手续费才是真相)

策略与回测 · 代码验证于 2026-09-09

网格交易怎么用 Python 准备数据并回测?(含手续费才是真相)

一句话回答: 网格交易的数据准备就三件事——定区间、定格数、定格距。用 AlphaFeed 的日线算出历史价格区间与 ATR,用分位数切出网格线,再写一个几十行的回测:价格下穿格线买一格、上穿卖一格。跑完你会得到一个反直觉但真实的结论:在单边趋势里、把手续费算进去,网格经常是亏钱的。

为什么会有这个问题 / 传统做法的痛点

网格交易被很多人当成”躺赚神器”,但真要落地会卡在两处:

  1. 参数没依据:区间上下沿、格数、每格间距全靠拍脑袋,格距太小频繁交易被手续费吃掉,太大又几乎不触发。
  2. 忽略成本和行情类型:网格靠”低买高卖的震荡差价”赚钱,一旦行情单边上涨或下跌,网格要么满仓套牢、要么空仓踏空,加上每笔的佣金/印花税,很容易由盈转亏。

正确做法是用真实历史数据把参数定出来,并在回测里如实计入手续费,先搞清楚”这个标的、这段行情,网格到底行不行”。

分步骤解决(每步配可运行代码)

第 1 步:取日线,算区间、ATR 与分位网格

import numpy as np, pandas as pd
from alphafeed import AlphaFeed

def grid_prep(symbol, count=250, n_grids=10):
    af = AlphaFeed()                                   # 读取 ALPHAFEED_API_KEY
    df = af.klines.get(symbol, period="1d", count=count, adjust="forward", to_dataframe=True)
    df = df.sort_values("trade_date").reset_index(drop=True)

    # 区间:用 10%/90% 分位数,避免被极端值拉宽(比直接 min/max 更实用)
    lo_q, hi_q = df["close"].quantile(0.10), df["close"].quantile(0.90)
    levels = np.linspace(lo_q, hi_q, n_grids + 1)      # n_grids 格 → n_grids+1 条线

    # ATR(14) 作为"格距是否合理"的参考尺子
    tr = pd.concat([(df["high"] - df["low"]),
                    (df["high"] - df["close"].shift()).abs(),
                    (df["low"] - df["close"].shift()).abs()], axis=1).max(axis=1)
    atr = float(tr.rolling(14).mean().iloc[-1])
    return df, levels, atr

df, levels, atr = grid_prep("510300.SH", count=250, n_grids=10)   # 沪深300ETF
print("区间: %.3f ~ %.3f | ATR14=%.4f" % (df["low"].min(), df["high"].max(), atr))
print("网格线:", [round(x, 3) for x in levels])

格距怎么定? 让单格间距和 ATR 一个量级:格距 ≈ ATR 时,日常波动大致能触发一格。格距远小于 ATR → 过度交易;远大于 ATR → 很少成交。

第 2 步:写一个诚实计入手续费的网格回测

def backtest_grid(df, levels, shares=100, fee=0.0006):
    levels = sorted(levels)
    realized = 0.0; trades = 0; buys = []      # buys: 已买入未卖出的格价(FIFO)
    prev = df["close"].iloc[0]
    for c in df["close"].iloc[1:]:
        for lv in levels:
            if prev > lv >= c:                 # 收盘下穿格线 → 买一格
                buys.append(lv); trades += 1
            elif prev < lv <= c and buys:      # 收盘上穿格线 → 卖一格(卖最早买的)
                bp = buys.pop(0)
                realized += (lv - bp) * shares - (lv + bp) * shares * fee   # 计双边费用
                trades += 1
        prev = c
    return {"realized_pnl": round(realized, 2), "open_grids": len(buys), "trades": trades}

print(backtest_grid(df, levels))

真实跑通(510300.SH 近 250 日、10 格、每格 100 份、费率万 6):{'realized_pnl': -94.21, 'open_grids': 7, 'trades': 197}。注意这个负数:这段时间沪深 300 偏上行,价格穿过区间上沿后网格空仓踏空、还剩 7 格未平仓;而 197 次交易的手续费累计吃掉了大部分震荡差价——这正是网格在趋势行情里的典型失败样式。

参数与字段说明

参数含义建议
count取多少根日线定区间250≈一年;区间要覆盖你预期的震荡范围
n_grids网格数越多越密、交易越频繁、手续费越高
分位区间10%/90% 分位比 min/max 更抗极端值;也可用近端箱体
atr平均真实波幅让”格距 ≈ ATR”,平衡触发率与成本
fee单边费率A 股买入佣金、卖出佣金+印花税,见 FAQ

关键坑与注意事项

常见问题(FAQ)

Q:网格交易到底能不能赚钱? A:只在足够震荡、且格距/成本匹配时才有正期望。趋势市、高频小格距、高手续费都会让它亏钱。先用真实数据回测,别信”稳赚”话术。

Q:手续费该设多少? A:A 股买入通常万 2.5~万 3 佣金,卖出另有千 1 印花税;ETF 无印花税但有佣金。示例 fee=0.0006 是双边近似,严谨做法买卖分开、单独加印花税。

Q:可以在 ETF 上做网格吗? A:可以,很多人正是拿宽基/行业 ETF 做网格。把 symbol 换成对应 ETF 代码即可(用 af.quotes.get(universes="CN_ETF") 可取全 ETF 列表)。

Q:需要付费吗? A:单标的历史日线可用免费额度体验;ETF 全列表等 universes 池查询需 Starter 及以上,详见官网定价页。

小结

网格交易的数据准备可以很工程化:用 AlphaFeed 的日线定区间、用 ATR 定格距、用分位数切网格线,再用一段诚实计费的回测验证。真正的价值不是”证明网格能赚”,而是用真实数据快速证伪——先看清这个标的、这段行情、这套参数下网格是盈是亏,再决定要不要上。

相关链接:


免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。