☰
量化交易行情区间划分:历史分位数、ATR轨道与z-score三招实战
2026/10/21 2:59:59 网站建设 项目流程

做量化交易的人,几乎每天都要回答同一个问题:现在的行情,到底处在什么状态?是趋势在启动,还是震荡要变盘?这个问题看似基础,实际决定了仓位怎么摆、止损怎么设、策略要不要切换。很多新手喜欢对着K线画几条趋势线,一把尺子走天下。但真正的量化做法不是“画区间”,而是“算区间”——从历史数据里提取出可复现、可回测、可验证的信号,把模糊的盘感变成确定性的规则。

今天聊的这三招,本质上是三个不同维度的数据视角:历史分位数回答“现在贵不贵”,ATR动态轨道回答“波动有没有突破常规边界”,z-score均值回归带宽回答“价格偏离中枢有多远”。单看每一个都不复杂,但组合起来,就能搭出一套完整的行情运行区间划分框架。无论你是做程序化交易、量化投研,还是单纯想用更数据化的方式复盘行情,这套思路都值得参考。

1. 为什么量化交易必须先把“运行区间”算明白

1.1 区间判错,整套策略体系跟着翻车

交易决策看着是“买”和“卖”,本质上是状态识别。同样的价格突破,在震荡区间里可能是假突破,在趋势区间里就是行情启动。如果区间判断错了,后面所有决策都会连锁出错。

仓位管理就是最直接的例子。趋势区间里,波动方向明确,仓位可以适当放大;震荡区间里,价格来回抽打,重仓就是在给市场送钱。如果你把震荡行情误判成趋势,重仓追进去,结果就是反复止损,资金曲线一路向南。反过来,真正的趋势来了,你却按震荡思路做高抛低吸,卖飞之后就只能看着行情一路狂奔。

止损止盈的设置也完全依赖区间边界。边界画得太窄,正常波动就会把你扫出场;边界画得太宽,真出风险时亏损已经大到无法承受。量化交易里最怕的不是亏钱,而是亏得没有逻辑。区间算错,止损止盈就失去了参照物,整个风控体系都是空中楼阁。

策略切换就更不用说了。均线趋势策略在震荡区间里会被反复打脸,网格类的均值回归策略在趋势行情里会不断逆势加仓最后被套死。一套完整的量化系统,必须有明确的信号告诉它“当前适合跑哪种策略”。这个信号,归根结底就是行情运行区间的状态标签。

1.2 数据驱动与“拍脑袋画线”的本质区别

传统的盘感画线,最大的问题是无法参数化。同样的K线,两个人能画出完全不同的趋势线,谁对谁错只能事后验证。而且画线依赖人眼选择关键点位,中途换一个关键点,整条线就变了,根本没法写进程序里做回测。

量化的做法是把区间定义变成公式。比如“当前价格处于过去250个交易日收盘价的第80个百分位”,这就是一个客观的、可复算、可回测的描述。你不需要争论这个位置高不高,因为它有一个明确的数值定义。任何人拿到同一份数据和同一套参数,都能算出同样的结果。这就像体检不靠医生说“感觉还行”,而是看血压、血糖、血脂的具体数值有没有超标。

数据驱动的另一个优势是动态更新。市场价格在变,波动率在变,区间不能是一个固定的价格范围,必须是滚动窗口里自动重算的。比如价格从10块涨到100块,同样10个点的波动意义完全不同。只有用相对指标、百分比、标准差、真实波幅这类自适应工具,才能跨越价格尺度去描述区间状态。

2. 第一招:历史分位数定位,给行情“称体重”

2.1 分位数的核心逻辑:先问价格贵不贵

历史分位数这个思路特别朴素:把当前价格扔进过去一段时间的价格分布里,看看它比历史多少比例的样本更高。如果当前价格处于过去250日的第90个百分位,意思就是过去一年的时间里,只有10%的日子价格比现在更高。直观理解就是:这个位置在历史尺度上偏贵了。

为什么要用分位数,而不是直接看最高价、最低价?因为最高最低价本质上是0%和100%分位,它们对极端值极其敏感。一次意外插针(比如流动性枯竭导致的瞬间暴跌)会把最低价拉到一个几乎不可能触及的位置,这时候你拿它当区间下沿,实际操作就没有参考意义。分位数天然有稳健性,它不关心极端值具体是多少,只关心当前价格在分布中的相对位置。

