Use Beijing time for next-open countdown and day open labels.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+108
-17
@@ -118,15 +118,18 @@ def suggest_lot_by_balance(balance: float) -> float:
|
|||||||
return LOT_MID
|
return LOT_MID
|
||||||
|
|
||||||
|
|
||||||
|
def _as_bjt(now: Optional[datetime] = None) -> datetime:
|
||||||
|
dt = now or datetime.now(BJT)
|
||||||
|
if dt.tzinfo is None:
|
||||||
|
return dt.replace(tzinfo=BJT)
|
||||||
|
return dt.astimezone(BJT)
|
||||||
|
|
||||||
|
|
||||||
def current_session_label(now: Optional[datetime] = None, market_open: bool = True) -> str:
|
def current_session_label(now: Optional[datetime] = None, market_open: bool = True) -> str:
|
||||||
"""北京时间盘段:亚盘/欧盘/美盘(可重叠显示);休市单独标注。"""
|
"""北京时间盘段:亚盘/欧盘/美盘(可重叠显示);休市单独标注。"""
|
||||||
if not market_open:
|
if not market_open:
|
||||||
return "休市"
|
return "休市"
|
||||||
dt = now or datetime.now(BJT)
|
dt = _as_bjt(now)
|
||||||
if dt.tzinfo is None:
|
|
||||||
dt = dt.replace(tzinfo=BJT)
|
|
||||||
else:
|
|
||||||
dt = dt.astimezone(BJT)
|
|
||||||
h = dt.hour + dt.minute / 60.0
|
h = dt.hour + dt.minute / 60.0
|
||||||
asia = 8.0 <= h < 16.0
|
asia = 8.0 <= h < 16.0
|
||||||
euro = 15.0 <= h < 24.0
|
euro = 15.0 <= h < 24.0
|
||||||
@@ -143,10 +146,75 @@ def current_session_label(now: Optional[datetime] = None, market_open: bool = Tr
|
|||||||
return "+".join(parts)
|
return "+".join(parts)
|
||||||
|
|
||||||
|
|
||||||
def format_open_elapsed(open_ts: float, market_open: bool) -> str:
|
# 黄金常见时段(一律按北京时间 UTC+8):周一 07:00 开 → 周六 05:00 收;日内约 05:00–07:00
|
||||||
"""开盘后已过多久;休市时显示休市(仍可看下方 OHLC)。"""
|
GOLD_OPEN_HOUR = 7
|
||||||
|
GOLD_OPEN_MINUTE = 0
|
||||||
|
GOLD_CLOSE_HOUR = 5
|
||||||
|
GOLD_CLOSE_MINUTE = 0
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_bjt(dt: datetime, fmt: str = "%m-%d %H:%M") -> str:
|
||||||
|
"""格式化为北京时间字符串(不依赖系统时区)。"""
|
||||||
|
return _as_bjt(dt).strftime(fmt)
|
||||||
|
|
||||||
|
|
||||||
|
def next_market_open(now: Optional[datetime] = None) -> datetime:
|
||||||
|
"""估算下一次开盘时刻,返回带 BJT 时区的 datetime。"""
|
||||||
|
dt = _as_bjt(now)
|
||||||
|
open_hm = (GOLD_OPEN_HOUR, GOLD_OPEN_MINUTE, 0)
|
||||||
|
close_hm = (GOLD_CLOSE_HOUR, GOLD_CLOSE_MINUTE, 0)
|
||||||
|
t = (dt.hour, dt.minute, dt.second)
|
||||||
|
wd = dt.weekday() # Mon=0 … Sun=6
|
||||||
|
|
||||||
|
# 周末休市:周六 05:00(北京)→ 下周一 07:00(北京)
|
||||||
|
if wd == 5 and t >= close_hm:
|
||||||
|
days = 2
|
||||||
|
elif wd == 6:
|
||||||
|
days = 1
|
||||||
|
elif wd == 0 and t < open_hm:
|
||||||
|
days = 0
|
||||||
|
# 工作日日内休市窗口 05:00–07:00(北京)
|
||||||
|
elif wd <= 4 and close_hm <= t < open_hm:
|
||||||
|
days = 0
|
||||||
|
else:
|
||||||
|
# 非预期休市(节假日等):推到下一个可交易日的北京时间 07:00
|
||||||
|
days = 1
|
||||||
|
while True:
|
||||||
|
cand_date = (dt + timedelta(days=days)).date()
|
||||||
|
cwd = datetime(cand_date.year, cand_date.month, cand_date.day, tzinfo=BJT).weekday()
|
||||||
|
if cwd <= 4:
|
||||||
|
break
|
||||||
|
days += 1
|
||||||
|
if days > 10:
|
||||||
|
break
|
||||||
|
|
||||||
|
target = (dt + timedelta(days=days)).date()
|
||||||
|
return datetime(
|
||||||
|
target.year, target.month, target.day,
|
||||||
|
GOLD_OPEN_HOUR, GOLD_OPEN_MINUTE, 0, tzinfo=BJT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def format_countdown(seconds: int) -> str:
|
||||||
|
seconds = max(0, int(seconds))
|
||||||
|
days, rem = divmod(seconds, 86400)
|
||||||
|
hours, rem = divmod(rem, 3600)
|
||||||
|
mins, secs = divmod(rem, 60)
|
||||||
|
if days > 0:
|
||||||
|
return f"距开盘 {days}天{hours}小时{mins}分"
|
||||||
|
if hours > 0:
|
||||||
|
return f"距开盘 {hours}小时{mins}分"
|
||||||
|
if mins > 0:
|
||||||
|
return f"距开盘 {mins}分{secs}秒"
|
||||||
|
return f"距开盘 {secs}秒"
|
||||||
|
|
||||||
|
|
||||||
|
def format_open_elapsed(open_ts: float, market_open: bool, now: Optional[datetime] = None) -> str:
|
||||||
|
"""开盘后已过多久;休市时显示距下次开盘倒计时。"""
|
||||||
if not market_open:
|
if not market_open:
|
||||||
return "休市"
|
nxt = next_market_open(now)
|
||||||
|
left = max(0, int((nxt - _as_bjt(now)).total_seconds()))
|
||||||
|
return format_countdown(left)
|
||||||
elapsed = max(0, int(time.time() - open_ts))
|
elapsed = max(0, int(time.time() - open_ts))
|
||||||
days, rem = divmod(elapsed, 86400)
|
days, rem = divmod(elapsed, 86400)
|
||||||
hours, rem = divmod(rem, 3600)
|
hours, rem = divmod(rem, 3600)
|
||||||
@@ -237,16 +305,20 @@ class Mt5Bridge:
|
|||||||
ask = float(tick.ask)
|
ask = float(tick.ask)
|
||||||
high = max(high, bid, ask)
|
high = max(high, bid, ask)
|
||||||
low = min(low, bid, ask)
|
low = min(low, bid, ask)
|
||||||
bar_day = datetime.fromtimestamp(open_ts).strftime("%m-%d")
|
open_dt = datetime.fromtimestamp(open_ts, BJT)
|
||||||
|
bar_day = open_dt.strftime("%m-%d")
|
||||||
|
nxt = None if market_open else next_market_open()
|
||||||
return {
|
return {
|
||||||
"open": open_px,
|
"open": open_px,
|
||||||
"high": high,
|
"high": high,
|
||||||
"low": low,
|
"low": low,
|
||||||
"open_time": datetime.fromtimestamp(open_ts),
|
"open_time": open_dt,
|
||||||
"open_ts": open_ts,
|
"open_ts": open_ts,
|
||||||
"bar_day": bar_day,
|
"bar_day": bar_day,
|
||||||
"market_open": market_open,
|
"market_open": market_open,
|
||||||
"elapsed_text": format_open_elapsed(float(open_ts), market_open),
|
"elapsed_text": format_open_elapsed(float(open_ts), market_open),
|
||||||
|
"next_open": nxt,
|
||||||
|
"next_open_text": fmt_bjt(nxt) if nxt else "",
|
||||||
}
|
}
|
||||||
|
|
||||||
def atr(self, symbol: str, period: int = ATR_PERIOD) -> float:
|
def atr(self, symbol: str, period: int = ATR_PERIOD) -> float:
|
||||||
@@ -1460,8 +1532,12 @@ class OrderApp(tk.Tk):
|
|||||||
self.lbl_account.configure(text="余额 --")
|
self.lbl_account.configure(text="余额 --")
|
||||||
|
|
||||||
sess = current_session_label(market_open=market_open)
|
sess = current_session_label(market_open=market_open)
|
||||||
|
if not market_open:
|
||||||
|
sess_txt = f"休市 · {format_open_elapsed(0, False)}"
|
||||||
|
else:
|
||||||
|
sess_txt = f"时段 {sess}"
|
||||||
self.lbl_session.configure(
|
self.lbl_session.configure(
|
||||||
text=f"时段 {sess}",
|
text=sess_txt,
|
||||||
bg="#3a2820" if not market_open else "#243044",
|
bg="#3a2820" if not market_open else "#243044",
|
||||||
fg=COLOR_WARN if not market_open else COLOR_ACCENT,
|
fg=COLOR_WARN if not market_open else COLOR_ACCENT,
|
||||||
)
|
)
|
||||||
@@ -1493,11 +1569,18 @@ class OrderApp(tk.Tk):
|
|||||||
f"{amp:.{digits}f} ({amp_pct:.2f}%)",
|
f"{amp:.{digits}f} ({amp_pct:.2f}%)",
|
||||||
COLOR_ACCENT,
|
COLOR_ACCENT,
|
||||||
)
|
)
|
||||||
ot = day["open_time"].strftime("%m-%d %H:%M")
|
ot = fmt_bjt(day["open_time"])
|
||||||
tag = "交易中" if day["market_open"] else "休市 · 显示最近交易日"
|
if day["market_open"]:
|
||||||
self.lbl_day_ohlc.configure(text=f"{tag} · 日线开盘 {ot}")
|
tag = "交易中"
|
||||||
|
else:
|
||||||
|
nxt_txt = day.get("next_open_text") or ""
|
||||||
|
tag = f"休市 · {day['elapsed_text']}"
|
||||||
|
if nxt_txt:
|
||||||
|
tag += f"(预计开盘 {nxt_txt} 北京时间)"
|
||||||
|
self.lbl_day_ohlc.configure(text=f"{tag} · 日线开盘 {ot} 北京时间")
|
||||||
except Exception:
|
except Exception:
|
||||||
self._set_metric("elapsed", "休市" if not market_open else "--", COLOR_WARN)
|
left = format_open_elapsed(0, False) if not market_open else "--"
|
||||||
|
self._set_metric("elapsed", left, COLOR_WARN)
|
||||||
self._set_metric("open", "--")
|
self._set_metric("open", "--")
|
||||||
self._set_metric("high", "--")
|
self._set_metric("high", "--")
|
||||||
self._set_metric("low", "--")
|
self._set_metric("low", "--")
|
||||||
@@ -1531,8 +1614,16 @@ class OrderApp(tk.Tk):
|
|||||||
f"{amp:.{digits}f} ({amp_pct:.2f}%)",
|
f"{amp:.{digits}f} ({amp_pct:.2f}%)",
|
||||||
COLOR_ACCENT,
|
COLOR_ACCENT,
|
||||||
)
|
)
|
||||||
self.lbl_session.configure(text="时段 休市", bg="#3a2820", fg=COLOR_WARN)
|
self.lbl_session.configure(
|
||||||
self.lbl_day_ohlc.configure(text="休市 · 显示最近交易日日线")
|
text=f"休市 · {day['elapsed_text']}",
|
||||||
|
bg="#3a2820",
|
||||||
|
fg=COLOR_WARN,
|
||||||
|
)
|
||||||
|
nxt = day.get("next_open_text") or ""
|
||||||
|
tip = f"休市 · {day['elapsed_text']}"
|
||||||
|
if nxt:
|
||||||
|
tip += f"(预计开盘 {nxt} 北京时间)"
|
||||||
|
self.lbl_day_ohlc.configure(text=f"{tip} · 显示最近交易日日线")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user