关于 exchange-session-detector
用于在时间序列数据中准确识别交易时段和节假日的 Skill,面向需要替换简单小时范围判断的开发者,涵盖夏令时、午休和提前收市。
主要功能
- 0110 个交易所 MIC 注册表
- 02支持夏令时的时段识别
- 03处理节假日与午休
- 04预计算分钟并查找
- 05ClickHouse 时段 SQL 模式
适用场景
- 01为 DataFrame 添加交易时段标记
- 02识别交易所节假日
- 03在 ClickHouse 中分类时段
- 04升级固定小时范围判断
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ terrylica/ cc-skills --skill exchange-session-detector安装后在 Claude Code 中输入/exchange-session-detector确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/clickhouse-session-sql.mdexchange-registry.mdsession-detector-pattern.md详细介绍
交易时段检测器
用于检测交易所交易时段的生产级模式,完整支持夏令时、节假日和午休时段。已在 exness-data-preprocess 中通过 10 家全球交易所的验证。
自我演进技能:此技能会在使用过程中不断改进。如果说明有误、参数发生偏移,或需要采用变通方案,请立即修复此文件,不要拖延。仅针对真实且可复现的问题进行更新。
适用场景
- 为时间序列 DataFrame 添加时段标记(is_nyse_session、is_lse_session 等)
- 检测时间戳是否处于任意主要交易所的交易时段内
- 检查节假日(NYSE、LSE,或两者均休市时的“主要节假日”)
- 处理亚洲交易所的午休时段(Tokyo、Hong Kong、Singapore)
- 将简单的小时范围检查升级为生产级准确度
- 为时段分类构建 ClickHouse 物化列
架构概览
ExchangeConfig registry (exchanges.py) SessionDetector (session_detector.py)
┌──────────────────────────────────┐ ┌──────────────────────────────────────┐
│ 10 frozen dataclasses │ │ Wraps exchange_calendars library │
│ ISO 10383 MIC codes │─────▶│ Pre-computes trading minutes (sets) │
│ IANA timezones for DST │ │ Vectorized .isin() lookup (2.2x) │
│ Local open/close hours │ │ Holiday detection (NYSE + LSE) │
└──────────────────────────────────┘ └──────────────────────────────────────┘
快速入门
import exchange_calendars as xcals
import pandas as pd
# Single-exchange check
cal = xcals.get_calendar("XNYS") # NYSE via ISO 10383 MIC
cal.is_open_on_minute(pd.Timestamp("2024-07-04 14:30", tz="UTC")) # False (July 4th)
cal.is_open_on_minute(pd.Timestamp("2024-07-05 14:30", tz="UTC")) # True
# Full session detection across 10 exchanges
from session_detector import SessionDetector
detector = SessionDetector()
df = detector.detect_sessions_and_holidays(dates_df)
# Adds: is_us_holiday, is_uk_holiday, is_major_holiday, is_{exchange}_session
时段检测的两个层级
第 1 层:简单的小时范围(大多数项目的起点)
# Pattern from opendeviationbar/ouroboros.py
EXCHANGE_SESSION_HOURS = {
"sydney": {"tz": "Australia/Sydney", "start": 10, "end": 16},
"tokyo": {"tz": "Asia/Tokyo", "start": 9, "end": 15},
"london": {"tz": "Europe/London", "start": 8, "end": 17},
"newyork": {"tz": "America/New_York", "start": 10, "end": 16},
}
def is_in_session(session_name, timestamp_utc):
info = EXCHANGE_SESSION_HOURS[session_name]
tz = zoneinfo.ZoneInfo(info["tz"])
local_time = timestamp_utc.astimezone(tz)
if local_time.weekday() >= 5:
return False
return info["start"] <= local_time.hour < info["end"]
可正确处理:通过 zoneinfo 转换夏令时,并排除周末。
无法处理:
- 节假日(圣诞节、感恩节、银行假日)
- 午休时段(Tokyo 11:30-12:30、HK 12:00-13:00、SGX 12:00-13:00)
- 半日交易/提前收市时段
- 小时以下的精度(NYSE 在 9:30 开市,而非 10:00;LSE 在 16:30 收市,而非 17:00)
- 交易所交易时间变更(Tokyo 于 2024 年 11 月 5 日延长至 15:30)
第 2 层:exchange_calendars(生产级)
exchange_calendars 库(持续维护,可通过 pip 安装,支持 50 多家交易所)会通过 is_open_on_minute() 自动处理以上所有情况。该库内部使用 IANA 时区数据,因此无需任何手动逻辑即可正确处理夏令时转换。
阅读 references/exchange-registry.md,了解包含 MIC 代码、时区和开市/收市时间的完整 10 家交易所注册表。
阅读 references/session-detector-pattern.md,了解完整的 SessionDetector 实现模式,其中包含预计算交易分钟和向量化查找。
交易所注册表
通过 ISO 10383 MIC 代码支持 10 家交易所:
| 交易所 | MIC 代码 | 时区 | 交易时间(当地) | 午休时段 |
|---|---|---|---|---|
| NYSE | XNYS | America/New_York | 09:30 - 16:00 | - |
| LSE | XLON | Europe/London | 08:00 - 16:30 | - |
| SIX | XSWX | Europe/Zurich | 09:00 - 17:30 | - |
| FWB | XFRA | Europe/Berlin | 09:00 - 17:30 | - |
| TSX | XTSE | America/Toronto | 09:30 - 16:00 | - |
| NZX | XNZE | Pacific/Auckland | 10:00 - 16:45 | - |
| JPX | XTKS | Asia/Tokyo | 09:00 - 15:00 | 11:30 - 12:30 JST |
| ASX | XASX | Australia/Sydney | 10:00 - 16:00 | - |
| HKEX | XHKG | Asia/Hong_Kong | 09:30 - 16:00 | 12:00 - 13:00 HKT |
| SGX | XSES | Asia/Singapore | 09:00 - 17:00 | 12:00 - 13:00 SGT |
新增交易所只需进行一项更改:向注册表字典添加一个 ExchangeConfig 条目。SessionDetector、架构生成和列命名都会自动同步更新。
性能:预计算交易分钟
朴素做法是针对每个时间戳和每家交易所调用一次 calendar.is_open_on_minute(),复杂度为 O(N * E),且常数因子较高。经过验证的模式会将所有交易分钟预先计算到集合中,以实现 O(1) 查找:
# Pre-compute once (startup cost, amortized over millions of lookups)
trading_minutes = detector._precompute_trading_minutes(start_date, end_date)
# Returns: {"nyse": {ts1, ts2, ...}, "lse": {ts1, ts2, ...}, ...}
# Vectorized lookup via pandas .isin() — 2.2x faster than per-row .apply()
df["is_nyse_session"] = df["ts"].isin(trading_minutes["nyse"]).astype(int)
预计算过程本身会在内部使用 is_open_on_minute(),因此午休时段、节假日和交易时间变更都会得到正确处理。
节假日检测
# NYSE holidays (excludes weekends — only official closures)
nyse_holidays = {
pd.to_datetime(h).date()
for h in calendar.regular_holidays.holidays(start=start, end=end, return_name=False)
}
# Major holiday = both NYSE AND LSE closed
df["is_major_holiday"] = ((df["is_us_holiday"] == 1) & (df["is_uk_holiday"] == 1)).astype(int)
ClickHouse 集成
对于服务端会话检测(例如物化列),使用 IANA 时区名称时,ClickHouse 的 toTimezone() 会自动处理夏令时:
-- DST-aware hour extraction (matches Python zoneinfo behavior)
ALTER TABLE my_table
UPDATE is_nyse_session = if(
toHour(toTimezone(toDateTime(intDiv(close_time_ms, 1000)), 'America/New_York')) >= 9
AND toHour(toTimezone(toDateTime(intDiv(close_time_ms, 1000)), 'America/New_York')) < 16
AND toDayOfWeek(toTimezone(toDateTime(intDiv(close_time_ms, 1000)), 'America/New_York')) <= 5,
1, 0
) WHERE 1 = 1
限制:ClickHouse 的 toTimezone() 可以处理夏令时,但无法处理节假日或午休时间。对于这些情况,请在 Python 中计算并将标记写回,或在 ClickHouse 中维护节假日日历表。
升级路径:从 Hour-Range 升级到 exchange_calendars
pip install exchange_calendars(或添加到pyproject.toml)- 将固定小时数字典替换为
ExchangeConfig注册表(参见references/exchange-registry.md) - 将
zoneinfo小时检查替换为SessionDetector.detect_sessions_and_holidays() - 更新测试,覆盖:节假日、午休时间、夏令时切换、提前收盘
exchange_calendars 库安装后约占 10MB,除 pandas 和 numpy 外没有重型依赖。日历数据已打包(运行时不会发起网络请求)。
参考资料
| 文件 | 内容 |
|---|---|
| exchange-registry.md | 完整的 ExchangeConfig 注册表,采用 frozen dataclass 模式 |
| session-detector-pattern.md | 完整的 SessionDetector 类,包含预计算分钟数 |
| clickhouse-session-sql.md | 用于服务端会话检测的 ClickHouse SQL 模式 |
来源
经过验证的实现:~/eon/exness-data-preprocess/src/exness_data_preprocess/session_detector.py + exchanges.py
简化版前身:<project>/python/<package>/ouroboros.py(仅 Tier 1)
执行后复盘
此技能完成后,在结束前检查:
- 命令是否成功? — 如果没有,请修复导致失败的指令或错误表。
- 参数或输出是否发生变化? — 如果底层工具的接口发生变化,请更新 Usage 示例和 Parameters 表,使其保持一致。
- 是否需要变通方案? — 如果你不得不临时调整(更换标志、增加步骤),请更新此 SKILL.md,以免下次调用时重复采用相同的变通方案。
只有在问题真实且可复现时才更新,不要基于推测进行更新。