Files
qihuo/market_sessions.py
T
dekun 9875ee6d44 本地监控止盈止损、盘前自动连CTP,并完善保证金与推荐手数。
- 止盈止损改为程序本地监控,触发后市价平仓(含跳空)
- 交易前30分钟后台自动连接 CTP
- 保证金占用上限默认30%,可在系统设置修改
- K线标准蜡烛图红跌绿涨,费率表全宽固定表头
- 品种推荐按保证金比例×总资金计算推荐手数

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 12:18:18 +08:00

103 lines
2.8 KiB
Python

"""国内期货交易时段与盘前连接窗口。"""
from __future__ import annotations
from datetime import datetime, timedelta
from typing import Optional
from zoneinfo import ZoneInfo
TZ = ZoneInfo("Asia/Shanghai")
# 各交易段开盘时刻 (时, 分)
SESSION_OPENS = (
(9, 0),
(13, 30),
(21, 0),
)
def is_trading_session(now: Optional[datetime] = None) -> bool:
d = now or datetime.now(TZ)
if d.tzinfo is None:
d = d.replace(tzinfo=TZ)
else:
d = d.astimezone(TZ)
wd = d.weekday()
if wd == 6:
return False
if wd == 5 and d.hour < 21:
return False
t = d.hour * 60 + d.minute()
def in_range(sh: int, sm: int, eh: int, em: int) -> bool:
return t >= sh * 60 + sm and t < eh * 60 + em
if in_range(9, 0, 11, 30):
return True
if in_range(13, 30, 15, 0):
return True
if in_range(21, 0, 24, 0):
return True
if in_range(0, 0, 2, 30):
return True
return False
def _session_open_allowed(day: datetime, hour: int, minute: int) -> bool:
wd = day.weekday()
if (hour, minute) == (9, 0) or (hour, minute) == (13, 30):
return wd < 5
if (hour, minute) == (21, 0):
if wd < 5:
return True
return wd == 5
return False
def iter_session_starts(
start: datetime,
*,
hours_ahead: int = 36,
) -> list[datetime]:
"""列出 start 之后若干小时内的各段开盘时刻。"""
if start.tzinfo is None:
start = start.replace(tzinfo=TZ)
else:
start = start.astimezone(TZ)
end = start + timedelta(hours=hours_ahead)
out: list[datetime] = []
day = start.replace(hour=0, minute=0, second=0, microsecond=0)
while day <= end:
for h, m in SESSION_OPENS:
if not _session_open_allowed(day, h, m):
continue
dt = day.replace(hour=h, minute=m)
if dt > start and dt <= end:
out.append(dt)
day += timedelta(days=1)
out.sort()
return out
def minutes_until_next_session(now: Optional[datetime] = None) -> Optional[float]:
d = now or datetime.now(TZ)
if d.tzinfo is None:
d = d.replace(tzinfo=TZ)
else:
d = d.astimezone(TZ)
starts = iter_session_starts(d, hours_ahead=48)
if not starts:
return None
return (starts[0] - d).total_seconds() / 60.0
def in_premarket_connect_window(
now: Optional[datetime] = None,
*,
minutes_before: int = 30,
) -> bool:
"""距下一段开盘 <= minutes_before 分钟,且当前尚未进入交易时段。"""
if is_trading_session(now):
return False
mins = minutes_until_next_session(now)
if mins is None:
return False
return 0 < mins <= float(minutes_before)