diff --git a/desktop/gold_order_app.py b/desktop/gold_order_app.py index d633fa9..0ee08c7 100644 --- a/desktop/gold_order_app.py +++ b/desktop/gold_order_app.py @@ -65,6 +65,12 @@ COLOR_BUY = "#108040" COLOR_SELL = "#a02828" COLOR_BTN_OFF = "#555555" COLOR_WARN = "#e6a23c" +COLOR_BG = "#121218" +COLOR_CARD = "#1a1a22" +COLOR_CARD2 = "#22222e" +COLOR_MUTED = "#8a8a9c" +COLOR_TEXT = "#f0f0f5" +COLOR_ACCENT = "#4cc2ff" BE_MIN_MOVE_USD = 3.0 # 价格向有利方向走出该美金距离才可保本(非账户浮盈) BE_OFFSET_USD = 1.0 # 保本止损 = 开仓价 ± 1$ @@ -109,8 +115,10 @@ def suggest_lot_by_balance(balance: float) -> float: return LOT_MID -def current_session_label(now: Optional[datetime] = None) -> str: - """北京时间盘段:亚盘/欧盘/美盘(可重叠显示)。""" +def current_session_label(now: Optional[datetime] = None, market_open: bool = True) -> str: + """北京时间盘段:亚盘/欧盘/美盘(可重叠显示);休市单独标注。""" + if not market_open: + return "休市" dt = now or datetime.now(BJT) if dt.tzinfo is None: dt = dt.replace(tzinfo=BJT) @@ -132,6 +140,21 @@ def current_session_label(now: Optional[datetime] = None) -> str: return "+".join(parts) +def format_open_elapsed(open_ts: float, market_open: bool) -> str: + """开盘后已过多久;休市时显示休市(仍可看下方 OHLC)。""" + if not market_open: + return "休市" + elapsed = max(0, int(time.time() - open_ts)) + days, rem = divmod(elapsed, 86400) + hours, rem = divmod(rem, 3600) + mins, _ = divmod(rem, 60) + if days > 0: + return f"已开盘 {days}天{hours}小时{mins}分" + if hours > 0: + return f"已开盘 {hours}小时{mins}分" + return f"已开盘 {mins}分" + + class TradeError(Exception): pass @@ -179,29 +202,48 @@ class Mt5Bridge: raise TradeError(f"无法读取品种信息 {symbol}") return info - def day_ohlc(self, symbol: str) -> dict[str, Any]: - """今日(服务器日线)开高低;用最新 tick 实时修正最高/最低。""" + def market_is_open(self, symbol: str) -> bool: + """根据最新 tick 是否新鲜判断是否在交易(休市也仍可读日线)。""" self.ensure_symbol(symbol) - rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_D1, 0, 1) + info = mt5.symbol_info(symbol) + if info is not None and hasattr(info, "trade_mode"): + # 完全禁止交易 + if int(info.trade_mode) == 0: + return False + tick = mt5.symbol_info_tick(symbol) + if tick is None: + return False + age = time.time() - float(tick.time) + return age < 180 # 3 分钟内有报价视为开市 + + def day_ohlc(self, symbol: str) -> dict[str, Any]: + """最近交易日日线开高低(休市仍显示);开市时用 tick 修正最高/最低。""" + self.ensure_symbol(symbol) + rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_D1, 0, 2) if rates is None or len(rates) < 1: - raise TradeError("无法读取今日日线,请确认品种有 D1 数据") + raise TradeError("无法读取日线,请确认品种有 D1 数据") bar = rates[0] open_px = float(bar["open"]) high = float(bar["high"]) low = float(bar["low"]) open_ts = int(bar["time"]) + market_open = self.market_is_open(symbol) tick = mt5.symbol_info_tick(symbol) - if tick is not None: + if market_open and tick is not None: bid = float(tick.bid) ask = float(tick.ask) high = max(high, bid, ask) low = min(low, bid, ask) + bar_day = datetime.fromtimestamp(open_ts).strftime("%m-%d") return { "open": open_px, "high": high, "low": low, "open_time": datetime.fromtimestamp(open_ts), "open_ts": open_ts, + "bar_day": bar_day, + "market_open": market_open, + "elapsed_text": format_open_elapsed(float(open_ts), market_open), } def atr(self, symbol: str, period: int = ATR_PERIOD) -> float: @@ -656,9 +698,9 @@ class OrderApp(tk.Tk): super().__init__() _init_mt5_constants() self.title(APP_TITLE) - self.geometry("1080x940") - self.minsize(980, 860) - self.configure(bg="#1e1e1e") + self.geometry("1100x980") + self.minsize(1000, 900) + self.configure(bg=COLOR_BG) try: self.tk.call("tk", "scaling", 1.45) except tk.TclError: @@ -670,113 +712,165 @@ class OrderApp(tk.Tk): self._be_done_tickets: set = set() # 已拉过保本的单号,不再判断 self._pos_sig = None self._pos_profit_labels: dict = {} + self._metric_values: dict = {} self._build_ui() self.after(200, self._try_connect_and_refresh) self.after(3000, self._hub_heartbeat) + def _card(self, parent: tk.Misc, pad: int = 12) -> tk.Frame: + outer = tk.Frame(parent, bg=COLOR_BG) + card = tk.Frame( + outer, + bg=COLOR_CARD, + highlightthickness=1, + highlightbackground="#2a2a38", + highlightcolor="#2a2a38", + ) + card.pack(fill=tk.BOTH, expand=True, padx=0, pady=0) + inner = tk.Frame(card, bg=COLOR_CARD) + inner.pack(fill=tk.BOTH, expand=True, padx=pad, pady=pad) + outer._inner = inner # type: ignore[attr-defined] + return outer + + def _metric_box(self, parent: tk.Misc, key: str, title: str) -> None: + box = tk.Frame(parent, bg=COLOR_CARD2, highlightthickness=0) + box.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=4) + tk.Label( + box, text=title, bg=COLOR_CARD2, fg=COLOR_MUTED, + font=("Microsoft YaHei UI", 10), + ).pack(anchor="w", padx=10, pady=(8, 0)) + val = tk.Label( + box, text="--", bg=COLOR_CARD2, fg=COLOR_TEXT, + font=("Microsoft YaHei UI", 13, "bold"), + ) + val.pack(anchor="w", padx=10, pady=(2, 8)) + self._metric_values[key] = val + def _build_ui(self) -> None: style = ttk.Style(self) try: style.theme_use("clam") except tk.TclError: pass - style.configure("TLabel", background="#1e1e1e", foreground="#f0f0f0", font=("Microsoft YaHei UI", 12)) - style.configure("Status.TLabel", background="#1e1e1e", foreground="#c0c0c0", font=("Microsoft YaHei UI", 11)) - style.configure("TFrame", background="#1e1e1e") - style.configure("TEntry", fieldbackground="#2b2b2b", foreground="#ffffff", font=("Microsoft YaHei UI", 12)) - style.configure("TCheckbutton", background="#1e1e1e", foreground="#f0f0f0", font=("Microsoft YaHei UI", 11)) - root = ttk.Frame(self, padding=18) - root.pack(fill=tk.BOTH, expand=True) + style.configure("TLabel", background=COLOR_BG, foreground=COLOR_TEXT, font=("Microsoft YaHei UI", 12)) + style.configure("Status.TLabel", background=COLOR_BG, foreground=COLOR_MUTED, font=("Microsoft YaHei UI", 11)) + style.configure("TFrame", background=COLOR_BG) + style.configure("Card.TFrame", background=COLOR_CARD) + style.configure("TEntry", fieldbackground="#2a2a36", foreground="#ffffff", font=("Microsoft YaHei UI", 12)) + style.configure("TCheckbutton", background=COLOR_CARD, foreground=COLOR_TEXT, font=("Microsoft YaHei UI", 11)) + style.map("TCheckbutton", background=[("active", COLOR_CARD)]) - head = ttk.Frame(root) - head.pack(fill=tk.X) - ttk.Label(head, text="品种", font=("Microsoft YaHei UI", 13, "bold")).pack(side=tk.LEFT) + root = tk.Frame(self, bg=COLOR_BG) + root.pack(fill=tk.BOTH, expand=True, padx=16, pady=14) + + # —— 顶栏 —— + head_outer = self._card(root, pad=12) + head_outer.pack(fill=tk.X, pady=(0, 10)) + head = head_outer._inner # type: ignore[attr-defined] + left = tk.Frame(head, bg=COLOR_CARD) + left.pack(side=tk.LEFT) + tk.Label(left, text=APP_TITLE, bg=COLOR_CARD, fg=COLOR_ACCENT, + font=("Microsoft YaHei UI", 14, "bold")).pack(side=tk.LEFT) + tk.Label(left, text=" 品种", bg=COLOR_CARD, fg=COLOR_MUTED, + font=("Microsoft YaHei UI", 11)).pack(side=tk.LEFT, padx=(16, 4)) self.var_symbol = tk.StringVar(value=DEFAULT_SYMBOL) - ttk.Entry(head, textvariable=self.var_symbol, width=16).pack(side=tk.LEFT, padx=8) - self.lbl_account = ttk.Label(head, text="余额 --", font=("Microsoft YaHei UI", 13, "bold")) - self.lbl_account.pack(side=tk.LEFT, padx=12) - - quote_row = ttk.Frame(root) - quote_row.pack(fill=tk.X, pady=(10, 0)) - self.lbl_quote = ttk.Label(quote_row, text="Bid -- Ask --", font=("Microsoft YaHei UI", 14, "bold")) - self.lbl_quote.pack(side=tk.LEFT) - self.lbl_session = tk.Label( - quote_row, - text="时段 --", - bg="#2a3a55", - fg="#7ec8ff", + ttk.Entry(left, textvariable=self.var_symbol, width=14).pack(side=tk.LEFT) + self.lbl_account = tk.Label( + left, text="余额 --", bg=COLOR_CARD, fg=COLOR_TEXT, font=("Microsoft YaHei UI", 12, "bold"), - padx=10, - pady=2, + ) + self.lbl_account.pack(side=tk.LEFT, padx=16) + self.lbl_session = tk.Label( + head, text="时段 --", bg="#243044", fg=COLOR_ACCENT, + font=("Microsoft YaHei UI", 11, "bold"), padx=12, pady=4, ) self.lbl_session.pack(side=tk.RIGHT) - self.lbl_info = ttk.Label(root, text="点差 -- ATR -- SL$ -- TP$ -- 持仓 --", font=("Microsoft YaHei UI", 12)) - self.lbl_info.pack(anchor="w", pady=(4, 2)) - self.lbl_day_ohlc = ttk.Label( - root, - text="开盘 -- | 今开 -- | 最高 -- | 最低 --", - font=("Microsoft YaHei UI", 12), + # —— 报价 + 日线指标 —— + quote_outer = self._card(root, pad=12) + quote_outer.pack(fill=tk.X, pady=(0, 10)) + q = quote_outer._inner # type: ignore[attr-defined] + self.lbl_quote = tk.Label( + q, text="Bid -- Ask --", bg=COLOR_CARD, fg=COLOR_TEXT, + font=("Microsoft YaHei UI", 18, "bold"), ) - self.lbl_day_ohlc.pack(anchor="w", pady=(2, 2)) - today_row = ttk.Frame(root) - today_row.pack(fill=tk.X, pady=(0, 12)) + self.lbl_quote.pack(anchor="w") + self.lbl_info = tk.Label( + q, text="点差 -- ATR -- SL$ -- TP$ -- 持仓 --", + bg=COLOR_CARD, fg=COLOR_MUTED, font=("Microsoft YaHei UI", 11), + ) + self.lbl_info.pack(anchor="w", pady=(4, 10)) + + metrics = tk.Frame(q, bg=COLOR_CARD) + metrics.pack(fill=tk.X) + self._metric_box(metrics, "elapsed", "开盘时长") + self._metric_box(metrics, "open", "开盘价") + self._metric_box(metrics, "high", "最高价") + self._metric_box(metrics, "low", "最低价") + self.lbl_day_ohlc = tk.Label( + q, text="", bg=COLOR_CARD, fg=COLOR_MUTED, font=("Microsoft YaHei UI", 10), + ) + self.lbl_day_ohlc.pack(anchor="w", pady=(8, 0)) + + # —— 今日战绩 —— + today_outer = self._card(root, pad=10) + today_outer.pack(fill=tk.X, pady=(0, 10)) + today_row = today_outer._inner # type: ignore[attr-defined] self.lbl_today_base = tk.Label( - today_row, text="今日开单 -- 次 | ", bg="#1e1e1e", fg=COLOR_NEUTRAL, + today_row, text="今日开单 -- 次", bg=COLOR_CARD, fg=COLOR_MUTED, font=("Microsoft YaHei UI", 11), ) self.lbl_today_base.pack(side=tk.LEFT) self.lbl_today_closed = tk.Label( - today_row, text="已平 --", bg="#1e1e1e", fg=COLOR_NEUTRAL, + today_row, text=" 已平 --", bg=COLOR_CARD, fg=COLOR_NEUTRAL, font=("Microsoft YaHei UI", 11, "bold"), ) self.lbl_today_closed.pack(side=tk.LEFT) self.lbl_today_mid = tk.Label( - today_row, text=" | 浮盈 -- | 合计 ", bg="#1e1e1e", fg=COLOR_NEUTRAL, + today_row, text=" 浮盈 -- 合计 ", bg=COLOR_CARD, fg=COLOR_MUTED, font=("Microsoft YaHei UI", 11), ) self.lbl_today_mid.pack(side=tk.LEFT) self.lbl_today_total = tk.Label( - today_row, text="--", bg="#1e1e1e", fg=COLOR_NEUTRAL, + today_row, text="--", bg=COLOR_CARD, fg=COLOR_NEUTRAL, font=("Microsoft YaHei UI", 11, "bold"), ) self.lbl_today_total.pack(side=tk.LEFT) self.lbl_today_ratio = tk.Label( - today_row, text=" | 盈亏比 -- | 盈利率 --", bg="#1e1e1e", fg=COLOR_NEUTRAL, + today_row, text=" 盈亏比 -- 盈利率 --", bg=COLOR_CARD, fg=COLOR_MUTED, font=("Microsoft YaHei UI", 11, "bold"), ) self.lbl_today_ratio.pack(side=tk.LEFT) self.lbl_day_lock = tk.Label( - today_row, - text="", - bg="#1e1e1e", - fg=COLOR_WARN, - font=("Microsoft YaHei UI", 11, "bold"), - padx=8, - pady=2, + today_row, text="", bg=COLOR_CARD, fg=COLOR_WARN, + font=("Microsoft YaHei UI", 11, "bold"), padx=8, pady=2, ) self.lbl_day_lock.pack(side=tk.RIGHT) - form = ttk.Frame(root) + # —— 下单参数 —— + form_outer = self._card(root, pad=12) + form_outer.pack(fill=tk.X, pady=(0, 10)) + form_wrap = form_outer._inner # type: ignore[attr-defined] + form = ttk.Frame(form_wrap) form.pack(fill=tk.X) for i in range(3): form.columnconfigure(i, weight=1) - - ttk.Label(form, text="手数").grid(row=0, column=0, sticky="w") - self.lbl_sl_title = ttk.Label(form, text="SL倍") + style.configure("Form.TLabel", background=COLOR_CARD, foreground=COLOR_MUTED, font=("Microsoft YaHei UI", 11)) + ttk.Label(form, text="手数", style="Form.TLabel").grid(row=0, column=0, sticky="w") + self.lbl_sl_title = ttk.Label(form, text="SL倍", style="Form.TLabel") self.lbl_sl_title.grid(row=0, column=1, sticky="w", padx=6) - self.lbl_tp_title = ttk.Label(form, text="TP倍") + self.lbl_tp_title = ttk.Label(form, text="TP倍", style="Form.TLabel") self.lbl_tp_title.grid(row=0, column=2, sticky="w") self.var_lot = tk.StringVar(value=f"{DEFAULT_LOT:.2f}") self.var_atr_mult = tk.StringVar(value=f"{DEFAULT_ATR_MULT:.2f}") self.var_tp = tk.StringVar(value=f"{DEFAULT_TP_ATR_MULT:.2f}") - ttk.Entry(form, textvariable=self.var_lot, width=12).grid(row=1, column=0, sticky="ew", pady=8, ipady=4) - ttk.Entry(form, textvariable=self.var_atr_mult, width=12).grid(row=1, column=1, sticky="ew", padx=8, pady=8, ipady=4) - ttk.Entry(form, textvariable=self.var_tp, width=12).grid(row=1, column=2, sticky="ew", pady=8, ipady=4) + ttk.Entry(form, textvariable=self.var_lot, width=12).grid(row=1, column=0, sticky="ew", pady=6, ipady=5) + ttk.Entry(form, textvariable=self.var_atr_mult, width=12).grid(row=1, column=1, sticky="ew", padx=8, pady=6, ipady=5) + ttk.Entry(form, textvariable=self.var_tp, width=12).grid(row=1, column=2, sticky="ew", pady=6, ipady=5) - opt = ttk.Frame(root) - opt.pack(fill=tk.X, pady=(0, 10)) + opt = tk.Frame(form_wrap, bg=COLOR_CARD) + opt.pack(fill=tk.X, pady=(6, 0)) self.var_use_atr = tk.BooleanVar(value=True) ttk.Checkbutton( opt, @@ -784,37 +878,29 @@ class OrderApp(tk.Tk): variable=self.var_use_atr, command=self._sync_sl_label, ).pack(side=tk.LEFT) - self._mk_btn(opt, "按资金手数", "#3a5a80", self.on_auto_lot, h=34, w=110).pack(side=tk.LEFT, padx=10) - ttk.Label( + self._mk_btn(opt, "按资金手数", "#3a5a80", self.on_auto_lot, h=34, w=110).pack(side=tk.LEFT, padx=12) + tk.Label( opt, - text=( - f"点一下按余额改手数:≥{BALANCE_HIGH:.0f}→{LOT_HIGH:.2f}," - f"{BALANCE_LOW:.0f}~→{LOT_MID:.2f},<{BALANCE_LOW:.0f}→{LOT_LOW:.2f};" - f"同向≤{MAX_SIDE_LOTS:.2f} · 最多{MAX_OPEN_POSITIONS}笔" - ), - style="Status.TLabel", + text=f"小资金点一下→{LOT_LOW:.2f}手 · 同向≤{MAX_SIDE_LOTS:.2f} · 最多{MAX_OPEN_POSITIONS}笔", + bg=COLOR_CARD, fg=COLOR_MUTED, font=("Microsoft YaHei UI", 10), ).pack(side=tk.LEFT) self.lbl_open_block = tk.Label( - root, - text="", - bg="#1e1e1e", - fg=COLOR_WARN, - font=("Microsoft YaHei UI", 12, "bold"), - anchor="center", + root, text="", bg=COLOR_BG, fg=COLOR_WARN, + font=("Microsoft YaHei UI", 12, "bold"), anchor="center", ) self.lbl_open_block.pack(fill=tk.X, pady=(0, 4)) - btns = ttk.Frame(root) - btns.pack(fill=tk.X, pady=6) + btns = tk.Frame(root, bg=COLOR_BG) + btns.pack(fill=tk.X, pady=4) btns.columnconfigure(0, weight=1) btns.columnconfigure(1, weight=1) - self.btn_buy = self._mk_btn(btns, "买入", COLOR_BUY, self.on_buy, h=46) + self.btn_buy = self._mk_btn(btns, "买入", COLOR_BUY, self.on_buy, h=48) self.btn_buy.grid(row=0, column=0, sticky="ew", padx=(0, 6), ipady=8) - self.btn_sell = self._mk_btn(btns, "卖出", COLOR_SELL, self.on_sell, h=46) + self.btn_sell = self._mk_btn(btns, "卖出", COLOR_SELL, self.on_sell, h=48) self.btn_sell.grid(row=0, column=1, sticky="ew", padx=(6, 0), ipady=8) - close = ttk.Frame(root) + close = tk.Frame(root, bg=COLOR_BG) close.pack(fill=tk.X, pady=8) for i in range(3): close.columnconfigure(i, weight=1) @@ -822,12 +908,18 @@ class OrderApp(tk.Tk): self._mk_btn(close, "平空", "#5a3232", self.on_close_sell, h=38).grid(row=0, column=1, sticky="ew", padx=4, ipady=4) self._mk_btn(close, "全平", "#464628", self.on_close_all, h=38).grid(row=0, column=2, sticky="ew", padx=(4, 0), ipady=4) - self.lbl_status = ttk.Label(root, text="就绪", style="Status.TLabel", wraplength=900) - self.lbl_status.pack(anchor="w", pady=(12, 6)) + self.lbl_status = tk.Label( + root, text="就绪", bg=COLOR_BG, fg=COLOR_MUTED, + font=("Microsoft YaHei UI", 11), anchor="w", wraplength=960, + ) + self.lbl_status.pack(fill=tk.X, pady=(8, 4)) - pos_bar = ttk.Frame(root) - pos_bar.pack(fill=tk.X, pady=(10, 6)) - ttk.Label(pos_bar, text="持仓记录", font=("Microsoft YaHei UI", 13, "bold")).pack(side=tk.LEFT) + pos_bar = tk.Frame(root, bg=COLOR_BG) + pos_bar.pack(fill=tk.X, pady=(8, 6)) + tk.Label( + pos_bar, text="持仓记录", bg=COLOR_BG, fg=COLOR_TEXT, + font=("Microsoft YaHei UI", 13, "bold"), + ).pack(side=tk.LEFT) self.btn_break_even = self._mk_btn(pos_bar, "保本", COLOR_BE, self.on_break_even, h=34, w=90) self.btn_break_even.pack(side=tk.RIGHT, padx=4) self._mk_btn(pos_bar, "打开复盘", "#3a5a80", self.on_open_review, h=34, w=100).pack( @@ -880,14 +972,18 @@ class OrderApp(tk.Tk): self.pos_canvas.bind("", lambda e: self.pos_canvas.bind_all("", self._on_pos_mousewheel)) self.pos_canvas.bind("", lambda e: self.pos_canvas.unbind_all("")) - tip = ttk.Label( + tip = tk.Label( root, text=( - f"最多同时 {MAX_OPEN_POSITIONS} 笔持仓;同向≤{MAX_SIDE_LOTS:.2f} 手。" - "亏≥5%或盈≥10%达标后禁止开仓。保本:价格朝有利方向走出>3$ 后改止损到开仓价±1$。" + f"最多同时 {MAX_OPEN_POSITIONS} 笔 · 同向≤{MAX_SIDE_LOTS:.2f} 手 · " + "亏≥5%或盈≥10%禁止开仓 · 保本:有利方向>3$ 后止损到开仓价±1$" ), - style="Status.TLabel", - wraplength=920, + bg=COLOR_BG, + fg=COLOR_MUTED, + font=("Microsoft YaHei UI", 10), + wraplength=960, + justify=tk.LEFT, + anchor="w", ) tip.pack(anchor="w", pady=(8, 0)) @@ -938,7 +1034,7 @@ class OrderApp(tk.Tk): return s or DEFAULT_SYMBOL def set_status(self, text: str, ok: bool = True) -> None: - self.lbl_status.configure(text=text, foreground="#7CFC00" if ok else "#FF7F50") + self.lbl_status.configure(text=text, fg="#7CFC00" if ok else "#FF7F50") def _parse_float(self, value: str, name: str) -> float: s = value.strip().replace(",", ".") @@ -1273,18 +1369,35 @@ class OrderApp(tk.Tk): self.refresh_quotes() self.after(REFRESH_MS, self._try_connect_and_refresh) + def _set_metric(self, key: str, text: str, fg: str = COLOR_TEXT) -> None: + lbl = self._metric_values.get(key) + if lbl is not None: + lbl.configure(text=text, fg=fg) + def refresh_quotes(self) -> None: symbol = self._symbol() try: if not self.bridge.connected: self.bridge.connect() - t = self.bridge.tick(symbol) info = self.bridge.symbol_info(symbol) digits = int(info.digits) - bid = float(t.bid) - ask = float(t.ask) - spread = ask - bid - buy, sell, profit = self.bridge.exposure(symbol) + market_open = False + bid = ask = 0.0 + try: + t = self.bridge.tick(symbol) + bid = float(t.bid) + ask = float(t.ask) + market_open = self.bridge.market_is_open(symbol) + except TradeError: + bid = float(getattr(info, "bid", 0) or getattr(info, "last", 0) or 0) + ask = float(getattr(info, "ask", 0) or bid) + market_open = False + + spread = ask - bid if ask and bid else 0.0 + try: + buy, sell, profit = self.bridge.exposure(symbol) + except TradeError: + buy = sell = profit = 0.0 net = buy - sell pos = f"{net:.2f}" if buy > 0 and sell > 0: @@ -1311,38 +1424,62 @@ class OrderApp(tk.Tk): except TradeError: self.lbl_account.configure(text="余额 --") - self.lbl_quote.configure(text=f"Bid {bid:.{digits}f} Ask {ask:.{digits}f}") - self.lbl_session.configure(text=f"时段 {current_session_label()}") + sess = current_session_label(market_open=market_open) + self.lbl_session.configure( + text=f"时段 {sess}", + bg="#3a2820" if not market_open else "#243044", + fg=COLOR_WARN if not market_open else COLOR_ACCENT, + ) + if bid > 0: + self.lbl_quote.configure(text=f"Bid {bid:.{digits}f} Ask {ask:.{digits}f}") + else: + self.lbl_quote.configure(text="Bid -- Ask --") self.lbl_info.configure( text=( - f"点差 {spread:.{digits}f} ATRM15 {atr_txt} " - f"SL${sl_txt} TP${tp_txt} 持仓 {pos} 浮盈 {profit:.2f}" + f"点差 {spread:.{digits}f} ATR {atr_txt} " + f"SL${sl_txt} TP${tp_txt} 持仓 {pos} 浮盈 {profit:.2f}" ) ) try: day = self.bridge.day_ohlc(symbol) - ot = day["open_time"].strftime("%H:%M") - self.lbl_day_ohlc.configure( - text=( - f"开盘 {ot} | 今开 {day['open']:.{digits}f} | " - f"最高 {day['high']:.{digits}f} | 最低 {day['low']:.{digits}f}" - ) - ) + elapsed_fg = COLOR_WARN if not day["market_open"] else COLOR_ACCENT + self._set_metric("elapsed", day["elapsed_text"], elapsed_fg) + self._set_metric("open", f"{day['open']:.{digits}f}") + self._set_metric("high", f"{day['high']:.{digits}f}", COLOR_PROFIT) + self._set_metric("low", f"{day['low']:.{digits}f}", COLOR_LOSS) + ot = day["open_time"].strftime("%m-%d %H:%M") + tag = "交易中" if day["market_open"] else "休市 · 显示最近交易日" + self.lbl_day_ohlc.configure(text=f"{tag} · 日线开盘 {ot}") except Exception: - self.lbl_day_ohlc.configure(text="开盘 -- | 今开 -- | 最高 -- | 最低 --") + self._set_metric("elapsed", "休市" if not market_open else "--", COLOR_WARN) + self._set_metric("open", "--") + self._set_metric("high", "--") + self._set_metric("low", "--") + self.lbl_day_ohlc.configure(text="日线暂不可用(休市也会尽量显示最近交易日)") try: self._refresh_today_stats(symbol) except Exception: - self.lbl_today_base.configure(text="今日开单 -- 次 | ") - self.lbl_today_closed.configure(text="已平 --", fg=COLOR_NEUTRAL) - self.lbl_today_mid.configure(text=" | 浮盈 -- | 合计 ") + self.lbl_today_base.configure(text="今日开单 -- 次") + self.lbl_today_closed.configure(text=" 已平 --", fg=COLOR_NEUTRAL) + self.lbl_today_mid.configure(text=" 浮盈 -- 合计 ") self.lbl_today_total.configure(text="--", fg=COLOR_NEUTRAL) - self.lbl_today_ratio.configure(text=" | 盈亏比 -- | 盈利率 --", fg=COLOR_NEUTRAL) + self.lbl_today_ratio.configure(text=" 盈亏比 -- 盈利率 --", fg=COLOR_NEUTRAL) self._refresh_positions(symbol, digits) self._sync_open_buttons(symbol) except TradeError: - pass + # 断连时仍尝试刷新日线卡片 + try: + day = self.bridge.day_ohlc(symbol) + digits = 3 + self._set_metric("elapsed", day["elapsed_text"], COLOR_WARN) + self._set_metric("open", f"{day['open']:.{digits}f}") + self._set_metric("high", f"{day['high']:.{digits}f}", COLOR_PROFIT) + self._set_metric("low", f"{day['low']:.{digits}f}", COLOR_LOSS) + self.lbl_session.configure(text="时段 休市", bg="#3a2820", fg=COLOR_WARN) + self.lbl_day_ohlc.configure(text="休市 · 显示最近交易日日线") + except Exception: + pass except Exception: pass @@ -1360,9 +1497,9 @@ class OrderApp(tk.Tk): floating = st["floating"] total = st["total"] rate = st["profit_rate"] * 100.0 - self.lbl_today_base.configure(text=f"今日开单 {st['opens']} 次 | ") - self.lbl_today_closed.configure(text=f"已平 {closed:+.2f}", fg=self._pnl_color(closed)) - self.lbl_today_mid.configure(text=f" | 浮盈 {floating:+.2f} | 合计 ") + self.lbl_today_base.configure(text=f"今日开单 {st['opens']} 次") + self.lbl_today_closed.configure(text=f" 已平 {closed:+.2f}", fg=self._pnl_color(closed)) + self.lbl_today_mid.configure(text=f" 浮盈 {floating:+.2f} 合计 ") self.lbl_today_total.configure(text=f"{total:+.2f}", fg=self._pnl_color(total)) self.lbl_today_ratio.configure( text=f" | 盈亏比 {st['pl_ratio_txt']} | 盈利率 {rate:+.2f}%", diff --git a/releases/黄金市价辅助.exe b/releases/黄金市价辅助.exe index b552903..932aa99 100644 Binary files a/releases/黄金市价辅助.exe and b/releases/黄金市价辅助.exe differ