简介这份文档面向对量化投资与Python股票分析感兴趣的投资者聚焦网格交易策略中网格大小固定的痛点讲解如何让网格随市场波动自动调整。内容以标准差衡量价格波动幅度波动剧烈时放大网格、平缓时收窄网格并将买入与卖出网格差异化设置与KDJ指标中的RSV值挂钩上升趋势中买入网格变小、卖出网格变大下降趋势则相反文中还给出初始网格0.5元、RSV为0.85时的具体算例。资源包仅含1个docx文件约165KB篇幅精炼适合作为策略思路的案头参考。目前已有6372人学习下载读者可从中获得一套可落地的网格自适应调整逻辑与参数设定方法并理解其降低过早满仓或空仓风险、控制回撤的思路便于结合Python自行实现与验证。1. 从一次“卖飞”说起为什么要让网格自己长大去年帮一个做量化的朋友复盘账户他给我看了一张交割单某只票从 9 块一路涨到 14 块他的网格策略在 10.5 块就把仓位卖光了后面 3 块多的涨幅一分没吃到。他原话是“策略没错是我把网格设小了”。这个场景几乎每个跑过网格交易的人都遇到过——固定网格在震荡市里像台印钞机一遇到单边行情就变成绞肉机。这份方案要解决的正是这个问题让网格大小跟着波动率和趋势自动调整而不是靠人拍脑袋定一个 5 毛或 1 块。它适合两类人正在做量化投资课程设计、需要一套完整可跑代码的学生以及想把网格策略从“手工参数”升级成“自适应参数”的从业者。整套东西用 Python 实现核心就两个公式但要把它们串成能回测、能出信号、能验收的系统中间有不少细节值得掰开讲。2. 需求与技术选型为什么是标准差加 RSV 这套组合2.1 固定网格的两个死穴与自适应要补的洞先把问题定义清楚。网格交易的本质是在一个价格区间里低买高卖母仓打底价格每跌一格买一份每涨一格卖一份。它的盈利来源是波动次数不是趋势方向。所以固定网格有两个绕不过去的坑一是资金利用率低这个方案不碰二是极端行情下的“过早满仓/过早空仓”。大涨时网格太密几格就卖完了大跌时网格太密几格就买满了。根因是网格尺寸是个常数而市场波动是个变量。自适应要补的洞就很明确了波动大的时候网格要变宽让策略“胃口”能吞下大波浪波动小的时候网格要变窄把小起伏也舔干净。同时还要解决方向问题——同样是宽网格上涨趋势里应该“难卖易买”下跌趋势里应该“难买易卖”否则光调大小不调买卖比例趋势来了照样翻车。这两件事分别对应两个技术手段用标准差刻画波动幅度用 RSV 刻画趋势位置。2.2 技术栈与模块划分整套方案用 Python 落地依赖控制在最小集合方便课设环境直接跑起来。模块职责关键依赖数据层读取日线 OHLC计算滚动统计量pandas、numpy指标层计算滚动标准差、RSV、布林带宽度pandas网格引擎根据波动和 RSV 生成买卖网格与信号纯 Python回测层模拟成交、记录持仓与净值曲线pandas展示层输出网格宽度曲线、信号点、收益统计matplotlib选 pandas 而不是自己写循环是因为滚动标准差、滚动最高最低价这些操作向量化之后一行搞定课设里最怕的就是用 for 循环算指标跑一次回测等半天。matplotlib 只用来出图不做交互界面避免把方案拖进 GUI 的泥潭。2.3 两个核心公式的工程化表达第一个公式是波动基准网格。取近 N 个交易日的收盘价、最高价、最低价算一个综合标准差。工程上我一般这么处理先算收盘价的标准差 σ_c再算 (最高价-最低价) 的均值 σ_r两者加权得到一个波动度量 σ。基准网格 G_base 取 σ 的一个倍数比如 G_base k × σk 是可调系数默认 1.5 左右。这个倍数不是拍脑袋它决定了网格对波动的敏感度k 太小网格变化太剧烈k 太大又退化成固定网格。第二个公式是买卖网格差异化。用 RSV (收盘价 - 最低价) / (最高价 - 最低价)取近 N 日窗口。RSV 接近 1 说明收盘价贴着区间高点处于上涨趋势此时买入网格变小、卖出网格变大RSV 接近 0 则反过来。设总网格宽度为 2 × G_base则买入网格 G_buy 2 × G_base × (1 - RSV)卖出网格 G_sell 2 × G_base × RSV原文的例子是 G_base 0.5、RSV 0.85得 G_buy 0.15、G_sell 0.85和公式对得上。这里有个容易忽略的点G_buy G_sell 恒等于 2 × G_base也就是说总宽度由波动决定买卖比例由趋势决定两个维度解耦调参时不会互相打架。提示RSV 的窗口和标准差的窗口可以不同。我一般标准差用 20 日RSV 用 9 日前者看波动幅度后者看短期趋势位置混用同一个窗口反而钝化。3. 总体设计从指标计算到信号生成的流水线3.1 数据流与状态机设计整条流水线是单向的原始 OHLC → 滚动统计量 → 基准网格 → 买卖网格 → 当前档位 → 交易信号。关键在于“当前档位”这个状态怎么维护。网格策略不是每天独立决策它依赖上一次成交价。我的做法是维护一个 anchor_price锚定价初始设为建仓日收盘价每次成交后把锚定价更新为成交价然后判断现价 ≤ anchor - G_buy 则触发买入现价 ≥ anchor G_sell 则触发卖出。这样网格是“浮动”的锚点跟着成交走而不是钉死在初始价位上。状态机只有三个状态空仓等待、持仓中、满仓。母仓建好之后进入持仓中买入触发后档位下移卖出触发后档位上移。用状态机而不是一堆 if-else 堆在一起好处是回测时能清楚看到每次状态迁移的原因出问题好排查。3.2 网格引擎的核心实现下面这段是网格引擎的骨架去掉回测细节只保留信号生成逻辑。import pandas as pd import numpy as np def compute_grid_params(df, std_window20, rsv_window9, k1.5): 计算每日的自适应网格参数 # 波动度量收盘价标准差与振幅均值的加权 sigma_c df[close].rolling(std_window).std() sigma_r (df[high] - df[low]).rolling(std_window).mean() sigma 0.5 * sigma_c 0.5 * sigma_r # 基准网格乘以系数 k 控制敏感度 g_base k * sigma # RSV近 rsv_window 日的相对位置 low_n df[low].rolling(rsv_window).min() high_n df[high].rolling(rsv_window).max() rsv (df[close] - low_n) / (high_n - low_n 1e-9) # 防除零 # 买卖网格差异化总宽度 2*g_base g_buy 2 * g_base * (1 - rsv) g_sell 2 * g_base * rsv return pd.DataFrame({ g_base: g_base, rsv: rsv, g_buy: g_buy, g_sell: g_sell }) def generate_signals(df, params, init_shares1000): 基于锚定价生成买卖信号 anchor df[close].iloc[0] position init_shares signals [] for i in range(1, len(df)): price df[close].iloc[i] gb params[g_buy].iloc[i] gs params[g_sell].iloc[i] if pd.isna(gb) or pd.isna(gs): signals.append(0) continue if price anchor - gb and position 0: signals.append(1) # 买入信号 anchor price # 锚点跟随成交价 position 100 elif price anchor gs and position 100: signals.append(-1) # 卖出信号 anchor price position - 100 else: signals.append(0) return signals这段代码有三个设计点值得说。第一sigma用收盘价标准差和振幅均值各占一半单用收盘价标准差会低估日内剧烈波动的情况单用振幅又会忽略趋势连续性加权是折中。第二rsv分母加了1e-9一字板或者停牌复牌时 high 等于 low不加这个会出 inf 把整列污染掉这是血泪经验。第三锚定价在成交后立即更新而不是每天重置否则网格会变成“每天重新画一遍”回测结果虚高。3.3 参数怎么调k 值、窗口与档位粒度k是这套方案里最需要动手调的参数。k 偏大网格宽交易频率低适合波动大的标的k 偏小网格窄交易频繁手续费会吃掉利润。我一般先用 1.5 跑一遍看年化交易次数如果超过 60 次就往上调低于 15 次就往下调。窗口方面std_window取 20 是经验值对应一个月的交易日rsv_window取 9 是 KDJ 的经典参数短周期对趋势拐点更敏感。档位粒度每次买卖多少股要和母仓规模匹配母仓 1000 股、每档 100 股意味着最多能扛 10 档下跌这个比例要结合标的的历史最大回撤来定别设成 5 档一个中等回调就满仓了。4. 模块实现回测、风控与可视化三件套4.1 回测引擎成交假设与净值计算回测最容易翻车的地方是成交假设太乐观。我的做法是信号在收盘后生成次日开盘价成交滑点按 0.1% 计手续费按万三双边。净值曲线每天按收盘价 mark-to-market而不是只在成交日更新否则回撤会被严重低估。def backtest(df, signals, init_cash500000, lot100, slippage0.001, fee0.0003): cash init_cash shares 0 nav_list [] anchor df[close].iloc[0] for i in range(1, len(df)): open_px df[open].iloc[i] close_px df[close].iloc[i] sig signals[i - 1] # 前一日信号次日执行 if sig 1 and cash open_px * lot * (1 slippage fee): cost open_px * (1 slippage) * lot cash - cost * (1 fee) shares lot elif sig -1 and shares lot: proceeds open_px * (1 - slippage) * lot cash proceeds * (1 - fee) shares - lot nav_list.append(cash shares * close_px) return pd.Series(nav_list, indexdf.index[1:])注意signals[i-1]这个下标信号是收盘后算的必须用前一天的信号在当天开盘执行写成signals[i]就是未来函数回测收益会好得离谱实盘一跑就现原形。这是量化课设里最高频的扣分点答辩时老师一眼就能看出来。4.2 风控模块最大持仓与单日亏损熔断自适应网格解决了“网格大小”问题但没解决“极端行情下还要不要继续买”的问题。我加了两道闸一是最大持仓上限比如母仓的 3 倍到了就只卖不买二是单日亏损熔断当日净值回撤超过 5% 就暂停开新仓第二天再恢复。这两条不是原文里的但一个能交付的方案不能只有 alpha 没有风控否则回测曲线再漂亮也不敢用。风控逻辑放在信号生成之后、回测执行之前作为一个过滤器def risk_filter(signals, nav, max_drawdown0.05): filtered signals.copy() peak nav.cummax() drawdown (nav - peak) / peak for i in range(1, len(filtered)): if drawdown.iloc[i] -max_drawdown: filtered[i] min(filtered[i], 0) # 熔断期只允许卖出 return filtered4.3 可视化三张图看懂策略在干什么出图不是为了好看是为了验收时能自证。第一张图画价格和布林带直观看到带宽随波动变化第二张图画 g_buy 和 g_sell 两条曲线验证 RSV 高时买入网格确实收窄第三张图画净值曲线和最大回撤区间。三张图往答辩 PPT 上一放比讲十页公式管用。matplotlib 里注意中文字体要提前设不然图上一堆方块现场很尴尬。5. 避坑与排查五个最容易翻车的地方5.1 现象回测收益高得离谱实盘完全对不上原因几乎都是未来函数。除了前面说的信号下标错位还有一种隐蔽的用当日最高最低价算 RSV 之后又用当日收盘价判断是否触发。正确做法是 RSV 用截至前一日的数据算当日只做执行。排查方法很简单把信号整体滞后一天再回测如果收益断崖式下跌说明原来有偷价。5.2 现象网格宽度曲线剧烈抖动一天宽一天窄原因是标准差窗口太短或者 k 值太小波动度量被单日异常值带偏。解决是把std_window拉到 20 以上并对g_base做一次 3 日平滑让网格变化有惯性。网格是慢变量不该跟着日线跳舞。5.3 现象RSV 出现 NaN 或 inf整列信号失效停牌、一字板、新股上市初期都会让 high 等于 low分母为零。除了加1e-9还要在数据清洗阶段把连续停牌超过 5 日的区间标记出来这些区间不生成信号。回测里遇到 NaN 直接continue而不是填 0填 0 会制造假信号。5.4 现象满仓之后还在发买入信号现金变负数这是档位管理和资金校验脱节。信号生成层不知道账户还有多少钱回测层又没做现金检查。解决是在回测执行前加一道cash cost判断同时信号层维护一个持仓计数器达到上限就不再发买入信号。两层校验缺一不可。5.5 现象参数一换收益天差地别不知道信哪个过拟合的典型症状。排查方法是把参数空间画成热力图看收益是不是只在一个很窄的格子里好看。如果是说明参数是拟合出来的不是策略本身有效。稳健的参数区域应该是一片高原不是一个尖峰。我一般要求 k 在 1.2 到 2.0 之间收益都为正才认为这套参数能用。6. 验收与答辩怎么证明这套网格真的自适应了验收这件事核心不是证明“策略能赚钱”而是证明“网格确实在跟着市场变”。答辩时老师最常问的一句是“你怎么知道是自适应起了作用而不是运气好”。我的应对办法是做三组对照回测用同一段数据、同一个标的只改网格逻辑对照组网格逻辑观察指标A 组固定网格 0.5 元基准收益、最大回撤、交易次数B 组仅标准差自适应买卖网格相同波动期网格宽度变化C 组标准差 RSV 差异化本方案趋势期买卖比例偏移如果 C 组相对 A 组在最大回撤上有明显改善且 B 组到 C 组的增量收益主要来自趋势段那自适应逻辑就站得住。这个对照表本身就是答辩材料比口头解释有说服力。具体操作上我会准备一个verify.py脚本把三组回测跑完自动输出一张对比表和两张图一张是网格宽度随时间的曲线标注出高波动区间和低波动区间肉眼可见宽度在变另一张是 RSV 与 g_buy/g_sell 的散点图验证 RSV 高时买入网格确实小。验收标准我一般定三条最大回撤相对固定网格降低 20% 以上年化交易次数在 20 到 60 次之间太低说明网格太钝太高说明手续费会吃掉利润参数在 k ∈ [1.2, 2.0] 范围内收益均为正证明不是过拟合。答辩现场还有一个技巧主动说出这套方法的边界。比如它解决的是网格大小问题不解决资金利用率问题RSV 在横盘震荡时会在 0.5 附近反复此时买卖网格接近相等退化成普通网格这是设计上的预期行为而不是 bug。主动划边界比被问住再解释强得多。从那以后我每次交付策略类方案都强制自己先跑一遍“滞后一天”的回测和参数热力图这两步能挡掉八成低级错误。希望这套结构能帮到你把课设或方案稳稳落地。本文还有配套的精品资源点击获取