简介:这份文档围绕网格交易中网格大小固定的痛点,面向已有一定Python基础的量化投资者与股票分析爱好者,讲解一种按市场波动自动调整网格的实战方法。内容以收盘价、最高价和最低价的标准差衡量波动程度,并借助KDJ指标中的RSV值对买入网格与卖出网格做差异化设置;文中还给出RSV为0.85时买入网格0.15元、卖出网格0.85元的计算示例,便于理解上升与下降趋势下如何减少过早满仓或空仓。包内仅含1个docx文件,压缩包约165KB,轻量便携,适合直接阅读、对照实践,也可作为用Python构建股票分析工具的参考。已有6377人学习,反映该主题在量化社区具有一定关注度。读者可从中获得动态网格的完整思路、参数联动逻辑与示例推演,用于优化网格策略的适应性并控制回撤风险。
1. 网格交易:为什么固定网格会死在趋势里
网格交易最反直觉的一点是:它的钱几乎都亏在单边行情上,而不是震荡。震荡市里这套策略近乎必胜,价格每来回一趟,账户就多一份差价。可一旦网格间距被写死成一个常数,策略就在两个方向上同时失去了判断力——大涨时几根阳线穿过所有网格,仓位被清空,后面的主升浪完整踏空;大跌时同样几根阴线把预留的子弹全部打光,浮亏叠着满仓亏损。
这份资源要解决的问题非常具体:让网格大小自己跟着波动走,剧烈时把胃口撑大,平静时把网眼收细,再用 RSV 把买入和卖出两个方向的间距拆开,让母仓能随价格趋势上下浮动。搞懂它,等于搞懂了网格交易从「固定刻度」跨到「自适应刻度」的那一步,也顺带看清了量化投资里一个反复出现的套路:把尺度参数和方向参数拆开来分别处理。
2. 标准差定宽:把网格从固定刻度换成波动率刻度
整套自适应方案拆成两步:第一步决定「网格有多宽」,第二步决定「这宽度在买入和卖出之间怎么分」。两步的输入完全独立,第一步只吃价格序列,第二步只吃 RSV。这种拆分不是随手为之,它对应着两个正交的问题——市场现在有多大的波动空间,以及价格现在站在这个空间的哪一侧。先看第一步。
2.1 网格间距的本质是价格尺度
固定网格的隐式假设是「价格尺度和时间无关」,这在一只股票上几乎从不成立。同一只票,横盘半年时日振幅可能只有 0.8%,财报出来那周能到 4%。用同一个 0.5 元去切这两种状态,前者可能一周都不成交一次,后者一天击穿三层网格。问题不在参数选得不好,在于尺子本身是错的。
用标准差定宽,本质是把网格从「绝对价格空间」映射到「波动率归一化空间」。设近 N 日价格的标准差为 σ,网格宽度取 G = k·σ,翻译过来就是:网格线之间的距离恒等于 k 个标准差。价格波动放大,σ 变大,G 跟着变大,网格自动稀疏;波动收敛,G 自动收细。这和布林带的思路是同一个统计内核——布林带用 MA ± m·σ 画通道,带宽随波动伸缩;网格用 k·σ 定间距,间距也随波动伸缩。区别只在于布林带描述的是「价格大概率落在哪」,网格描述的是「价格每走多远我出手一次」。
这个映射还有一个附带好处:σ 是绝对价格单位,股价从 10 元涨到 20 元,σ 通常也会跟着放大,网格宽度自动跟着股价走。省掉了「股价翻倍后要手动把网格乘 2」这种纯体力活,也避免了低价股网格过密、高价股网格过疏的经典错配。
2.2 为什么样本里要放最高价和最低价
常规的波动率计算只吃收盘价,因为收盘价噪声小、连续性好。但网格策略的成交判定靠的是日内价格穿越网格线,真正决定「今天会不会触发」的是最高价和最低价,收盘价反而没那么重要。如果只用收盘价算 σ,日内那些插针行情会被系统性地低估——某天低开 5% 又被拉回,收盘价看着纹丝不动,σ 认为一切正常,可网格已经被打穿了两层。
把最高价、最低价一起纳入样本,就是把日内极值信息喂给了波动尺度。常见的实现有两种:一种是把 close、high、low 三个序列在窗口内的值直接拼成一个样本集合求标准差;另一种是三个序列各自求滚动标准差再取平均。两者结果不同,直接拼接会让 σ 系统性偏大,因为三个序列高度相关,趋势项被重复计入三次,相当于把方向性涨跌误当成了波动。取平均更接近「真实的日内波动幅度」,代价是计算三次、慢一点。做日线级别的网格,这个开销完全可以接受。
提示:窗口长度一般取 20 个交易日,约等于一个自然月。窗口太短,σ 会被单日暴涨暴跌带偏;窗口太长,对波动率切换的反应会滞后半个月,等于网格永远慢市场半拍。
2.3 从 σ 到网格宽:系数、窗口和上下限
k 是唯一一个纯人工的系数,控制「网格相对波动率是密还是疏」。k 取 1.0 意味着网格间距等于一个标准差,在这个尺度上,价格一天之内穿越网格的概率不算高也不算低,属于中性设定。k 小于 1 网格变密、成交频繁、手续费压力大;k 大于 1 网格变疏、单次成交利润厚、但可能长期不成交。实战里这个值通常落在 0.5 到 1.5 之间,而且它对最终收益的影响比想象中大得多,后面第 6 章会专门讲这个敏感度问题。
光有 k·σ 还不够,必须加两个硬边界。下限来自交易成本:一次完整的买入加卖出,双边佣金加印花税加滑点,保守估计 0.15% 起步,网格宽度如果小于这个数的两倍,做一次亏一次,做得越勤亏得越快。所以网格宽度的下限通常设成现价的 1.5% 到 2%,具体取决于券商费率和标的流动性。上限来自成交频率:如果 σ 因为某段极端行情被撑得很大,网格宽到 20%,一年可能都不触发一次,策略就退化成了买入持有。上限一般设在现价的 8% 到 12%。
| 参数 | 含义 | 典型取值 | 调大的后果 |
|---|---|---|---|
| n | 波动率统计窗口 | 20 个交易日 | 反应变慢,对波动切换不敏感 |
| k | 网格宽度相对 σ 的倍数 | 0.5 ~ 1.5 | 网格变疏,成交频率下降 |
| min_pct | 网格宽度占现价的下限 | 1.5% ~ 2% | 单次利润变厚,但小额波动被浪费 |
| max_pct | 网格宽度占现价的上限 | 8% ~ 12% | 极端行情下仍能保持成交 |
2.4 代码实现与平滑处理
把上面的推导落到 pandas 上,核心就十几行。注意三个序列各自求标准差再平均,而不是拼接后求一次。
import numpy as np import pandas as pd def grid_width(close, high, low, n=20, k=1.0, min_pct=0.015, max_pct=0.10): """ 返回每日的网格宽度(绝对价格单位) close/high/low: 等长的 Series,按日期升序,且必须是前复权数据 n: 波动率统计窗口 k: 网格宽度相对标准差的倍数 min_pct: 网格宽度占现价的下限,用来兜住手续费和滑点 max_pct: 网格宽度占现价的上限,防止极端行情下长期不成交 """ # 三个序列分别求滚动标准差;直接拼接会重复计入趋势项,σ 会系统性偏大 s_close = close.rolling(n, min_periods=n).std(ddof=1) s_high = high.rolling(n, min_periods=n).std(ddof=1) s_low = low.rolling(n, min_periods=n).std(ddof=1) sigma = (s_close + s_high + s_low) / 3.0 raw = k * sigma # 核心尺度项:网格等于 k 个标准差 lo = close * min_pct # 成本地板 hi = close * max_pct # 频率天花板 return raw.clip(lower=lo, upper=hi)这段代码里有两个容易忽略的点。min_periods=n保证了前 n-1 天返回 NaN 而不是拿半截样本硬算,否则回测开头几天的网格宽度会莫名其妙地偏小。clip的上下界是乘在现价上的,而不是乘在 σ 上,因为成本的绝对值和股价挂钩,跟波动率没关系。
但直接拿这个结果去排网格线会出问题:σ 是逐日滚动的,每天窗口滑一格,G 就会小幅跳动。价格纹丝不动,网格线自己却挪了位置,可能刚好挪到现价附近,凭空触发一次假成交。所以 G 在进入交易逻辑之前通常还要过一层平滑:
def smooth_width(width, alpha=0.3, max_step=0.2): """对网格宽度做限幅 + 指数平滑,避免网格线自己跳到价格上""" out = width.copy() prev = width.iloc[0] for i in range(1, len(width)): target = width.iloc[i] # 单日变动幅度限幅:最多上下浮动 20% target = np.clip(target, prev * (1 - max_step), prev * (1 + max_step)) # 指数平滑,alpha 越小越迟钝 prev = alpha * target + (1 - alpha) * prev out.iloc[i] = prev return outmax_step限幅解决的是「跳变」,alpha平滑解决的是「抖动」。两个参数都在拿响应速度换稳定性,取值太激进等于没做自适应,取值太保守又追不上波动率切换。经验上 alpha 取 0.2 到 0.3、max_step 取 0.2 是个不容易翻车的起点。
3. RSV 拆格:买卖间距不对称,母仓怎么跟着价格漂
第一步算出的 G 只是「一格有多宽」,如果买卖两侧都用这个宽度,得到的就是标准的对称网格——涨一格卖一份,跌一格买一份,仓位围绕母仓上下摆动,长期均值回归。这份资源真正的巧思在第二步:把总宽度 2G 在买入侧和卖出侧重新分配,比例由 RSV 决定。分配完之后,策略的行为就不再对称了。
3.1 RSV 的几何含义:收盘价在区间中的位置
RSV 是 KDJ 指标计算链条上的第一个量,定义很朴素:RSV =(收盘价 − N 日最低价)/(N 日最高价 − N 日最低价)。它度量的是「今天的收盘价站在最近 N 天价格区间的百分之几位置」,取值天然落在 0 到 1 之间。N 在 KDJ 里的默认值是 9,原文没写窗口,但这个量的分母就是区间高度,窗口必须和前后的最高最低价对齐。
这个量的几何意义比它的名字重要得多。RSV 趋近 1,说明收盘价贴着区间上沿,价格在过去九天里一直在往上顶;RSV 趋近 0,说明收盘价贴着下沿,价格在往下砸。它不是一个预测指标,而是一个位置指标——描述当前价格在近期波动区间的相对高度。用它来分配买卖网格,等于承认了一个事实:网格宽度应该随价格在区间中的位置而变化,而不是随时间的推移而变化。
和第一步的标准差对比一下会更清楚。σ 描述的是「区间有多高」,RSV 描述的是「价格站在区间多高的位置」。前者是尺度,后者是位置,两者恰好构成一个完整的坐标描述。
3.2 买卖网格的守恒分配:2G 怎么切
原文给的分配规则是:设第一步算出的网格宽度为 G,则买入网格与卖出网格之和恒等于 2G。RSV 为 r 时:
买入网格 = 2G × (1 − r) 卖出网格 = 2G × r拿原文的例子验算:G = 0.5 元,2G = 1 元,r = 0.85,则买入网格 = 1 × 0.15 = 0.15 元,卖出网格 = 1 × 0.85 = 0.85 元。此时股价每跌 0.15 元买一份,每涨 0.85 元才卖一份。
这个 2G 的系数不是随手定的,它保证了一个重要性质:当 r = 0.5 时,买入网格 = 卖出网格 = G,策略精确退化为对称网格。也就是说,自适应机制在 RSV 中性时是「透明」的,不会凭空改变策略的基本行为。r 偏离 0.5 越多,不对称越明显,但两侧之和始终锁死在 2G,总的波动尺度完全由第一步的 σ 控制。这是两个模块正交的关键——第二步只搬运宽度,不创造宽度。
| RSV 值 | 市场位置 | 买入网格 | 卖出网格 | 策略倾向 |
|---|---|---|---|---|
| 0.85 | 贴近区间上沿 | 0.15 元 | 0.85 元 | 密集买入,稀疏卖出 |
| 0.50 | 区间中部 | 0.50 元 | 0.50 元 | 对称网格 |
| 0.15 | 贴近区间下沿 | 0.85 元 | 0.15 元 | 稀疏买入,密集卖出 |
3.3 方向权重的实际效果:母仓随价格漂移
单纯看公式容易觉得这只是「参数调了一下」,但把它放到趋势行情里跑一遍,效果就出来了。
上涨趋势中,收盘价持续贴着区间上沿,RSV 长期在 0.8 以上。买入网格被压到 0.15,意味着价格从当前位置回撤 0.15 元就补一份仓;卖出网格被撑到 0.85,意味着要涨 0.85 元才减一份。结果就是:小幅回撤就加仓,大涨才减仓,净持仓在上涨过程中不降反升。这正好解掉了固定网格最疼的那个场景——价格从 11 块一路涨到 15 块,固定网格在 11.5、12、12.5 一路卖光,后面 2.5 块的涨幅一股没吃到。自适应网格在这个过程里卖得极慢,母仓被价格「抬」着往上走。
下跌趋势中一切反过来。RSV 长期在 0.2 以下,买入网格被撑到 0.85,卖出网格压到 0.15。价格跌 0.85 元才买一份,反弹 0.15 元就卖一份,净持仓持续下降。这解掉的是另一个场景——从 11 块跌到 7 块,固定网格在 10.5、10、9.5 一路买满,抄底抄在半山腰,剩下 3 块钱的跌幅只能干看着。自适应网格在下跌初期就开始减仓,买入节奏被拉得很慢,满仓点被大幅推后。
原文把这叫「母仓在一定程度上随价格上下浮动,避免爆仓和空仓」,更技术化的说法是:RSV 在这里充当了一个动量信号,用趋势跟踪的逻辑去调制一个均值回归策略。两种假设本来互斥,但作用在不同尺度上就不冲突了——网格负责在日内到周级别的波动里薅差价,RSV 负责在月级别的趋势里调整仓位中枢。
3.4 代码实现与参数含义
把两步接起来,计算每日买卖网格的函数如下。注意 RSV 必须做截断,原因下一章会详细讲。
def split_grids(total_width, rsv, rsv_floor=0.2, rsv_cap=0.8): """ 把总宽度 2G 分配到买入侧和卖出侧 total_width: 第一步算出的网格宽度 G rsv: (close - low_n) / (high_n - low_n),窗口默认 9 rsv_floor/cap: 对 RSV 做截断,防止趋势末端把某一侧压成 0 """ r = np.clip(rsv, rsv_floor, rsv_cap) buy_grid = 2 * total_width * (1 - r) # 价格每跌这么多买一份 sell_grid = 2 * total_width * r # 价格每涨这么多卖一份 return buy_grid, sell_grid def grids_today(df, i, k=1.0, n=20, rsv_n=9): """给定第 i 行,返回当日的买入网格和卖出网格""" close, high, low = df["close"], df["high"], df["low"] g = grid_width(close, high, low, n=n, k=k).iloc[i] hh = high.iloc[i - rsv_n + 1:i + 1].max() ll = low.iloc[i - rsv_n + 1:i + 1].min() # 区间高度为 0 时(连续一字板)给中性值,避免除零 rsv = (close.iloc[i] - ll) / (hh - ll) if hh > ll else 0.5 return split_grids(g, rsv)三个参数值得单独说。rsv_n取 9 是 KDJ 的惯例,窗口越短 RSV 越敏感、越容易在 0 和 1 之间来回跳,窗口越长越平滑、但反应越慢。rsv_floor和rsv_cap是这套实现里最容易被忽略的防御措施,把 RSV 限制在 0.2 到 0.8,等价于给买卖网格各设一个 0.4G 的最小值,防止某一侧被压成零宽度——零宽度的网格意味着「任何微小波动都触发」,那基本等于失控。
4. 锚点与记账:自适应网格真正容易翻车的地方
公式看起来很干净,但从公式到能跑的代码之间隔着一层东西:网格线到底画在哪里。固定网格里这个问题不存在,母仓成本 11 块、网格 0.5 元,网格线就是 10.5、11.5、10、12 一路排下去,写死即可。自适应网格里 G 每天都变,如果还按「母仓成本 ± 固定倍数」来排线,第二天宽度一变,所有网格线全部重排,可能瞬间全部跳到现价下方或上方,触发一堆莫名其妙的成交。
4.1 锚点:自适应网格比固定网格多出来的一个状态量
标准解法是引入一个滚动锚点:网格线不围绕成本价排,而是围绕「上一次成交价」排。买入线 = 锚点 − buy_grid,卖出线 = 锚点 + sell_grid,每次成交后把锚点更新成这次成交价。这样网格线永远贴着价格走,宽度变化只影响「下一格有多远」,不影响「当前站在哪」。
这个改动看起来小,实际改变了策略的性质。固定网格是路径无关的——不管价格怎么走,网格线位置不变。滚动锚点的网格是路径相关的——锚点记录了策略上一次出手的位置,成交历史会影响后续所有网格线的位置。回测时如果每天重算网格却忘了维护锚点,得到的收益曲线会和实盘完全对不上,这是这套策略最容易出问题的地方之一。
注意:锚点必须用「成交价」更新,不能用「当日收盘价」更新。用收盘价更新会引入前视偏差,回测里看起来能精准踩点,实盘时根本复现不出来。
4.2 成交判定与价格假设
判定逻辑本身不难:日内最高价 ≥ 卖出线就卖出,日内最低价 ≤ 买入线就买入。难点在成交价怎么取。
如果直接假设「卖在卖出线、买在买入线」,回测会系统性高估收益。原因很简单:价格能穿越网格线,通常意味着当天有动能,跳空开盘就是典型情况。某天直接高开 4%,卖出线在 +1% 的位置,实际成交价是开盘价而不是卖出线,你并没有占到那 3% 的便宜。保守的取法是:
# 卖出:跳空高开时按开盘价成交,不占便宜 sell_px = max(sell_line, open_price) # 买入:跳空低开时按开盘价成交,也不占便宜 buy_px = min(buy_line, open_price)另一个简化是「一天最多成交一次」。真实情况里价格可能在一天内来回穿越多条网格线,但日线数据无法还原顺序,硬要模拟只能上分钟数据。日线级别做网格,一天一单是行业里普遍接受的近似,代价是回测的成交次数会略少于实盘,属于偏保守的误差方向,可以接受。
4.3 状态机实现
把锚点、持仓、现金、成交判定打包成一个状态机,核心循环大概是这样:
from dataclasses import dataclass @dataclass class GridState: anchor: float # 上一次成交价,所有网格线以它为基准 shares: int # 当前持仓股数 cash: float # 可用现金 fee: float = 0.001 # 单边费率,含佣金与印花税的粗估 def try_fill(state, open_, high, low, buy_grid, sell_grid, lot_cash): """ lot_cash: 每一格投入的金额,决定每次买卖的股数 返回 'buy' / 'sell' / 'hold' """ sell_line = state.anchor + sell_grid if high >= sell_line: px = max(sell_line, open_) # 保守成交价 shares = int(lot_cash / px) // 100 * 100 # 向下取整到手 shares = min(shares, state.shares) # 不允许卖穿 if shares > 0: state.cash += shares * px * (1 - state.fee) state.shares -= shares state.anchor = px # 锚点跟随成交价上移 return "sell" buy_line = state.anchor - buy_grid if low <= buy_line: px = min(buy_line, open_) shares = int(lot_cash / px) // 100 * 100 cost = shares * px * (1 + state.fee) if shares > 0 and cost <= state.cash: # 现金不足则放弃这一格 state.cash -= cost state.shares += shares state.anchor = px # 锚点跟随成交价下移 return "buy" return "hold"// 100 * 100是 A 股一手 100 股的约束,非整手的买单在实盘里根本发不出去,回测里不处理会导致收益虚高。min(shares, state.shares)防止持仓算成负数,cost <= state.cash防止现金透支——这两个检查看起来是废话,但在参数调得比较激进时(网格很密、每格金额很大),它们是真的会触发的。
lot_cash的确定方式决定了资金曲线。常见做法是:总资金先划出一部分作为母仓一次性建仓,剩下的现金除以预估的最大加仓格数,得到每格金额。这个「最大加仓格数」本身又依赖网格宽度——网格越宽,同样跌幅下能触发的次数越少,所以每格可以给得更大。这形成了一个闭环,也是为什么自适应网格在下跌行情里更抗揍:网格自动变宽 → 同样跌幅触发次数减少 → 现金消耗速度下降。
5. 避坑排查:自适应网格上线后最常见的四类翻车
纸面推演再漂亮,跑起来该遇到的问题一个都不会少。下面四条是按「症状最明显、排查最费时」排序的实战记录,每条都按现象、原因、解决三段来写。
5.1 网格宽度日间抖动,假信号把手续费吃光
现象:回测里成交次数是预期的三到五倍,胜率看着还行,但扣掉手续费后收益接近零甚至转负。逐笔看成交记录,很多笔的价格几乎一样,隔一两天就买一次卖一次。
原因:σ 是滚动计算的,每天窗口滑一格,G 就跳一下,网格线跟着跳。价格实际没怎么动,网格线自己挪到了价格附近,于是触发了成交。窗口越短、k 越小,这个抖动越剧烈。
解决:给 G 加平滑和限幅,也就是 2.4 节那个smooth_width。另外把锚点更新严格绑在成交上——只要没成交,锚点不动,网格线就围绕同一个基准,宽度小幅度变化不至于立刻产生穿越。这两条一起加,假信号通常能压掉七八成。
5.2 RSV 饱和,趋势末端单边失控
现象:在一段单边上涨里,买入异常密集,现金很快耗尽;或者在一段单边下跌里,卖出异常密集,很早就空仓。回撤曲线在趋势反转的那一刻突然变陡。
原因:RSV 的取值是 [0, 1],趋势末端会长时间贴边。RSV 逼近 1 时,买入网格 = 2G × (1 − r) 趋近于 0,意味着任何微小回撤都触发买入;RSV 逼近 0 时反过来,卖出网格趋近 0。原文的公式本身没有对极端值做保护。
解决:对 RSV 截断,限制在 0.2 到 0.8 之间,等价于给买卖网格各设 0.4G 的下限。这个改动的副作用是趋势末端的仓位漂移变慢,但换来的是不会在一天之内把子弹打光或者把仓位清空。截断区间越窄越保守,0.2/0.8 是个平衡点,如果要更保守可以收窄到 0.3/0.7。
5.3 除权与停牌造成的价格断层
现象:某一天突然触发大量成交,之后所有网格线位置都不对了,回测收益出现无法解释的跳变。
原因:用了未复权数据。送股、分红当天股价会出现一个非市场因素的跳空,跌 30% 那种。σ 会把这个跳空当成剧烈波动,G 瞬间撑到上限;锚点如果恰好在那天更新,后面所有网格线都偏了。停牌复牌同理,停牌一个月出来补跌,效果一样。
解决:全部改用前复权数据,这是硬性前提,不是可选项。另外加一道保险:单日涨跌幅超过阈值(比如 11%,超过常规涨跌停幅度)时,判定为数据异常或特殊事件,当天不交易并重置锚点为当日收盘价。停牌期间的行数据要么剔除、要么用前值填充后再标记为不可交易日。
5.4 回测成交假设过于乐观,实盘对不上
现象:回测年化两位数,实盘跑三个月差一大截,成交价普遍比回测差。
原因:回测里默认「价格触及网格线就按网格线成交」,实盘里要么没挂单、要么挂单排不上队、要么跳空直接开在别处。另外手续费按万分之几算,实际还有印花税、过户费、滑点,双边加起来常常是回测假设的两到三倍。
解决:成交价用前面说的max/min(网格线, 开盘价)保守取法,费率按双边 0.15% 以上估,再加 0.05% 到 0.1% 的滑点。做完这两件事,回测收益通常会掉三成左右,但剩下的那部分才是能落地的。判断一套参数能不能用,看的是扣完成本之后的曲线,不是扣之前的。
6. 设计取舍:尺度与方向解耦之后,还剩什么没解决
这套方案最值得学的地方不是那两个公式,而是它的分解方式:σ 管尺度、RSV 管方向,两者相乘之前互不干扰,中性点精确退化为对称网格。这种正交拆解让每个参数都有独立的物理含义,调参时知道自己在调什么——调 k 是调「网格稀不稀」,调 rsv_n 是调「仓位漂移快不快」,两者不会互相污染。
代价也很实在。RSV 用的是 9 日窗口,σ 用的是 20 日窗口,两个时间尺度不一致,会出现「尺度慢、方向快」的错配:波动率还没反应过来,RSV 已经在 0 和 1 之间跳了好几轮,导致网格宽度还是老样子,买卖比例却大幅摆动。这不是 bug,是两套统计量各自的最优窗口不同带来的固有摩擦。
更根本的问题在于,这整套机制是一个精心调过的启发式,不是从什么最优控制理论推出来的。k、n、rsv_n、截断区间、平滑系数,五个参数全是人工设定,参数空间不小,过拟合的风险实打实存在。验证的时候我一般会做两件事:
| 验证项 | 做法 | 看什么 |
|---|---|---|
| 参数敏感性 | k 从 0.5 扫到 1.5,步长 0.1,其余固定 | 收益/回撤曲面是否平滑,有没有孤立的尖峰 |
| 同区间对照 | 同样资金、同样母仓比例,跑一版固定网格 | 超额收益是否集中在趋势行情段 |
第一项看的是稳定性。如果 k 取 0.9 时年化 15%、取 1.0 时掉到 3%,说明这个参数在拟合噪声,实盘大概率落在 3% 那一侧。第二项看的是归因。自适应网格相对固定网格的超额,应该主要出现在单边行情里;如果震荡市里也大幅跑赢,那多半是回测成交假设出了问题,不是策略真的更聪明。
还有一个原文没展开、但绕不开的取舍:这套方法只解决了「极端行情下过早满仓或空仓」,资金利用率低的问题原封不动。母仓占掉四成、剩余现金还要留足加仓空间,实际能动的钱始终只有一半左右。想提高利用率就得加杠杆或者压缩母仓比例,而这两条路都会放大回撤,和自适应机制降低回撤的初衷直接冲突。
从那以后,我每次给网格策略调参,都会先把固定网格的对照组跑一遍,再决定要不要开自适应——如果自适应相对固定网格的超额收益在样本外消失,那这个「自适应」就只是把噪声包装成了参数。希望这套拆解能帮到你,尤其是那句「先跑对照组」,它比任何一组参数都值钱。
本文还有配套的精品资源,点击获取