分位数还有一个好处:跨品种可比。股票、商品、债券、甚至不同国家的市场,价格量纲完全不同,但用0到100的分位数来描述位置,就能放在同一个框架下比较。你在做多品种策略时,可以用统一的分位阈值生成交易信号,省去了为每个品种单独调参数的麻烦。

2.2 滚动窗口计算与代码实现

实现方式不复杂,用Python加Pandas就能完成。核心是对滚动窗口内的历史序列做一次排序比较,把当前值与窗口内所有历史值逐一对比,计算“当前值大于历史值的比例”。

import pandas as pd import numpy as np def rolling_percentile(series, window=250): return series.rolling(window).apply( lambda x: (x[-1] > x).mean() * 100, raw=True ) # 假设 df 有收盘价列 'close' df['位置分位'] = rolling_percentile(df['close'], window=250)

这段代码里,rolling窗口取最近250个收盘价,lambda函数把窗口内最后一个价格(也就是当前价)跟窗口内全部价格比较,算出当前价大于历史价格的比例,再乘以100转成百分位。raw=True能让apply的计算速度提升不少,数据量大时体感很明显。

默认参数取250,刚好覆盖一年左右的交易日,适合日线级别的中线策略。如果你是做短线交易,窗口可以缩短到60到120;做超长周期配置,可以拉长到500到750。窗口越长,分位数越稳定,但对近期市场状态的敏感度越低;窗口越短,反应越快,但噪音也越大。这个取舍没有绝对最优,取决于你的交易周期。

2.3 分位数区间的实操用法与注意点

我最常用的做法是把位置分位切成三个区域:小于20%算低位区,20%到80%算中位区,大于80%算高位区。低位区对应的策略倾向是分批加仓或者定投买入,因为安全边际相对充足;高位区则要主动降低仓位、收紧止损,或者干脆离场等待。

除了看价格本身的分位,强烈建议同时算一个波动率分位。把过去N日的ATR(后面会细讲)或者收益率标准差扔进同样的滚动分位计算,看当前波动率处于历史什么水平。我自己的经验是:价格分位和波动率分位同时突破80%以上,往往对应行情剧烈变化或者变盘临界点。这种时候,单纯的“贵”或者“便宜”已经不够用了,必须结合波动状态判断是不是有大行情在酝酿。

用分位数时最容易犯的错,是把“历史高位”直接等同于“行情见顶”。分位数只是概率视角,它告诉你当前价格在统计分布中的位置,不代表不能再涨。一个强势品种可以长期贴着90分位线上方运行,你用分位数做“见顶”判断去做空,会被市场反复教育。分位数适合做区间状态描述和风险预算管理,不适合单独当反转信号用。

3. 第二招:ATR动态轨道,给区间画“围墙”

3.1 ATR是什么,为什么用它来定义区间宽度

ATR全称是平均真实波幅(Average True Range),衡量的是行情“日常震动的幅度”。它解决的问题是:区间到底应该画多宽。如果不用波动率来定义宽度,直接用固定的价格点位,那行情从10块涨到100块之后,原来的宽度就会显得荒谬。

用ATR来画区间边界,本质上是在问:当前价格相对某个中枢的偏离,是否超过了正常波动的“围墙”?这个围墙不是固定高度的砖墙,而是随波动率伸缩的弹力墙。市场安静的时候,围墙收窄;市场躁动的时候,围墙自动放宽。这跟生活里的情况很像:跟一个情绪稳定的人交流,正常说话就能达成共识;跟一个情绪波动大的人交流,你说话的音量和措辞都得留出更大的余地。

ATR自带自适应能力,所以特别适合跨品种使用。股票每天波动两三个点算正常,但某些高波动商品每天波动五六个点也不算异常。用固定点位画区间,一个品种一个参数,换品种就要重新调;用ATR倍数画区间,同样的系数可以直接套到不同品种上,省掉大量适配工作。

3.2 从TR到ATR再到上下轨的完整计算

先算TR(True Range,真实波幅),它取三个差值的最大值:当日最高价减最低价、当日最高价减昨日收盘价的绝对值、当日最低价减昨日收盘价的绝对值。为什么要取最大值?因为如果出现跳空缺口,单看当日高低价区间会漏掉跳空造成的波动,TR用昨收作为参照把缺口也计入真实波幅。

