简介:一份面向量化投资与Python股票分析人群的方法文档,聚焦网格交易中网格大小固定导致的过早满仓或空仓问题。核心方案以市场波动性为锚,利用收盘价、最高价、最低价的标准差动态调整网格间隔,并结合RSV值对买入网格与卖出网格差异化设置,使策略在上升趋势中偏向买入、下降趋势中偏向卖出,提升资金利用效率、降低回撤风险。文档完整拆解了标准差与RSV的计算逻辑,并以初始网格0.5元、RSV为0.85为例,演示买入网格设0.15元、卖出网格设0.85元的拆分方法,公式与算例相互对应,便于读者迁移到自建Python股票分析程序或魔灯软件中验证,可操作性较强。资源包共1个docx文件,约165KB,已有6362人学习,适合希望提升网格交易适应性的散户、量化策略爱好者及Python量化学习者阅读参考。
1. 网格交易不是躺赚:问题出在“网格大小”是个不变量
网格交易在量化投资里算是最容易上手的一类策略:设定一个固定网格大小,涨了卖、跌了买,靠波动来回刷利润。但固定网格的毛病也很明显——大涨时早早卖光,大跌时早早满仓,价格还在波动,手里的筹码和现金却先见底了。这份《一种自动调整网格交易策略中网格大小的方法》讲的就是怎么解决这个问题:用标准差感知行情波动烈度,用RSV值分配买入和卖出网格的比例,让网格大小跟着行情自动伸缩。文档配套了开源软件“魔灯”的完整Python源码,既能直接用来做股票分析,也是学Python股票分析和量化交易的好素材,适合会写点Python的投资者,以及想拿真实策略练手的开发者。
2. 用标准差给网格做“尺子”:布林线思路里藏着波动自适应的答案
2.1 为什么固定网格扛不住行情:波动率不是一个常量
先看一个反直觉的结论:同样一只股票,在窄幅横盘时每天振幅可能只有0.3元,在趋势加速时单日振幅能到1.2元。如果你把网格固定在0.5元,横盘期触发太少,吃不到利润;趋势期又触发太密,几笔就打完子弹。问题不在于网格策略本身,而在于你拿了一把没有刻度的尺子去量一直在变化的波动。
文档里给的第一条思路,是让网格大小跟波动率挂钩,而且明确说了要同时参考“收盘价、最高价和最低价的标准差”。为什么不是只算收盘价的标准差?因为收盘价只反映每个交易日的结算位置,盘中冲高或砸出来的长影线会直接把风险带出来,但收盘价序列里看不到。最高价和最低价的标准差可以把极值波动吃进去,三者取平均相当于对K线的“实体”和“影线”各打了一个权重。
具体到实现,我一般会这样分解:先分别算三组价格序列在同一个窗口期的标准差,然后取平均。这个平均值就是你当前行情波动烈度的大致刻度。布林线的原理可以参考一下——布林带宽度随着价格波动变大而张开、变小而收拢,网格大小本质上就是“布林带宽度的等比缩放值”。
2.2 网格大小怎么定:标准差倍数k才是真正的调节旋钮
拿到标准差σ之后,网格大小 = k × σ。这个k的取值决定了你的策略性格,参考范围如下:
| k值 | 网格相对波动幅度 | 适合行情 | 主要风险 |
|---|---|---|---|
| 1.0 | 网格 = 1倍σ | 窄幅震荡 | 手续费侵蚀、触发过于频繁 |
| 2.0 | 网格 = 2倍σ | 普通震荡 | 相对均衡,最常用 |
| 3.0 | 网格 = 3倍σ | 大波动行情 | 触发过少,资金利用率低 |
如果当前σ是0.25元,k取2.0,网格就是0.5元,这正好对应文档里那个“0.5元网格”的经典例子。反过来说,如果行情从平稳转剧烈,σ从0.25涨到0.5,同样k=2.0,网格就自动放大到1.0元。这样做的好处是:行情剧烈时单笔买卖覆盖的价差更大,不会因为密集成交把仓位打完;行情平稳时网格自动收窄,小波动也能被舔到。
实际调参时我建议先固定k,再去找合适的窗口期。窗口期太短σ会乱跳,太长又反应迟钝。先用20日(大约一个月交易日)起步,观察回测中网格尺寸的变化是否平滑,再决定要不要缩短或拉长。
2.3 代码落地:20行Python算出自适应网格
import numpy as np def calc_grid_with_std(high, low, close, window=20, k=2.0): # 分别计算收盘价、最高价、最低价在窗口期内的标准差 sigma_close = np.std(close[-window:], ddof=1) sigma_high = np.std(high[-window:], ddof=1) sigma_low = np.std(low[-window:], ddof=1) # 三者的平均值作为波动参考值 sigma = (sigma_close + sigma_high + sigma_low) / 3.0 # 网格大小 = 波动参考值 * 倍数k grid = k * sigma return grid, sigma # 示例调用:最近20个交易日的数据 # high/low/close 为三个 numpy 数组,按时间顺序排列 # grid, sigma = calc_grid_with_std(high, low, close, window=20, k=2.0)这段代码的主要逻辑是三段:先分别求三组价格的标准差,再取平均,最后乘以k。这里的ddof=1表示用样本标准差而不是总体标准差,量化计算里建议统一用样本标准差,否则窗口期改变时结果会有小幅漂移,不利于后续调参对比。window=20代表20个交易日,k=2.0对应布林线默认的两倍标准差口径。
有一点要特别提醒:这个计算出来的grid是“基础网格”,它是你后面买入网格和卖出网格的总盘子,不是直接用现价加减它来挂单。真正的买卖网格分配,要等下一步算出RSV之后再做,否则你只解决了“尺度”问题,没解决“方向”问题。
3. 买入卖出网格分开设:RSV值才是母仓调平的关键
3.1 RSV的本质:收盘价在最近高低区间里的位置
标准差解决了“网格该多大”的问题,但还没解决“往哪边倾斜”的问题。文档里用的工具是RSV,公式是:RSV =(收盘价 - 最低价)/(最高价 - 最低价)。这个公式在KDJ指标里出现过,它衡量的其实是当前收盘价处在近N日最高价和最低价区间里的相对位置。
RSV有一个很直观的规律:上涨趋势中,收盘价往往贴着最高价走,RSV接近1;下跌趋势中,收盘价贴近最低价,RSV接近0。这个数字不是用来预测涨跌的,而是用来描述“当前价格在区间里靠上还是靠下”。在网格策略里,这个位置信息恰好可以决定买卖的倾斜方向。
文档给的策略倾向是:上涨趋势中要“易买难卖”——买入网格变小,卖出网格变大;下跌趋势中反过来——“难买易卖”,买入网格变大,卖出网格变小。这里的“买入网格小”意味着价格稍微下跌一点就触发买入,成交更频繁;“卖出网格大”意味着要涨更多才卖,持仓能拿得更久。这样设计是为了让母仓在上涨时多留一段,在下跌时减少接飞刀的频率。
3.2 买卖网格的分配公式:为什么买入+卖出=2×基础网格
文档里有一段很关键的计算示例:基础网格是0.5元,买入和卖出网格之和应该为1元,也就是2×基础网格。当RSV=0.85时,买入网格=1×(1-0.85)=0.15元,卖出网格=1×0.85=0.85元。翻译成人话就是:股价跌0.15元就买一份,涨0.85元才卖一份。
这个“买入+卖出=2×基础网格”的设计我理解是为了保证对称性:无论网格怎么分配,买卖两个方向吃到的区间跨度总数不变,基础网格0.5元意味着一个完整网格周期要吃掉1元价差。RSV只是在1元总池子里做切割:
| 市场状态 | RSV取值 | 买入网格 | 卖出网格 | 实际效果 |
|---|---|---|---|---|
| 强上涨 | 0.85 | 0.15 | 0.85 | 跌一点就买,涨很多才卖 |
| 震荡 | 0.50 | 0.50 | 0.50 | 买卖对称 |
| 弱下跌 | 0.15 | 0.85 | 0.15 | 跌很多才买,涨一点就卖 |
注意一个容易被绕进去的点:RSV越低,买入网格越大,意味着你要等更大的跌幅才出手,而不是“越跌越买”。很多网格策略死在下跌行情里,就是因为在下跌初期买得太勤快,等到真正便宜的时候已经没有现金了。RSV这种方式本质上是给买入行为加了一个“距离阻尼”,趋势越弱,买入间隔拉得越开。
3.3 代码:把RSV写进网格计算,得到动态买卖触发价
def calc_rsv(high, low, close, window=9): # 取窗口期内的最高价和最低价 high_n = high[-window:].max() low_n = low[-window:].min() # 防止分母为0 if high_n == low_n: return 0.5 rsv = (close[-1] - low_n) / (high_n - low_n) # 限制在0到1之间,避免极端值 return min(1.0, max(0.0, rsv)) def calc_pair_grid(high, low, close, base_grid=0.5, window=9): rsv_val = calc_rsv(high, low, close, window) total_grid = 2 * base_grid buy_grid = total_grid * (1 - rsv_val) sell_grid = total_grid * rsv_val return buy_grid, sell_grid, rsv_val参数说明:window=9是KDJ指标的默认参数,对应9日高低点范围。如果标的波动节奏更快,可以缩到5;如果想过滤更多噪音,可以放到14。base_grid是上一步标准差计算的结果,你把第2章的grid传进来即可。返回的buy_grid是“下跌多少触发买入”,sell_grid是“上涨多少触发卖出”,不是绝对价格。
实际挂单时,触发价 = 当前参考价 - buy_grid(买单)和当前参考价 + sell_grid(卖单)。这里有个小技巧:把RSV做一次平滑再代入公式,比如取RSV的3日移动平均,能减少突变带来的网格跳变。文档里直接用的是原始RSV,但我实盘时会多这一步,因为原始RSV在震荡行情里会像心电图一样乱跳,直接用它算网格容易导致买卖网格大小频繁翻转。
4. 固定网格为什么迟早出事:算清空仓和满仓那笔账
4.1 上涨行情里,固定网格等于主动提前下车
拿文档里那只长期在7到15元区间震荡的银行股来说,假设基准价11元,网格0.5元,向上每涨0.5元卖出一份母仓。从11元涨到14元,触发点依次是11.5、12、12.5、13、13.5、14,一共6次卖出。如果你的母仓分成6份,到这里就直接卖光了;分10份的话也只剩下40%的仓位,后面涨到15、16跟你关系不大。
固定网格的数学特点就是:网格越小,卖光的价位越近。很多人觉得网格小赚钱快,实际上小网格在趋势行情里是给策略“提前设了一个止盈上限”。而自适应网格在RSV=0.85的场景下,卖出网格是0.85元,从11元到14元的触发点是11.85、12.7、13.55,只有3次,仓位能留到14.4以上才卖完。同样是看对了一波上涨,动态网格赚到的波段明显更长。
4.2 下跌行情里,固定网格等于加速打光子弹
下跌方向同理。固定网格0.5元,从11元往下跌,触发点依次是10.5、10、9.5、9、8.5、8,跌到8元时6份子弹全部打出,现金归零。如果股价继续跌到7元,你只能看着持仓浮亏干瞪眼。更麻烦的是,由于买得密,平均持仓成本下降得慢,稍微反弹一点根本回不了本。
用RSV分配逻辑后,下跌趋势RSV接近0.15,买入网格变成0.85元,触发点变成10.15、9.3、8.45、7.6,跌到8元只买了3次,现金消耗比固定网格少一半。这多出来的现金就是你的容错空间,也是后续行情真的见底时还能出手的底气。
4.3 代码模拟:同一段行情下两种网格的触发次数对比
def count_triggers(start_price, end_price, grid, direction='up'): # 计算从start_price到end_price这段行情里的触发次数 levels = [] step = grid if direction == 'up' else -grid price = start_price + step while (price <= end_price) if direction == 'up' else (price >= end_price): levels.append(price) price += step return len(levels) # 上涨趋势从11涨到14 print("固定网格0.5触发次数:", count_triggers(11, 14, 0.5, 'up')) # 输出 6 print("动态网格0.85触发次数:", count_triggers(11, 14, 0.85, 'up')) # 输出 3 # 下跌趋势从11跌到8 print("固定网格0.5触发次数:", count_triggers(11, 8, 0.5, 'down')) # 输出 6 print("动态网格0.85触发次数:", count_triggers(11, 8, 0.85, 'down')) # 输出 3这个模拟只统计触发次数,不含持仓份额,但足以说明问题:同样的涨跌幅度,自适应网格把买卖点的间距拉远了,减少了无效换手,也拉长了持仓周期。真正实盘时,你还需要把每笔的买卖数量考虑进去,因为买入网格和卖出网格大小不同,单笔成交份额可能也要跟着调整。文档里没有细说份额调整,我一般会按“固定金额买固定份数”来处理,网格越大时单笔金额也适当放大,这样能让每笔利润保持在相近水平。
5. 网格实盘避坑手记:五个让策略翻车的细节
5.1 标准差窗口设得太短:网格忽大忽小
现象:把window设成5天后,网格大小在0.15元到0.8元之间剧烈跳动,成交记录七零八落,今天买一档,明天又隔三档才买。
原因:短窗口里一根大阳线或大阴线会直接改变σ,而σ是整个网格计算的分母,它对单日异常波动太敏感。
解决:先用20日窗口稳住基本盘,做短线也至少设到10日以上。如果还是觉得跳,可以再对σ做一次EMA平滑,比如取当日σ和昨日σ的加权平均,能滤掉大部分毛刺。
5.2 RSV在震荡期反复误报:买卖网格频繁翻转
现象:价格横在一个窄箱体里,RSV在0.4到0.6之间来回晃动,导致买入网格和卖出网格频繁切换,策略表现得像精神分裂。
原因:震荡行情里收盘价在区间中间上下随机摆动,RSV天然就是高噪声的,原始值直接进入公式会产生大量无效信号。
解决:给RSV加一个中性带。我一般会把0.45到0.55之间的RSV强制当作0.5处理,此时买卖网格相等,策略退化为普通对称网格。只有RSV走出这个区间才启动差异化分配,这样能减少至少一半的无效切换。
5.3 用现价算触发价:行情一跳就全部错位
现象:盘中用最新价加网格差值算出触发价去挂单,结果价格快速跳动时,你刚算出来的价格已经成交或远离盘口,挂单撤了又挂,最后什么都没买到。
原因:现价是时刻变化的,用它做基准相当于在流沙上盖房子。网格交易应该用固定参考价(一个已经确认的价格点)来计算触发档位。
解决:每个交易时段开始时,用基准价(比如昨日收盘价或上一笔成交价)一次性算出上下几档的限价单,价格触发后,再以触发价为新基准重新计算下一档。这样网格的触发价是静态的,跟盘口跳动解耦。
5.4 手续费和滑点吃掉小网格利润
现象:设置了0.15元的买入网格,单笔买入金额不高,看起来波动一来就有利润。结果月底一算账,手续费加滑点占了毛利润的一半还多。
原因:网格太小导致单笔毛利变薄,而交易成本是固定的,网格越小,成本占比越高。A股还有卖出印花税,这笔钱不能忽略。
解决:设一个网格下限,满足“单笔毛利 > 双边手续费 + 预估滑点 + 印花税”。比如双边手续费和滑点合计约0.15%时,对10元股票来说最小有效网格至少要到0.03元以上,实际建议再放大两三倍才值得做。
5.5 有RSV过滤照样过早满仓:缺少硬止损红线
现象:遇到单边阴跌,RSV一路低于0.2,买入网格被拉得很大,但跌到足够深的位置时还是不可避免地在成交。等跌到你的心理底部时,现金已经打得差不多了。
原因:RSV只是调节买入节奏,不是阻断买入信号。它在趋势行情里只能拖慢买入速度,没法彻底避免错误买入。
解决:给策略加一个硬红线。比如持仓浮亏超过某个比例时暂停买入,或者RSV连续5个交易日低于0.1时强制进入防守状态。不要迷信动态网格能包治百病,它优化的是仓位管理节奏,止损逻辑还是要独立写一套。
6. 跑通这套逻辑:从文档到回测验证的最小闭环
6.1 把两块逻辑串成主循环
你不需要一开始就写完整的交易系统,只需要把第2章的标准差网格和第3章的RSV分配串到一个主循环里:
for each_day in range(window, len(prices)): # 第1步:用近20日数据算基础网格 base_grid, sigma = calc_grid_with_std( high, low, close, window=20, k=2.0 ) # 第2步:用近9日数据算RSV并分配买卖网格 buy_grid, sell_grid, rsv_val = calc_pair_grid( high, low, close, base_grid=base_grid, window=9 ) # 第3步:先看是否有持仓触发卖出 # 再看是否有现金触发买入 # 触发后记录成交价格、持仓数量、剩余现金这个顺序不要乱。先算基准网格,再算分配比例,最后才是成交判断。如果先把买卖网格算出来再去算标准差,你会在窗口期里混入未来数据,回测结果会虚高。
6.2 用三个指标判断动态网格是否真的更好
验证这套方法有没有效果,别只看总收益率。我会固定一个时间段,分别跑“固定网格”和“动态网格”,然后对比三个数字:总收益、最大回撤、资金利用率。资金利用率 = 持仓市值 / 总资金,动态网格的优势应该是回撤更小、资金利用率略低但波动更平缓。如果动态网格的总收益反而更低,那说明你的k和window参数没配对,优先去调k而不是去改RSV窗口。
我第一次跑这套逻辑时,把标准差窗口从20改成5想抓更细的波动,结果回撤直接翻倍,净值曲线走了一个深V,那一次让我彻底明白——网格策略的每个参数都不是孤立的,窗口变短会让网格变小,网格变小又会让触发变密,最后资金消耗速度失控。从那以后我每次改策略都强制把固定网格跑一遍、动态网格跑一遍,同一段历史数据对比之后才敢换参数,希望帮到你。
本文还有配套的精品资源,点击获取