一句话结论:ES 日盘第一根 5 分钟 K 线开在最高价或最低价(开盘那一侧没有影线),16 年只有 3.6% 的日子;这些日子里,76.2% 当天会回到开盘价。
回来得还很快:一半的日子在 10:00 之前就回到开盘价了。整根日 K 线开在最高或最低价的,3924 天里只有 35 天(0.89%)。
作用:第一根 K 线开在最低价、一路往上走时,先别假设开盘价就是今天的最低点;四分之三的日子价格会回来测试开盘价,这是一个可以等的回调位置。
日 K 线几乎都有影线:开盘价正好就是全天最高价或最低价的日子非常少。那如果开盘的第一根 K 线开在它自己的最高价或最低价上(英文叫 shaved open,开盘那一侧被”刮光”了,没有影线),之后价格会不会回来测试开盘价?要等多久?
2010–2026 年 · 第一根 5 分钟 K 线开在最高价或最低价
3.64%
3924 天里有 143 天 · 开在最低价 78 天,开在最高价 66 天
之后多久回到开盘价只看第一根开在最高价或最低价的日子。横轴是日盘的时间,纵轴是已经回到开盘价的日子占比;虚线是第一根收线。
第一根换成不同周期第一根越长,开在最高 / 最低价越少见,回来的比例也越低。
| 第一根 | 开在最高 / 最低价 | 当天回到开盘价 | 开盘价守到收盘 |
|---|---|---|---|
| 1 分钟 | 3077.82% | 89.6% | 11.4% |
| 5 分钟 | 1433.64% | 76.2% | 24.5% |
| 15 分钟 | 942.40% | 63.8% | 37.2% |
| 30 分钟 | 802.04% | 57.5% | 43.8% |
| 60 分钟 | 581.48% | 41.4% | 60.3% |
开在最低价 = 开盘价和第一根 K 线的最低价完全相等,也就是第一根没有下影线;开在最高价同理。回到开盘价 = 第一根收线以后,价格再次碰到开盘价。开盘价守到收盘 = 开盘价就是全天日盘的最低(最高)价。鼠标移到图上(手机上点一下)看具体数字。
很少见:第一根 5 分钟只有 3.6%
3924 个交易日里,第一根 5 分钟 K 线开在最低价的 78 天,开在最高价的 66 天,合计 143 天(3.64%;有 1 天两边都是,是 2020-03-16 开盘就熔断那天)。
第一根越长,越难做到”开盘价就是这根的最高或最低价”:1 分钟是 7.8%,15 分钟 2.4%,60 分钟只有 1.5%。
大多数会回来,而且回来得快
第一根 5 分钟开在最高 / 最低价的 143 天里:
| 到什么时候 | 已经回到开盘价 |
|---|---|
| 9:40(第 2 根收线) | 24.5% |
| 10:00 | 49.7% |
| 10:30 | 61.5% |
| 11:30 | 66.4% |
| 收盘 | 76.2% |
回来的日子里,一半在第 3 根(9:45)收线前就回来了。所以这种开盘的”刮光”一侧,往往很快就被补上影线。
剩下的 24.5% 是开盘价一直守到收盘、成为全天最低(最高)价的日子,也就是日 K 线开在最低(最高)价。
第一根越长,越不容易回来
| 第一根 | 出现 | 回到开盘价 | 守到收盘 |
|---|---|---|---|
| 1 分钟 | 307 天(7.82%) | 89.6% | 11.4% |
| 5 分钟 | 143 天(3.64%) | 76.2% | 24.5% |
| 15 分钟 | 94 天(2.40%) | 63.8% | 37.2% |
| 30 分钟 | 80 天(2.04%) | 57.5% | 43.8% |
| 60 分钟 | 58 天(1.48%) | 41.4% | 60.3% |
道理很简单:日 K 线开在最高 / 最低价的日子固定就是那 35 天,不管第一根用多长都会被算进去;第一根越长,其他”假的”刮光开盘越少,剩下的越接近这 35 天,回来的比例自然越低。第一根用 60 分钟时,开在最高 / 最低价的日子里已经有六成是开盘价守到收盘了。
所以看这件事,第一根用 1–30 分钟比较合适;60 分钟的第一根已经占了日盘的七分之一,留给价格”回来”的时间少了很多。
定义和复现
开盘就是最高 / 最低价
- 数据:清洗后的 ES 1 分钟 K 线,2010-06 到 2026-09,共 3924 个交易日,清洗方法见这篇。
- 日盘:美东 09:30–16:15。开盘价:09:30 那根 1 分钟 K 线的开盘价。
- 第一根 K 线:分别用 1、5、15、30、60 分钟,由 1 分钟 K 线合成。
- 开在最低价:开盘价和第一根的最低价完全相等,也就是第一根没有下影线。开在最高价同理。
- 回到开盘价:第一根收线以后,价格再次碰到开盘价(开在最低价的,看最低价有没有跌回开盘价)。
- 开盘价守到收盘:开盘价就是全天日盘的最低价(最高价),等于日 K 线开在最低价(最高价)。
我有一份清洗过的 ES 期货 1 分钟 K 线 CSV(Databento 的 ES.c.0 连续合约,列有 ts_event、open、high、low,时间是 UTC)。请帮我写 Python 代码,统计“日盘第一根 K 线开在最高价或最低价(没有上影线或下影线)以后,价格会不会回到开盘价”。规则如下: 1. 时间先转成美东时间(US/Eastern),自动处理夏令时。只看日盘 09:30–16:15,也就是 1 分钟 K 线从 09:30 到 16:14。开盘价 = 09:30 那根 1 分钟 K 线的开盘价。 2. 第一根 K 线分别用 1、5、15、30、60 分钟(用 1 分钟 K 线合成,比如 5 分钟就是 09:30–09:34 这 5 根)。 3. 开在最低价:开盘价和第一根的最低价完全相等(不留误差);开在最高价:开盘价和第一根的最高价完全相等。两种合起来算“开在最高或最低价”。 4. 回到开盘价:第一根收线以后,开在最低价的看某一分钟的最低价 ≤ 开盘价,开在最高价的看最高价 ≥ 开盘价。记下第一次回到开盘价是哪一分钟。 5. 开盘价守到收盘:开盘价就是全天日盘的最低价(或最高价)。这和“日 K 线开在最高价或最低价”是同一件事,和第一根用什么周期无关。 6. 打印:样本天数,日 K 线开在最高价、开在最低价的天数;每种第一根周期下,开在最高或最低价的天数和占比、当天回到开盘价的比例、开盘价守到收盘的比例;第一根是 5 分钟时,再打印到 9:40、10:00、10:30、11:30 已经回到开盘价的比例,以及回来的日子里一半在几点之前回来。 请每一步加中文注释,说明为什么这样做。
# 开盘就是最高 / 最低价:日盘第一根 K 线开在它自己的最高价或最低价上,之后价格会不会回到开盘价?
# 输入:清洗后的 ES 1 分钟 K 线(Databento ES.c.0,时间是 UTC)
import numpy as np
import pandas as pd
df = pd.read_csv("ES-1min_标准版.csv", usecols=["ts_event", "open", "high", "low"])
t = pd.to_datetime(df.ts_event, utc=True).dt.tz_convert("US/Eastern").dt.tz_localize(None)
m = t.dt.hour * 60 + t.dt.minute - 570 # 距 09:30 的分钟数
rth = df[(m >= 0) & (m < 405)].assign(date=t.dt.normalize(), m=m) # 日盘 09:30–16:15
# 排成“每天一行、405 列(每分钟一列)”的表,没有成交的那一分钟是空的
H = rth.pivot(index="date", columns="m", values="high").reindex(columns=range(405)).values
L = rth.pivot(index="date", columns="m", values="low").reindex(columns=range(405)).values
op = rth.pivot(index="date", columns="m", values="open")[0].values # 开盘价 = 09:30 那根的开盘价
day_hi, day_lo = np.nanmax(H, 1), np.nanmin(L, 1)
keep = (op == day_hi) | (op == day_lo) # 开盘价就是全天的最高或最低价
print(f"样本 {len(op)} 天;日 K 线开在最高价 {(op == day_hi).sum()} 天,开在最低价 {(op == day_lo).sum()} 天,合计 {100 * keep.mean():.2f}%")
for T in [1, 5, 15, 30, 60]: # 第一根 K 线用不同周期
on_lo = op == np.nanmin(L[:, :T], 1) # 开在最低价:开盘价 = 第一根的最低价(完全相等)
on_hi = op == np.nanmax(H[:, :T], 1) # 开在最高价
shaved = on_lo | on_hi
# 第一根收线以后,第一次回到开盘价是哪一分钟(开在最低价看最低价 ≤ 开盘价,开在最高价看最高价 ≥ 开盘价)
back = np.where(on_lo[:, None], L <= op[:, None], H >= op[:, None])
back[:, :T] = False
when = np.where(back.any(1), back.argmax(1), -1)[shaved]
n = shaved.sum()
print(f"第一根 {T:2d} 分钟:开在最高或最低价 {n} 天({100 * n / len(op):.2f}%,最低 {on_lo.sum()}、最高 {on_hi.sum()});"
f"当天回到开盘价 {100 * (when >= 0).mean():.1f}%;开盘价守到收盘 {100 * keep[shaved].mean():.1f}%")
if T == 5:
for hhmm, mins in [("9:40", 10), ("10:00", 30), ("10:30", 60), ("11:30", 120)]:
print(f" 到 {hhmm} 已经回到开盘价:{100 * ((when >= 0) & (when < mins)).mean():.1f}%")
med = int(np.median(when[when >= 0])) + 1 # 回来的日子里,回来时间的中位数(那一分钟收线时,距 09:30 的分钟)
print(f" 回来的日子里,一半在 {9 + (30 + med) // 60}:{(30 + med) % 60:02d} 之前就回来了")局限
- 样本很小:第一根 5 分钟只有 143 天,按年份拆开每年只有几天到二十天,图上选单独一年时比例会跳得很厉害。
- 只看碰没碰到开盘价:回到开盘价以后是反弹还是跌穿,这里没有统计。
- 对数据很敏感:“开在最高 / 最低价”要求开盘价和最高(最低)价完全相等,差一个跳动单位就不算。换一个数据源,开盘第一笔成交的取法稍有不同,天数就可能不一样。