Add key-level auto trade, AI analysis, and trading UX improvements.
Key monitors use 5m close triggers with WeChat alerts and box/convergence auto orders; add pending-order worker, structured WeChat notify, AI settings/messages, session clock, CTP margin sizing, and dual-layer position limits. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -106,6 +106,96 @@ def minutes_until_next_session(now: Optional[datetime] = None) -> Optional[float
|
||||
return (starts[0] - d).total_seconds() / 60.0
|
||||
|
||||
|
||||
def _session_open_label(dt: datetime) -> str:
|
||||
h, m = dt.hour, dt.minute
|
||||
if (h, m) == (9, 0):
|
||||
return "日盘开盘"
|
||||
if (h, m) == (13, 30):
|
||||
return "午盘开盘"
|
||||
if (h, m) == (21, 0):
|
||||
return "夜盘开盘"
|
||||
return "开盘"
|
||||
|
||||
|
||||
def _fmt_countdown(seconds: int) -> str:
|
||||
s = max(0, int(seconds))
|
||||
h, rem = divmod(s, 3600)
|
||||
m, sec = divmod(rem, 60)
|
||||
if h > 0:
|
||||
return f"{h}小时{m:02d}分{sec:02d}秒"
|
||||
if m > 0:
|
||||
return f"{m}分{sec:02d}秒"
|
||||
return f"{sec}秒"
|
||||
|
||||
|
||||
def _day_close_dt(d: datetime) -> datetime:
|
||||
return d.replace(hour=15, minute=0, second=0, microsecond=0)
|
||||
|
||||
|
||||
def _night_close_dt(d: datetime) -> datetime:
|
||||
t = d.hour * 60 + d.minute
|
||||
if t >= 21 * 60:
|
||||
nxt = (d + timedelta(days=1)).replace(hour=2, minute=30, second=0, microsecond=0)
|
||||
return nxt
|
||||
return d.replace(hour=2, minute=30, second=0, microsecond=0)
|
||||
|
||||
|
||||
def _current_break_close(d: datetime) -> tuple[Optional[datetime], Optional[datetime], Optional[str], Optional[str]]:
|
||||
"""当前交易段内的休盘/收盘时刻与标签。"""
|
||||
t = d.hour * 60 + d.minute
|
||||
if 9 * 60 <= t < 11 * 60 + 30:
|
||||
br = d.replace(hour=11, minute=30, second=0, microsecond=0)
|
||||
cl = _day_close_dt(d)
|
||||
return br, cl, "午间休盘", "日盘收盘"
|
||||
if 13 * 60 + 30 <= t < 15 * 60:
|
||||
cl = _day_close_dt(d)
|
||||
return None, cl, None, "日盘收盘"
|
||||
if t >= 21 * 60 or t < 2 * 60 + 30:
|
||||
cl = _night_close_dt(d)
|
||||
return None, cl, None, "夜盘收盘"
|
||||
return None, None, None, None
|
||||
|
||||
|
||||
def trading_session_clock(now: Optional[datetime] = None) -> dict:
|
||||
"""顶栏展示:当前时间、交易状态、距开盘/休盘/收盘倒计时。"""
|
||||
d = now or datetime.now(TZ)
|
||||
if d.tzinfo is None:
|
||||
d = d.replace(tzinfo=TZ)
|
||||
else:
|
||||
d = d.astimezone(TZ)
|
||||
in_sess = is_trading_session(d)
|
||||
out = {
|
||||
"now": d.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"now_time": d.strftime("%m-%d %H:%M:%S"),
|
||||
"in_session": in_sess,
|
||||
"status_label": "交易时间段" if in_sess else "非交易时间段",
|
||||
}
|
||||
if not in_sess:
|
||||
starts = iter_session_starts(d, hours_ahead=72)
|
||||
if starts:
|
||||
nxt = starts[0]
|
||||
secs = int(max(0, (nxt - d).total_seconds()))
|
||||
out["next_open_at"] = nxt.strftime("%m-%d %H:%M")
|
||||
out["next_open_label"] = _session_open_label(nxt)
|
||||
out["secs_to_open"] = secs
|
||||
out["countdown_open"] = _fmt_countdown(secs)
|
||||
return out
|
||||
br, cl, br_label, cl_label = _current_break_close(d)
|
||||
if br and br > d:
|
||||
secs = int((br - d).total_seconds())
|
||||
out["break_at"] = br.strftime("%H:%M")
|
||||
out["break_label"] = br_label or "休盘"
|
||||
out["secs_to_break"] = secs
|
||||
out["countdown_break"] = _fmt_countdown(secs)
|
||||
if cl and cl > d:
|
||||
secs = int((cl - d).total_seconds())
|
||||
out["close_at"] = cl.strftime("%H:%M")
|
||||
out["close_label"] = cl_label or "收盘"
|
||||
out["secs_to_close"] = secs
|
||||
out["countdown_close"] = _fmt_countdown(secs)
|
||||
return out
|
||||
|
||||
|
||||
def in_premarket_connect_window(
|
||||
now: Optional[datetime] = None,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user