# 假设 df 包含 high / low / close 列 df['prev_close'] = df['close'].shift(1) df['TR'] = df[['high', 'low', 'prev_close']].max(axis=1) - df[['high', 'low', 'prev_close']].min(axis=1) df['ATR'] = df['TR'].rolling(14).mean() # 构造动态轨道 df['mid'] = df['close'].ewm(span=20).mean() df['up'] = df['mid'] + 1.5 * df['ATR'] df['down'] = df['mid'] - 1.5 * df['ATR']

中线用EMA20(20日指数移动平均),因为它比简单均线更贴合近期价格走势,滞后性稍微小一点。上下轨各偏移1.5倍ATR,这个倍数不是随便拍的。在法国数学家布列坦的通道理论里,价格在统计学上大概率会落在一定倍数的真实波幅范围内,1.5倍到2倍ATR是实践经验里比较常用的区间边界。倍数越小,轨道越窄,信号越灵敏,但也会产生更多假突破;倍数越大,轨道越宽,信号越可靠,但等你确认趋势时行情已经走了一段。

你也可以对ATR用Wilder平滑方式计算,那是最早提出的算法,效果差别不大,但需要做兼容老指标的读者可以留意。这里为了代码简单,用的rolling均值。

3.3 轨道突破的判定与常见误区

轨道建好之后,判定逻辑非常直观:收盘价站上上轨,行情进入强势区间;收盘价跌破下轨,行情进入弱势区间;价格在上下轨之间运行,属于震荡区间。

但直接拿突破当信号,会被假突破坑到怀疑人生。我建议加两道过滤条件:一是突破必须持续至少两根K线收盘确认,二是在突破基础上要求幅度超过0.5倍ATR。这两个条件能过滤掉大量盘中插针和临门一脚的假动作。用生活中的话说,一个人说要离职,嘴上一说和真的提交了离职申请、交接了工作,完全是两回事。你要等他办完手续再相信。

还有一个常见误区:ATR突然膨胀导致轨道快速变宽,有人误以为区间变大了,可以重仓操作。实际上,ATR急剧放大往往意味着市场正在变盘,极端波动正在释放。这时候最该做的是降低仓位、收缩杠杆,而不是因为轨道变宽就加大投入。我习惯在这种时候打开第一招的分位数面板,看看波动率分位是否已经冲到了历史极端水平。如果确实在极端区,那基本说明行情进入高不确定性阶段,最好的操作是观望或轻仓。

4. 第三招:均值回归与z-score,衡量“偏离多少算极端”

4.1 为什么用z-score而不是拍脑袋定极值

第三个维度是偏离度。先解释z-score:它等于“当前价格减去均值,再除以标准差”,衡量的是当前价格偏离中枢多少个标准差。标准差代表的是历史波动的离散程度,所以z-score天然就是把“偏离程度”做了标准化处理。

举个例子:同样涨了5%,在一个日波动只有1%的低波动环境里,这是惊天大涨,z-score可能已经飙到4以上;但在一个日波动经常达到5%的高波动环境里,这只是日常波动,z-score可能还不到1。如果你只盯着涨跌幅,就很难分清这5%到底算不算极端。z-score把不同波动环境拉到同一个尺度上去比较,这正是区间划分里最需要的东西。

金融资产的价格序列虽然不完全服从正态分布,但在大多数常规市况下,均值回归特性是存在的。价格偏离均线过远,回归均线的概率和力度都会增大。所以z-score可以当作“温度计”来用:体温超过39度是高烧,低于35度是低体温,正常范围是36到37度。温度本身不告诉你病因是什么,但它告诉你身体状态已经偏离了平衡区间。

4.2 结合布林带思路的带宽设计

如果你想快速实现这个维度,布林带就是现成的参考案例。布林带的中轨是N日均线,上下轨是中轨加减k倍标准差。当价格穿越上轨或者下轨时,本质上是z-score突破了正负k。

df['ma'] = df['close'].rolling(20).mean() df['std'] = df['close'].rolling(20).std() df['z_score'] = (df['close'] - df['ma']) / df['std'] # 布林带上下轨(其实就是 z_score 达到 ±2 的位置) df['boll_up'] = df['ma'] + 2 * df['std'] df['boll_down'] = df['ma'] - 2 * df['std']

