update
This commit is contained in:
@@ -1,58 +0,0 @@
|
|||||||
import configparser
|
|
||||||
from pathlib import Path
|
|
||||||
import sys
|
|
||||||
|
|
||||||
miniQMTPath = r'D:\\Programs\\DTQMT\\userdata_mini' # miniQMT软件的安装路径
|
|
||||||
# miniQMTPath = ''
|
|
||||||
account_no:str = '99082560'
|
|
||||||
console_log = True
|
|
||||||
log_level = "INFO"
|
|
||||||
|
|
||||||
def get_config_path() -> Path:
|
|
||||||
"""获取配置文件的正确路径(兼容开发环境和打包后的可执行文件)"""
|
|
||||||
if getattr(sys, 'frozen', False):
|
|
||||||
# 打包后的可执行文件环境
|
|
||||||
# sys._MEIPASS是PyInstaller解压临时文件的目录
|
|
||||||
# 配置文件应该放在可执行文件同目录下
|
|
||||||
base_path = Path(sys.executable).parent
|
|
||||||
else:
|
|
||||||
# 开发环境
|
|
||||||
base_path = Path(__file__).resolve().parent
|
|
||||||
|
|
||||||
return base_path / 'config.ini'
|
|
||||||
|
|
||||||
def save_config(miniQmtPath:str, account_no:str):
|
|
||||||
"""创建默认配置文件"""
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config['config'] = {
|
|
||||||
'miniQMTPath': miniQmtPath,
|
|
||||||
'account_no': account_no
|
|
||||||
}
|
|
||||||
config_path = get_config_path()
|
|
||||||
with open(config_path, 'w') as configfile:
|
|
||||||
config.write(configfile)
|
|
||||||
print(f'已创建默认配置文件: {config_path}')
|
|
||||||
|
|
||||||
def exist_config() -> bool:
|
|
||||||
"""检查配置文件是否存在"""
|
|
||||||
config_path = get_config_path()
|
|
||||||
return config_path.exists()
|
|
||||||
|
|
||||||
def initConfig() -> bool:
|
|
||||||
global miniQMTPath, account_no, log_level
|
|
||||||
|
|
||||||
# 获取配置文件路径
|
|
||||||
config_path = get_config_path()
|
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(config_path, encoding='utf-8')
|
|
||||||
miniQMTPath = config.get('config','miniQMTPath')
|
|
||||||
account_no = config.get('config','account_no')
|
|
||||||
log_level = config.get('config','log_level')
|
|
||||||
|
|
||||||
# 判断miniQMTPath是否为空,并且目录是否存在
|
|
||||||
if not miniQMTPath or not Path(miniQMTPath).exists():
|
|
||||||
print('请先配置miniQMTPath')
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Global configuration variables
|
||||||
|
# Define these BEFORE imports to avoid circular dependency issues with logger
|
||||||
|
console_log = True
|
||||||
|
miniQMTPath = None
|
||||||
|
miniQMTAccount = None
|
||||||
|
log_level = "1"
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from core.config.config_model import ConfigModel, CfgKeyLogLevel, CfgKeyMiniQmtPath, CfgKeyMiniQmtAccount, CfgKeyConsoleLog
|
||||||
|
from core.database import db
|
||||||
|
|
||||||
|
def initConfig() -> bool:
|
||||||
|
"""Initialize configuration from database"""
|
||||||
|
global miniQMTPath, miniQMTAccount, log_level, console_log
|
||||||
|
|
||||||
|
# Ensure connection and tables
|
||||||
|
db.connect(reuse_if_open=True)
|
||||||
|
if not db.table_exists(ConfigModel._meta.table_name):
|
||||||
|
db.create_tables([ConfigModel])
|
||||||
|
|
||||||
|
# Check and initialize keys
|
||||||
|
_init_key(CfgKeyLogLevel, "1")
|
||||||
|
_init_key(CfgKeyConsoleLog, "True")
|
||||||
|
_init_key(CfgKeyMiniQmtPath, None)
|
||||||
|
_init_key(CfgKeyMiniQmtAccount, None)
|
||||||
|
|
||||||
|
# Load values
|
||||||
|
try:
|
||||||
|
miniQMTPath = _get_value(CfgKeyMiniQmtPath)
|
||||||
|
miniQMTAccount = _get_value(CfgKeyMiniQmtAccount)
|
||||||
|
log_level = _get_value(CfgKeyLogLevel) or "1"
|
||||||
|
console_log = _get_value(CfgKeyConsoleLog) or "True"
|
||||||
|
console_log = console_log.lower() == "true"
|
||||||
|
|
||||||
|
# console_log is not in DB currently, keeping default True or could add to DB
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading config: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Validate path
|
||||||
|
if not miniQMTPath or not Path(miniQMTPath).exists():
|
||||||
|
print('请先配置miniQMTPath')
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _init_key(key: str, default_value: str | None):
|
||||||
|
"""Helper to initialize a key if it doesn't exist"""
|
||||||
|
try:
|
||||||
|
ConfigModel.get(ConfigModel.key == key)
|
||||||
|
except ConfigModel.DoesNotExist:
|
||||||
|
ConfigModel.create(key=key, value=default_value)
|
||||||
|
|
||||||
|
def _get_value(key: str) -> str | None:
|
||||||
|
"""Helper to get value safely"""
|
||||||
|
try:
|
||||||
|
return ConfigModel.get(ConfigModel.key == key).value
|
||||||
|
except ConfigModel.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def save_config(key: str, value: str):
|
||||||
|
"""Save configuration to database"""
|
||||||
|
_update_key(key, value)
|
||||||
|
print(f'配置已更新: {key}={value}')
|
||||||
|
|
||||||
|
def _update_key(key: str, value: str):
|
||||||
|
try:
|
||||||
|
record = ConfigModel.get(ConfigModel.key == key)
|
||||||
|
record.value = value
|
||||||
|
record.save()
|
||||||
|
except ConfigModel.DoesNotExist:
|
||||||
|
ConfigModel.create(key=key, value=value)
|
||||||
|
|
||||||
|
def exist_config() -> bool:
|
||||||
|
"""Check if essential config exists"""
|
||||||
|
path = _get_value(CfgKeyMiniQmtPath)
|
||||||
|
account = _get_value(CfgKeyMiniQmtAccount)
|
||||||
|
return bool(path and account)
|
||||||
|
|
||||||
|
def getLogLevel() -> str:
|
||||||
|
"""获取配置中的日志级别"""
|
||||||
|
return log_level
|
||||||
|
|
||||||
|
def getConsoleLog() -> bool:
|
||||||
|
"""获取配置中的控制台日志设置"""
|
||||||
|
return console_log
|
||||||
|
|
||||||
|
def getMiniQMTPath() -> str | None:
|
||||||
|
"""获取配置中的miniQMT路径"""
|
||||||
|
return miniQMTPath
|
||||||
|
|
||||||
|
def getMiniQMTAccount() -> str | None:
|
||||||
|
"""获取配置的miniQMT账号"""
|
||||||
|
return miniQMTAccount
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from peewee import CharField
|
||||||
|
from core.database import BaseModel, db
|
||||||
|
|
||||||
|
CfgKeyLogLevel = "log_level"
|
||||||
|
CfgKeyConsoleLog = "console_log"
|
||||||
|
CfgKeyMiniQmtPath = "miniQMTPath"
|
||||||
|
CfgKeyMiniQmtAccount = "miniQMTAccount"
|
||||||
|
|
||||||
|
class ConfigModel(BaseModel):
|
||||||
|
key = CharField(unique=True)
|
||||||
|
value = CharField(null=True)
|
||||||
+1
-2
@@ -1,10 +1,9 @@
|
|||||||
from peewee import SqliteDatabase, Model
|
from peewee import SqliteDatabase, Model
|
||||||
from core.logger import LogLevel, PrintLog
|
|
||||||
|
|
||||||
# 连接到SQLite数据库
|
# 连接到SQLite数据库
|
||||||
db: SqliteDatabase = SqliteDatabase('example.db')
|
db: SqliteDatabase = SqliteDatabase('example.db')
|
||||||
db.connect()
|
db.connect()
|
||||||
PrintLog(LogLevel.INFO, '- [成功]数据库连接')
|
print("Database connected")
|
||||||
|
|
||||||
# 定义基础模型类
|
# 定义基础模型类
|
||||||
class BaseModel(Model):
|
class BaseModel(Model):
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
|
|
||||||
# 市场数据监听控制事件
|
|
||||||
EventMarketActiveSwitch = "market_active_switch" # 市场数据状态变更
|
|
||||||
MarketDataUpdate = "market_data_update" # 市价更新
|
|
||||||
MarketOrderCreated = "market_order_created" # 市价单创建
|
|
||||||
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:
|
class EventBus:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.listeners = {} # 管理各种event的订阅情况
|
self.listeners = {} # 管理各种event的订阅情况
|
||||||
@@ -25,5 +12,6 @@ class EventBus:
|
|||||||
for listener in self.listeners[event_type]:
|
for listener in self.listeners[event_type]:
|
||||||
listener(data)
|
listener(data)
|
||||||
|
|
||||||
# 创建事件总线实例
|
# 订阅与发布事件示例
|
||||||
event_bus = EventBus()
|
# event_bus.subscribe('my_event', handle_event)
|
||||||
|
# event_bus.publish('my_event', {'key': 'value'})
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from .eventbus import EventBus
|
||||||
|
|
||||||
|
# Pring Log
|
||||||
|
EventPrintLog = "print_log" # 打印日志
|
||||||
|
|
||||||
|
# 创建事件总线实例
|
||||||
|
loggerEBus = EventBus()
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from eventbus import EventBus
|
||||||
|
|
||||||
|
# 市场数据监听控制事件
|
||||||
|
EventMarketActiveSwitch = "market_active_switch" # 市场数据状态变更
|
||||||
|
MarketDataUpdate = "market_data_update" # 市价更新
|
||||||
|
MarketOrderCreated = "market_order_created" # 市价单创建
|
||||||
|
MarketOrderTraded = "market_order_traded" # 市价单成交
|
||||||
|
|
||||||
|
# 创建事件总线实例
|
||||||
|
marketDataEventBus = EventBus()
|
||||||
+4
-4
@@ -1,7 +1,7 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from core.eventbus import EventPrintLog, event_bus
|
from core.ebus.logger_ebus import EventPrintLog, loggerEBus
|
||||||
import config
|
from core.config import config as config
|
||||||
|
|
||||||
|
|
||||||
class LogLevel(Enum):
|
class LogLevel(Enum):
|
||||||
@@ -21,6 +21,6 @@ class LogData:
|
|||||||
|
|
||||||
def PrintLog(level:LogLevel, message:str):
|
def PrintLog(level:LogLevel, message:str):
|
||||||
data = LogData(level, message)
|
data = LogData(level, message)
|
||||||
event_bus.publish(EventPrintLog, data)
|
loggerEBus.publish(EventPrintLog, data)
|
||||||
if config.console_log:
|
if config.getConsoleLog():
|
||||||
print(f'{level.name} {message}')
|
print(f'{level.name} {message}')
|
||||||
|
|||||||
@@ -22,8 +22,14 @@ class MainEntry:
|
|||||||
self.icon = None
|
self.icon = None
|
||||||
# 非 macOS 使用系统托盘(pystray);macOS 使用原生菜单栏
|
# 非 macOS 使用系统托盘(pystray);macOS 使用原生菜单栏
|
||||||
self.systray_supported = sys.platform != "darwin"
|
self.systray_supported = sys.platform != "darwin"
|
||||||
|
|
||||||
|
# 主内容容器
|
||||||
|
self.main_frame = tk.Frame(self.master, bg="#f0f0f0")
|
||||||
|
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
|
||||||
|
|
||||||
# 首次进入根据平台构建菜单
|
# 首次进入根据平台构建菜单
|
||||||
self.create_menu()
|
self.create_menu()
|
||||||
|
self.create_dashboard()
|
||||||
|
|
||||||
def build_menu_model(self):
|
def build_menu_model(self):
|
||||||
# 菜单模型统一描述所有菜单:
|
# 菜单模型统一描述所有菜单:
|
||||||
@@ -65,6 +71,29 @@ class MainEntry:
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def create_dashboard(self):
|
||||||
|
# 根据菜单模型构建主窗口按钮面板
|
||||||
|
for widget in self.main_frame.winfo_children():
|
||||||
|
widget.destroy()
|
||||||
|
|
||||||
|
model = self.build_menu_model()
|
||||||
|
|
||||||
|
for group in model:
|
||||||
|
# 为每个分组创建 LabelFrame
|
||||||
|
group_frame = tk.LabelFrame(self.main_frame, text=group["label"], bg="#f0f0f0", padx=10, pady=10)
|
||||||
|
group_frame.pack(fill=tk.X, pady=10, padx=10)
|
||||||
|
|
||||||
|
for it in group["items"]:
|
||||||
|
if it.get("separator"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
fn = getattr(self, it["action"]) if it.get("action") else None
|
||||||
|
state = tk.NORMAL if it.get("enabled", True) else tk.DISABLED
|
||||||
|
|
||||||
|
# 创建按钮
|
||||||
|
btn = tk.Button(group_frame, text=it["label"], command=fn, state=state)
|
||||||
|
btn.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
def create_menu(self):
|
def create_menu(self):
|
||||||
# 根据统一菜单模型与平台类型,渲染到系统托盘或 Tk 菜单栏
|
# 根据统一菜单模型与平台类型,渲染到系统托盘或 Tk 菜单栏
|
||||||
model = self.build_menu_model()
|
model = self.build_menu_model()
|
||||||
@@ -119,6 +148,7 @@ class MainEntry:
|
|||||||
self.qmt_enabled = True
|
self.qmt_enabled = True
|
||||||
PrintLog(LogLevel.INFO, "QMT 市场数据已开启")
|
PrintLog(LogLevel.INFO, "QMT 市场数据已开启")
|
||||||
self.create_menu()
|
self.create_menu()
|
||||||
|
self.create_dashboard()
|
||||||
|
|
||||||
def handler(self):
|
def handler(self):
|
||||||
# 通用占位处理:当前仅记录点击行为,后续可替换为具体业务逻辑
|
# 通用占位处理:当前仅记录点击行为,后续可替换为具体业务逻辑
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
# 创建事件总线实例
|
|
||||||
marketDataEventBus = EventBus()
|
|
||||||
@@ -20,7 +20,6 @@ class QmtV(XtQuantTraderCallback):
|
|||||||
self.isMarketActive = False
|
self.isMarketActive = False
|
||||||
self.refresh_thread = threading.Thread(target=self.marketStatusNotifier, daemon=True)
|
self.refresh_thread = threading.Thread(target=self.marketStatusNotifier, daemon=True)
|
||||||
self.refresh_thread.start()
|
self.refresh_thread.start()
|
||||||
# time.sleep(3.1)
|
|
||||||
|
|
||||||
def getTrader(self) -> XtQuantTrader:
|
def getTrader(self) -> XtQuantTrader:
|
||||||
return self.xttrader
|
return self.xttrader
|
||||||
@@ -42,8 +41,8 @@ class QmtV(XtQuantTraderCallback):
|
|||||||
else:
|
else:
|
||||||
self.inited = True
|
self.inited = True
|
||||||
|
|
||||||
self.account = StockAccount(config.account_no, 'STOCK') # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue]
|
self.account = StockAccount(config.miniQMTAccount, 'STOCK') # pyright: ignore[reportAssignmentType, reportAttributeAccessIssue]
|
||||||
PrintLog(LogLevel.INFO, f'- [成功]交易账号对象初始化完成, 账号: {config.account_no}') # pyright: ignore[reportOptionalMemberAccess]
|
PrintLog(LogLevel.INFO, f'- [成功]交易账号对象初始化完成, 账号: {config.miniQMTAccount}') # pyright: ignore[reportOptionalMemberAccess]
|
||||||
subscribe_result = self.xttrader.subscribe(self.account)
|
subscribe_result = self.xttrader.subscribe(self.account)
|
||||||
PrintLog(LogLevel.INFO, f'- [{'成功' if subscribe_result == 0 else '失败'}:{subscribe_result}]交易状态订阅')
|
PrintLog(LogLevel.INFO, f'- [{'成功' if subscribe_result == 0 else '失败'}:{subscribe_result}]交易状态订阅')
|
||||||
if subscribe_result != 0:
|
if subscribe_result != 0:
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from peewee import CharField, DateField
|
||||||
|
|
||||||
|
from core.database import BaseModel, db
|
||||||
|
|
||||||
|
class StockInfo(BaseModel):
|
||||||
|
stock_code = CharField(unique=True, primary_key=True)
|
||||||
|
stock_name = CharField()
|
||||||
@@ -97,17 +97,8 @@ class ConfigWindow:
|
|||||||
return
|
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:
|
try:
|
||||||
with open(config_path, 'w') as configfile:
|
sdConstants.save_config(mini_qmt_path.replace('\\', '/'), account_number)
|
||||||
config.write(configfile)
|
|
||||||
messagebox.showinfo("成功", "配置已保存")
|
messagebox.showinfo("成功", "配置已保存")
|
||||||
self.root.destroy()
|
self.root.destroy()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user