
简介这份文档面向对量化投资与Python股票分析感兴趣的投资者聚焦网格交易策略中网格大小固定的痛点讲解如何依据市场波动动态调整网格以缓解极端行情下过早满仓或空仓的问题。内容围绕两条主线展开一是用收盘价、最高价与最低价的标准差衡量波动程度波动剧烈时放大网格、平缓时缩小网格二是将买入与卖出网格差异化设置与KDJ指标中的RSV值挂钩上升趋势中买入网格变小、卖出网格变大下降趋势则相反并给出初始网格0.5元、RSV为0.85时拆分为0.15元与0.85元的具体算例。资源包仅含1个docx文档约165KB篇幅精炼适合作为策略思路参考与参数设定手册。目前已有6372人学习读者可从中获得一套可落地的自适应网格调整逻辑理解标准差与RSV在仓位管理中的配合方式并据此在Python量化分析中改造自己的网格策略。1. 从一份文档到一套可运行的网格策略这项目到底解决什么问题手里这份资源是一份.docx文档标题叫《一种自动调整网格交易策略中网格大小的方法》。它不讲虚的核心就一件事把网格交易里那个最要命、最玄学的参数——网格大小——从“拍脑袋定死”改成“跟着行情自动变”。文档里用平安银行在 7 到 15 元区间震荡的例子把网格交易讲得很直白11 块建母仓涨 5 毛卖一点跌 5 毛买一点十年价格回到原点普通持有不赚不赔网格却能靠波动反复薅利润。但它也点破了网格交易的两大硬伤资金利用率低暂且不论真正致命的是极端行情下过早满仓或过早空仓。这份文档给出的解法就是让网格宽度随波动率伸缩、让买卖网格差异化配套的 Python 股票分析软件叫魔灯。适合谁想用 Python 做量化分析、又不想从零造轮子的开发者以及被固定网格坑过的散户。2. 策略内核拆解标准差定宽度RSV 定买卖不对称这一章把文档里那套“自动调整网格大小”的方法拆成可落地的计算逻辑。它不是一个黑匣子本质是两层参数映射第一层用波动率决定网格总宽度第二层用趋势强弱把总宽度拆成买入网格和卖出网格。理解这两层后面看代码和改参数才有抓手。2.1 第一层用标准差给网格“量胃口”文档的思路很朴素股价波动剧烈时网格太小会被大波浪瞬间击穿导致早早满仓或空仓波动平缓时网格太大又舔不到小起伏。所以网格宽度应该和近期价格的离散程度挂钩。它明确提到参考布林线指标的原理——布林带宽度随价格波动而变化而布林带的中轨和上下轨正是用移动平均和标准差算出来的。落到实现上常见做法是取近 N 个交易日的收盘价、最高价、最低价计算一个综合的标准差指标再乘一个系数映射成基础网格宽度。文档没有给出具体公式的每一行但方向很清楚波动率越大基础网格越宽。我一般会这样落地import numpy as np import pandas as pd def calc_base_grid(df, window20, k0.5): 根据近 window 日的高低价波动计算基础网格宽度 df: 含 high/low/close 列的 DataFrame按日期升序 window: 回看窗口默认 20 日 k: 波动率到网格宽度的缩放系数需按标的调 recent df.tail(window) # 用高低价差的标准差衡量波动剧烈程度 hl_spread recent[high] - recent[low] vol hl_spread.std() # 收盘价标准差作为辅助避免单一指标失真 close_std recent[close].std() # 综合波动率取两者均值 combined_vol (vol close_std) / 2 base_grid combined_vol * k return round(base_grid, 3)这段代码里window决定策略对行情的“记忆长度”20 日大约是一个月交易日太短会被单日异动带偏太长反应迟钝k是最需要调的参数它把统计上的波动幅度翻译成实际下单的价格间距k偏大网格偏宽、交易频率低但单笔利润厚k偏小则相反。hl_spread用最高最低价差而不是只用收盘价是因为网格交易关心的是日内能触及的价格范围高低价差更贴近真实可成交区间。这一步算出来的base_grid就是文档里举例的“0.5 元”那个总宽度。2.2 第二层用 RSV 把总宽度劈成买卖两半有了总宽度文档的关键创新在于不平均分配。它引入 KDJ 指标里的 RSV 值RSV 收盘价 - 最低价/最高价 - 最低价。这个值的含义是收盘价在当期价格区间里的相对位置上涨趋势中收盘价贴近最高价RSV 趋近 1下跌趋势中趋近 0。文档给的分配规则是买入网格 总宽度 ×1 - RSV卖出网格 总宽度 × RSV。举例 RSV0.85 时总宽 1 元拆成买入 0.15、卖出 0.85意思是涨势里“易买难卖”跌一小截就接、涨一大截才卖让母仓随趋势上移避免过早卖光。def split_grid(base_grid, df, rsv_window9): 用 RSV 把基础网格拆成买入网格和卖出网格 base_grid: 第一层算出的总宽度 rsv_window: RSV 回看周期KDJ 常用 9 recent df.tail(rsv_window) highest recent[high].max() lowest recent[low].min() close df[close].iloc[-1] # 防止分母为 0 的极端情况 if highest lowest: rsv 0.5 else: rsv (close - lowest) / (highest - lowest) buy_grid base_grid * (1 - rsv) sell_grid base_grid * rsv return round(buy_grid, 3), round(sell_grid, 3), round(rsv, 3)rsv_window取 9 是 KDJ 的惯例短周期对趋势拐点更敏感代价是 RSV 抖动大、网格宽度会频繁跳变实盘里可以加个平滑或者设最小网格下限防止buy_grid被压到接近 0 导致疯狂挂单。highest和lowest用窗口内的极值而不是当日值是为了让 RSV 反映一段趋势而非单日噪声。注意当highest lowest一字板或停牌后复牌时分母为零必须兜底成 0.5否则整个策略会抛异常——这是文档没写但实盘一定会遇到的坑。2.3 把两层串成一次完整的网格刷新单次计算只是零件真正要跑起来得把“取数 → 算基础宽度 → 拆买卖网格 → 生成挂单价位”串成一条链路。文档描述的使用方式是魔灯提示你在股价降落一毛五时买入、上涨八毛五时卖出说明最终输出的是相对当前价的触发价位。def refresh_grid(df, k0.5, window20, rsv_window9): 返回当前价、买入触发价、卖出触发价 base calc_base_grid(df, windowwindow, kk) buy_g, sell_g, rsv split_grid(base, df, rsv_windowrsv_window) current df[close].iloc[-1] buy_price round(current - buy_g, 2) sell_price round(current sell_g, 2) return { current: current, base_grid: base, rsv: rsv, buy_grid: buy_g, sell_grid: sell_g, buy_trigger: buy_price, sell_trigger: sell_price }buy_trigger和sell_trigger就是策略每天开盘前要重算的挂单参考价。这里有个设计取舍触发价基于最新收盘价滚动计算意味着网格会随价格整体平移而不是固定一张静态网格表。这正是文档“自适应”的落点——网格不是画好就不动的而是每次刷新都重新量一遍市场的胃口。参数上k和window决定网格的“体型”rsv_window决定买卖不对称的灵敏度三者要一起调单独改一个往往越调越乱。3. 工程落地从文档公式到可运行的 Python 分析脚本文档本身是方法论述配套的魔灯是 Python 写的股票分析软件作者公开源码并鼓励有 Python 基础的人在此基础上开发自己的分析工具。这一章按“一个能跑的最小工程”来组织数据从哪来、模块怎么分、主循环怎么写、结果怎么看。不依赖魔灯完整仓库也能复现核心逻辑因为策略内核就是上一章那几十行。3.1 目录结构与模块职责一个可持续迭代的量化小项目最忌讳把所有逻辑塞进一个脚本。按职责拆开后面改参数、换数据源、加回测都省事。我一般会这样组织grid_strategy/ ├── data/ │ └── loader.py # 行情数据获取与清洗 ├── core/ │ ├── grid.py # 网格宽度计算第 2 章逻辑 │ └── signal.py # 买卖触发判断 ├── backtest/ │ └── engine.py # 简易回测循环 ├── config.py # 参数集中管理 └── main.py # 入口串流程loader.py负责把原始行情整理成含date/open/high/low/close/volume的 DataFrame并保证按日期升序、无缺失grid.py放calc_base_grid和split_gridsignal.py根据当前持仓和触发价决定买还是卖config.py把k、window、rsv_window、初始母仓比例这些参数集中避免散落在各处改漏。这种拆法的好处是回测和实盘共用同一套core只换执行层。3.2 数据加载与参数配置数据源文档没指定常见做法是用公开行情接口拉日线落成本地 CSV 缓存避免每次回测都请求网络。配置集中管理是血泪经验——参数散落时改一个忘一个回测结果对不上账。# config.py GRID_CONFIG { k: 0.5, # 波动率缩放系数 window: 20, # 基础网格回看窗口 rsv_window: 9, # RSV 回看周期 initial_position: 0.4, # 母仓占总资金比例 max_position: 0.9, # 最大仓位防满仓 min_grid: 0.05, # 最小网格防过密挂单 }initial_position对应文档说的“50 万资金拿 20 万做母仓”留足下跌加仓的弹药max_position是文档没强调但实盘必须加的保险防止连续下跌把仓位打满后彻底失去应对能力min_grid用来兜住 RSV 极端时buy_grid趋近 0 的问题低于这个值就按最小值挂。# data/loader.py import pandas as pd def load_daily(csv_path): df pd.read_csv(csv_path, parse_dates[date]) df df.sort_values(date).reset_index(dropTrue) # 丢弃关键字段缺失的行避免计算时 NaN 扩散 df df.dropna(subset[open, high, low, close]) return dfparse_dates保证日期是 datetime 类型后面按窗口切片才不会错位dropna针对停牌或数据源缺字段的情况宁可少几天数据也不要让 NaN 污染标准差计算。3.3 回测主循环让网格真正“动”起来回测循环要模拟的是每天用截至当日的数据刷新网格判断当日价格是否触及买入或卖出触发价成交后更新持仓和现金。这里最容易翻车的是“未来函数”——用当天收盘后才知道的信息去决定当天开盘的操作。严谨做法是用前一日及之前的数据算网格用当日价格判断触发。# backtest/engine.py from core.grid import calc_base_grid, split_grid def run_backtest(df, cfg, init_cash500000): cash init_cash shares 0 # 初始建母仓 first_price df[close].iloc[cfg[window]] shares int(init_cash * cfg[initial_position] / first_price / 100) * 100 cash - shares * first_price trades [] for i in range(cfg[window], len(df)): hist df.iloc[:i] # 只用历史数据杜绝未来函数 today df.iloc[i] base calc_base_grid(hist, windowcfg[window], kcfg[k]) buy_g, sell_g, rsv split_grid(base, hist, rsv_windowcfg[rsv_window]) buy_g max(buy_g, cfg[min_grid]) sell_g max(sell_g, cfg[min_grid]) prev_close hist[close].iloc[-1] buy_price prev_close - buy_g sell_price prev_close sell_g # 当日最低价触及买入价则买入 if today[low] buy_price and cash buy_price * 100: lot int(cash * 0.1 / buy_price / 100) * 100 if lot 0 and (shares lot) * buy_price init_cash * cfg[max_position]: shares lot cash - lot * buy_price trades.append((today[date], buy, buy_price, lot)) # 当日最高价触及卖出价则卖出 if today[high] sell_price and shares 100: lot int(shares * 0.1 / 100) * 100 if lot 0: shares - lot cash lot * sell_price trades.append((today[date], sell, sell_price, lot)) final_value cash shares * df[close].iloc[-1] return final_value, tradeshist df.iloc[:i]是防未来函数的关键第 i 天只能用第 i 天之前的数据算网格prev_close用前收而不是当日收盘定触发价因为当日收盘时已经无法按那个价成交。每次买卖按持仓或现金的 10% 下单是简化处理实盘可以按网格层数均分。max_position检查放在买入前防止仓位突破上限。回测结果final_value要和“同期买入持有”的收益对比才有意义——文档里平安银行十年不涨的例子正是网格策略相对持有策略的超额来源。3.4 结果验证看什么指标才算跑通跑完回测不能只看最终赚了多少至少要看三样交易次数、最大回撤、以及和买入持有的对比。交易次数太少说明网格太宽或k太大策略几乎没动最大回撤反映极端行情下的风险文档反复强调的“过早满仓”就体现在这里和买入持有对比才能判断自适应网格是否真的比死拿强。我一般会在engine.py里顺手记录每日净值曲线回测结束后打印总收益 / 最大回撤 / 交易笔数 / 同期持有收益四个数四个数一起看才不会被单一指标骗。4. 避坑与排查自适应网格最容易翻车的几个地方这套方法逻辑不复杂但实盘和回测里踩的坑一个不少。下面几条是我和身边人真遇到过、或者文档方法落地时必然撞上的按“现象 → 原因 → 解决”记下来。4.1 网格宽度剧烈跳变挂单一天一个样现象某天base_grid算出 0.8第二天变成 0.2触发价大幅漂移回测里出现大量莫名其妙的成交。原因window太短加上行情刚好经历一根大阳线或大阴线标准差被单日极值放大k又偏大导致宽度过冲。解决把window拉到 20 以上对base_grid做 3 到 5 日的移动平均平滑或者给网格宽度设上下限比如不超过当前价的 5%、不低于 0.5%让参数变化有惯性。4.2 RSV 极端值把买入网格压成 0现象连续上涨行情里buy_grid算出来 0.01 甚至 0策略在每个微小回调都挂买单手续费吃掉利润。原因RSV 趋近 1 时1 - RSV趋近 0文档公式本身没有下限保护。解决加min_grid兜底buy_grid max(base_grid * (1 - rsv), min_grid)同时给 RSV 做截断比如限制在 0.1 到 0.9 之间避免趋势末端参数失真。4.3 回测收益漂亮实盘一上就亏现象回测年化很好看实盘跑两周发现成交价和回测对不上收益差一大截。原因回测假设“最低价触及买入价就能按买入价成交”实盘有滑点、有涨跌停无法成交、有挂单排队另外回测用了当日最高最低价判断触发实盘盘中根本不知道当日极值。解决回测里给成交价加滑点买入上浮、卖出下调各 0.1% 到 0.2%涨跌停日跳过交易并且把触发判断改成盘中实时价而非当日极值让回测假设贴近真实执行。4.4 母仓比例拍太高下跌没子弹现象按文档例子拿了 40% 做母仓结果标的连续下跌现金很快用完后面更低的价位只能干看。原因initial_position和max_position没配合好且没有考虑标的的历史最大回撤。解决用历史最大回撤反推母仓上限比如标的曾跌 50%母仓就别超过 40%并给加仓设梯度而不是每格等额max_position留 10% 以上现金做极端行情缓冲。4.5 数据复权没处理网格价位全错现象标的除权除息后历史价格和当前价格不在同一尺度算出来的网格宽度和触发价完全失真。原因用了未复权数据或者前复权/后复权混用。解决全流程统一用前复权数据加载时一次性处理完再进策略如果数据源只给不复权价自己按除权因子换算别让复权问题混进网格计算里。5. 二次开发把文档方法改成你自己的策略拿到这套东西最有价值的不是照抄参数而是把它当成一个可替换的骨架。文档的核心贡献是“波动率定宽度 RSV 定不对称”这个结构但每一层都可以换。下面几条改造思路按投入产出比排。第一换波动率度量。标准差只是其中一种换成 ATR平均真实波幅往往更贴合网格场景因为 ATR 直接基于高低价差和跳空对网格关心的“日内能走多远”更敏感。改法是把calc_base_grid里的hl_spread.std()换成 ATR 计算k重新标定即可接口不变。第二换趋势指标。RSV 来自 KDJ反应快但抖动大可以换成 RSI 或者价格相对均线的偏离度甚至用两个指标的均值做平滑。关键是保持“输出一个 0 到 1 之间的趋势强度值”这个契约split_grid的分配公式不用动。第三加一层风控开关。文档方法解决的是网格宽度自适应但没解决“趋势单边时该不该暂停网格”。可以加一个简单规则当价格连续 N 日偏离均线超过阈值暂停逆势加仓只保留减仓等回归再恢复。这层开关在回测里能明显压低最大回撤。第四把参数寻优做成表格对比。k、window、rsv_window三个参数组合空间不大直接网格搜索跑一遍用下面这种表看结果比拍脑袋调参靠谱kwindowrsv_window总收益最大回撤交易笔数0.420918.2%12.5%860.520921.7%15.1%640.6301416.9%9.8%41看表时别只挑收益最高的回撤和交易笔数要一起权衡——交易笔数太少说明参数把策略调成了“几乎不动”样本不足的收益不可信。验证改造是否有效我习惯用同一段行情、同一初始资金把原版和改版并排跑只比“相对买入持有的超额收益”和“最大回撤”两个数其他指标都是辅助。改完一个参数就重跑一次别一次改三处否则出了问题根本不知道是哪处引起的。说个自己的教训早些年我调这类策略总想一次把参数调到最优结果回测曲线漂亮得不像话实盘一上就原形毕露后来才明白那是过拟合。从那以后我每次改完参数都强制留一段没参与调参的行情做样本外验证样本外能站住才敢往下走。这套文档方法本身是作者实战总结的骨架靠谱但参数一定得用你自己的标的和时段重新标定别直接抄例子里的 0.5 和 0.85。希望帮到你。本文还有配套的精品资源点击获取