默认k通常取2,对应正态分布下约95%的置信区间。意思是如果价格走势接近正态分布,那么价格落在上下轨之间的概率大约是95%,冲出轨道的概率只有5%左右。布林带之所以被广泛应用,就是因为它用统计分布给“极端”画了一条客观的线。

我建议在实盘里把z-score的阈值拆成三档。|z|小于1属于正常波动区,价格在中枢附近来回震荡;1到2属于偏极端区,开始进入“警惕区”,不追单但要关注;大于2属于极端区,趋势可能还在延续,但均值回归的潜在力量也在累积。短线交易者可以考虑用EMA而不是SMA来算均值和标准差,EMA对近期行情反应更快,z-score会比标准布林带更灵敏,但噪音也会相对增多,需要自行权衡。

4.3 三招的交叉验证逻辑

单独用z-score有个明显的问题:它假设了分布近似正态,而金融数据的尾巴比正态分布更厚。也就是说,z-score等于3的情形,实际出现的频率可能比理论值高出不少。所以不能只看z-score单点数值就做反转决策,必须跟前两招交叉验证。

交叉验证是我自己去掉很多假信号的关键。比如z-score超过2,说明价格极端偏离中枢;同时看第一招的位置分位,如果分位数也大于90%,说明不仅偏离大,而且处于历史性的高位区域,这时候追多就要非常谨慎了。反过来,如果z-score超过2但分位数只在70%左右,说明波动环境本身偏大,极端偏离未必代表历史高位,这时候“回归”的动力可能不如前者强。

另外还要看第二招的轨道状态。如果z-score极高且价格已经突破了ATR上轨,说明短期动能极强,这时候均值回归逻辑暂时让位于趋势逻辑,优先顺着趋势方向持有,而不是急着逆势做反转。只有当价格冲出轨道后又快速回到轨道内部,再配合高z-score和高位分位数,才是真正值得关注的变盘信号。

5. 三招合体:一套可落地的区间划分系统

5.1 状态矩阵设计:趋势区、震荡区、极端区

三招单独看都是技术指标,但合体之后,它们各自回答一个维度的不同问题:分位数回答位置高低,ATR轨道回答波动状态,z-score回答偏离程度。把三个答案做成一张状态矩阵,就能对当前行情打标签。

位置分位ATR轨道状态z-score综合状态
20%~80%价格在轨道内部|z| < 1震荡区,适合高抛低吸
>80%价格在上轨上方z > 2强势过热区,跟随但严设止损
<20%价格在下轨下方z < -2弱势极端区,等待反转或顺势空头
20%~80%突破上轨但未持续1 < z < 2假突破嫌疑区,观察过滤
>90%在轨道内回落z回落至1以下冲高回落预警区,减仓应对

这张表不用做得多复杂,关键是每个状态都要有明确的触发条件,让程序能自动判定。你可以直接把这些条件写进代码里,每天收盘后输出一个状态标签,比如“震荡区-偏多”“过热区-预警”之类的。这个过程完全可以自动化,它不会告诉你明天涨还是跌,但它会告诉你当前市场处于什么样的概率分布里。

5.2 仓位管理与策略切换的参考框架

有了状态标签,仓位管理和策略切换就有了依据。震荡区里,价格来回波动,适合网格、高抛低吸类策略,仓位控制在基准仓位的六成左右,因为反复止损会磨损本金。趋势区里,价格朝一个方向推进,适合均线跟踪、动量策略,仓位可以放大到基准仓位的一点二倍到一点五倍,但不能突破总资金的风险预算上限。

极端区是最难处理的。我的原则是:不轻易逆势操作,但要重新评估已有仓位。如果趋势仍然完好,极端区里可以继续持有,但必须把止损收窄到关键轨道附近;如果出现冲高回落且快速回到轨道内部的信号,立刻减仓,不要恋战。极端区本质上是“不确定性最高、方向最容易反转”的区域,保住本金比抓住最后一段利润更重要。

这套系统的价值不在于预判,而在于给每个决策一个边界。就像开车遇到弯道,你不会知道弯道对面有什么,但你知道应该减速、握紧方向盘、准备刹车。状态标签就是交易里的“减速提示”。

5.3 一个简化的实战流程示例

每天收盘后,我建议按下面这个流程跑一遍:

第一步,计算三个核心指标的日度数值:位置分位、ATR轨道上下轨、z-score。第二步,依据状态矩阵给当天行情打标签。第三步,根据标签调整第二天的策略参数:震荡区就启用网格参数,趋势区就切换为动量参数,极端区就降低整体仓位并收紧止损。第四步,每周回顾一次本周的状态标签跟实际行情是否匹配,记录误判案例。

