一句话结论:用昨天日盘的区间当尺子,从昨日高点往上量、从昨日低点往下量,今天至少一边走满 1 倍的只有 22.8%;走到 0.5 倍(半个区间)的有 48.4%。
开盘位置是最好的线索:开在昨日高点之上,67.4% 会向上走到 0.5 倍;开在昨日低点之下,74.0% 向下走到 0.5 倍,48.9% 走满 1 倍。
作用:昨日区间外一倍的位置,当天很少走到,单看一边只有一成多;但开在昨日低点之下的日子是例外,将近一半会向下走满 1 倍。
这篇研究来自两个好奇。
第一个是止损。Al Brooks 经常把止损放在离某个区间一倍区间高度的地方,再根据盘面的发展,自己选择止损的位置;而这个区间,有时候就是昨天的震荡区间。那么一天之内,价格真的会走到昨日区间外一倍的地方吗?
第二个是追突破。我做突破的时候,看到开盘一串强势的连续阳线,常常忍不住进场,结果进场以后却被套在当天的最高处。这是怎么回事?今天到底还能走多远,走到哪里就差不多到头了?
所以我把昨天日盘的区间(最高价 − 最低价)当成一把尺子:从昨日高点往上量、从昨日低点往下量,看今天最远能走到几倍。突破昨日高低点那篇已经知道,九成的日子会突破至少一边;这篇接着往下问:突破以后能走多远?昨天怎么收、今天开在哪里,会不会影响走多远?
2010–2026 年 · 全部日子 · 今天至少一边走到昨日区间的 1 倍
22.8%
样本 3816 天 · 走到 0.5 倍 48.4% · 突破昨日高低点 89.6%
今天走到昨日区间的几倍横轴是昨日区间的倍数:0 倍就是突破昨日高点 / 低点,0.5 倍是半个区间,1 倍是整整一个区间(两条虚线)。纵轴是走到的日子占比。
昨日区间 = 昨天日盘最高价 − 最低价。向上走到 k 倍 = 今天最高价 ≥ 昨日高点 + k × 区间,向下 = 今天最低价 ≤ 昨日低点 − k × 区间;0 倍要严格突破(至少 1 个跳动单位)。昨天收在哪里,按收盘价在昨日区间里的位置分成三份。鼠标移到图上(手机上点一下)看具体数字。
走得越远越少见
| 走到昨日区间的 | 向上 | 向下 | 至少一边 |
|---|---|---|---|
| 0 倍(突破昨日高低点) | 55.7% | 43.7% | 89.6% |
| 0.5 倍 | 26.7% | 23.1% | 48.4% |
| 1 倍 | 11.1% | 11.8% | 22.8% |
| 2 倍 | 1.7% | 3.0% | 4.6% |
- 第一行就是突破昨日高低点那篇的数字(样本、规则完全一样):九成的日子会突破至少一边。
- 但突破以后能走多远,下降得很快:一半的日子能走到半个区间,只有两成多能走满一个区间。
- 走过 0.85 倍左右,向下就比向上多了:只是突破昨日高低点,向上更常见(55.7% 对 43.7%);到 0.8 倍时两边差不多(15.4% 对 15.3%);再往外,向下一直比向上多,到 2 倍时向下(3.0%)差不多是向上(1.7%)的两倍。换个角度看:向下突破的日子里,27.1% 会走满 1 倍;向上突破的日子里只有 19.9%。
所以走满一整个昨日区间的日子并不多,只有两成多;走到半个区间的则有一半。
昨天的 K 线:决定往哪边走
下面两张表里,↑0.5 表示向上走到 0.5 倍,↓1 表示向下走满 1 倍,以此类推。
| 条件 | ↑0.5 | ↓0.5 | ↑1 | ↓1 |
|---|---|---|---|---|
| 全部(3816 天) | 26.7% | 23.1% | 11.1% | 11.8% |
| 昨天收阳(2080 天) | 34.6% | 16.2% | 14.2% | 7.8% |
| 昨天收阴(1709 天) | 16.8% | 31.4% | 7.3% | 16.7% |
| 昨天收在上 1/3(1685 天) | 37.0% | 13.6% | 14.9% | 6.2% |
| 昨天收在中间(1082 天) | 24.9% | 26.2% | 11.4% | 14.3% |
| 昨天收在下 1/3(1049 天) | 11.9% | 35.2% | 4.6% | 18.3% |
- 方向会跟着昨天走:昨天收阳(或收在上 1/3),今天向上走到 0.5 倍的概率是向下的两倍多;昨天收阴反过来。
- 但不会走得更远:不管昨天怎么收,“至少一边走到 0.5 倍”都在 46%–50% 之间。昨天的 K 线能帮你选方向,但不能告诉你今天会不会是大日子。
- 收在哪里(上 / 中 / 下 1/3)和阳线阴线的效果差不多,收在两端的更明显一些。
今天开在哪里:影响最大
| 今天开在 | ↑0.5 | ↓0.5 | ↑1 | ↓1 |
|---|---|---|---|---|
| 昨日高点之上(947 天) | 67.4% | 5.1% | 32.8% | 1.7% |
| 区间上 1/3(885 天) | 25.9% | 10.5% | 7.1% | 4.2% |
| 区间中间(771 天) | 11.9% | 14.7% | 3.6% | 6.1% |
| 区间下 1/3(647 天) | 5.6% | 32.1% | 1.9% | 11.6% |
| 昨日低点之下(566 天) | 3.9% | 74.0% | 1.4% | 48.9% |
- 低开比高开走得更远:同样是开在昨日区间之外,开在低点之下的日子,74.0% 向下走到 0.5 倍、48.9% 走满 1 倍;开在高点之上的日子,向上只有 67.4% 和 32.8%。跌出昨日区间的日子,往往跌得更急、更远。
- 开在昨日区间之外,才有机会走满 1 倍:开在高点之上,三分之一的日子向上走满 1 倍;开在低点之下,将近一半向下走满 1 倍。
- 开在区间中间,今天大概率是小日子:两边加起来,走到 0.5 倍的只有 26.1%,走满 1 倍的只有 9.7%;连突破昨日高低点都只有 73.3%,是所有开盘位置里最低的。
定义和复现
今天能走出昨日区间几倍
- 数据:清洗后的 ES 1 分钟 K 线,2010-06 到 2026-09,清洗方法见这篇。
- 日盘:美东 09:30–16:15,不含夜盘。今天和昨天都只看日盘。
- 昨天是哪一天:上一个股市交易日;昨天被清洗掉了,或者两天不是同一个合约,今天就跳过。和突破昨日高低点那篇完全一样,一共 3816 天。
- 昨日区间:R = 昨日最高价 − 昨日最低价。
- 走到 k 倍:向上看今天最高价 ≥ 昨日高点 + k × R,向下看今天最低价 ≤ 昨日低点 − k × R。0 倍要严格突破(至少 1 个跳动单位),和突破那篇一致。
- 昨天收在哪里:(收盘 − 最低) ÷ R,≥ 2/3 是上 1/3,≤ 1/3 是下 1/3。这个比例也叫 IBS(Internal Bar Strength)。
- 今天开在哪里:(今天开盘 − 昨日最低) ÷ R,大于 1 是开在昨日高点之上,小于 0 是开在昨日低点之下,中间按三等分。
我有一份清洗过的 ES 期货 1 分钟 K 线 CSV(Databento 的 ES.c.0 连续合约,列有 ts_event、instrument_id、open、high、low、close,时间是 UTC)。请帮我写 Python 代码,统计“把昨天日盘的区间往上、往下各投一次,今天日盘能走到几倍”。规则如下: 1. 时间先转成美东时间(US/Eastern),自动处理夏令时。只看日盘 09:30–16:15,也就是 1 分钟 K 线从 09:30 到 16:14,不含夜盘。每天的开盘价 = 09:30 那根的开盘价,收盘价 = 16:14 那根的收盘价,最高 / 最低价 = 日盘所有 K 线的最高 / 最低。 2. “昨天”是今天之前最近的一个股市交易日,用纽交所日历判断:pandas 自带的节假日规则,再加上 2012-10-29、2012-10-30、2018-12-05、2025-01-09 临时休市。如果这个昨天不在我的数据里,或者两天的 instrument_id 不同,今天就跳过。 3. 昨日区间 R = 昨日最高价 − 昨日最低价。向上走了几倍 = (今天最高价 − 昨日最高价) ÷ R;向下走了几倍 = (昨日最低价 − 今天最低价) ÷ R。价格先换成跳动单位(× 4 取整)再比较,避免小数误差。 4. 走到 k 倍:k = 0 时要严格突破(至少高 / 低 1 个跳动单位);k > 0 时要求走了 ≥ k 倍。打印样本天数,以及 k = 0、0.5、0.8、1、2 时向上、向下、至少一边走到的比例。再每 0.05 倍看一次,找出从几倍开始向下走到的比例超过向上;并打印向上突破的日子里走满 1 倍的比例、向下突破的日子里走满 1 倍的比例。 5. 分组看 0.5 倍和 1 倍向上、向下走到的比例,以及 0、0.5、1 倍至少一边走到的比例: - 昨天收阳(收盘 > 开盘)/ 昨天收阴; - 昨天收盘在昨日区间里的位置 (收盘 − 最低) ÷ R:上 1/3(≥ 2/3)、中间、下 1/3(≤ 1/3); - 今天开盘在昨日区间里的位置 (开盘 − 昨日最低) ÷ R:昨日高点之上(> 1)、上 1/3、中间、下 1/3、昨日低点之下(< 0)。 请每一步加中文注释,说明为什么这样做。
# 今天能走出昨日区间几倍:用昨天日盘的区间当尺子,从昨日高点往上量、从昨日低点往下量
# 输入:清洗后的 ES 1 分钟 K 线(Databento ES.c.0,时间是 UTC,含 instrument_id 列)
import numpy as np
import pandas as pd
from pandas.tseries.holiday import (
AbstractHolidayCalendar, Holiday, GoodFriday, USMartinLutherKingJr, USPresidentsDay,
USMemorialDay, USLaborDay, USThanksgivingDay, nearest_workday, sunday_to_monday,
)
class NYSE(AbstractHolidayCalendar): # 纽交所全天休市日
rules = [
Holiday("元旦", month=1, day=1, observance=sunday_to_monday),
USMartinLutherKingJr, USPresidentsDay, GoodFriday, USMemorialDay,
Holiday("六月节", month=6, day=19, start_date="2022-01-01", observance=nearest_workday),
Holiday("独立日", month=7, day=4, observance=nearest_workday),
USLaborDay, USThanksgivingDay,
Holiday("圣诞节", month=12, day=25, observance=nearest_workday),
]
CLOSED = pd.to_datetime(["2012-10-29", "2012-10-30", "2018-12-05", "2025-01-09"]) # 临时休市
df = pd.read_csv("ES-1min_标准版.csv", usecols=["ts_event", "instrument_id", "open", "high", "low", "close"])
t = pd.to_datetime(df.ts_event, utc=True).dt.tz_convert("US/Eastern").dt.tz_localize(None)
hm = t.dt.hour * 100 + t.dt.minute
rth = df[(hm >= 930) & (hm < 1615)].assign(date=t.dt.normalize()) # 日盘 09:30–16:15
day = rth.groupby("date").agg(o=("open", "first"), h=("high", "max"), l=("low", "min"), c=("close", "last"),
contract=("instrument_id", "first"))
# 昨天 = 上一个股市交易日;昨天被清洗掉了、或者两天不是同一个合约的,跳过(和“突破昨日高低点”那篇一样)
cal = pd.bdate_range(day.index.min() - pd.Timedelta(days=10), day.index.max())
cal = cal[~cal.isin(NYSE().holidays(cal.min(), cal.max()).union(CLOSED))]
prev = pd.Series(cal[:-1], index=cal[1:]).reindex(day.index)
today = day[prev.isin(day.index)]
yday = day.loc[prev[today.index]].set_index(today.index)
same = today.contract == yday.contract
today, yday = today[same], yday[same]
# 换成跳动单位(0.25 点)的整数来比较,避免小数误差
tick = lambda x: np.round(x * 4).astype(int)
R = tick(yday.h - yday.l) # 昨日区间
up = tick(today.h - yday.h) # 今天最高价比昨日高点高了多少
dn = tick(yday.l - today.l) # 今天最低价比昨日低点低了多少
def hit(x, k):
"""走到 k 倍昨日区间:0 倍要严格突破(至少 1 个跳动单位),k > 0 要求 x ≥ k × R"""
return x > 0 if k == 0 else x * 20 >= round(k * 20) * R
print(f"样本 {len(R)} 天")
for k in [0, 0.5, 0.8, 1, 2]:
u, d = hit(up, k), hit(dn, k)
print(f"{k} 倍:向上 {100 * u.mean():.1f}% 向下 {100 * d.mean():.1f}% 至少一边 {100 * (u | d).mean():.1f}%")
# 从几倍开始,向下走到的比例超过向上(每 0.05 倍看一次)
cross = next(k / 20 for k in range(1, 41) if hit(dn, k / 20).mean() > hit(up, k / 20).mean())
print(f"从 {cross} 倍开始,向下比向上多")
# 突破以后走满 1 倍的比例:只看向上(向下)突破了昨日高点(低点)的日子
print(f"向上突破的日子里走满 1 倍 {100 * hit(up, 1)[hit(up, 0)].mean():.1f}%,向下突破的日子里 {100 * hit(dn, 1)[hit(dn, 0)].mean():.1f}%")
# 分组:昨天收阳 / 收阴;昨天收在区间的哪三分之一;今天开在昨日区间的哪里
ibs = (yday.c - yday.l) / (yday.h - yday.l) # 昨天收盘在区间里的位置,0 = 最低,1 = 最高
loc = (today.o - yday.l) / (yday.h - yday.l) # 今天开盘在昨日区间里的位置,> 1 是开在昨日高点之上
groups = {
"昨天收阳": yday.c > yday.o, "昨天收阴": yday.c < yday.o,
"收在上 1/3": ibs >= 2 / 3, "收在中间": (ibs > 1 / 3) & (ibs < 2 / 3), "收在下 1/3": ibs <= 1 / 3,
"开在昨日高点之上": loc > 1, "开在区间上 1/3": (loc >= 2 / 3) & (loc <= 1), "开在区间中间": (loc > 1 / 3) & (loc < 2 / 3),
"开在区间下 1/3": (loc >= 0) & (loc <= 1 / 3), "开在昨日低点之下": loc < 0,
}
flags = pd.DataFrame({"0.5 倍 上": hit(up, 0.5), "0.5 倍 下": hit(dn, 0.5), "1 倍 上": hit(up, 1), "1 倍 下": hit(dn, 1),
"0 倍 至少一边": hit(up, 0) | hit(dn, 0), "0.5 倍 至少一边": hit(up, 0.5) | hit(dn, 0.5), "1 倍 至少一边": hit(up, 1) | hit(dn, 1)})
rows = {}
for name, m in groups.items():
rows[name] = {"天数": m.sum(), **flags[m].mean()}
print(pd.DataFrame(rows).T.round(3).to_string())局限
- 只看能不能到,不看先到哪边:两边都走到 0.5 倍的日子,先往哪边走、中间会不会先打止损,这里没有统计。
- 没有看时间:走到某个倍数是在上午还是收盘前,对日内交易很重要,这里只看全天。
- 开在区间外的日子,开盘时已经走了一段:开在昨日高点之上时,开盘价本身就已经超出昨日高点一截,所以”走到 0.5 倍”实际要走的距离比看起来短。