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 time
import webbrowser import webbrowser
import tkinter as tk import tkinter as tk
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from tkinter import messagebox, simpledialog, ttk from tkinter import messagebox, simpledialog, ttk
from typing import Any, Optional, Tuple, Union from typing import Any, Optional, Tuple, Union
BJT = timezone(timedelta(hours=8))
# 顶层导入,避免打包 exe 漏依赖 # 顶层导入,避免打包 exe 漏依赖
import numpy # noqa: F401 import numpy # noqa: F401
import MetaTrader5 as mt5 import MetaTrader5 as mt5
@@ -99,6 +101,7 @@ def _init_mt5_constants() -> None:
def suggest_lot_by_balance(balance: float) -> float: def suggest_lot_by_balance(balance: float) -> float:
"""按余额档位手数:≥10000→0.10;1000~10000→0.05;<1000→0.01(点按钮即可)。"""
if balance >= BALANCE_HIGH: if balance >= BALANCE_HIGH:
return LOT_HIGH return LOT_HIGH
if balance < BALANCE_LOW: if balance < BALANCE_LOW:
@@ -106,6 +109,29 @@ def suggest_lot_by_balance(balance: float) -> float:
return LOT_MID 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): class TradeError(Exception):
pass pass
@@ -153,6 +179,31 @@ class Mt5Bridge:
raise TradeError(f"无法读取品种信息 {symbol}") raise TradeError(f"无法读取品种信息 {symbol}")
return info 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: def atr(self, symbol: str, period: int = ATR_PERIOD) -> float:
self.ensure_symbol(symbol) self.ensure_symbol(symbol)
rates = mt5.copy_rates_from_pos(symbol, ATR_TIMEFRAME, 1, period + 1) rates = mt5.copy_rates_from_pos(symbol, ATR_TIMEFRAME, 1, period + 1)
@@ -605,8 +656,8 @@ class OrderApp(tk.Tk):
super().__init__() super().__init__()
_init_mt5_constants() _init_mt5_constants()
self.title(APP_TITLE) self.title(APP_TITLE)
self.geometry("1080x900") self.geometry("1080x940")
self.minsize(980, 820) self.minsize(980, 860)
self.configure(bg="#1e1e1e") self.configure(bg="#1e1e1e")
try: try:
self.tk.call("tk", "scaling", 1.45) 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 = ttk.Label(head, text="余额 --", font=("Microsoft YaHei UI", 13, "bold"))
self.lbl_account.pack(side=tk.LEFT, padx=12) self.lbl_account.pack(side=tk.LEFT, padx=12)
self.lbl_quote = ttk.Label(root, text="Bid -- Ask --", font=("Microsoft YaHei UI", 14, "bold")) quote_row = ttk.Frame(root)
self.lbl_quote.pack(anchor="w", pady=(10, 0)) 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 = ttk.Label(root, text="点差 -- ATR -- SL$ -- TP$ -- 持仓 --", font=("Microsoft YaHei UI", 12))
self.lbl_info.pack(anchor="w", pady=(4, 2)) 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 = ttk.Frame(root)
today_row.pack(fill=tk.X, pady=(0, 12)) today_row.pack(fill=tk.X, pady=(0, 12))
self.lbl_today_base = tk.Label( 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) self._mk_btn(opt, "按资金手数", "#3a5a80", self.on_auto_lot, h=34, w=110).pack(side=tk.LEFT, padx=10)
ttk.Label( ttk.Label(
opt, 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", style="Status.TLabel",
).pack(side=tk.LEFT) ).pack(side=tk.LEFT)
@@ -1160,12 +1234,19 @@ class OrderApp(tk.Tk):
self.refresh_quotes() self.refresh_quotes()
def on_auto_lot(self) -> None: def on_auto_lot(self) -> None:
"""任意资金都可点:小资金(<1000)会设为 0.01 手。"""
try: try:
self.bridge.connect() self.bridge.connect()
bal = self.bridge.account_balance() bal = self.bridge.account_balance()
lot = suggest_lot_by_balance(bal) lot = suggest_lot_by_balance(bal)
self.var_lot.set(f"{lot:.2f}") 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: except TradeError as exc:
self.set_status(str(exc), ok=False) self.set_status(str(exc), ok=False)
@@ -1231,12 +1312,24 @@ class OrderApp(tk.Tk):
self.lbl_account.configure(text="余额 --") self.lbl_account.configure(text="余额 --")
self.lbl_quote.configure(text=f"Bid {bid:.{digits}f} Ask {ask:.{digits}f}") 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( self.lbl_info.configure(
text=( text=(
f"点差 {spread:.{digits}f} ATRM15 {atr_txt} " f"点差 {spread:.{digits}f} ATRM15 {atr_txt} "
f"SL${sl_txt} TP${tp_txt} 持仓 {pos} 浮盈 {profit:.2f}" 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: try:
self._refresh_today_stats(symbol) self._refresh_today_stats(symbol)
except Exception: except Exception:
Binary file not shown.