代码层面的输出可以非常简单,比如生成一张数据表,标注“日期、位置分位、ATR状态、z_score、综合标签”五个字段。长期积累下来,你就能看到自己这套区间划分系统在不同市场环境下的真实表现,哪些标签稳定、哪些标签容易误判,一目了然。

6. 实战中踩过的坑与排查清单

6.1 参数过拟合与敏感性分析

做量化的人最容易被“参数优化”带偏。为了让历史回测曲线好看,把ATR的周期、分位数的窗口、z-score的阈值反复调到一个“最佳值”,结果实盘一跑就变脸。过拟合的本质是你在拟合历史噪音,而不是市场规律。

我自己的做法是:先定理论默认值,比如ATR用14日、通道倍数用1.5、分位窗口用250、z-score阈值用±2,然后在默认值附近各取三到五个相邻值做敏感性测试。如果参数微调后策略表现波动不大,说明系统稳定;如果轻微调整就导致收益大幅变化,说明策略本身不扎实,参数只是被历史数据“记住”了。用参数热力图来看,你会希望最优参数落在一片“平原”上,而不是一座孤立的“尖峰”。

6.2 未来函数与数据穿越

区间划分系统里最容易出现未来函数的地方,是滚动窗口计算时误用了未来的数据。比如你想算今天的位置分位,结果代码里不知不觉把明天的数据也滑进了窗口,回测成绩当然漂亮得一塌糊涂,实盘却完全做不到。

检查方法有两个。第一个是代码审查,所有滚动窗口函数都要确认窗口内只包含当前及过去的数据,特别是不要用shift(-1)这种把未来数据往前搬的操作。第二个是逻辑抽查,随便挑几个历史交易日,手动用那一天的“当时能拿到”的数据重算指标,跟代码输出做对比。如果对不上,基本就是未来函数在作怪。说实话,改掉一个未来函数之后回测收益下降,是每个量化研究员都经历过的尴尬时刻,但也正是这个时刻,策略才真正开始变得可信。

6.3 指标滞后与假突破的应对思路

任何基于均线、ATR、滚动分位的指标都天然滞后,因为它们是对已经发生行情的统计描述。等ATR通道被突破时,趋势可能已经走了一大段。这不是指标本身的缺陷,而是确认逻辑必须付出的代价。

为了缓解滞后,我会增加一个“预警层”:当z-score绝对值突破1.5,或者位置分位突破80%,或者价格接近ATR轨道边缘时,先把状态标记为“关注”,但不立即触发交易。等收盘确认突破后再进场。这样既不过早入场被假动作骗,也不会在趋势启动太久之后才追进去。预警层的本质是把决策过程拆成两步:先缩小观察范围,再等待确认信号。

6.4 常见问题速查表

问题现象可能原因排查方向
回测表现很好,实盘持续亏损存在未来函数或参数过拟合检查滚动窗口是否混入未来数据,做参数敏感性分析
位置分位超过90%,但行情继续大涨分位数是概率描述,不代表行情见顶结合ATR轨道状态,别再单独拿分位当反转信号
ATR轨道频繁被上下穿越轨道倍数K设置过小,噪音容忍度低增大K值,或加入连续两根K线确认过滤
极端行情下所有指标同时失真价格跳空、流动性缺失增加成交量或成交额过滤,极端波动期暂停策略
z-score长期处于高位不回归市场处于趋势性暴涨暴跌阶段均值回归逻辑让位于趋势逻辑,顺势操作为主

这套区间划分系统不是什么黑科技,它只是把交易员脑子里那些模糊的“感觉”用数据表达了出来。它的优势在于:每一步都可以回测、每一个判断都有依据、每一条规则都可以不断迭代。无论你最终用不用这三招,养成用数据定义行情的习惯,都会让你的交易决策离“确定性”更近一步。

最后再分享一个实操小技巧:从第一招开始,单指标跑一两个月,把每天的区间状态和实际行情对照记录下来,再逐步叠加第二招、第三招。指标之间会互相打架,这个过程本身就是对你交易逻辑的检验。等你习惯用数据说话之后,回头看那些凭感觉画线的日子,你会明显感觉到自己的判断体系变得更扎实、更可复现。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询