From c59d29d52e218987b4484ee98a0ffaa920b9ba52 Mon Sep 17 00:00:00 2001 From: "GDP\\solonot" Date: Fri, 5 Dec 2025 17:43:13 +0800 Subject: [PATCH] init new structure --- .../constants.py | 0 core/{heat_review => daily_review}/model.py | 0 core/eventbus.py | 11 +- core/main_entry.py | 83 +++++++++ core/{ => market_data}/qmt.py | 0 core/readme.md | 11 ++ core/{ => strategy}/sfgrid/bus_events.py | 0 core/{ => strategy}/sfgrid/model.py | 0 core/{ => strategy}/sfgrid/sfgrid_strategy.py | 0 core/{ => strategy}/sfgrid/sfgrid_ui.py | 0 core/strategy/strategy_window.py | 158 ++++++++++++++++++ starter.py | 157 +---------------- 12 files changed, 264 insertions(+), 156 deletions(-) rename core/{heat_review => daily_review}/constants.py (100%) rename core/{heat_review => daily_review}/model.py (100%) create mode 100644 core/main_entry.py rename core/{ => market_data}/qmt.py (100%) create mode 100644 core/readme.md rename core/{ => strategy}/sfgrid/bus_events.py (100%) rename core/{ => strategy}/sfgrid/model.py (100%) rename core/{ => strategy}/sfgrid/sfgrid_strategy.py (100%) rename core/{ => strategy}/sfgrid/sfgrid_ui.py (100%) create mode 100644 core/strategy/strategy_window.py diff --git a/core/heat_review/constants.py b/core/daily_review/constants.py similarity index 100% rename from core/heat_review/constants.py rename to core/daily_review/constants.py diff --git a/core/heat_review/model.py b/core/daily_review/model.py similarity index 100% rename from core/heat_review/model.py rename to core/daily_review/model.py diff --git a/core/eventbus.py b/core/eventbus.py index 0a434dd..baa07fc 100644 --- a/core/eventbus.py +++ b/core/eventbus.py @@ -7,6 +7,10 @@ MarketOrderTraded = "market_order_traded" # 市价单成交 # Pring Log EventPrintLog = "print_log" # 打印日志 +# 订阅与发布事件示例 +# event_bus.subscribe('my_event', handle_event) +# event_bus.publish('my_event', {'key': 'value'}) + class EventBus: def __init__(self): self.listeners = {} # 管理各种event的订阅情况 @@ -21,12 +25,5 @@ class EventBus: for listener in self.listeners[event_type]: listener(data) - -# # 订阅事件 -# event_bus.subscribe('my_event', handle_event) - -# # 发布事件 -# event_bus.publish('my_event', {'key': 'value'}) - # 创建事件总线实例 event_bus = EventBus() \ No newline at end of file diff --git a/core/main_entry.py b/core/main_entry.py new file mode 100644 index 0000000..1bfb177 --- /dev/null +++ b/core/main_entry.py @@ -0,0 +1,83 @@ +# coding:utf-8 +import tkinter as tk +from core.logger import LogLevel, PrintLog +from PIL import Image +import pystray +import threading + +class MainEntry: + def __init__(self, master): + self.master = master + self.master.title("Main Board") + self.master.geometry("800x600") + self.master.configure(bg="#f0f0f0") + self.master.resizable(False, False) + self.master.protocol("WM_DELETE_WINDOW", self.hide_window) + self.qmt_enabled = False + self.icon = None + self.create_systray_icon() + + def create_systray_icon(self): + # 加载图标文件(需确保路径正确) + image = Image.open("logo.png") + # 定义托盘菜单 + menu = ( + pystray.MenuItem('交易大师', None, enabled=False), + pystray.MenuItem(" - 交易复盘", self.handler), + pystray.MenuItem(" - 市场数据", self.handler), + pystray.MenuItem(" - 快速下单", self.handler), + pystray.MenuItem('策略交易', None, enabled=False), + pystray.MenuItem(" - 交易看板", self.handler), + pystray.MenuItem(" - 策略中心", None), + pystray.MenuItem(" - 策略定制", None), + pystray.MenuItem('实时数据', None, enabled=False), + pystray.MenuItem(" - QMT (已关闭)" if not self.qmt_enabled else " - QMT (已开启)", self.marketDataSwitch), + pystray.Menu.SEPARATOR, + pystray.MenuItem(" - 控制台", self.show_window, default=True), + pystray.MenuItem(" - 设置", self.marketDataSwitch), + pystray.MenuItem(" - 退出", self.quit_window) + ) + + if self.icon: + self.icon.menu = menu + self.icon.update_menu() + else: + # 创建托盘图标 + self.icon = pystray.Icon("name", image, "标题", menu) + PrintLog(LogLevel.INFO, "创建托盘图标 start threading") + self.trayThread = threading.Thread(target=self.icon.run, daemon=True) + self.trayThread.start() + # 在后台线程运行托盘 + + def marketDataSwitch(self): + if self.qmt_enabled: + self.qmt_enabled = False + PrintLog(LogLevel.INFO, "QMT 市场数据已关闭") + else: + self.qmt_enabled = True + PrintLog(LogLevel.INFO, "QMT 市场数据已开启") + self.create_systray_icon() + + def handler(self): + PrintLog(LogLevel.INFO, f"点击了") + + def hide_window(self): + PrintLog(LogLevel.INFO, "隐藏主窗口") + self.master.withdraw() # 隐藏主窗口 + + def show_window(self): + self.icon.visible = True + PrintLog(LogLevel.INFO, "显示主窗口") + self.master.deiconify() # 显示主窗口 + + def quit_window(self, icon: pystray.Icon): + icon.stop() + PrintLog(LogLevel.INFO, "退出应用") + self.master.quit() + self.master.destroy() + + def run(self): + self.master.mainloop() + + + diff --git a/core/qmt.py b/core/market_data/qmt.py similarity index 100% rename from core/qmt.py rename to core/market_data/qmt.py diff --git a/core/readme.md b/core/readme.md new file mode 100644 index 0000000..d1edf62 --- /dev/null +++ b/core/readme.md @@ -0,0 +1,11 @@ +# 软件介绍 +软件名称:神之一手交易系统 +软件介绍:面向个人的交易管理系统,提供交易记录、复盘工具、持仓管理、资产监控、策略交易等功能。 + +# 模块介绍 +1. /core/daily_review: 每日复盘模块目录 +2. /core/market_data: 市场数据模块目录 +3. /core/quick_trade: 快速交易模块目录 +4. /core/strategy/builder: 策略构建模块目录 +5. /core/strategy/trade: 策略交易模块目录 +6. /core: 应用核心程序目录 \ No newline at end of file diff --git a/core/sfgrid/bus_events.py b/core/strategy/sfgrid/bus_events.py similarity index 100% rename from core/sfgrid/bus_events.py rename to core/strategy/sfgrid/bus_events.py diff --git a/core/sfgrid/model.py b/core/strategy/sfgrid/model.py similarity index 100% rename from core/sfgrid/model.py rename to core/strategy/sfgrid/model.py diff --git a/core/sfgrid/sfgrid_strategy.py b/core/strategy/sfgrid/sfgrid_strategy.py similarity index 100% rename from core/sfgrid/sfgrid_strategy.py rename to core/strategy/sfgrid/sfgrid_strategy.py diff --git a/core/sfgrid/sfgrid_ui.py b/core/strategy/sfgrid/sfgrid_ui.py similarity index 100% rename from core/sfgrid/sfgrid_ui.py rename to core/strategy/sfgrid/sfgrid_ui.py diff --git a/core/strategy/strategy_window.py b/core/strategy/strategy_window.py new file mode 100644 index 0000000..e053614 --- /dev/null +++ b/core/strategy/strategy_window.py @@ -0,0 +1,158 @@ +# coding:utf-8 +import os +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +import configparser +from core.main_ui import MainWindow +import config as sdConstants +from core.qmt import qmtv + +class ConfigWindow: + def __init__(self, root): + self.root = root + self.root.title("系统配置") + self.root.geometry("500x250") + self.root.resizable(False, False) + + # 居中显示 + self.root.withdraw() # 先隐藏窗口 + self.root.update_idletasks() + x = (self.root.winfo_screenwidth() // 2) - (500 // 2) + y = (self.root.winfo_screenheight() // 2) - (250 // 2) + self.root.geometry(f"500x250+{x}+{y}") + self.root.deiconify() # 再显示窗口 + + self.miniQMTPath = tk.StringVar() + self.account_no = tk.StringVar() + + self.create_widgets() + + def create_widgets(self): + # 创建主框架 + main_frame = ttk.Frame(self.root, padding="20") + main_frame.pack(fill=tk.BOTH, expand=True) + + # miniQMT路径配置 + path_frame = ttk.Frame(main_frame) + path_frame.pack(fill=tk.X, pady=5) + + path_label = ttk.Label(path_frame, text="miniQMT路径:") + path_label.pack(side=tk.LEFT) + + path_entry = ttk.Entry(path_frame, textvariable=self.miniQMTPath, width=40) + path_entry.pack(side=tk.LEFT, padx=(10, 5), fill=tk.X, expand=True) + + browse_btn = ttk.Button(path_frame, text="浏览", command=self.browse_folder) + browse_btn.pack(side=tk.LEFT) + + # 资金账号配置 + account_frame = ttk.Frame(main_frame) + account_frame.pack(fill=tk.X, pady=5) + + account_label = ttk.Label(account_frame, text="资金账号:") + account_label.pack(side=tk.LEFT) + + account_entry = ttk.Entry(account_frame, textvariable=self.account_no, width=40) + account_entry.pack(side=tk.LEFT, padx=(10, 0)) + + # 说明文本 + info_label = ttk.Label( + main_frame, + text="请配置miniQMT的userdata_mini路径和资金账号\n路径示例: D:/Programs/DTQMT/userdata_mini", + foreground="gray" + ) + info_label.pack(pady=10) + + # 按钮框架 + button_frame = ttk.Frame(main_frame) + button_frame.pack(fill=tk.X, pady=10) + + save_btn = ttk.Button(button_frame, text="保存配置", command=self.save_config) + save_btn.pack(side=tk.RIGHT) + + cancel_btn = ttk.Button(button_frame, text="取消", command=self.root.destroy) + cancel_btn.pack(side=tk.RIGHT, padx=(0, 10)) + + def browse_folder(self): + folder_selected = filedialog.askdirectory() + if folder_selected: + self.miniQMTPath.set(folder_selected) + + def save_config(self): + mini_qmt_path = self.miniQMTPath.get().strip() + account_number = self.account_no.get().strip() + + # 检查miniQMT路径 + if not mini_qmt_path: + messagebox.showerror("错误", "请选择miniQMT路径") + return + + if not os.path.exists(mini_qmt_path): + messagebox.showerror("错误", "miniQMT路径不存在") + return + + # 检查账号 + if not account_number: + messagebox.showerror("错误", "请输入资金账号") + return + + # 保存配置 + config = configparser.ConfigParser() + config['config'] = { + 'miniQMTPath': mini_qmt_path.replace('\\', '/'), + 'account_no': account_number, + 'log_level': 'INFO' + } + + config_path = sdConstants.get_config_path() + try: + with open(config_path, 'w') as configfile: + config.write(configfile) + messagebox.showinfo("成功", "配置已保存") + self.root.destroy() + except Exception as e: + messagebox.showerror("错误", f"保存配置失败: {str(e)}") + +def check_and_create_config(): + """检查配置文件,如果不存在则打开配置窗口""" + root = tk.Tk() + config_window = ConfigWindow(root) + root.mainloop() + +def initialize_system(): + """初始化系统""" + + try: + while True: + # 初始化配置 + if sdConstants.exist_config() and sdConstants.initConfig(): + # 初始化qmtv + qmtv.init_qmtv() + connected = qmtv.connect() + if connected: + # 连接成功,启动主窗口 + window = MainWindow(sdConstants.log_level) + window.run() + break + else: + option = messagebox.askokcancel("连接失败", "QMT连接失败,请检查") + if option: + check_and_create_config() + else: + break + else: + option = messagebox.askokcancel("错误", "请检查配置") + if option: + check_and_create_config() + else: + break + except Exception as e: + messagebox.showerror("错误", f"系统初始化失败: {str(e)}") + +if __name__ == "__main__": + import tkinter as tk + root = tk.Tk() + app = MainBoardWindow(root) + app.run() + + # initialize_system() \ No newline at end of file diff --git a/starter.py b/starter.py index 1bd2280..bc4356c 100644 --- a/starter.py +++ b/starter.py @@ -1,153 +1,12 @@ # coding:utf-8 -import os import tkinter as tk -from tkinter import ttk, filedialog, messagebox -import configparser -from core.main_ui import MainWindow -import config as sdConstants -from core.qmt import qmtv +from core.main_entry import MainEntry -class ConfigWindow: - def __init__(self, root): - self.root = root - self.root.title("系统配置") - self.root.geometry("500x250") - self.root.resizable(False, False) - - # 居中显示 - self.root.withdraw() # 先隐藏窗口 - self.root.update_idletasks() - x = (self.root.winfo_screenwidth() // 2) - (500 // 2) - y = (self.root.winfo_screenheight() // 2) - (250 // 2) - self.root.geometry(f"500x250+{x}+{y}") - self.root.deiconify() # 再显示窗口 - - self.miniQMTPath = tk.StringVar() - self.account_no = tk.StringVar() - - self.create_widgets() - - def create_widgets(self): - # 创建主框架 - main_frame = ttk.Frame(self.root, padding="20") - main_frame.pack(fill=tk.BOTH, expand=True) - - # miniQMT路径配置 - path_frame = ttk.Frame(main_frame) - path_frame.pack(fill=tk.X, pady=5) - - path_label = ttk.Label(path_frame, text="miniQMT路径:") - path_label.pack(side=tk.LEFT) - - path_entry = ttk.Entry(path_frame, textvariable=self.miniQMTPath, width=40) - path_entry.pack(side=tk.LEFT, padx=(10, 5), fill=tk.X, expand=True) - - browse_btn = ttk.Button(path_frame, text="浏览", command=self.browse_folder) - browse_btn.pack(side=tk.LEFT) - - # 资金账号配置 - account_frame = ttk.Frame(main_frame) - account_frame.pack(fill=tk.X, pady=5) - - account_label = ttk.Label(account_frame, text="资金账号:") - account_label.pack(side=tk.LEFT) - - account_entry = ttk.Entry(account_frame, textvariable=self.account_no, width=40) - account_entry.pack(side=tk.LEFT, padx=(10, 0)) - - # 说明文本 - info_label = ttk.Label( - main_frame, - text="请配置miniQMT的userdata_mini路径和资金账号\n路径示例: D:/Programs/DTQMT/userdata_mini", - foreground="gray" - ) - info_label.pack(pady=10) - - # 按钮框架 - button_frame = ttk.Frame(main_frame) - button_frame.pack(fill=tk.X, pady=10) - - save_btn = ttk.Button(button_frame, text="保存配置", command=self.save_config) - save_btn.pack(side=tk.RIGHT) - - cancel_btn = ttk.Button(button_frame, text="取消", command=self.root.destroy) - cancel_btn.pack(side=tk.RIGHT, padx=(0, 10)) - - def browse_folder(self): - folder_selected = filedialog.askdirectory() - if folder_selected: - self.miniQMTPath.set(folder_selected) - - def save_config(self): - mini_qmt_path = self.miniQMTPath.get().strip() - account_number = self.account_no.get().strip() - - # 检查miniQMT路径 - if not mini_qmt_path: - messagebox.showerror("错误", "请选择miniQMT路径") - return - - if not os.path.exists(mini_qmt_path): - messagebox.showerror("错误", "miniQMT路径不存在") - return - - # 检查账号 - if not account_number: - messagebox.showerror("错误", "请输入资金账号") - return - - # 保存配置 - config = configparser.ConfigParser() - config['config'] = { - 'miniQMTPath': mini_qmt_path.replace('\\', '/'), - 'account_no': account_number, - 'log_level': 'INFO' - } - - config_path = sdConstants.get_config_path() - try: - with open(config_path, 'w') as configfile: - config.write(configfile) - messagebox.showinfo("成功", "配置已保存") - self.root.destroy() - except Exception as e: - messagebox.showerror("错误", f"保存配置失败: {str(e)}") - -def check_and_create_config(): - """检查配置文件,如果不存在则打开配置窗口""" +# 这是应用的启动入口程序,负责初始化并启动主窗口。 +# 它创建一个Tkinter根窗口,实例化主窗口类MainBoardWindow, +# 并调用其run方法启动主事件循环。 +if __name__ == "__main__": + import tkinter as tk root = tk.Tk() - config_window = ConfigWindow(root) - root.mainloop() - -def initialize_system(): - """初始化系统""" - - try: - while True: - # 初始化配置 - if sdConstants.exist_config() and sdConstants.initConfig(): - # 初始化qmtv - qmtv.init_qmtv() - connected = qmtv.connect() - if connected: - # 连接成功,启动主窗口 - window = MainWindow(sdConstants.log_level) - window.run() - break - else: - option = messagebox.askokcancel("连接失败", "QMT连接失败,请检查") - if option: - check_and_create_config() - else: - break - else: - option = messagebox.askokcancel("错误", "请检查配置") - if option: - check_and_create_config() - else: - break - except Exception as e: - messagebox.showerror("错误", f"系统初始化失败: {str(e)}") - -if __name__ == '__main__': - initialize_system() \ No newline at end of file + app = MainEntry(root) + app.run() \ No newline at end of file