UI初稿
This commit is contained in:
+5
-7
@@ -1,9 +1,7 @@
|
|||||||
[config]
|
[config]
|
||||||
miniQMTPath=D:\\Programs\\DTQMT\\userdata_mini
|
miniqmtpath = /Users/gao/Workspace/quant
|
||||||
; miniQMTPath=D:\\Programs\\DTQMT_MN\\userdata_mini ; 测试账号
|
grid_price = 10.9,10.0,9.1,8.2,7.3,6.4,5.5,4.6,3.7,2.8,1.9,1.0
|
||||||
grid_price=1.665,1.660,1.655,1.650,1.645,1.640,1.635,1.630,1.625,1.620,1.615,1.610
|
|
||||||
; grid_price=11.0,10.0,9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0
|
|
||||||
grid_volume = 200
|
grid_volume = 200
|
||||||
account_no = '99082560'
|
account_no = 99082560
|
||||||
; account_no = '89009170' ; 测试账号
|
max_enabled_targets = 10
|
||||||
max_enabled_targets = 10
|
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ class SFGridController(XtQuantTraderCallback):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
xtdata.enable_hello = False
|
xtdata.enable_hello = False
|
||||||
strategy_db.db.connect()
|
|
||||||
strategy_db.db.create_tables([strategy_db.TradeTarget])
|
|
||||||
print('- [成功]数据库模块初始化')
|
|
||||||
|
|
||||||
session_id = int(time.time())
|
session_id = int(time.time())
|
||||||
|
|
||||||
|
|||||||
+41
-32
@@ -2,10 +2,11 @@
|
|||||||
from mimetypes import init
|
from mimetypes import init
|
||||||
import sys
|
import sys
|
||||||
sys.stdout.reconfigure(encoding='utf-8') # 设置标准输出编码为UTF-8 # type: ignore
|
sys.stdout.reconfigure(encoding='utf-8') # 设置标准输出编码为UTF-8 # type: ignore
|
||||||
from core.main_controller import SFGridController
|
from core import strategy_db
|
||||||
import core.util as util
|
# from core.main_controller import SFGridController
|
||||||
|
# import core.util as util
|
||||||
import sfgrid_constants as sdConstants
|
import sfgrid_constants as sdConstants
|
||||||
from xtquant import xtdata
|
# from xtquant import xtdata
|
||||||
import ui
|
import ui
|
||||||
|
|
||||||
def interact():
|
def interact():
|
||||||
@@ -13,35 +14,35 @@ def interact():
|
|||||||
import code
|
import code
|
||||||
code.InteractiveConsole(locals=globals()).interact()
|
code.InteractiveConsole(locals=globals()).interact()
|
||||||
|
|
||||||
def startMarketData():
|
# def startMarketData():
|
||||||
ctrl.startMarketData()
|
# ctrl.startMarketData()
|
||||||
|
|
||||||
def stopMarketData():
|
# def stopMarketData():
|
||||||
ctrl.stopMarketData()
|
# ctrl.stopMarketData()
|
||||||
|
|
||||||
def pool():
|
# def pool():
|
||||||
ctrl.print_pool()
|
# ctrl.print_pool()
|
||||||
|
|
||||||
def addTarget(stock_code):
|
# def addTarget(stock_code):
|
||||||
ctrl.add_trade_target(stock_code)
|
# ctrl.add_trade_target(stock_code)
|
||||||
|
|
||||||
def delTarget(index:int):
|
# def delTarget(index:int):
|
||||||
ctrl.del_trade_target(index)
|
# ctrl.del_trade_target(index)
|
||||||
|
|
||||||
def accountInfo():
|
# def accountInfo():
|
||||||
ctrl.print_account_info()
|
# ctrl.print_account_info()
|
||||||
|
|
||||||
def positionInfo():
|
# def positionInfo():
|
||||||
ctrl.print_position_info()
|
# ctrl.print_position_info()
|
||||||
|
|
||||||
def startTrade(index:int):
|
# def startTrade(index:int):
|
||||||
ctrl.start_stock_trade(index)
|
# ctrl.start_stock_trade(index)
|
||||||
|
|
||||||
def pauseTrade(index:int):
|
# def pauseTrade(index:int):
|
||||||
ctrl.pause_stock_trade(index)
|
# ctrl.pause_stock_trade(index)
|
||||||
|
|
||||||
def stockTradeCtrl(index: int):
|
# def stockTradeCtrl(index: int):
|
||||||
return ctrl.stock_trade_ctrl[ctrl.instrument_pool[index].stock_code]
|
# return ctrl.stock_trade_ctrl[ctrl.instrument_pool[index].stock_code]
|
||||||
|
|
||||||
def help():
|
def help():
|
||||||
print("基础指令:")
|
print("基础指令:")
|
||||||
@@ -61,13 +62,21 @@ def help():
|
|||||||
print(" ctrl - 访问控制器实例")
|
print(" ctrl - 访问控制器实例")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# app = ui.ProfessionalTradeUI(trade_targets=ctrl.instrument_pool)
|
|
||||||
# app.run()
|
strategy_db.db.connect()
|
||||||
|
strategy_db.db.create_tables([strategy_db.TradeTarget])
|
||||||
|
print('- [成功]数据库模块初始化')
|
||||||
|
|
||||||
|
targets = strategy_db.TradeTarget.select()
|
||||||
|
|
||||||
|
|
||||||
|
app = ui.TradeTargetUI(trade_targets=targets)
|
||||||
|
app.run()
|
||||||
|
|
||||||
sdConstants.initConfig()
|
# sdConstants.initConfig()
|
||||||
print(f'{sdConstants.account_no} : {sdConstants.miniQMTPath}')
|
# print(f'{sdConstants.account_no} : {sdConstants.miniQMTPath}')
|
||||||
ctrl: SFGridController = SFGridController(sdConstants.account_no, sdConstants.miniQMTPath)
|
# ctrl: SFGridController = SFGridController(sdConstants.account_no, sdConstants.miniQMTPath)
|
||||||
if ctrl.inited:
|
# if ctrl.inited:
|
||||||
interact()
|
# interact()
|
||||||
else:
|
# else:
|
||||||
print("控制器初始化失败")
|
# print("控制器初始化失败")
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
import random
|
import random
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk, messagebox, filedialog
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from core.strategy_db import TradeTarget
|
from core.strategy_db import TradeTarget
|
||||||
|
import configparser
|
||||||
|
import sfgrid_constants
|
||||||
|
|
||||||
class TradeTargetUI:
|
class TradeTargetUI:
|
||||||
def __init__(self):
|
def __init__(self, trade_targets: Optional[List[TradeTarget]] = None):
|
||||||
|
if trade_targets is not None:
|
||||||
|
self.trade_targets = trade_targets
|
||||||
|
else:
|
||||||
|
self.trade_targets = []
|
||||||
|
|
||||||
self.root = tk.Tk()
|
self.root = tk.Tk()
|
||||||
self.root.title("交易标的监控系统")
|
self.root.title("三疯交易系统")
|
||||||
self.root.geometry("1200x700")
|
self.root.geometry("1200x700")
|
||||||
|
|
||||||
# 创建界面
|
# 创建界面
|
||||||
@@ -16,67 +23,101 @@ class TradeTargetUI:
|
|||||||
|
|
||||||
def create_ui(self):
|
def create_ui(self):
|
||||||
"""创建UI界面"""
|
"""创建UI界面"""
|
||||||
|
# 创建菜单栏
|
||||||
|
self.create_menu_bar()
|
||||||
|
|
||||||
# 主框架
|
# 主框架
|
||||||
main_frame = ttk.Frame(self.root)
|
main_frame = ttk.Frame(self.root)
|
||||||
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
# 表格区域
|
# 表格区域
|
||||||
self.create_tables_area(main_frame)
|
self.create_tables_area(main_frame)
|
||||||
|
|
||||||
|
def create_menu_bar(self):
|
||||||
|
"""创建菜单栏"""
|
||||||
|
menubar = tk.Menu(self.root)
|
||||||
|
self.root.config(menu=menubar)
|
||||||
|
|
||||||
# 按钮区域
|
# 系统菜单
|
||||||
self.create_buttons_area(main_frame)
|
system_menu = tk.Menu(menubar, tearoff=0)
|
||||||
|
menubar.add_cascade(label="系统", menu=system_menu)
|
||||||
|
system_menu.add_command(label="系统设置", command=self.system_settings)
|
||||||
|
system_menu.add_separator()
|
||||||
|
system_menu.add_command(label="退出", command=self.root.quit)
|
||||||
|
|
||||||
def create_tables_area(self, parent):
|
def create_tables_area(self, parent):
|
||||||
"""创建表格区域"""
|
"""创建表格区域"""
|
||||||
tables_frame = ttk.Frame(parent)
|
# 上方交易标的区域
|
||||||
tables_frame.pack(fill=tk.BOTH, expand=True)
|
trade_frame = ttk.LabelFrame(parent, text="交易标的详情", padding=10)
|
||||||
|
trade_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 5))
|
||||||
|
|
||||||
# 左侧表格框架
|
# 创建交易标的表格
|
||||||
left_frame = ttk.LabelFrame(tables_frame, text="交易标的详情", padding=10)
|
self.create_trade_target_table(trade_frame)
|
||||||
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
|
|
||||||
|
|
||||||
# 右侧表格框架
|
# 下方操作日志区域(默认隐藏)
|
||||||
right_frame = ttk.LabelFrame(tables_frame, text="操作日志", padding=10)
|
self.log_frame = ttk.LabelFrame(parent, text="操作日志", padding=10)
|
||||||
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
|
# 默认不显示,通过工具栏按钮控制
|
||||||
|
# self.log_frame.pack(fill=tk.X, pady=(5, 0))
|
||||||
|
self.log_visible = False # 日志区域可见性标志
|
||||||
|
|
||||||
# 创建左侧交易标的表格
|
# 创建操作日志表格
|
||||||
self.create_trade_target_table(left_frame)
|
self.create_log_table(self.log_frame)
|
||||||
|
|
||||||
# 创建右侧操作日志表格
|
|
||||||
self.create_log_table(right_frame)
|
|
||||||
|
|
||||||
def create_trade_target_table(self, parent):
|
def create_trade_target_table(self, parent):
|
||||||
"""创建交易标的表格"""
|
"""创建交易标的表格"""
|
||||||
# 创建Treeview
|
# 创建工具栏
|
||||||
columns = (
|
toolbar_frame = ttk.Frame(parent)
|
||||||
"id", "stock_code", "stock_name", "current_position", "grid_index",
|
toolbar_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
"last_trade_price", "current_buy_price", "current_sell_price",
|
|
||||||
"current_buy_order_no", "current_sell_order_no",
|
# 工具栏按钮
|
||||||
"status", "enabled"
|
ttk.Button(toolbar_frame, text="▶️ 启动交易",
|
||||||
|
command=self.start_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(toolbar_frame, text="⏸ 暂停交易",
|
||||||
|
command=self.pause_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(toolbar_frame, text="➕ 添加标的",
|
||||||
|
command=self.add_trade_target, width=12).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(toolbar_frame, text="🗑 删除标的",
|
||||||
|
command=self.delete_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
|
# 添加分隔符
|
||||||
|
ttk.Separator(toolbar_frame, orient='vertical').pack(side=tk.LEFT, fill=tk.Y, padx=10)
|
||||||
|
|
||||||
|
# 日志显示/隐藏按钮
|
||||||
|
self.log_toggle_btn = ttk.Button(toolbar_frame, text="📋 显示日志",
|
||||||
|
command=self.toggle_log_panel, width=12)
|
||||||
|
self.log_toggle_btn.pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
|
# 添加分隔线
|
||||||
|
ttk.Separator(parent, orient='horizontal').pack(fill=tk.X, pady=5)
|
||||||
|
|
||||||
|
columns = ("ID",
|
||||||
|
"股票代码", "股票名称", "持仓数量", "网格索引",
|
||||||
|
"最新成交价", "计划买入价", "买入订单号", "计划卖出价", "卖出订单号",
|
||||||
|
"启用状态", "交易状态"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.trade_table = ttk.Treeview(parent, columns=columns, show='headings', height=12)
|
self.trade_table = ttk.Treeview(parent, columns=columns, show='headings', height=15)
|
||||||
|
|
||||||
# 定义列标题和宽度
|
# 专业化的列配置
|
||||||
column_configs = {
|
column_configs = {
|
||||||
"id": ("ID", 80),
|
"ID": (50, tk.CENTER),
|
||||||
"stock_code": ("股票代码", 80),
|
"股票代码": (90, tk.CENTER),
|
||||||
"stock_name": ("股票名称", 100),
|
"股票名称": (100, tk.CENTER),
|
||||||
"current_position": ("持仓数量", 90),
|
"持仓数量": (90, tk.CENTER),
|
||||||
"grid_index": ("网格索引", 80),
|
"网格索引": (80, tk.CENTER),
|
||||||
"last_trade_price": ("最新成交价", 90),
|
"最新成交价": (100, tk.CENTER),
|
||||||
"current_buy_price": ("当前买入价", 90),
|
"计划买入价": (100, tk.CENTER),
|
||||||
"current_sell_price": ("当前卖出价", 90),
|
"买入订单号": (100, tk.CENTER),
|
||||||
"current_buy_order_no": ("买入订单号", 120),
|
"计划卖出价": (100, tk.CENTER),
|
||||||
"current_sell_order_no": ("卖出订单号", 120),
|
"卖出订单号": (100, tk.CENTER),
|
||||||
"status": ("状态", 70),
|
"启用状态": (80, tk.CENTER),
|
||||||
"enabled": ("启用状态", 80)
|
"交易状态": (80, tk.CENTER)
|
||||||
}
|
}
|
||||||
|
|
||||||
for col in columns:
|
for col in columns:
|
||||||
title, width = column_configs[col]
|
width, anchor = column_configs[col]
|
||||||
self.trade_table.heading(col, text=title)
|
self.trade_table.heading(col, text=col)
|
||||||
self.trade_table.column(col, width=width, anchor=tk.CENTER)
|
self.trade_table.column(col, width=width, anchor=anchor) # type: ignore
|
||||||
|
|
||||||
# 填充数据
|
# 填充数据
|
||||||
self.populate_trade_table()
|
self.populate_trade_table()
|
||||||
@@ -85,24 +126,56 @@ class TradeTargetUI:
|
|||||||
scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.trade_table.yview)
|
scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.trade_table.yview)
|
||||||
self.trade_table.configure(yscrollcommand=scrollbar.set)
|
self.trade_table.configure(yscrollcommand=scrollbar.set)
|
||||||
|
|
||||||
# 布局
|
|
||||||
self.trade_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
self.trade_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
|
|
||||||
# 绑定双击事件
|
# 绑定双击事件
|
||||||
self.trade_table.bind("<Double-1>", self.on_table_double_click)
|
self.trade_table.bind("<Double-1>", self.on_table_double_click)
|
||||||
|
|
||||||
|
def get_status_indicator(self, target: TradeTarget) -> str:
|
||||||
|
"""获取状态指示器(带颜色色块的文本)"""
|
||||||
|
if target.status == 1:
|
||||||
|
# 绿色圆点表示交易中
|
||||||
|
return "🟢 已建仓"
|
||||||
|
elif target.status == 0:
|
||||||
|
# 黄色圆点表示暂停
|
||||||
|
return "🟡 未建仓"
|
||||||
|
else:
|
||||||
|
return "🔴 错误状态"
|
||||||
|
|
||||||
|
def get_trade_status_indicator(self, status: int) -> str:
|
||||||
|
"""获取交易状态指示器"""
|
||||||
|
if status == 1:
|
||||||
|
return "🟢 策略运行"
|
||||||
|
else:
|
||||||
|
return "🟡 策略暂停"
|
||||||
|
|
||||||
def populate_trade_table(self):
|
def populate_trade_table(self):
|
||||||
"""填充交易标的表格数据"""
|
"""填充交易标的表格数据"""
|
||||||
# 颜色标签
|
for temp in self.trade_targets:
|
||||||
self.trade_table.tag_configure('enabled', background='#f0f8ff') # 淡蓝色
|
target: TradeTarget = temp
|
||||||
self.trade_table.tag_configure('disabled', background='#f5f5f5') # 淡灰色
|
values = [
|
||||||
|
target.id, # type: ignore
|
||||||
|
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,
|
||||||
|
self.get_status_indicator(target),
|
||||||
|
self.get_trade_status_indicator(target.status) # type: ignore
|
||||||
|
]
|
||||||
|
|
||||||
|
self.trade_table.insert('', tk.END, values=values)
|
||||||
|
|
||||||
def create_log_table(self, parent):
|
def create_log_table(self, parent):
|
||||||
"""创建操作日志表格"""
|
"""创建操作日志表格"""
|
||||||
columns = ("timestamp", "level", "message")
|
columns = ("timestamp", "level", "message")
|
||||||
|
|
||||||
self.log_table = ttk.Treeview(parent, columns=columns, show='headings', height=12)
|
self.log_table = ttk.Treeview(parent, columns=columns, show='headings', height=8)
|
||||||
|
|
||||||
log_column_configs = {
|
log_column_configs = {
|
||||||
"timestamp": ("时间", 120),
|
"timestamp": ("时间", 120),
|
||||||
@@ -136,118 +209,6 @@ class TradeTargetUI:
|
|||||||
self.log_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
self.log_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
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):
|
def get_status_text(self, status):
|
||||||
"""获取状态文本"""
|
"""获取状态文本"""
|
||||||
status_map = {
|
status_map = {
|
||||||
@@ -264,179 +225,169 @@ class TradeTargetUI:
|
|||||||
values = self.trade_table.item(item)['values']
|
values = self.trade_table.item(item)['values']
|
||||||
self.add_log("DEBUG", f"双击查看详情: {values[0]} - {values[1]}")
|
self.add_log("DEBUG", f"双击查看详情: {values[0]} - {values[1]}")
|
||||||
|
|
||||||
|
def get_selected_target(self):
|
||||||
|
"""获取选中的交易标的"""
|
||||||
|
selected = self.trade_table.selection()
|
||||||
|
if not selected:
|
||||||
|
messagebox.showwarning("未选中", "请先选择一个交易标的")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取选中行的ID
|
||||||
|
item = selected[0]
|
||||||
|
values = self.trade_table.item(item)['values']
|
||||||
|
target_id = values[0]
|
||||||
|
|
||||||
|
# 从列表中找到对应的target对象
|
||||||
|
for target in self.trade_targets:
|
||||||
|
if target.id == target_id: # type: ignore
|
||||||
|
return target
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def start_selected_trade(self):
|
||||||
|
"""启动选中的交易"""
|
||||||
|
target = self.get_selected_target()
|
||||||
|
if not target:
|
||||||
|
return
|
||||||
|
|
||||||
|
if target.enabled: # type: ignore
|
||||||
|
messagebox.showinfo("提示", f"{target.stock_code} ({target.stock_name}) 已经在运行中")
|
||||||
|
return
|
||||||
|
|
||||||
|
result = messagebox.askyesno(
|
||||||
|
"确认启动",
|
||||||
|
f"确定要启动以下交易标的吗?\n\n"
|
||||||
|
f"股票代码: {target.stock_code}\n"
|
||||||
|
f"股票名称: {target.stock_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
target.enabled = True # type: ignore
|
||||||
|
self.add_log("INFO", f"已启动交易: {target.stock_code} - {target.stock_name}")
|
||||||
|
self.refresh_table()
|
||||||
|
messagebox.showinfo("启动成功", f"已启动 {target.stock_code} ({target.stock_name}) 的交易")
|
||||||
|
|
||||||
|
def pause_selected_trade(self):
|
||||||
|
"""暂停选中的交易"""
|
||||||
|
target = self.get_selected_target()
|
||||||
|
if not target:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not target.enabled: # type: ignore
|
||||||
|
messagebox.showinfo("提示", f"{target.stock_code} ({target.stock_name}) 已经是暂停状态")
|
||||||
|
return
|
||||||
|
|
||||||
|
result = messagebox.askyesno(
|
||||||
|
"确认暂停",
|
||||||
|
f"确定要暂停以下交易标的吗?\n\n"
|
||||||
|
f"股票代码: {target.stock_code}\n"
|
||||||
|
f"股票名称: {target.stock_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
target.enabled = False # type: ignore
|
||||||
|
self.add_log("INFO", f"已暂停交易: {target.stock_code} - {target.stock_name}")
|
||||||
|
self.refresh_table()
|
||||||
|
messagebox.showinfo("暂停成功", f"已暂停 {target.stock_code} ({target.stock_name}) 的交易")
|
||||||
|
|
||||||
|
def delete_selected_trade(self):
|
||||||
|
"""删除选中的交易标的"""
|
||||||
|
target = self.get_selected_target()
|
||||||
|
if not target:
|
||||||
|
return
|
||||||
|
|
||||||
|
result = messagebox.askyesno(
|
||||||
|
"确认删除",
|
||||||
|
f"确定要删除以下交易标的吗?\n\n"
|
||||||
|
f"股票代码: {target.stock_code}\n"
|
||||||
|
f"股票名称: {target.stock_name}\n\n"
|
||||||
|
f"⚠️ 此操作不可恢复!",
|
||||||
|
icon='warning'
|
||||||
|
)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
try:
|
||||||
|
self.trade_targets.remove(target)
|
||||||
|
self.add_log("WARNING", f"已删除交易标的: {target.stock_code} - {target.stock_name}")
|
||||||
|
self.refresh_table()
|
||||||
|
messagebox.showinfo("删除成功", f"已删除 {target.stock_code} ({target.stock_name})")
|
||||||
|
except Exception as e:
|
||||||
|
self.add_log("ERROR", f"删除失败: {str(e)}")
|
||||||
|
messagebox.showerror("删除失败", f"删除交易标的时出错:{str(e)}")
|
||||||
|
|
||||||
|
def add_trade_target(self):
|
||||||
|
"""添加新的交易标的"""
|
||||||
|
# TODO: 实现添加交易标的的对话框
|
||||||
|
messagebox.showinfo("提示", "添加交易标的功能待实现")
|
||||||
|
self.add_log("INFO", "点击添加交易标的按钮")
|
||||||
|
|
||||||
|
def toggle_log_panel(self):
|
||||||
|
"""切换日志面板的显示/隐藏"""
|
||||||
|
if self.log_visible:
|
||||||
|
# 隐藏日志面板
|
||||||
|
self.log_frame.pack_forget()
|
||||||
|
self.log_visible = False
|
||||||
|
self.log_toggle_btn.config(text="📋 显示日志")
|
||||||
|
else:
|
||||||
|
# 显示日志面板
|
||||||
|
self.log_frame.pack(fill=tk.X, pady=(5, 0))
|
||||||
|
self.log_visible = True
|
||||||
|
self.log_toggle_btn.config(text="📋 隐藏日志")
|
||||||
|
|
||||||
|
def refresh_table(self):
|
||||||
|
"""刷新表格数据"""
|
||||||
|
# 清空表格
|
||||||
|
for item in self.trade_table.get_children():
|
||||||
|
self.trade_table.delete(item)
|
||||||
|
|
||||||
|
# 重新填充
|
||||||
|
self.populate_trade_table()
|
||||||
|
|
||||||
def add_log(self, level, message):
|
def add_log(self, level, message):
|
||||||
"""添加日志记录"""
|
"""添加日志记录"""
|
||||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
self.log_table.insert('', 0, values=(timestamp, level, message))
|
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):
|
def system_settings(self):
|
||||||
"""专业版的系统设置"""
|
"""系统设置"""
|
||||||
settings_window = tk.Toplevel(self.root)
|
settings_window = tk.Toplevel(self.root)
|
||||||
settings_window.title("网格交易系统配置")
|
settings_window.title("网格交易系统配置")
|
||||||
settings_window.geometry("600x400")
|
|
||||||
|
|
||||||
# 创建选项卡
|
# 设置窗口大小
|
||||||
|
window_width = 700
|
||||||
|
window_height = 600
|
||||||
|
|
||||||
|
# 先设置为模态窗口
|
||||||
|
settings_window.transient(self.root)
|
||||||
|
|
||||||
|
# 确保主窗口完全初始化
|
||||||
|
self.root.update_idletasks()
|
||||||
|
|
||||||
|
# 获取主窗口的实际大小(包括边框)
|
||||||
|
# 使用winfo_rootx/rooty获取窗口在屏幕上的绝对位置
|
||||||
|
main_x = self.root.winfo_rootx()
|
||||||
|
main_y = self.root.winfo_rooty()
|
||||||
|
main_width = self.root.winfo_width()
|
||||||
|
main_height = self.root.winfo_height()
|
||||||
|
|
||||||
|
# 计算设置窗口相对于主窗口的居中位置
|
||||||
|
x = main_x + (main_width - window_width) // 2
|
||||||
|
y = main_y + (main_height - window_height) // 2
|
||||||
|
|
||||||
|
# 设置窗口大小和位置
|
||||||
|
settings_window.geometry(f"{window_width}x{window_height}+{x}+{y}")
|
||||||
|
settings_window.resizable(False, False)
|
||||||
|
|
||||||
|
# 设置为模态窗口
|
||||||
|
settings_window.grab_set()
|
||||||
|
|
||||||
|
# 添加底部按钮区域(先创建,确保固定在底部)
|
||||||
|
button_frame = ttk.Frame(settings_window)
|
||||||
|
button_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=20, pady=10)
|
||||||
|
|
||||||
|
# 创建选项卡(在按钮之后创建,填充剩余空间)
|
||||||
notebook = ttk.Notebook(settings_window)
|
notebook = ttk.Notebook(settings_window)
|
||||||
notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=(10, 0))
|
||||||
|
|
||||||
# 基础设置
|
# 基础设置
|
||||||
basic_frame = ttk.Frame(notebook)
|
basic_frame = ttk.Frame(notebook)
|
||||||
@@ -446,21 +397,262 @@ class ProfessionalTradeUI(TradeTargetUI):
|
|||||||
advanced_frame = ttk.Frame(notebook)
|
advanced_frame = ttk.Frame(notebook)
|
||||||
notebook.add(advanced_frame, text="高级设置")
|
notebook.add(advanced_frame, text="高级设置")
|
||||||
|
|
||||||
# 添加具体的配置选项
|
# 读取当前配置
|
||||||
settings = [
|
config = configparser.ConfigParser()
|
||||||
("网格宽度", "0.5%"),
|
config.read('config.ini')
|
||||||
("网格数量", "10层"),
|
|
||||||
("交易间隔", "30秒"),
|
# 创建输入框字典用于保存引用
|
||||||
("单笔数量", "100股")
|
entries = {}
|
||||||
|
|
||||||
|
# 网格价格计算参数
|
||||||
|
grid_params = {}
|
||||||
|
|
||||||
|
# 添加网格价格设置(特殊处理)
|
||||||
|
grid_price_frame = ttk.LabelFrame(basic_frame, text="网格价格设置", padding=15)
|
||||||
|
grid_price_frame.pack(fill=tk.X, padx=20, pady=10)
|
||||||
|
|
||||||
|
# 基准价格
|
||||||
|
base_price_row = ttk.Frame(grid_price_frame)
|
||||||
|
base_price_row.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(base_price_row, text="基准价格:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
||||||
|
base_price_entry = ttk.Entry(base_price_row, width=15, font=('Arial', 10))
|
||||||
|
base_price_entry.insert(0, "10.0")
|
||||||
|
base_price_entry.pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(base_price_row, text="元", foreground='gray', font=('Arial', 9)).pack(side=tk.LEFT)
|
||||||
|
grid_params['base_price'] = base_price_entry
|
||||||
|
|
||||||
|
# 网格类型
|
||||||
|
grid_type_row = ttk.Frame(grid_price_frame)
|
||||||
|
grid_type_row.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(grid_type_row, text="网格类型:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
grid_type_var = tk.StringVar(value="金额差")
|
||||||
|
ttk.Radiobutton(grid_type_row, text="百分比", variable=grid_type_var,
|
||||||
|
value="百分比", command=lambda: on_grid_type_change()).pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Radiobutton(grid_type_row, text="金额差", variable=grid_type_var,
|
||||||
|
value="金额差", command=lambda: on_grid_type_change()).pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
grid_params['grid_type'] = grid_type_var
|
||||||
|
|
||||||
|
# 网格大小
|
||||||
|
grid_size_row = ttk.Frame(grid_price_frame)
|
||||||
|
grid_size_row.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(grid_size_row, text="网格大小:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
||||||
|
grid_size_entry = ttk.Entry(grid_size_row, width=15, font=('Arial', 10))
|
||||||
|
grid_size_entry.insert(0, "1.0")
|
||||||
|
grid_size_entry.pack(side=tk.LEFT, padx=5)
|
||||||
|
grid_size_unit_label = ttk.Label(grid_size_row, text="元", foreground='gray', font=('Arial', 9))
|
||||||
|
grid_size_unit_label.pack(side=tk.LEFT)
|
||||||
|
grid_params['grid_size'] = grid_size_entry
|
||||||
|
grid_params['grid_size_unit_label'] = grid_size_unit_label
|
||||||
|
|
||||||
|
# 网格类型改变时更新单位
|
||||||
|
def on_grid_type_change():
|
||||||
|
if grid_type_var.get() == "百分比":
|
||||||
|
grid_size_unit_label.config(text="%")
|
||||||
|
grid_size_entry.delete(0, tk.END)
|
||||||
|
grid_size_entry.insert(0, "1.0")
|
||||||
|
else:
|
||||||
|
grid_size_unit_label.config(text="元")
|
||||||
|
grid_size_entry.delete(0, tk.END)
|
||||||
|
grid_size_entry.insert(0, "1.0")
|
||||||
|
update_preview()
|
||||||
|
|
||||||
|
# 上方网格数量
|
||||||
|
upper_grid_row = ttk.Frame(grid_price_frame)
|
||||||
|
upper_grid_row.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(upper_grid_row, text="上方网格数量:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
||||||
|
upper_grid_entry = ttk.Entry(upper_grid_row, width=15, font=('Arial', 10))
|
||||||
|
upper_grid_entry.insert(0, "1")
|
||||||
|
upper_grid_entry.pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(upper_grid_row, text="格", foreground='gray', font=('Arial', 9)).pack(side=tk.LEFT)
|
||||||
|
grid_params['upper_count'] = upper_grid_entry
|
||||||
|
|
||||||
|
# 下方网格数量
|
||||||
|
lower_grid_row = ttk.Frame(grid_price_frame)
|
||||||
|
lower_grid_row.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(lower_grid_row, text="下方网格数量:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
||||||
|
lower_grid_entry = ttk.Entry(lower_grid_row, width=15, font=('Arial', 10))
|
||||||
|
lower_grid_entry.insert(0, "10")
|
||||||
|
lower_grid_entry.pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(lower_grid_row, text="格", foreground='gray', font=('Arial', 9)).pack(side=tk.LEFT)
|
||||||
|
grid_params['lower_count'] = lower_grid_entry
|
||||||
|
|
||||||
|
# 预览按钮和结果显示
|
||||||
|
preview_row = ttk.Frame(grid_price_frame)
|
||||||
|
preview_row.pack(fill=tk.X, pady=10)
|
||||||
|
|
||||||
|
preview_result = tk.StringVar(value="点击'预览'查看生成的网格价格序列")
|
||||||
|
|
||||||
|
def calculate_grid_prices():
|
||||||
|
"""计算网格价格序列"""
|
||||||
|
try:
|
||||||
|
base_price = float(base_price_entry.get())
|
||||||
|
grid_size = float(grid_size_entry.get())
|
||||||
|
upper_count = int(upper_grid_entry.get())
|
||||||
|
lower_count = int(lower_grid_entry.get())
|
||||||
|
grid_type = grid_type_var.get()
|
||||||
|
|
||||||
|
prices = []
|
||||||
|
|
||||||
|
# 计算上方网格价格
|
||||||
|
for i in range(upper_count, 0, -1):
|
||||||
|
if grid_type == "百分比":
|
||||||
|
price = base_price * (1 + grid_size / 100 * i)
|
||||||
|
else: # 金额差
|
||||||
|
price = base_price + grid_size * i
|
||||||
|
prices.append(round(price, 3))
|
||||||
|
|
||||||
|
# 添加基准价格
|
||||||
|
prices.append(base_price)
|
||||||
|
|
||||||
|
# 计算下方网格价格
|
||||||
|
for i in range(1, lower_count + 1):
|
||||||
|
if grid_type == "百分比":
|
||||||
|
price = base_price * (1 - grid_size / 100 * i)
|
||||||
|
else: # 金额差
|
||||||
|
price = base_price - grid_size * i
|
||||||
|
# 确保价格不为负
|
||||||
|
if price >= 0:
|
||||||
|
prices.append(round(price, 3))
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
return prices
|
||||||
|
except ValueError as e:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_preview():
|
||||||
|
"""自动更新网格价格序列预览"""
|
||||||
|
prices = calculate_grid_prices()
|
||||||
|
if prices:
|
||||||
|
price_str = ",".join([str(p) for p in prices])
|
||||||
|
preview_result.set(f"网格价格序列: {price_str}")
|
||||||
|
else:
|
||||||
|
preview_result.set("参数错误,请检查!")
|
||||||
|
|
||||||
|
# 绑定输入变化自动预览
|
||||||
|
for entry_widget in (base_price_entry, grid_size_entry, upper_grid_entry, lower_grid_entry):
|
||||||
|
entry_widget.bind("<KeyRelease>", lambda e: update_preview())
|
||||||
|
entry_widget.bind("<FocusOut>", lambda e: update_preview())
|
||||||
|
# 初始预览
|
||||||
|
update_preview()
|
||||||
|
ttk.Label(preview_row, textvariable=preview_result,
|
||||||
|
font=('Arial', 10)).pack(side=tk.LEFT, padx=10)
|
||||||
|
|
||||||
|
# 添加其他基础配置选项
|
||||||
|
other_basic_frame = ttk.LabelFrame(basic_frame, text="交易设置", padding=15)
|
||||||
|
other_basic_frame.pack(fill=tk.X, padx=20, pady=10)
|
||||||
|
|
||||||
|
other_basic_settings = [
|
||||||
|
("网格交易手数", "grid_volume", config.get('config', 'grid_volume'), "每个网格的交易手数"),
|
||||||
|
("最大启用目标数", "max_enabled_targets", config.get('config', 'max_enabled_targets'), "同时运行的最大标的数量")
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, (label, default) in enumerate(settings):
|
for i, (label, key, default, tooltip) in enumerate(other_basic_settings):
|
||||||
frame = ttk.Frame(basic_frame)
|
frame = ttk.Frame(other_basic_frame)
|
||||||
frame.pack(fill=tk.X, padx=20, pady=10)
|
frame.pack(fill=tk.X, pady=5)
|
||||||
ttk.Label(frame, text=label, width=15).pack(side=tk.LEFT)
|
|
||||||
entry = ttk.Entry(frame, width=20)
|
label_widget = ttk.Label(frame, text=label + ":", width=15, font=('Arial', 10))
|
||||||
|
label_widget.pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
entry = ttk.Entry(frame, width=15, font=('Arial', 10))
|
||||||
entry.insert(0, default)
|
entry.insert(0, default)
|
||||||
entry.pack(side=tk.RIGHT)
|
entry.pack(side=tk.LEFT, padx=5)
|
||||||
|
entries[key] = entry
|
||||||
|
|
||||||
|
# 添加提示信息
|
||||||
|
tip_label = ttk.Label(frame, text=tooltip, font=('Arial', 9), foreground='gray')
|
||||||
|
tip_label.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
# 添加高级设置选项
|
||||||
|
account_frame = ttk.LabelFrame(advanced_frame, text="账号设置", padding=15)
|
||||||
|
account_frame.pack(fill=tk.X, padx=20, pady=10)
|
||||||
|
|
||||||
|
# 交易账号
|
||||||
|
account_row = ttk.Frame(account_frame)
|
||||||
|
account_row.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(account_row, text="交易账号:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
||||||
|
account_entry = ttk.Entry(account_row, width=15, font=('Arial', 10))
|
||||||
|
account_entry.insert(0, config.get('config', 'account_no'))
|
||||||
|
account_entry.pack(side=tk.LEFT, padx=5)
|
||||||
|
entries['account_no'] = account_entry
|
||||||
|
ttk.Label(account_row, text="QMT交易账号", font=('Arial', 9), foreground='gray').pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
# QMT路径特殊处理 - 使用文件浏览器
|
||||||
|
qmt_path_frame = ttk.LabelFrame(advanced_frame, text="软件路径", padding=15)
|
||||||
|
qmt_path_frame.pack(fill=tk.X, padx=20, pady=10)
|
||||||
|
|
||||||
|
qmt_row = ttk.Frame(qmt_path_frame)
|
||||||
|
qmt_row.pack(fill=tk.X, pady=5)
|
||||||
|
|
||||||
|
ttk.Label(qmt_row, text="QMT路径:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
qmt_entry = ttk.Entry(qmt_row, width=30, font=('Arial', 10))
|
||||||
|
qmt_entry.insert(0, config.get('config', 'miniQMTPath'))
|
||||||
|
qmt_entry.pack(side=tk.LEFT, padx=5)
|
||||||
|
entries['miniQMTPath'] = qmt_entry
|
||||||
|
|
||||||
|
def browse_qmt_path():
|
||||||
|
"""打开文件夹浏览器选择QMT路径"""
|
||||||
|
initial_dir = qmt_entry.get() if qmt_entry.get() else "/"
|
||||||
|
folder_path = filedialog.askdirectory(
|
||||||
|
title="选择miniQMT安装路径",
|
||||||
|
initialdir=initial_dir
|
||||||
|
)
|
||||||
|
if folder_path:
|
||||||
|
qmt_entry.delete(0, tk.END)
|
||||||
|
qmt_entry.insert(0, folder_path)
|
||||||
|
|
||||||
|
ttk.Button(qmt_row, text="📁 浏览...", command=browse_qmt_path, width=10).pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(qmt_row, text="miniQMT软件安装路径", font=('Arial', 9), foreground='gray').pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
# 定义保存和取消按钮的功能(button_frame已在上方创建)
|
||||||
|
def save_settings():
|
||||||
|
"""保存配置"""
|
||||||
|
try:
|
||||||
|
# 计算网格价格序列
|
||||||
|
grid_prices = calculate_grid_prices()
|
||||||
|
if not grid_prices:
|
||||||
|
messagebox.showerror("错误", "网格价格参数有误,请检查输入!")
|
||||||
|
return
|
||||||
|
|
||||||
|
grid_price_str = ",".join([str(p) for p in grid_prices])
|
||||||
|
|
||||||
|
# 更新配置对象
|
||||||
|
config.set('config', 'miniQMTPath', entries['miniQMTPath'].get())
|
||||||
|
config.set('config', 'grid_price', grid_price_str)
|
||||||
|
config.set('config', 'grid_volume', entries['grid_volume'].get())
|
||||||
|
config.set('config', 'account_no', entries['account_no'].get())
|
||||||
|
config.set('config', 'max_enabled_targets', entries['max_enabled_targets'].get())
|
||||||
|
|
||||||
|
# 写入配置文件
|
||||||
|
with open('config.ini', 'w') as configfile:
|
||||||
|
config.write(configfile)
|
||||||
|
|
||||||
|
# 重新加载配置到内存中
|
||||||
|
sfgrid_constants.initConfig()
|
||||||
|
|
||||||
|
messagebox.showinfo("成功", f"配置已保存!\n网格价格序列: {grid_price_str}\n部分配置可能需要重启程序后生效。")
|
||||||
|
self.add_log("INFO", f"系统配置已更新 - 网格数量: {len(grid_prices)}")
|
||||||
|
settings_window.destroy()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("错误", f"保存配置失败:{str(e)}")
|
||||||
|
self.add_log("ERROR", f"保存配置失败: {str(e)}")
|
||||||
|
|
||||||
|
def cancel_settings():
|
||||||
|
"""取消设置"""
|
||||||
|
settings_window.destroy()
|
||||||
|
|
||||||
|
# 在button_frame中添加按钮
|
||||||
|
ttk.Button(button_frame, text="💾 保存配置", command=save_settings, width=15).pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Button(button_frame, text="❌ 取消", command=cancel_settings, width=15).pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""运行程序"""
|
||||||
|
self.root.mainloop()
|
||||||
|
|
||||||
|
|
||||||
# 使用示例
|
# 使用示例
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -471,5 +663,5 @@ if __name__ == "__main__":
|
|||||||
print(" - 底部五个功能按钮提供操作")
|
print(" - 底部五个功能按钮提供操作")
|
||||||
|
|
||||||
# 创建并运行界面
|
# 创建并运行界面
|
||||||
app = ProfessionalTradeUI()
|
app = TradeTargetUI()
|
||||||
app.run()
|
app.run()
|
||||||
|
|||||||
Reference in New Issue
Block a user