首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >网格交易怎么用 Python 准备数据并回测?(含手续费才是真相)

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

原创
作者头像
用户7083614
发布2026-09-13 10:00:36
发布2026-09-13 10:00:36
210
举报

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

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

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

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

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

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

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

代码语言:python
复制
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 步:写一个诚实计入手续费的网格回测

代码语言:python
复制
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

关键坑与注意事项

  • 网格只适合震荡,不适合趋势:单边上涨会踏空(满仓卖光后价格继续涨),单边下跌会越买越套。回测负收益往往不是代码错,而是行情类型不匹配
  • 手续费是网格的头号杀手:格数越多、格距越小,交易越频繁,成本越高。上例 197 笔交易的费用就足以由盈转亏。务必把 fee(含印花税)算进去。
  • 成交假设偏乐观:代码假设在格线价精确成交。实盘用限价单可能不成交、市价单有滑点;ETF/个股流动性不同结果差异大。
  • 未平仓格是浮动盈亏open_grids 是回测结束时仍持有的格,其浮盈浮亏未计入 realized_pnl,评估时要一并考虑。
  • 复权口径:跨除权日务必 adjust="forward",否则除权跳空会被误当成穿越网格线。

常见问题(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 定格距、用分位数切网格线,再用一段诚实计费的回测验证。真正的价值不是"证明网格能赚",而是用真实数据快速证伪——先看清这个标的、这段行情、这套参数下网格是盈是亏,再决定要不要上。

参考

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 为什么会有这个问题 / 传统做法的痛点
  • 分步骤解决(每步配可运行代码)
    • 第 1 步:取日线,算区间、ATR 与分位网格
    • 第 2 步:写一个诚实计入手续费的网格回测
    • 参数与字段说明
  • 关键坑与注意事项
  • 常见问题(FAQ)
  • 小结
  • 参考
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档