From 4325bbafb7063b8d4fb331b1a10d7db06d98b803 Mon Sep 17 00:00:00 2001 From: "GDP\\solonot" Date: Fri, 31 Oct 2025 23:35:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96tkinter=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.ini | 2 +- starter.py | 4 +- ui.py | 475 ++++++++++++++++++++++++++++++ ui/__pycache__/ui.cpython-312.pyc | Bin 2264 -> 0 bytes ui/ui.py | 39 --- 5 files changed, 479 insertions(+), 41 deletions(-) create mode 100644 ui.py delete mode 100644 ui/__pycache__/ui.cpython-312.pyc delete mode 100644 ui/ui.py diff --git a/config.ini b/config.ini index dcaccbd..9770206 100644 --- a/config.ini +++ b/config.ini @@ -3,4 +3,4 @@ miniQMTPath=D:\Programs\DTQMT\userdata_mini grid_price=1.665,1.660,1.655,1.650,1.645,1.640,1.635,1.630,1.625,1.620,1.615 grid_volume = 100 account_no = '99082560' -max_enabled_targets = 10 ; not enabled this limitation \ No newline at end of file +max_enabled_targets = 10 \ No newline at end of file diff --git a/starter.py b/starter.py index 3736232..a9aba78 100644 --- a/starter.py +++ b/starter.py @@ -5,6 +5,7 @@ from core.main_controller import ctrl import core.util as util import sfgrid_constants as sdConstants from xtquant import xtdata +import ui def interact(): """执行后进入repl模式""" @@ -62,6 +63,7 @@ def help(): if __name__ == '__main__': - interact() + app = ui.ProfessionalTradeUI(trade_targets=ctrl.instrument_pool) + app.run() # InitUI() # Loop() diff --git a/ui.py b/ui.py new file mode 100644 index 0000000..e24be14 --- /dev/null +++ b/ui.py @@ -0,0 +1,475 @@ +import random +import tkinter as tk +from tkinter import ttk +from typing import List +from datetime import datetime +from core.strategy_db import TradeTarget + +class TradeTargetUI: + def __init__(self): + self.root = tk.Tk() + self.root.title("交易标的监控系统") + self.root.geometry("1200x700") + + # 创建界面 + self.create_ui() + + def create_ui(self): + """创建UI界面""" + # 主框架 + main_frame = ttk.Frame(self.root) + main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + # 表格区域 + self.create_tables_area(main_frame) + + # 按钮区域 + self.create_buttons_area(main_frame) + + def create_tables_area(self, parent): + """创建表格区域""" + tables_frame = ttk.Frame(parent) + tables_frame.pack(fill=tk.BOTH, expand=True) + + # 左侧表格框架 + left_frame = ttk.LabelFrame(tables_frame, text="交易标的详情", padding=10) + left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5)) + + # 右侧表格框架 + right_frame = ttk.LabelFrame(tables_frame, text="操作日志", padding=10) + right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0)) + + # 创建左侧交易标的表格 + self.create_trade_target_table(left_frame) + + # 创建右侧操作日志表格 + self.create_log_table(right_frame) + + def create_trade_target_table(self, parent): + """创建交易标的表格""" + # 创建Treeview + columns = ( + "id", "stock_code", "stock_name", "current_position", "grid_index", + "last_trade_price", "current_buy_price", "current_sell_price", + "current_buy_order_no", "current_sell_order_no", + "status", "enabled" + ) + + self.trade_table = ttk.Treeview(parent, columns=columns, show='headings', height=12) + + # 定义列标题和宽度 + column_configs = { + "id": ("ID", 80), + "stock_code": ("股票代码", 80), + "stock_name": ("股票名称", 100), + "current_position": ("持仓数量", 90), + "grid_index": ("网格索引", 80), + "last_trade_price": ("最新成交价", 90), + "current_buy_price": ("当前买入价", 90), + "current_sell_price": ("当前卖出价", 90), + "current_buy_order_no": ("买入订单号", 120), + "current_sell_order_no": ("卖出订单号", 120), + "status": ("状态", 70), + "enabled": ("启用状态", 80) + } + + for col in columns: + title, width = column_configs[col] + self.trade_table.heading(col, text=title) + self.trade_table.column(col, width=width, anchor=tk.CENTER) + + # 填充数据 + self.populate_trade_table() + + # 滚动条 + scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.trade_table.yview) + self.trade_table.configure(yscrollcommand=scrollbar.set) + + # 布局 + self.trade_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + # 绑定双击事件 + self.trade_table.bind("", self.on_table_double_click) + + def populate_trade_table(self): + """填充交易标的表格数据""" + # 颜色标签 + self.trade_table.tag_configure('enabled', background='#f0f8ff') # 淡蓝色 + self.trade_table.tag_configure('disabled', background='#f5f5f5') # 淡灰色 + + def create_log_table(self, parent): + """创建操作日志表格""" + columns = ("timestamp", "level", "message") + + self.log_table = ttk.Treeview(parent, columns=columns, show='headings', height=12) + + log_column_configs = { + "timestamp": ("时间", 120), + "level": ("级别", 60), + "message": ("消息", 200) + } + + for col in columns: + title, width = log_column_configs[col] + self.log_table.heading(col, text=title) + self.log_table.column(col, width=width, anchor=tk.W) + + # 填充示例日志 + sample_logs = [ + ("2024-01-15 10:30:15", "INFO", "系统启动成功"), + ("2024-01-15 10:31:22", "DEBUG", "加载交易标的: 5个"), + ("2024-01-15 10:32:45", "INFO", "000001 - 网格交易线程启动"), + ("2024-01-15 10:33:10", "WARNING", "601318 - 未启用交易"), + ("2024-01-15 10:34:30", "ERROR", "300750 - 订单提交失败"), + ("2024-01-15 10:35:18", "INFO", "600036 - 买入订单创建成功"), + ("2024-01-15 10:36:05", "INFO", "数据刷新完成") + ] + + for log in sample_logs: + self.log_table.insert('', tk.END, values=log) + + # 滚动条 + scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.log_table.yview) + self.log_table.configure(yscrollcommand=scrollbar.set) + + self.log_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + def create_buttons_area(self, parent): + """创建按钮区域""" + buttons_frame = ttk.LabelFrame(parent, text="操作面板", padding=15) + buttons_frame.pack(fill=tk.X, pady=(10, 0)) + + # 创建五个功能按钮 + self.create_five_buttons(buttons_frame) + + def create_five_buttons(self, parent): + """创建五个功能按钮""" + button_configs = [ + ("刷新数据", self.refresh_data, "🔄"), + ("启用交易", self.enable_trading, "✅"), + ("禁用交易", self.disable_trading, "⛔"), + ("修改参数", self.modify_parameters, "⚙️"), + ("系统设置", self.system_settings, "🔧") + ] + + for text, command, icon in button_configs: + btn = ttk.Button( + parent, + text=f"{icon} {text}", + command=command, + width=15 + ) + btn.pack(side=tk.LEFT, padx=8) + + def refresh_data(self): + """刷新数据功能""" + # 模拟数据更新 + for target in self.trade_targets: + if target.enabled: + # 随机更新价格和仓位 + target.last_trade_price += round((random.random() - 0.5) * 0.5, 2) + target.current_position += random.randint(-50, 50) + + # 重新填充表格 + for item in self.trade_table.get_children(): + self.trade_table.delete(item) + + for target in self.trade_targets: + values = [ + target.id, + target.stock_code, + target.stock_name, + target.current_position, + target.grid_index, + target.last_trade_price, + target.current_buy_price, + target.current_buy_order_no, + target.current_sell_price, + target.current_sell_order_no, + self.get_status_text(target.status), + "是" if target.enabled else "否" + ] + + tags = ('enabled',) if target.enabled else ('disabled',) + self.trade_table.insert('', tk.END, values=values, tags=tags) + + # 添加日志 + self.add_log("INFO", "数据刷新完成") + + def enable_trading(self): + """启用交易功能""" + selected = self.trade_table.selection() + if selected: + item = selected[0] + values = self.trade_table.item(item)['values'] + # 在实际应用中,这里会调用实际的启用逻辑 + self.add_log("INFO", f"启用交易标的: {values[0]}") + + def disable_trading(self): + """禁用交易功能""" + selected = self.trade_table.selection() + if selected: + item = selected[0] + values = self.trade_table.item(item)['values'] + self.add_log("INFO", f"禁用交易标的: {values[0]}") + + def modify_parameters(self): + """修改参数功能""" + selected = self.trade_table.selection() + if selected: + item = selected[0] + values = self.trade_table.item(item)['values'] + # 弹出修改对话框 + self.show_parameter_dialog() + + def system_settings(self): + """系统设置功能""" + # 弹出系统设置对话框 + settings_window = tk.Toplevel(self.root) + settings_window.title("系统设置") + settings_window.geometry("400x300") + + ttk.Label(settings_window, text="网格交易系统设置", font=('Arial', 12, 'bold')).pack(pady=20) + + # 添加一些设置选项 + options = [ + ("自动刷新间隔", "5秒"), + ("价格变动阈值", "0.1%"), + ("最大持仓数量", "10000"), + ("最小交易数量", "100") + ] + + for label, value in options: + frame = ttk.Frame(settings_window) + frame.pack(fill=tk.X, padx=20, pady=10) + + ttk.Label(frame, text=label, width=20).pack(side=tk.LEFT) + ttk.Label(frame, text=value).pack(side=tk.RIGHT) + + def get_status_text(self, status): + """获取状态文本""" + status_map = { + 0: "新标的", + 1: "交易中" + } + return status_map.get(status, "未知") + + def on_table_double_click(self, event): + """表格双击事件""" + selected = self.trade_table.selection() + if selected: + item = selected[0] + values = self.trade_table.item(item)['values'] + self.add_log("DEBUG", f"双击查看详情: {values[0]} - {values[1]}") + + def add_log(self, level, message): + """添加日志记录""" + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self.log_table.insert('', 0, values=(timestamp, level, message)) + + def run(self): + """运行程序""" + # 初始填充数据 + for target in self.trade_targets: + values = [ + target.stock_code, + target.stock_name, + target.current_position, + target.grid_index, + target.last_trade_price, + target.current_buy_price, + target.current_buy_order_no, + target.current_sell_price, + target.current_sell_order_no, + self.get_status_text(target.status), + "是" if target.enabled else "否" + ] + + tags = ('enabled',) if target.enabled else ('disabled',) + self.trade_table.insert('', tk.END, values=values, tags=tags) + + self.root.mainloop() + +# 更专业的版本,集成实际的TradeTarget对象 +class ProfessionalTradeUI(TradeTargetUI): + def __init__(self, trade_targets: List[TradeTarget] = None): + if trade_targets is not None: + self.trade_targets = trade_targets + else: + self.trade_targets = self.create_advanced_sample_data() + super().__init__() + + def create_trade_target_table(self, parent): + """专业版的交易标的表格""" + columns = ("ID", + "股票代码", "股票名称", "持仓数量", "网格索引", + "最新成交价", "当前买入价", "当前买入订单号", "当前卖出价", "当前卖出订单号", + "启用状态", "交易状态" + ) + + self.trade_table = ttk.Treeview(parent, columns=columns, show='headings', height=15) + + # 专业化的列配置 + column_configs = { + "ID": (50, tk.CENTER), + "股票代码": (90, tk.CENTER), + "股票名称": (100, tk.CENTER), + "持仓数量": (90, tk.CENTER), + "网格索引": (80, tk.CENTER), + "最新成交价": (100, tk.CENTER), + "当前买入价": (100, tk.CENTER), + "当前买入订单号": (100, tk.CENTER), + "当前卖出订单号": (100, tk.CENTER), + "当前卖出价": (100, tk.CENTER), + "启用状态": (80, tk.CENTER), + "交易状态": (80, tk.CENTER), + "买入订单号": (120, tk.CENTER), + "卖出订单号": (120, tk.CENTER) + } + + for col in columns: + width, anchor = column_configs[col] + self.trade_table.heading(col, text=col) + self.trade_table.column(col, width=width, anchor=anchor) + + # 带颜色的标签 + self.trade_table.tag_configure('trading', background='#e8f5e8') # 交易中的绿色背景 + self.trade_table.tag_configure('paused', background='#fff3cd') # 暂停的黄色背景 + self.trade_table.tag_configure('inactive', background='#f8d7da') # 禁用的红色背景 + + # 填充数据 + for temp in self.trade_targets: + target:TradeTarget = temp + values = [ + target.id, + target.stock_code, + target.stock_name, + target.current_position, + target.grid_index, + f"{target.last_trade_price:.2f}", + f"{target.current_buy_price:.2f}", + target.current_buy_order_no, + f"{target.current_sell_price:.2f}", + target.current_sell_order_no, + "已启用" if target.enabled else "未启用", + "交易中" if target.status == 1 else "新标的" + ] + + # 根据状态设置标签 + if target.enabled and target.status == 1: + tags = ('trading',) + elif target.enabled and target.status == 0: + tags = ('paused',) + else: + tags = ('inactive',) + + self.trade_table.insert('', tk.END, values=values, tags=tags) + + # 滚动条 + scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.trade_table.yview) + self.trade_table.configure(yscrollcommand=scrollbar.set) + + self.trade_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + def refresh_data(self): + """专业版的刷新数据功能""" + for target in self.trade_targets: + if target.enabled: + # 模拟实时的价格变动 + price_change = (random.random() - 0.5) * 0.1 + target.last_trade_price = round(target.last_trade_price + price_change, 2) + + # 更新表格 + for item in self.trade_table.get_children(): + self.trade_table.delete(item) + + for target in self.trade_targets: + values = [ + target.id, + target.stock_code, + target.stock_name, + target.current_position, + target.grid_index, + f"{target.last_trade_price:.2f}", + f"{target.current_buy_price:.2f}", + f"{target.current_sell_price:.2f}", + "已启用" if target.enabled else "未启用", + "交易中" if target.status == 1 else "新标的" + ] + + # 设置标签 + if target.enabled and target.status == 1: + tags = ('trading',) + elif target.enabled and target.status == 0: + tags = ('paused',) + else: + tags = ('inactive',) + + self.trade_table.insert('', tk.END, values=values, tags=tags) + + self.add_log("INFO", "专业版数据刷新完成 - 包含实时价格模拟") + + def show_parameter_dialog(self): + """显示参数修改对话框""" + dialog = tk.Toplevel(self.root) + dialog.title("交易参数设置") + dialog.geometry("500x400") + + # 创建配置表单 + ttk.Label(dialog, text="交易标的参数配置", font=('Arial', 12, 'bold')).pack(pady=20) + + # 创建标签和输入框 + form_frame = ttk.Frame(dialog) + form_frame.pack(fill=tk.BOTH, expand=True, padx=20) + + # 这里可以添加具体的参数配置表单 + ttk.Label(form_frame, text="双击表格项可查看详情", font=('Arial', 10)).pack(pady=10) + + def system_settings(self): + """专业版的系统设置""" + settings_window = tk.Toplevel(self.root) + settings_window.title("网格交易系统配置") + settings_window.geometry("600x400") + + # 创建选项卡 + notebook = ttk.Notebook(settings_window) + notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + # 基础设置 + basic_frame = ttk.Frame(notebook) + notebook.add(basic_frame, text="基础设置") + + # 高级设置 + advanced_frame = ttk.Frame(notebook) + notebook.add(advanced_frame, text="高级设置") + + # 添加具体的配置选项 + settings = [ + ("网格宽度", "0.5%"), + ("网格数量", "10层"), + ("交易间隔", "30秒"), + ("单笔数量", "100股") + ] + + for i, (label, default) in enumerate(settings): + frame = ttk.Frame(basic_frame) + frame.pack(fill=tk.X, padx=20, pady=10) + ttk.Label(frame, text=label, width=15).pack(side=tk.LEFT) + entry = ttk.Entry(frame, width=20) + entry.insert(0, default) + entry.pack(side=tk.RIGHT) + +# 使用示例 +if __name__ == "__main__": + print("交易标的监控系统启动...") + print("功能说明:") + print(" - 左侧表格显示所有交易标的详细信息") + print(" - 右侧表格显示操作日志") + print(" - 底部五个功能按钮提供操作") + + # 创建并运行界面 + app = ProfessionalTradeUI() + app.run() diff --git a/ui/__pycache__/ui.cpython-312.pyc b/ui/__pycache__/ui.cpython-312.pyc deleted file mode 100644 index 388aabe3ee4f89e55e6de658034486cf70c02335..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2264 zcma)7O-vg{6rQ!m>mOhPl;6;T36QXk;L<7zn5HQZ10m431VJq%%XqhV-L=CeIp7+grZ@!tG zdGF0HMMVyPTIh%pYHa}gh6kjX^Wbb8fi*A;IKWXN@L_2ZsffvEikN+73Xjcb41G|? zP_#s7AFX4HXpPu>HVW`2-p15NWW|WH%>$oa{*bd~ z)UN@BA_m1oP#6+q$gR9zz^vGbb}J787DYcaJPa2Rt06gxGi~MBDR9pBg5g6DXC^Bl z&k~|F<`C;Fm;vEeh~1DJBpzsfIni?#j#kRZC|p61A;VP$CTJ^v;j~wJYDk49 zINU;zAqk7IY7(&aLBWYQg;8|;iA%`&=*yk#&V#$jA%WwsoqRrjys>op#aBl=t0$|Q zC!32$I}a1B$nEJ*pB+E`A-kQ*Zh!wjrW9i4PaYgUT6ns#bo|Yar#LE4aq3L7cULx)Zipa|rY_uZfJ$_1&@hNHa2ro>86wQ7MX#$;+$mtHa)@V>CvEjZ~ zKx50Kz$qb(4&|m4Afg^#itvh@)QZqxNJ)zPP>AR8)Q~i(L4Ha=n4jW5(4_Q#*!M{ z7ZsG@J{iYw0s6=5QB6yMtfOKzzBaWom3BB&T?a*Fi|?g8S?E|8m>XDbSdQ<(ngi&_ zz-kp%?_RvI2j9vTmabk~98BHXhj#3>TiUq?yRrqv%eCvJUk`sfwlS7za;r`5bU{a| z`$^7exp5CxW=pHquC84Dv?bNQUsADFyHcAjsZZVBXR0$yoyydunfi6F%3R*Om|@#g zwk^%Nx9_X$O_jNs>e0QND&tHum(~L+b7j+&ak*8OJMDV&=MvS`sWP3&3)(ZVT!rP! z6OSi19hv6qYV-AUbNlw~OvixQF_7+fXAj;*o65jS6;|$6b?w1!{JH&e{qq9{(7Mn& z*Sj?IaP;HRMc=Qm>I|I}2;z=397j07&Zbd^UJ#-OVXS?wk!(UaiK9zz;b-|rGyo_# zMD-TNck^5$EPu;MYLKP%c$RT(mhrB4#=0Wq7ii(ydz058J7(l0C8+j z0d%wf_2K$dwovEQO7fZB>O~2I-`=>MW4WBt)27Jq=t&5<