
简介这份文档面向对量化投资与Python股票分析感兴趣的投资者聚焦网格交易策略中网格大小固定所导致的过早满仓或空仓问题讲解一种让网格随市场波动自动调整的改进思路。内容以收盘价、最高价、最低价的标准差衡量波动程度并借助KDJ指标中的RSV值对买入网格与卖出网格实行差异化设置配合0.5元初始网格、RSV为0.85时买入0.15元、卖出0.85元等算例说明如何顺应趋势优化买卖决策。资源包共1个docx文件约165KB以图文文档形式完整呈现方法原理与计算逻辑便于对照阅读和整理笔记。该资源已有6372人学习适合希望理解网格交易自适应机制、并借助Python构建股票分析工具的读者参考可从中获取波动率度量、网格差异化设置及回撤控制的具体思路。1. 网格交易策略的痛点为什么固定网格总在极端行情里翻车去年帮一个做量化的朋友复盘他的实盘账户发现一个很典型的现象震荡市里网格策略跑得挺欢一到单边行情就出问题——大涨时早早把仓位卖光眼睁睁看着后面一大段利润溜走大跌时又过早满仓越跌越买最后子弹打光只能躺平。他自己也承认问题不在策略逻辑而在网格大小是写死的。这份资源讲的正是这个痛点一种让网格大小随市场波动自动调整的方法核心是用标准差衡量波动剧烈程度、用 RSV 值区分买卖方向让买入网格和卖出网格不再对称。适合已经跑过基础网格、想解决极端行情下过早满仓或空仓问题的开发者也适合拿它当 Python 量化分析的练手项目。2. 策略架构拆解标准差定基准RSV 分买卖这套方法把网格大小的计算拆成两步走先定一个总网格宽度再按趋势把总宽度拆成买入和卖出两个不对称的间距。整个计算链路不依赖复杂模型输入就是收盘价、最高价、最低价这几个基础行情字段输出是下一次触发买入和卖出各自需要的价格间距。理解这条链路后面看代码和改参数才不会迷路。2.1 第一步用标准差算出总网格宽度总网格宽度的思路借用了布林带的思想——布林带的宽度随价格波动变化波动大带宽就宽波动小带宽就窄。这里把近一段时期的收盘价、最高价、最低价拿来算标准差标准差越大说明近期价格越活跃网格就该放大让策略的“胃口”能吞下大波浪不至于价格稍微一动就触发满仓或清仓。常见做法是取一个滚动窗口比如最近 20 个交易日把每天的收盘价、最高价、最低价分别纳入计算得到一个综合的波动度量。窗口长度的选择直接影响策略灵敏度窗口太短网格大小会跟着短期噪声剧烈跳动交易频率失控窗口太长反应迟钝遇到行情切换时网格还停留在旧状态。我一般会先用 20 日左右做基准再根据标的的波动特性微调。import numpy as np import pandas as pd def calc_base_grid(df, window20, scale1.0): 用滚动标准差计算基础网格宽度 df: 含 high/low/close 列的行情 DataFrame按日期升序 window: 滚动窗口长度默认 20 个交易日 scale: 缩放系数用来把标准差映射到实际价格间距 # 对收盘价、最高价、最低价分别取滚动标准差 std_close df[close].rolling(window).std() std_high df[high].rolling(window).std() std_low df[low].rolling(window).std() # 三者取均值作为综合波动度量避免单一字段偏差 combined_std (std_close std_high std_low) / 3 # 乘以缩放系数得到基础网格宽度单位与价格一致 base_grid combined_std * scale return base_grid这段代码的关键在combined_std这一行单独用收盘价标准差也能跑但把最高价和最低价的标准差一起平均进来对日内振幅大的标的更稳。scale参数是留给二次开发的调节旋钮默认 1.0 时网格宽度约等于一个标准差想让策略更保守网格更大、交易更少就调大想更激进就调小。注意rolling前window行会是 NaN实盘里要等数据攒够再开始计算回测时直接丢掉前window行即可。2.2 第二步用 RSV 把总宽度拆成不对称的买卖网格有了总宽度接下来解决买卖对称的问题。原文的思路是引入 KDJ 指标里的 RSV 值公式很直白RSV 收盘价 - 最低价/最高价 - 最低价。这个值落在 0 到 1 之间上涨趋势里收盘价贴近最高价RSV 趋近 1下跌趋势里收盘价贴近最低价RSV 趋近 0。拆分的逻辑是假设总网格宽度为 G当前 RSV 为 r则买入网格 G × (1 - r)卖出网格 G × r。拿原文的例子验证一下G 为 0.5 元、r 为 0.85 时买入网格 0.5 × 0.15 0.075 元卖出网格 0.5 × 0.85 0.425 元。原文举例时把总宽度说成 1 元、买入 0.15、卖出 0.85本质是同一个比例关系只是基准宽度取值不同。核心结论一致趋势向上时买入间距变小、卖出间距变大实现“易买难卖”让母仓能跟着价格往上飘一点避免一涨就清仓。def split_grid(base_grid, close, high, low): 按 RSV 拆分买入/卖出网格 base_grid: 第一步算出的总网格宽度 close/high/low: 当前周期的收盘、最高、最低价 返回 (买入网格, 卖出网格) price_range high - low if price_range 0: # 一字板等极端情况RSV 无定义退化为对称网格 return base_grid / 2, base_grid / 2 rsv (close - low) / price_range # 限制在 0~1防止异常数据导致网格为负 rsv min(max(rsv, 0.0), 1.0) buy_grid base_grid * (1 - rsv) sell_grid base_grid * rsv return buy_grid, sell_gridprice_range 0这个分支是血泪经验一字涨停或跌停时最高价等于最低价直接除会抛异常或得到 inf策略在那种行情下本来也不该挂单退化成对称网格或者干脆跳过当天更安全。rsv的截断同样必要行情数据偶有脏值不截断可能算出负的买入网格挂单逻辑会直接翻车。2.3 数据流与模块划分从行情到挂单的完整链路把两步串起来整个策略的数据流是行情数据进入 → 滚动标准差算基础网格 → 当日 RSV 拆分买卖网格 → 与当前持仓和现金比对 → 生成买入或卖出信号 → 交给交易执行层。对应到代码结构我一般会拆成三个模块data_loader负责拉取和清洗行情grid_engine放上面两个计算函数和信号生成逻辑executor处理下单和持仓记账。这样拆的好处是回测和实盘能共用grid_engine只换executor的实现。信号生成部分要维护一个“上次成交价”的状态每次价格相对上次成交价变动超过对应方向的网格才触发。买入用买入网格判断卖出用卖出网格判断两个间距不同所以同一个价格变动幅度在上涨和下跌时触发的概率天然不对称这正是策略想要的效果。3. 跑起来环境准备与最小可运行示例这套方法本身不依赖重型框架Python 加 pandas、numpy 就能把计算链路跑通。想验证策略效果再补一个回测循环即可。下面按“装环境 → 造数据 → 跑计算 → 看输出”的顺序走一遍新手照着敲能出结果熟手可以直接把函数抠进自己的项目。3.1 依赖安装与目录约定依赖只有三个pandas处理行情表、numpy做数值计算、matplotlib可选用来把网格宽度随价格变化的曲线画出来直观检查。安装命令很常规pip install pandas numpy matplotlib目录上建议按职责分开放避免所有代码堆在一个文件里modeng_grid/ ├── data/ │ └── sample_ohlc.csv # 行情数据含 date/open/high/low/close ├── grid_engine.py # 网格计算与信号生成 ├── backtest.py # 简易回测循环 └── run_demo.py # 入口跑通最小示例行情数据如果没有现成的可以先用模拟数据验证逻辑。真实标的的历史数据从常见行情接口导出成 CSV 即可字段名对齐high/low/close就能直接喂进计算函数。3.2 用模拟行情跑通网格计算没有真实数据时用随机游走造一段带趋势切换的行情能同时看到网格在震荡段和单边段的不同表现。下面这段代码生成 200 个交易日的数据前 100 天震荡、后 100 天单边上涨然后逐日计算买卖网格。import numpy as np import pandas as pd from grid_engine import calc_base_grid, split_grid np.random.seed(42) n 200 # 前 100 天震荡后 100 天单边上涨 returns np.concatenate([ np.random.normal(0, 0.01, 100), np.random.normal(0.008, 0.01, 100) ]) close 10 * np.exp(np.cumsum(returns)) high close * (1 np.abs(np.random.normal(0, 0.005, n))) low close * (1 - np.abs(np.random.normal(0, 0.005, n))) df pd.DataFrame({close: close, high: high, low: low}) df[base_grid] calc_base_grid(df, window20, scale1.0) results [] for i in range(len(df)): if pd.isna(df.loc[i, base_grid]): continue bg df.loc[i, base_grid] buy_g, sell_g split_grid(bg, df.loc[i, close], df.loc[i, high], df.loc[i, low]) results.append((i, bg, buy_g, sell_g)) res_df pd.DataFrame(results, columns[idx, base_grid, buy_grid, sell_grid]) print(res_df.tail(10))跑完看res_df的尾部单边上涨段里sell_grid应该明显大于buy_grid说明策略在上涨中卖出间距拉大、买入间距收窄符合“易买难卖”的设计。如果两个值长期接近检查 RSV 计算用的 high/low 是否和 close 对得上数据错位是这类计算最常见的翻车点。3.3 简易回测循环验证网格调整是否真的减少过早满仓计算正确只是第一步还得看它对交易行为的影响。写一个最小回测初始现金 50 万、母仓 20 万价格相对上次成交价变动超过对应网格就成交固定金额记录持仓和现金变化。重点观察单边上涨段里仓位是否比固定网格保留得更多。def run_backtest(df, initial_cash500000, position_value200000, trade_amount10000): cash initial_cash shares position_value / df.loc[0, close] last_price df.loc[0, close] records [] for i in range(1, len(df)): if pd.isna(df.loc[i, base_grid]): continue price df.loc[i, close] buy_g, sell_g split_grid(df.loc[i, base_grid], price, df.loc[i, high], df.loc[i, low]) if price last_price - buy_g and cash trade_amount: shares trade_amount / price cash - trade_amount last_price price elif price last_price sell_g and shares * price trade_amount: shares - trade_amount / price cash trade_amount last_price price records.append((i, price, shares, cash, shares * price cash)) return pd.DataFrame(records, columns[idx, price, shares, cash, total])trade_amount控制单次成交金额太小会被手续费吃掉太大则网格还没铺开就满仓了一般取总资金的 2% 到 5%。last_price是触发判断的锚点只在成交后更新这样价格在网格内来回晃不会反复触发。回测结果里对比total曲线和纯持有曲线能直观看到网格调整在震荡段的增益和单边段的回撤控制。4. 避坑与排查实盘前必须过的几道坎策略逻辑看着简单真跑起来问题不少。下面几条是我和身边人踩过的按“现象 → 原因 → 解决”整理实盘前逐条对一遍能省不少事。4.1 网格宽度忽大忽小交易频率失控现象回测里某几天连续触发十几次买卖手续费把利润吃光。原因滚动窗口太短标准差被单日异常波动带偏基础网格瞬间缩到很小。解决窗口不要低于 15 个交易日并在calc_base_grid输出上加一个下限比如base_grid base_grid.clip(lowerprice * 0.005)保证网格至少是价格的 0.5%防止极端缩水。4.2 RSV 在涨跌停日算出异常值现象一字板当天买入网格变成负数或极大值挂单逻辑报错。原因最高价等于最低价分母为零。解决就是 2.2 节里那个price_range 0分支退化为对称网格或直接跳过当日信号。实盘里涨跌停本来也难成交跳过最稳妥。4.3 回测收益漂亮实盘一跑就亏现象回测年化很好看实盘上线两周回撤明显。原因回测按收盘价成交没考虑滑点和手续费也没处理停牌和跳空。解决回测里给每次成交加千分之一到千分之三的滑点手续费按券商实际费率扣跳空日按开盘价成交而不是收盘价。这几项加上后收益会缩水一大截但那才是接近真实的数字。4.4 母仓和网格资金比例失调导致过早满仓现象下跌段没跌多少就满仓了后面继续跌只能干看。原因单次成交金额相对总资金太大或者买入网格在下跌趋势里被 RSV 放大得不够。解决单次成交金额控制在总资金 2% 到 5%同时检查下跌段 RSV 是否确实趋近 0、买入网格是否相应变大。如果买入网格没变大回头查 RSV 公式里 high/low 的取值周期是否和网格计算窗口一致。5. 二次开发把网格引擎接进自己的策略框架这套方法的价值不在照抄而在把“波动自适应 买卖不对称”这两个思路抽出来接到自己的持仓管理和风控体系里。下面说几个我实际改过的方向以及验证改动是否有效的办法。第一个方向是替换波动度量。标准差只是其中一种换成 ATR平均真实波幅往往对跳空更敏感因为 ATR 把前一日收盘价也纳入计算。改法是把calc_base_grid里的combined_std换成 ATR 序列接口不变回测对比两者在跳空频繁的标的上谁的回撤更小。第二个方向是给 RSV 加平滑原始 RSV 单日跳动大用 3 日移动平均后的 RSV 拆网格交易频率会明显下降适合资金量大、不想频繁操作的场景。第三个方向是加一层总仓位约束。原方法只调网格间距不管总仓位上限极端下跌里仍可能满仓。我一般会在信号生成后加一道检查当前持仓市值超过总资产 70% 时买入信号降级为不操作只保留卖出。这个约束和网格调整是正交的叠加上去不会破坏原有逻辑。验证改动是否有效别只看总收益。我习惯固定看三个指标最大回撤、单边行情段的仓位保留率、年化换手次数。改动后如果收益涨了但换手翻倍大概率是过拟合了短期噪声换一段样本外数据再跑一遍两个区间都稳才算数。改造方向改动位置观察指标适用场景标准差换 ATRcalc_base_grid最大回撤跳空频繁的标的RSV 加平滑split_grid前处理年化换手次数大资金、低频操作总仓位上限信号生成后单边段仓位保留率极端行情风控最后说个教训。早些年我改这类策略总喜欢一次动好几个参数回测一好看就上线结果实盘出问题根本定位不到是哪个改动引起的。从那以后我每次只改一个变量改完先在样本外区间跑一遍再拿模拟盘挂两周确认行为符合预期才碰实盘。这套流程慢但省下来的排查时间远比多花的多。希望帮到你。本文还有配套的精品资源点击获取