Show day OHLC, session label, clarify auto-lot for small balance.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-17 21:12:59 +08:00
parent d860156b11
commit 1a641f58bc
2 changed files with 100 additions and 7 deletions
+100 -7
View File
@@ -9,10 +9,12 @@ import threading
import time
import webbrowser
import tkinter as tk
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from tkinter import messagebox, simpledialog, ttk
from typing import Any, Optional, Tuple, Union
BJT = timezone(timedelta(hours=8))
# 顶层导入,避免打包 exe 漏依赖
import numpy # noqa: F401
import MetaTrader5 as mt5
@@ -99,6 +101,7 @@ def _init_mt5_constants() -> None:
def suggest_lot_by_balance(balance: float) -> float:
"""按余额档位手数:≥10000→0.10;1000~10000→0.05;<1000→0.01(点按钮即可)。"""
if balance >= BALANCE_HIGH:
return LOT_HIGH
if balance < BALANCE_LOW:
@@ -106,6 +109,29 @@ def suggest_lot_by_balance(balance: float) -> float:
return LOT_MID
def current_session_label(now: Optional[datetime] = None) -> str:
"""北京时间盘段:亚盘/欧盘/美盘(可重叠显示)。"""
dt = now or datetime.now(BJT)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=BJT)
else:
dt = dt.astimezone(BJT)
h = dt.hour + dt.minute / 60.0
asia = 8.0 <= h < 16.0
euro = 15.0 <= h < 24.0
us = h >= 20.0 or h < 5.0
parts = []
if asia:
parts.append("亚盘")
if euro:
parts.append("欧盘")
if us:
parts.append("美盘")
if not parts:
return "过渡时段"
return "+".join(parts)
class TradeError(Exception):
pass
@@ -153,6 +179,31 @@ class Mt5Bridge:
raise TradeError(f"无法读取品种信息 {symbol}")
return info
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, 1)
if rates is None or len(rates) < 1:
raise TradeError("无法读取今日日线,请确认品种有 D1 数据")
bar = rates[0]
open_px = float(bar["open"])
high = float(bar["high"])
low = float(bar["low"])
open_ts = int(bar["time"])
tick = mt5.symbol_info_tick(symbol)
if tick is not None:
bid = float(tick.bid)
ask = float(tick.ask)
high = max(high, bid, ask)
low = min(low, bid, ask)
return {
"open": open_px,
"high": high,
"low": low,
"open_time": datetime.fromtimestamp(open_ts),
"open_ts": open_ts,
}
def atr(self, symbol: str, period: int = ATR_PERIOD) -> float:
self.ensure_symbol(symbol)
rates = mt5.copy_rates_from_pos(symbol, ATR_TIMEFRAME, 1, period + 1)
@@ -605,8 +656,8 @@ class OrderApp(tk.Tk):
super().__init__()
_init_mt5_constants()
self.title(APP_TITLE)
self.geometry("1080x900")
self.minsize(980, 820)
self.geometry("1080x940")
self.minsize(980, 860)
self.configure(bg="#1e1e1e")
try:
self.tk.call("tk", "scaling", 1.45)
@@ -645,10 +696,29 @@ class OrderApp(tk.Tk):
self.lbl_account = ttk.Label(head, text="余额 --", font=("Microsoft YaHei UI", 13, "bold"))
self.lbl_account.pack(side=tk.LEFT, padx=12)
self.lbl_quote = ttk.Label(root, text="Bid -- Ask --", font=("Microsoft YaHei UI", 14, "bold"))
self.lbl_quote.pack(anchor="w", pady=(10, 0))
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",
font=("Microsoft YaHei UI", 12, "bold"),
padx=10,
pady=2,
)
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),
)
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_today_base = tk.Label(
@@ -717,7 +787,11 @@ class OrderApp(tk.Tk):
self._mk_btn(opt, "按资金手数", "#3a5a80", self.on_auto_lot, h=34, w=110).pack(side=tk.LEFT, padx=10)
ttk.Label(
opt,
text=f"同向加仓上限 {MAX_SIDE_LOTS:.2f} 手 · 最多同时 {MAX_OPEN_POSITIONS} 笔",
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",
).pack(side=tk.LEFT)
@@ -1160,12 +1234,19 @@ class OrderApp(tk.Tk):
self.refresh_quotes()
def on_auto_lot(self) -> None:
"""任意资金都可点:小资金(<1000)会设为 0.01 手。"""
try:
self.bridge.connect()
bal = self.bridge.account_balance()
lot = suggest_lot_by_balance(bal)
self.var_lot.set(f"{lot:.2f}")
self.set_status(f"按资金设置手数:余额 {bal:.2f} → {lot:.2f}", ok=True)
if bal < BALANCE_LOW:
tip = f"小资金档:余额 {bal:.2f} → 手数 {lot:.2f}"
elif bal >= BALANCE_HIGH:
tip = f"大资金档:余额 {bal:.2f} → 手数 {lot:.2f}"
else:
tip = f"中资金档:余额 {bal:.2f} → 手数 {lot:.2f}"
self.set_status(tip, ok=True)
except TradeError as exc:
self.set_status(str(exc), ok=False)
@@ -1231,12 +1312,24 @@ class OrderApp(tk.Tk):
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()}")
self.lbl_info.configure(
text=(
f"点差 {spread:.{digits}f} ATRM15 {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}"
)
)
except Exception:
self.lbl_day_ohlc.configure(text="开盘 -- | 今开 -- | 最高 -- | 最低 --")
try:
self._refresh_today_stats(symbol)
except Exception:
Binary file not shown.