update
This commit is contained in:
+3
-4
@@ -1,6 +1,5 @@
|
|||||||
[config]
|
[config]
|
||||||
miniqmtpath = D:/Programs/DTQMT/userdata_mini
|
miniqmtpath = D:/Programs/DTQMT_MN/userdata_mini
|
||||||
grid_price = 11.0,10.0,9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0,0.0
|
; account_no = 99082560
|
||||||
grid_volume = 100
|
account_no = 89009170
|
||||||
account_no = 99082560
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import xtquant.xtconstant as xtconstant
|
||||||
|
|
||||||
|
HeatTypeUpStop = "UpStop" # 涨停
|
||||||
|
HeatTypeDragonTiger = "DragonTiger" # 龙虎榜
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from peewee import CharField, DateField
|
||||||
|
|
||||||
|
from core.database import BaseModel, db
|
||||||
|
|
||||||
|
class HeatStock(BaseModel):
|
||||||
|
stock_code = CharField(unique=True)
|
||||||
|
stock_name = CharField()
|
||||||
|
heat_type = CharField()
|
||||||
|
date = DateField()
|
||||||
|
|
||||||
|
db.create_tables([HeatStock])
|
||||||
+46
-9
@@ -1,6 +1,6 @@
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from core.logger import LogLevel, PrintLog
|
from core.logger import LogLevel
|
||||||
from core.sfgrid.sfgrid_ui import TradeTargetUI
|
from core.sfgrid.sfgrid_ui import TradeTargetUI
|
||||||
|
|
||||||
|
|
||||||
@@ -35,16 +35,20 @@ class MainWindow:
|
|||||||
tab_bar_frame = ttk.Frame(content_area)
|
tab_bar_frame = ttk.Frame(content_area)
|
||||||
tab_bar_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
|
tab_bar_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
|
||||||
|
|
||||||
|
# 创建自定义样式
|
||||||
|
self.create_custom_styles()
|
||||||
|
|
||||||
# 创建Tab按钮(垂直排列,文字垂直显示)
|
# 创建Tab按钮(垂直排列,文字垂直显示)
|
||||||
self.tab_buttons = []
|
self.tab_buttons = []
|
||||||
strategy_names = ["蒙派\n策略", "涨停\n分析"]
|
strategy_names = ["蒙派\n策略", "涨停\n复盘"]
|
||||||
|
|
||||||
for idx, name in enumerate(strategy_names):
|
for idx, name in enumerate(strategy_names):
|
||||||
btn = ttk.Button(
|
btn = ttk.Button(
|
||||||
tab_bar_frame,
|
tab_bar_frame,
|
||||||
text=name,
|
text=name,
|
||||||
command=lambda i=idx: self.switch_strategy_tab(i),
|
command=lambda i=idx: self.switch_strategy_tab(i),
|
||||||
width=4
|
width=4,
|
||||||
|
style='Bookmark.TButton' # 使用自定义书签样式
|
||||||
)
|
)
|
||||||
btn.pack(side=tk.TOP, pady=2, fill=tk.X)
|
btn.pack(side=tk.TOP, pady=2, fill=tk.X)
|
||||||
self.tab_buttons.append(btn)
|
self.tab_buttons.append(btn)
|
||||||
@@ -98,6 +102,38 @@ class MainWindow:
|
|||||||
# 默认显示第一个策略
|
# 默认显示第一个策略
|
||||||
self.switch_strategy_tab(0)
|
self.switch_strategy_tab(0)
|
||||||
|
|
||||||
|
def create_custom_styles(self):
|
||||||
|
"""创建自定义样式"""
|
||||||
|
style = ttk.Style()
|
||||||
|
|
||||||
|
# 创建书签样式
|
||||||
|
style.configure(
|
||||||
|
'Bookmark.TButton',
|
||||||
|
relief='flat',
|
||||||
|
borderwidth=1,
|
||||||
|
padding=(5, 10),
|
||||||
|
foreground='black',
|
||||||
|
background='#FFE599', # 浅黄色背景,类似便签纸
|
||||||
|
font=('Arial', 10, 'bold')
|
||||||
|
)
|
||||||
|
|
||||||
|
# 设置焦点样式(选中状态)
|
||||||
|
style.map(
|
||||||
|
'Bookmark.TButton',
|
||||||
|
background=[('active', '#F1C232'), ('pressed', '#F1C232')],
|
||||||
|
relief=[('pressed', 'sunken')]
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建选中状态的书签样式
|
||||||
|
style.configure(
|
||||||
|
'SelectedBookmark.TButton',
|
||||||
|
relief='flat',
|
||||||
|
borderwidth=1,
|
||||||
|
padding=(5, 10),
|
||||||
|
background='#3D85C6', # 蓝色背景表示选中状态
|
||||||
|
font=('Arial', 10, 'bold')
|
||||||
|
)
|
||||||
|
|
||||||
def create_global_log_panel(self, parent):
|
def create_global_log_panel(self, parent):
|
||||||
"""创建全局日志面板"""
|
"""创建全局日志面板"""
|
||||||
# 日志区域(默认隐藏)
|
# 日志区域(默认隐藏)
|
||||||
@@ -143,7 +179,6 @@ class MainWindow:
|
|||||||
# 删除所有日志项
|
# 删除所有日志项
|
||||||
for item in self.log_table.get_children():
|
for item in self.log_table.get_children():
|
||||||
self.log_table.delete(item)
|
self.log_table.delete(item)
|
||||||
self.add_log(LogLevel.DEBUG, "日志已清空")
|
|
||||||
|
|
||||||
def create_strategy_frames(self, strategy_names):
|
def create_strategy_frames(self, strategy_names):
|
||||||
"""创建各个策略的Frame"""
|
"""创建各个策略的Frame"""
|
||||||
@@ -184,9 +219,12 @@ class MainWindow:
|
|||||||
|
|
||||||
def update_tab_button_styles(self):
|
def update_tab_button_styles(self):
|
||||||
"""更新Tab按钮的样式以显示选中状态"""
|
"""更新Tab按钮的样式以显示选中状态"""
|
||||||
# 注意:ttk.Button的样式需要通过ttk.Style来设置
|
# 重置所有按钮为普通书签样式
|
||||||
# 这里简化处理,仅作为接口预留
|
for i, btn in enumerate(self.tab_buttons):
|
||||||
pass
|
if i == self.current_strategy_index:
|
||||||
|
btn.configure(style='SelectedBookmark.TButton') # 选中状态
|
||||||
|
else:
|
||||||
|
btn.configure(style='Bookmark.TButton') # 普通状态
|
||||||
|
|
||||||
def toggle_log_panel(self):
|
def toggle_log_panel(self):
|
||||||
"""切换日志面板的显示/隐藏"""
|
"""切换日志面板的显示/隐藏"""
|
||||||
@@ -210,5 +248,4 @@ class MainWindow:
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""运行程序"""
|
"""运行程序"""
|
||||||
self.root.mainloop()
|
self.root.mainloop()
|
||||||
|
|
||||||
+281
-321
@@ -8,7 +8,6 @@ import time
|
|||||||
from core import constants
|
from core import constants
|
||||||
import core.eventbus as eBus
|
import core.eventbus as eBus
|
||||||
from core.logger import LogLevel, PrintLog
|
from core.logger import LogLevel, PrintLog
|
||||||
from core.sfgrid.bus_events import ActionEventAddTradeTarget
|
|
||||||
from core.sfgrid.model import SFGridTradeTarget
|
from core.sfgrid.model import SFGridTradeTarget
|
||||||
import configparser
|
import configparser
|
||||||
import config
|
import config
|
||||||
@@ -27,9 +26,6 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
self.init_trade_target_pool()
|
self.init_trade_target_pool()
|
||||||
eBus.event_bus.subscribe(eBus.MarketDataUpdate, self.onMarketDataUpdated)
|
eBus.event_bus.subscribe(eBus.MarketDataUpdate, self.onMarketDataUpdated)
|
||||||
|
|
||||||
# 创建刷新线程标志
|
|
||||||
self.refresh_thread_running = False # 默认不启动刷新线程
|
|
||||||
|
|
||||||
# 市场监控数据
|
# 市场监控数据
|
||||||
self.marketData: dict[str, Any] = {} # 存储市场数据 {stock_code: {stock_name, last_price, time}}
|
self.marketData: dict[str, Any] = {} # 存储市场数据 {stock_code: {stock_name, last_price, time}}
|
||||||
|
|
||||||
@@ -39,7 +35,7 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
# 创建界面
|
# 创建界面
|
||||||
self.create_ui()
|
self.create_ui()
|
||||||
|
|
||||||
self.start_ui_refresh()
|
self.start_refresh_thread()
|
||||||
|
|
||||||
def onMarketDataUpdated(self, data):
|
def onMarketDataUpdated(self, data):
|
||||||
# 收集所有市场数据用于市场监控
|
# 收集所有市场数据用于市场监控
|
||||||
@@ -48,7 +44,7 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
id:int = self.stockCodeIdMap[stock_code]
|
id:int = self.stockCodeIdMap[stock_code]
|
||||||
tradeTarget = self.tradeTargetData[id]
|
tradeTarget = self.tradeTargetData[id]
|
||||||
lastPrice = float("{:.3f}".format(tickData['lastPrice']))
|
lastPrice = float("{:.3f}".format(tickData['lastPrice']))
|
||||||
print(f'股票代码: {stock_code} {id}, 市场数据更新 {lastPrice}')
|
PrintLog(LogLevel.INFO, f'股票代码: {stock_code} {id}, 市场数据更新 {lastPrice}')
|
||||||
tradeTarget.market_price = lastPrice # type: ignore
|
tradeTarget.market_price = lastPrice # type: ignore
|
||||||
self.updateTradeTarget(tradeTarget)
|
self.updateTradeTarget(tradeTarget)
|
||||||
stock_controller: SFGridStrategy = self.strategy_ctrl[id]
|
stock_controller: SFGridStrategy = self.strategy_ctrl[id]
|
||||||
@@ -71,61 +67,41 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
'last_price': tickData['lastPrice'],
|
'last_price': tickData['lastPrice'],
|
||||||
'time': current_time
|
'time': current_time
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh_targets(self):
|
|
||||||
# 更新标的池
|
|
||||||
results = SFGridTradeTarget.select()
|
|
||||||
for temp in results:
|
|
||||||
result :SFGridTradeTarget = temp
|
|
||||||
self.tradeTargetData[result.get_id()] = result
|
|
||||||
self.stockCodeIdMap[str(result.stock_code)] = result.get_id()
|
|
||||||
|
|
||||||
|
|
||||||
def init_trade_target_pool(self):
|
def init_trade_target_pool(self):
|
||||||
self.refresh_targets()
|
results = SFGridTradeTarget.select()
|
||||||
|
for temp in results:
|
||||||
|
tradeTarget :SFGridTradeTarget = temp
|
||||||
|
id = tradeTarget.get_id()
|
||||||
|
|
||||||
for id, tradeTarget in self.tradeTargetData.items():
|
|
||||||
status = "新建" if tradeTarget.status == 0 else "已建初始仓"
|
status = "新建" if tradeTarget.status == 0 else "已建初始仓"
|
||||||
PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {tradeTarget.stock_code}-{tradeTarget.stock_name} 当前持仓: {qmtv.getStockPosition(tradeTarget.stock_code)} 网格索引: {tradeTarget.grid_index} 基准价格 {tradeTarget.getPriceGrid()[tradeTarget.grid_index]} 状态: {status} 启用交易线程: {'自动交易中' if tradeTarget.enabled else '交易已停止'}') # type: ignore
|
|
||||||
|
|
||||||
tradeTarget.current_position = qmtv.getStockPosition(tradeTarget.stock_code) # type: ignore
|
tradeTarget.current_position = qmtv.getStockPosition(tradeTarget.stock_code) # type: ignore
|
||||||
result = tradeTarget.save()
|
tradeTarget.save()
|
||||||
PrintLog(LogLevel.INFO, f' |- 同步[{tradeTarget.stock_code}-{tradeTarget.stock_name}]持仓信息[{'成功' if result == 1 else '失败'}]')
|
PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {tradeTarget.stock_code}-{tradeTarget.stock_name} 当前持仓: {qmtv.getStockPosition(tradeTarget.stock_code)} 网格索引: {tradeTarget.grid_index} 基准价格 {tradeTarget.getPriceGrid()[tradeTarget.grid_index]} 状态: {status} 启用交易线程: {'自动交易中' if tradeTarget.enabled else '交易已停止'}') # type: ignore
|
||||||
stockTradeController = SFGridStrategy(tradeTarget) # type: ignore
|
stockTradeController = SFGridStrategy(tradeTarget) # type: ignore
|
||||||
self.strategy_ctrl[id] = stockTradeController # pyright: ignore[reportArgumentType]
|
self.strategy_ctrl[id] = stockTradeController # pyright: ignore[reportArgumentType]
|
||||||
self.updateTradeTarget(tradeTarget)
|
self.updateTradeTarget(tradeTarget)
|
||||||
|
|
||||||
PrintLog(LogLevel.INFO, f'- [成功]交易标的信息初始化, 共 {len(self.tradeTargetData)} 个标的')
|
PrintLog(LogLevel.INFO, f'- [成功]交易标的信息初始化, 共 {len(self.tradeTargetData)} 个标的')
|
||||||
|
|
||||||
def start_refresh_thread(self):
|
|
||||||
"""启动刷新线程"""
|
|
||||||
if not hasattr(self, 'refresh_thread') or not self.refresh_thread.is_alive():
|
|
||||||
self.refresh_thread = threading.Thread(target=self.refresh_loop, daemon=True)
|
|
||||||
self.refresh_thread.start()
|
|
||||||
|
|
||||||
def refresh_loop(self):
|
|
||||||
"""刷新循环"""
|
|
||||||
while self.refresh_thread_running:
|
|
||||||
# 在主线程中更新UI
|
|
||||||
self.after(0, self.refresh_table)
|
|
||||||
self.after(0, self.populate_market_table)
|
|
||||||
time.sleep(0.2) # 每0.5秒刷新一次
|
|
||||||
|
|
||||||
def stop_refresh_thread(self):
|
|
||||||
"""停止刷新线程"""
|
|
||||||
self.refresh_thread_running = False
|
|
||||||
|
|
||||||
|
|
||||||
def onTradeEnabled(self, target:SFGridTradeTarget):
|
|
||||||
PrintLog(LogLevel.INFO, f"交易启用: {target.stock_code} - {target.stock_name}")
|
|
||||||
|
|
||||||
def onTradeDisabled(self, target:SFGridTradeTarget):
|
|
||||||
PrintLog(LogLevel.INFO, f"交易禁用: {target.stock_code} - {target.stock_name}")
|
|
||||||
|
|
||||||
def updateTradeTarget(self, target: SFGridTradeTarget):
|
def updateTradeTarget(self, target: SFGridTradeTarget):
|
||||||
# 更新或添加数据到本地缓存
|
# 更新或添加数据到本地缓存
|
||||||
self.tradeTargetData[target.get_id()] = target
|
self.tradeTargetData[target.get_id()] = target
|
||||||
self.stockCodeIdMap[target.stock_code] = target.get_id() # type: ignore
|
self.stockCodeIdMap[target.stock_code] = target.get_id() # type: ignore
|
||||||
|
|
||||||
|
def start_refresh_thread(self):
|
||||||
|
"""启动刷新线程"""
|
||||||
|
self.refresh_thread = threading.Thread(target=self.refresh_loop, daemon=True)
|
||||||
|
self.refresh_thread.start()
|
||||||
|
PrintLog(LogLevel.INFO, "UI刷新线程已启动")
|
||||||
|
|
||||||
|
def refresh_loop(self):
|
||||||
|
"""刷新循环"""
|
||||||
|
while True:
|
||||||
|
self.after(0, self.refresh_table)
|
||||||
|
self.after(0, self.populate_market_table)
|
||||||
|
time.sleep(0.2) # 每0.5秒刷新一次
|
||||||
|
|
||||||
def create_ui(self):
|
def create_ui(self):
|
||||||
"""创建UI界面"""
|
"""创建UI界面"""
|
||||||
@@ -144,33 +120,16 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
command=self.pause_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
|
command=self.pause_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
|
||||||
ttk.Button(toolbar_frame, text="➕ 添加标的",
|
ttk.Button(toolbar_frame, text="➕ 添加标的",
|
||||||
command=self.add_trade_target, width=12).pack(side=tk.LEFT, padx=2)
|
command=self.add_trade_target, width=12).pack(side=tk.LEFT, padx=2)
|
||||||
ttk.Button(toolbar_frame, text="🛠 网格配置",
|
|
||||||
command=self.grid_settings, width=12).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(toolbar_frame, text="🗑 删除标的",
|
ttk.Button(toolbar_frame, text="🗑 删除标的",
|
||||||
command=self.delete_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
|
command=self.delete_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(toolbar_frame, text="🛠 交易设置",
|
||||||
# 添加抽屉按钮到工具栏最右侧
|
command=self.trade_settings, width=12).pack(side=tk.LEFT, padx=2)
|
||||||
self.toggle_market_monitor_btn = ttk.Button(toolbar_frame, text="◀▶",
|
ttk.Button(toolbar_frame, text="▣ 实时监控",
|
||||||
command=self.toggle_market_monitor, width=3)
|
command=self.toggle_market_monitor, width=12).pack(side=tk.RIGHT, padx=2)
|
||||||
self.toggle_market_monitor_btn.pack(side=tk.RIGHT, padx=2)
|
|
||||||
|
|
||||||
# 表格区域
|
# 表格区域
|
||||||
self.create_tables_area(main_frame)
|
self.create_tables_area(main_frame)
|
||||||
|
|
||||||
def start_ui_refresh(self):
|
|
||||||
"""启动UI刷新线程"""
|
|
||||||
if not self.refresh_thread_running:
|
|
||||||
self.refresh_thread_running = True
|
|
||||||
self.start_refresh_thread()
|
|
||||||
PrintLog(LogLevel.INFO, "UI刷新线程已启动")
|
|
||||||
|
|
||||||
def stop_ui_refresh(self):
|
|
||||||
"""停止UI刷新线程"""
|
|
||||||
if self.refresh_thread_running:
|
|
||||||
self.stop_refresh_thread()
|
|
||||||
self.refresh_thread_running = False
|
|
||||||
PrintLog(LogLevel.INFO, "UI刷新线程已停止")
|
|
||||||
|
|
||||||
|
|
||||||
def create_tables_area(self, parent):
|
def create_tables_area(self, parent):
|
||||||
"""创建表格区域"""
|
"""创建表格区域"""
|
||||||
@@ -191,6 +150,19 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
|
|
||||||
# 创建市场监控表格
|
# 创建市场监控表格
|
||||||
self.create_market_monitor_table(self.market_frame)
|
self.create_market_monitor_table(self.market_frame)
|
||||||
|
|
||||||
|
|
||||||
|
def toggle_market_monitor(self):
|
||||||
|
"""切换市场监控窗口显示/隐藏"""
|
||||||
|
if self.market_monitor_visible:
|
||||||
|
# 隐藏市场监控窗口
|
||||||
|
self.market_frame.pack_forget()
|
||||||
|
self.market_monitor_visible = False
|
||||||
|
else:
|
||||||
|
# 显示市场监控窗口
|
||||||
|
self.market_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
|
||||||
|
self.market_monitor_visible = True
|
||||||
|
|
||||||
|
|
||||||
def create_trade_target_table(self, parent):
|
def create_trade_target_table(self, parent):
|
||||||
"""创建交易标的表格"""
|
"""创建交易标的表格"""
|
||||||
@@ -216,7 +188,7 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
"当前订单价": (90, tk.E),
|
"当前订单价": (90, tk.E),
|
||||||
"当前订单号": (90, tk.E),
|
"当前订单号": (90, tk.E),
|
||||||
"当前订单类型": (90, tk.E),
|
"当前订单类型": (90, tk.E),
|
||||||
"交易状态": (80, tk.E)
|
"交易状态": (80, tk.CENTER)
|
||||||
}
|
}
|
||||||
|
|
||||||
for col in columns:
|
for col in columns:
|
||||||
@@ -343,14 +315,14 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
|
|
||||||
if result:
|
if result:
|
||||||
# 发布事件通知主控制器添加标的
|
# 发布事件通知主控制器添加标的
|
||||||
self.onAddTradeTarget(stock_code)
|
self.addTradeTarget(stock_code)
|
||||||
|
|
||||||
def get_trade_enabled_indicator(self, enabled: bool) -> str:
|
def get_trade_enabled_indicator(self, enabled: bool) -> str:
|
||||||
"""获取交易状态指示器"""
|
"""获取交易状态指示器"""
|
||||||
if enabled:
|
if enabled:
|
||||||
return "🟢 策略运行"
|
return "▶ 运行中"
|
||||||
else:
|
else:
|
||||||
return "🟡 策略暂停"
|
return "⏸ 已停止"
|
||||||
|
|
||||||
def populate_trade_table(self):
|
def populate_trade_table(self):
|
||||||
"""填充交易标的表格数据"""
|
"""填充交易标的表格数据"""
|
||||||
@@ -495,9 +467,8 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
icon='warning'
|
icon='warning'
|
||||||
)
|
)
|
||||||
|
|
||||||
id = target.get_id()
|
|
||||||
if result:
|
if result:
|
||||||
# 通过事件总线发出删除动作
|
id = target.get_id()
|
||||||
try:
|
try:
|
||||||
# 从数据库中删除
|
# 从数据库中删除
|
||||||
target.delete_instance()
|
target.delete_instance()
|
||||||
@@ -553,7 +524,7 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 发布事件通知主控制器添加标的
|
# 发布事件通知主控制器添加标的
|
||||||
eBus.event_bus.publish(ActionEventAddTradeTarget, stock_code)
|
self.addTradeTarget(stock_code)
|
||||||
add_window.destroy()
|
add_window.destroy()
|
||||||
|
|
||||||
def cancel_add():
|
def cancel_add():
|
||||||
@@ -595,143 +566,221 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
# 刷新市场监控表格
|
# 刷新市场监控表格
|
||||||
self.populate_market_table()
|
self.populate_market_table()
|
||||||
|
|
||||||
def system_settings(self):
|
def trade_settings(self):
|
||||||
"""系统设置"""
|
"""网格配置功能"""
|
||||||
|
target = self.get_selected_target()
|
||||||
|
if not target:
|
||||||
|
return
|
||||||
|
|
||||||
# 获取顶层窗口
|
# 获取顶层窗口
|
||||||
root = self.winfo_toplevel()
|
root = self.winfo_toplevel()
|
||||||
|
|
||||||
settings_window = tk.Toplevel(root)
|
# 创建顶层窗口
|
||||||
settings_window.title("网格交易系统配置")
|
view_window = tk.Toplevel(root)
|
||||||
|
view_window.title(f"网格配置查看 - {target.stock_code} ({target.stock_name})")
|
||||||
|
view_window.geometry("500x450")
|
||||||
|
view_window.resizable(False, False)
|
||||||
|
|
||||||
# 设置窗口大小
|
# 检查标的的状态,status为1时仅可查看
|
||||||
window_width = 700
|
# if target.status == 1:
|
||||||
window_height = 550
|
# # 创建只读的网格配置查看窗口
|
||||||
|
# self.create_grid_view_window(target)
|
||||||
|
# else:
|
||||||
|
# # 创建可编辑的网格配置窗口
|
||||||
|
# self.create_grid_config_window(target)
|
||||||
|
|
||||||
|
def create_grid_view_window(self, target: SFGridTradeTarget):
|
||||||
|
"""创建网格配置查看窗口(只读)"""
|
||||||
|
# 获取顶层窗口
|
||||||
|
root = self.winfo_toplevel()
|
||||||
|
|
||||||
# 先设置为模态窗口
|
# 创建顶层窗口
|
||||||
settings_window.transient(root)
|
view_window = tk.Toplevel(root)
|
||||||
|
view_window.title(f"网格配置查看 - {target.stock_code} ({target.stock_name})")
|
||||||
|
view_window.geometry("500x450")
|
||||||
|
view_window.resizable(False, False)
|
||||||
|
|
||||||
# 确保主窗口完全初始化
|
# 设置窗口模态
|
||||||
|
view_window.transient(root)
|
||||||
|
view_window.grab_set()
|
||||||
|
|
||||||
|
# 居中显示
|
||||||
root.update_idletasks()
|
root.update_idletasks()
|
||||||
|
x = root.winfo_x() + (root.winfo_width() // 2) - 250
|
||||||
|
y = root.winfo_y() + (root.winfo_height() // 2) - 225
|
||||||
|
view_window.geometry(f"500x450+{x}+{y}")
|
||||||
|
|
||||||
# 获取主窗口的实际大小(包括边框)
|
# 创建主框架
|
||||||
# 使用winfo_rootx/rooty获取窗口在屏幕上的绝对位置
|
main_frame = ttk.Frame(view_window, padding=20)
|
||||||
main_x = root.winfo_rootx()
|
main_frame.pack(fill=tk.BOTH, expand=True)
|
||||||
main_y = root.winfo_rooty()
|
|
||||||
main_width = root.winfo_width()
|
|
||||||
main_height = root.winfo_height()
|
|
||||||
|
|
||||||
# 计算设置窗口相对于主窗口的居中位置
|
# 显示股票信息
|
||||||
x = main_x + (main_width - window_width) // 2
|
info_frame = ttk.LabelFrame(main_frame, text="标的详情", padding=10)
|
||||||
y = main_y + (main_height - window_height) // 2
|
info_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
|
|
||||||
# 设置窗口大小和位置
|
ttk.Label(info_frame, text=f"股票代码: {target.stock_code}").grid(row=0, column=0, sticky=tk.W, pady=2)
|
||||||
settings_window.geometry(f"{window_width}x{window_height}+{x}+{y}")
|
ttk.Label(info_frame, text=f"股票名称: {target.stock_name}").grid(row=0, column=1, sticky=tk.W, padx=(20, 0), pady=2)
|
||||||
settings_window.resizable(False, False)
|
ttk.Label(info_frame, text=f"状态: 已建初始仓(仅查看模式)").grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=2)
|
||||||
|
|
||||||
# 设置为模态窗口
|
# 创建网格配置查看框架
|
||||||
settings_window.grab_set()
|
config_frame = ttk.LabelFrame(main_frame, text="网格配置", padding=10)
|
||||||
|
config_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
|
|
||||||
# 添加底部按钮区域(先创建,确保固定在底部)
|
# 基准价格
|
||||||
button_frame = ttk.Frame(settings_window)
|
base_price_frame = ttk.Frame(config_frame)
|
||||||
button_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=20, pady=10)
|
base_price_frame.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(base_price_frame, text="基准价格:", width=15).pack(side=tk.LEFT)
|
||||||
|
ttk.Label(base_price_frame, text=f"{target.grid_start_price:.3f}", width=15, anchor=tk.W).pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(base_price_frame, text="元", foreground='gray').pack(side=tk.LEFT)
|
||||||
|
|
||||||
# 创建选项卡(在按钮之后创建,填充剩余空间)
|
# 网格大小
|
||||||
notebook = ttk.Notebook(settings_window)
|
grid_size_frame = ttk.Frame(config_frame)
|
||||||
notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=(10, 0))
|
grid_size_frame.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(grid_size_frame, text="网格大小:", width=15).pack(side=tk.LEFT)
|
||||||
|
ttk.Label(grid_size_frame, text=f"{target.grid_size:.3f}", width=15, anchor=tk.W).pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(grid_size_frame, text="元", foreground='gray').pack(side=tk.LEFT)
|
||||||
|
|
||||||
# 基础设置
|
# 网格交易量
|
||||||
basic_frame = ttk.Frame(notebook)
|
grid_volume_frame = ttk.Frame(config_frame)
|
||||||
notebook.add(basic_frame, text="基础设置")
|
grid_volume_frame.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(grid_volume_frame, text="网格交易量:", width=15).pack(side=tk.LEFT)
|
||||||
|
ttk.Label(grid_volume_frame, text=str(target.grid_volume), width=15, anchor=tk.W).pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(grid_volume_frame, text="股", foreground='gray').pack(side=tk.LEFT)
|
||||||
|
|
||||||
# 高级设置
|
# 上方网格数量
|
||||||
advanced_frame = ttk.Frame(notebook)
|
upper_count_frame = ttk.Frame(config_frame)
|
||||||
notebook.add(advanced_frame, text="高级设置")
|
upper_count_frame.pack(fill=tk.X, pady=5)
|
||||||
|
ttk.Label(upper_count_frame, text="上方网格数量:", width=15).pack(side=tk.LEFT)
|
||||||
|
ttk.Label(upper_count_frame, text=str(target.grid_upper_count), width=15, anchor=tk.W).pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(upper_count_frame, text="格", foreground='gray').pack(side=tk.LEFT)
|
||||||
|
|
||||||
# 读取当前配置
|
# 下方网格数量
|
||||||
config = configparser.ConfigParser()
|
lower_count_frame = ttk.Frame(config_frame)
|
||||||
config_path = config.get('config', 'config_path')
|
lower_count_frame.pack(fill=tk.X, pady=5)
|
||||||
config.read(config_path, encoding='utf-8')
|
ttk.Label(lower_count_frame, text="下方网格数量:", width=15).pack(side=tk.LEFT)
|
||||||
|
ttk.Label(lower_count_frame, text=str(target.grid_lower_count), width=15, anchor=tk.W).pack(side=tk.LEFT, padx=5)
|
||||||
|
ttk.Label(lower_count_frame, text="格", foreground='gray').pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
# 生成网格价格序列
|
||||||
|
price_grid_frame = ttk.LabelFrame(main_frame, text="网格价格序列", padding=10)
|
||||||
|
price_grid_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
|
|
||||||
|
# 计算并显示网格价格序列
|
||||||
|
price_list = target.getPriceGrid()
|
||||||
|
price_text = ", ".join([f"{price:.3f}" for price in price_list])
|
||||||
|
|
||||||
|
# 创建文本框显示网格价格序列
|
||||||
|
text_frame = ttk.Frame(price_grid_frame)
|
||||||
|
text_frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
text_widget = tk.Text(text_frame, height=4, wrap=tk.WORD)
|
||||||
|
text_widget.insert(tk.END, price_text)
|
||||||
|
text_widget.config(state=tk.DISABLED) # 只读
|
||||||
|
|
||||||
|
scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=text_widget.yview)
|
||||||
|
text_widget.configure(yscrollcommand=scrollbar.set)
|
||||||
|
|
||||||
|
text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
|
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
|
|
||||||
|
# 关闭按钮
|
||||||
|
button_frame = ttk.Frame(main_frame)
|
||||||
|
button_frame.pack(fill=tk.X, pady=(10, 0))
|
||||||
|
ttk.Button(button_frame, text="关闭", command=view_window.destroy).pack(side=tk.RIGHT, padx=5)
|
||||||
|
|
||||||
|
def create_grid_config_window(self, target: SFGridTradeTarget):
|
||||||
|
"""创建网格配置窗口(可编辑)"""
|
||||||
|
# 获取顶层窗口
|
||||||
|
root = self.winfo_toplevel()
|
||||||
|
|
||||||
|
# 创建顶层窗口
|
||||||
|
config_window = tk.Toplevel(root)
|
||||||
|
config_window.title(f"网格配置 - {target.stock_code} ({target.stock_name})")
|
||||||
|
config_window.geometry("550x550")
|
||||||
|
config_window.resizable(False, False)
|
||||||
|
|
||||||
|
# 设置窗口模态
|
||||||
|
config_window.transient(root)
|
||||||
|
config_window.grab_set()
|
||||||
|
|
||||||
|
# 居中显示
|
||||||
|
root.update_idletasks()
|
||||||
|
x = root.winfo_x() + (root.winfo_width() // 2) - 275
|
||||||
|
y = root.winfo_y() + (root.winfo_height() // 2) - 275
|
||||||
|
config_window.geometry(f"550x550+{x}+{y}")
|
||||||
|
|
||||||
|
# 创建主框架
|
||||||
|
main_frame = ttk.Frame(config_window, padding=20)
|
||||||
|
main_frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# 显示股票信息
|
||||||
|
info_frame = ttk.LabelFrame(main_frame, text="标的详情", padding=10)
|
||||||
|
info_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
|
|
||||||
|
ttk.Label(info_frame, text=f"股票代码: {target.stock_code}").grid(row=0, column=0, sticky=tk.W, pady=2)
|
||||||
|
ttk.Label(info_frame, text=f"股票名称: {target.stock_name}").grid(row=0, column=1, sticky=tk.W, padx=(20, 0), pady=2)
|
||||||
|
ttk.Label(info_frame, text=f"状态: 新标的(可配置模式)").grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=2)
|
||||||
|
|
||||||
|
# 创建网格配置框架
|
||||||
|
config_frame = ttk.LabelFrame(main_frame, text="网格配置", padding=15)
|
||||||
|
config_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
|
|
||||||
# 创建输入框字典用于保存引用
|
# 创建输入框字典用于保存引用
|
||||||
entries = {}
|
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_frame = ttk.Frame(config_frame)
|
||||||
base_price_row.pack(fill=tk.X, pady=5)
|
base_price_frame.pack(fill=tk.X, pady=5)
|
||||||
ttk.Label(base_price_row, text="基准价格:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
ttk.Label(base_price_frame, text="基准价格:", width=15).pack(side=tk.LEFT)
|
||||||
base_price_entry = ttk.Entry(base_price_row, width=15, font=('Arial', 10))
|
base_price_entry = ttk.Entry(base_price_frame, width=15)
|
||||||
base_price_entry.insert(0, "10.0")
|
base_price_entry.insert(0, str(target.grid_start_price))
|
||||||
base_price_entry.pack(side=tk.LEFT, padx=5)
|
base_price_entry.pack(side=tk.LEFT, padx=5)
|
||||||
ttk.Label(base_price_row, text="元", foreground='gray', font=('Arial', 9)).pack(side=tk.LEFT)
|
ttk.Label(base_price_frame, text="元", foreground='gray').pack(side=tk.LEFT)
|
||||||
grid_params['base_price'] = base_price_entry
|
entries['grid_start_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_frame = ttk.Frame(config_frame)
|
||||||
grid_size_row.pack(fill=tk.X, pady=5)
|
grid_size_frame.pack(fill=tk.X, pady=5)
|
||||||
ttk.Label(grid_size_row, text="网格大小:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
ttk.Label(grid_size_frame, text="网格大小:", width=15).pack(side=tk.LEFT)
|
||||||
grid_size_entry = ttk.Entry(grid_size_row, width=15, font=('Arial', 10))
|
grid_size_entry = ttk.Entry(grid_size_frame, width=15)
|
||||||
grid_size_entry.insert(0, "1.0")
|
grid_size_entry.insert(0, str(target.grid_size))
|
||||||
grid_size_entry.pack(side=tk.LEFT, padx=5)
|
grid_size_entry.pack(side=tk.LEFT, padx=5)
|
||||||
grid_size_unit_label = ttk.Label(grid_size_row, text="元", foreground='gray', font=('Arial', 9))
|
ttk.Label(grid_size_frame, text="元", foreground='gray').pack(side=tk.LEFT)
|
||||||
grid_size_unit_label.pack(side=tk.LEFT)
|
entries['grid_size'] = grid_size_entry
|
||||||
grid_params['grid_size'] = grid_size_entry
|
|
||||||
grid_params['grid_size_unit_label'] = grid_size_unit_label
|
|
||||||
|
|
||||||
# 网格类型改变时更新单位
|
# 网格交易量
|
||||||
def on_grid_type_change():
|
grid_volume_frame = ttk.Frame(config_frame)
|
||||||
if grid_type_var.get() == "百分比":
|
grid_volume_frame.pack(fill=tk.X, pady=5)
|
||||||
grid_size_unit_label.config(text="%")
|
ttk.Label(grid_volume_frame, text="网格交易量:", width=15).pack(side=tk.LEFT)
|
||||||
grid_size_entry.delete(0, tk.END)
|
grid_volume_entry = ttk.Entry(grid_volume_frame, width=15)
|
||||||
grid_size_entry.insert(0, "1.0")
|
grid_volume_entry.insert(0, str(target.grid_volume))
|
||||||
else:
|
grid_volume_entry.pack(side=tk.LEFT, padx=5)
|
||||||
grid_size_unit_label.config(text="元")
|
ttk.Label(grid_volume_frame, text="手", foreground='gray').pack(side=tk.LEFT)
|
||||||
grid_size_entry.delete(0, tk.END)
|
entries['grid_volume'] = grid_volume_entry
|
||||||
grid_size_entry.insert(0, "1.0")
|
|
||||||
update_preview()
|
|
||||||
|
|
||||||
# 上方网格数量
|
# 上方网格数量
|
||||||
upper_grid_row = ttk.Frame(grid_price_frame)
|
upper_count_frame = ttk.Frame(config_frame)
|
||||||
upper_grid_row.pack(fill=tk.X, pady=5)
|
upper_count_frame.pack(fill=tk.X, pady=5)
|
||||||
ttk.Label(upper_grid_row, text="上方网格数量:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
ttk.Label(upper_count_frame, text="上方网格数量:", width=15).pack(side=tk.LEFT)
|
||||||
upper_grid_entry = ttk.Entry(upper_grid_row, width=15, font=('Arial', 10))
|
upper_count_entry = ttk.Entry(upper_count_frame, width=15)
|
||||||
upper_grid_entry.insert(0, "1")
|
upper_count_entry.insert(0, str(target.grid_upper_count))
|
||||||
upper_grid_entry.pack(side=tk.LEFT, padx=5)
|
upper_count_entry.pack(side=tk.LEFT, padx=5)
|
||||||
ttk.Label(upper_grid_row, text="格", foreground='gray', font=('Arial', 9)).pack(side=tk.LEFT)
|
ttk.Label(upper_count_frame, text="格", foreground='gray').pack(side=tk.LEFT)
|
||||||
grid_params['upper_count'] = upper_grid_entry
|
entries['grid_upper_count'] = upper_count_entry
|
||||||
|
|
||||||
# 下方网格数量
|
# 下方网格数量
|
||||||
lower_grid_row = ttk.Frame(grid_price_frame)
|
lower_count_frame = ttk.Frame(config_frame)
|
||||||
lower_grid_row.pack(fill=tk.X, pady=5)
|
lower_count_frame.pack(fill=tk.X, pady=5)
|
||||||
ttk.Label(lower_grid_row, text="下方网格数量:", width=15, font=('Arial', 10)).pack(side=tk.LEFT)
|
ttk.Label(lower_count_frame, text="下方网格数量:", width=15).pack(side=tk.LEFT)
|
||||||
lower_grid_entry = ttk.Entry(lower_grid_row, width=15, font=('Arial', 10))
|
lower_count_entry = ttk.Entry(lower_count_frame, width=15)
|
||||||
lower_grid_entry.insert(0, "10")
|
lower_count_entry.insert(0, str(target.grid_lower_count))
|
||||||
lower_grid_entry.pack(side=tk.LEFT, padx=5)
|
lower_count_entry.pack(side=tk.LEFT, padx=5)
|
||||||
ttk.Label(lower_grid_row, text="格", foreground='gray', font=('Arial', 9)).pack(side=tk.LEFT)
|
ttk.Label(lower_count_frame, text="格", foreground='gray').pack(side=tk.LEFT)
|
||||||
grid_params['lower_count'] = lower_grid_entry
|
entries['grid_lower_count'] = lower_count_entry
|
||||||
|
|
||||||
# 预览按钮和结果显示
|
# 预览按钮和结果显示
|
||||||
preview_row = ttk.Frame(grid_price_frame)
|
preview_frame = ttk.LabelFrame(main_frame, text="网格价格序列预览", padding=10)
|
||||||
preview_row.pack(fill=tk.X, pady=10)
|
preview_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
|
|
||||||
preview_result = tk.StringVar(value="点击'预览'查看生成的网格价格序列")
|
preview_result = tk.StringVar(value="点击'预览'查看生成的网格价格序列")
|
||||||
|
|
||||||
@@ -740,18 +789,14 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
try:
|
try:
|
||||||
base_price = float(base_price_entry.get())
|
base_price = float(base_price_entry.get())
|
||||||
grid_size = float(grid_size_entry.get())
|
grid_size = float(grid_size_entry.get())
|
||||||
upper_count = int(upper_grid_entry.get())
|
upper_count = int(upper_count_entry.get())
|
||||||
lower_count = int(lower_grid_entry.get())
|
lower_count = int(lower_count_entry.get())
|
||||||
grid_type = grid_type_var.get()
|
|
||||||
|
|
||||||
prices = []
|
prices = []
|
||||||
|
|
||||||
# 计算上方网格价格
|
# 计算上方网格价格
|
||||||
for i in range(upper_count, 0, -1):
|
for i in range(upper_count, 0, -1):
|
||||||
if grid_type == "百分比":
|
price = base_price + grid_size * i
|
||||||
price = base_price * (1 + grid_size / 100 * i)
|
|
||||||
else: # 金额差
|
|
||||||
price = base_price + grid_size * i
|
|
||||||
prices.append(round(price, 3))
|
prices.append(round(price, 3))
|
||||||
|
|
||||||
# 添加基准价格
|
# 添加基准价格
|
||||||
@@ -759,10 +804,7 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
|
|
||||||
# 计算下方网格价格
|
# 计算下方网格价格
|
||||||
for i in range(1, lower_count + 1):
|
for i in range(1, lower_count + 1):
|
||||||
if grid_type == "百分比":
|
price = base_price - grid_size * i
|
||||||
price = base_price * (1 - grid_size / 100 * i)
|
|
||||||
else: # 金额差
|
|
||||||
price = base_price - grid_size * i
|
|
||||||
# 确保价格不为负
|
# 确保价格不为负
|
||||||
if price >= 0:
|
if price >= 0:
|
||||||
prices.append(round(price, 3))
|
prices.append(round(price, 3))
|
||||||
@@ -770,145 +812,75 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
break
|
break
|
||||||
|
|
||||||
return prices
|
return prices
|
||||||
except ValueError as e:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def update_preview():
|
def update_preview():
|
||||||
"""自动更新网格价格序列预览"""
|
"""更新网格价格序列预览"""
|
||||||
prices = calculate_grid_prices()
|
prices = calculate_grid_prices()
|
||||||
if prices:
|
if prices:
|
||||||
price_str = ",".join([str(p) for p in prices])
|
price_str = ", ".join([str(p) for p in prices])
|
||||||
preview_result.set(f"网格价格序列: {price_str}")
|
preview_result.set(f"网格价格序列: {price_str}")
|
||||||
else:
|
else:
|
||||||
preview_result.set("参数错误,请检查!")
|
preview_result.set("参数错误,请检查输入!")
|
||||||
|
|
||||||
# 绑定输入变化自动预览
|
# 绑定输入变化自动预览
|
||||||
for entry_widget in (base_price_entry, grid_size_entry, upper_grid_entry, lower_grid_entry):
|
for entry_widget in entries.values():
|
||||||
entry_widget.bind("<KeyRelease>", lambda e: update_preview())
|
entry_widget.bind("<KeyRelease>", lambda e: update_preview())
|
||||||
entry_widget.bind("<FocusOut>", lambda e: update_preview())
|
entry_widget.bind("<FocusOut>", lambda e: update_preview())
|
||||||
|
|
||||||
|
# 预览按钮
|
||||||
|
preview_button_frame = ttk.Frame(preview_frame)
|
||||||
|
preview_button_frame.pack(fill=tk.X, pady=5)
|
||||||
|
# ttk.Button(preview_button_frame, text="预览", command=update_preview).pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
# 预览结果显示
|
||||||
|
preview_label = ttk.Label(preview_button_frame, textvariable=preview_result, foreground='blue')
|
||||||
|
preview_label.pack(side=tk.LEFT, padx=10)
|
||||||
|
|
||||||
# 初始预览
|
# 初始预览
|
||||||
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)
|
button_frame = ttk.Frame(main_frame)
|
||||||
other_basic_frame.pack(fill=tk.X, padx=20, pady=10)
|
button_frame.pack(fill=tk.X, pady=(10, 0))
|
||||||
|
|
||||||
other_basic_settings = [
|
def save_config():
|
||||||
("网格交易手数", "grid_volume", config.get('config', 'grid_volume'), "每个网格的交易手数")
|
|
||||||
]
|
|
||||||
|
|
||||||
for i, (label, key, default, tooltip) in enumerate(other_basic_settings):
|
|
||||||
frame = ttk.Frame(other_basic_frame)
|
|
||||||
frame.pack(fill=tk.X, pady=5)
|
|
||||||
|
|
||||||
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.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:
|
try:
|
||||||
# # 计算网格价格序列
|
# 获取输入值
|
||||||
# grid_prices = calculate_grid_prices()
|
grid_start_price = float(base_price_entry.get())
|
||||||
# if not grid_prices:
|
grid_size = float(grid_size_entry.get())
|
||||||
# messagebox.showerror("错误", "网格价格参数有误,请检查输入!")
|
grid_volume = int(grid_volume_entry.get())
|
||||||
# return
|
grid_upper_count = int(upper_count_entry.get())
|
||||||
|
grid_lower_count = int(lower_count_entry.get())
|
||||||
|
|
||||||
# grid_price_str = ",".join([str(p) for p in grid_prices])
|
# 更新target对象(使用setattr来正确设置Peewee字段的值)
|
||||||
|
setattr(target, 'grid_start_price', grid_start_price)
|
||||||
|
setattr(target, 'grid_size', grid_size)
|
||||||
|
setattr(target, 'grid_volume', grid_volume)
|
||||||
|
setattr(target, 'grid_upper_count', grid_upper_count)
|
||||||
|
setattr(target, 'grid_lower_count', grid_lower_count)
|
||||||
|
|
||||||
# # 更新配置对象
|
# 保存到数据库
|
||||||
# config.set('config', 'miniQMTPath', entries['miniQMTPath'].get())
|
target.save()
|
||||||
# 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_path = config.get_config_path()
|
config_window.destroy()
|
||||||
# with open(config_path, 'w', encoding='utf-8') as configfile:
|
|
||||||
# config.write(configfile)
|
|
||||||
|
|
||||||
# # 重新加载配置到内存中
|
# 添加日志
|
||||||
# config.initConfig()
|
PrintLog(LogLevel.INFO, f"网格配置已保存: {target.stock_code} - {target.stock_name}")
|
||||||
|
messagebox.showinfo("成功", "网格配置已保存!")
|
||||||
|
|
||||||
# messagebox.showinfo("成功", f"配置已保存!\n网格价格序列: {grid_price_str}\n部分配置可能需要重启程序后生效。")
|
except ValueError:
|
||||||
# self.add_log(LogLevel.INFO, f"系统配置已更新 - 网格数量: {len(grid_prices)}")
|
messagebox.showerror("错误", "输入参数有误,请检查!")
|
||||||
# settings_window.destroy()
|
except Exception as e:
|
||||||
|
messagebox.showerror("错误", f"保存配置失败:{str(e)}")
|
||||||
# except Exception as e:
|
PrintLog(LogLevel.ERROR, f"保存网格配置失败: {str(e)}")
|
||||||
# messagebox.showerror("错误", f"保存配置失败:{str(e)}")
|
|
||||||
# self.add_log(LogLevel.ERROR, f"保存配置失败: {str(e)}")
|
|
||||||
pass
|
|
||||||
|
|
||||||
def cancel_settings():
|
# 保存和取消按钮
|
||||||
"""取消设置"""
|
ttk.Button(button_frame, text="保存", command=save_config).pack(side=tk.RIGHT, padx=5)
|
||||||
# settings_window.destroy()
|
ttk.Button(button_frame, text="取消", command=config_window.destroy).pack(side=tk.RIGHT, padx=5)
|
||||||
pass
|
|
||||||
|
|
||||||
# 在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 grid_settings(self):
|
|
||||||
"""网格修正功能"""
|
|
||||||
target = self.get_selected_target()
|
|
||||||
if not target:
|
|
||||||
return
|
|
||||||
|
|
||||||
# 创建网格修正窗口
|
|
||||||
self.create_grid_correction_window(target)
|
|
||||||
|
|
||||||
def create_grid_correction_window(self, target: SFGridTradeTarget):
|
def create_grid_correction_window(self, target: SFGridTradeTarget):
|
||||||
"""创建网格修正窗口"""
|
"""创建网格修正窗口"""
|
||||||
@@ -1099,13 +1071,14 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
self.update_position_status(current_position, required_position, position_status_label)
|
self.update_position_status(current_position, required_position, position_status_label)
|
||||||
|
|
||||||
|
|
||||||
def onAddTradeTarget(self, stock_code: str):
|
def addTradeTarget(self, stock_code: str, gridIndex: int = 1):
|
||||||
"""处理添加交易标的事件"""
|
"""处理添加交易标的事件"""
|
||||||
try:
|
try:
|
||||||
stock_name = qmtv.getInstrumentName(stock_code)
|
stock_name = qmtv.getInstrumentName(stock_code)
|
||||||
if not stock_name:
|
if not stock_name:
|
||||||
PrintLog(LogLevel.ERROR, f'无法获取股票代码 {stock_code} 的名称,请检查代码是否正确')
|
PrintLog(LogLevel.ERROR, f'无法获取股票代码 {stock_code} 的名称,请检查代码是否正确')
|
||||||
return
|
return
|
||||||
|
PrintLog(LogLevel.DEBUG, f'添加交易标的: {stock_code} {stock_name}')
|
||||||
|
|
||||||
# 检查是否已存在该标的
|
# 检查是否已存在该标的
|
||||||
existing_target = SFGridTradeTarget.get_or_none(SFGridTradeTarget.stock_code == stock_code)
|
existing_target = SFGridTradeTarget.get_or_none(SFGridTradeTarget.stock_code == stock_code)
|
||||||
@@ -1120,7 +1093,7 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
stock_code=stock_code,
|
stock_code=stock_code,
|
||||||
market_price=0.0,
|
market_price=0.0,
|
||||||
current_position=pos,
|
current_position=pos,
|
||||||
grid_index=0,
|
grid_index=gridIndex,
|
||||||
last_trade_price=0.0,
|
last_trade_price=0.0,
|
||||||
plan_buy_price=0.0,
|
plan_buy_price=0.0,
|
||||||
plan_sell_price=0.0,
|
plan_sell_price=0.0,
|
||||||
@@ -1148,16 +1121,3 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
PrintLog(LogLevel.INFO, f"网格修正已应用: {target.stock_code} - {target.stock_name}, 网格索引: {grid_index}")
|
PrintLog(LogLevel.INFO, f"网格修正已应用: {target.stock_code} - {target.stock_name}, 网格索引: {grid_index}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
PrintLog(LogLevel.ERROR, f"网格修正更新失败: {str(e)}")
|
PrintLog(LogLevel.ERROR, f"网格修正更新失败: {str(e)}")
|
||||||
|
|
||||||
def toggle_market_monitor(self):
|
|
||||||
"""切换市场监控窗口显示/隐藏"""
|
|
||||||
if self.market_monitor_visible:
|
|
||||||
# 隐藏市场监控窗口
|
|
||||||
self.market_frame.pack_forget()
|
|
||||||
self.toggle_market_monitor_btn.config(text="▶")
|
|
||||||
self.market_monitor_visible = False
|
|
||||||
else:
|
|
||||||
# 显示市场监控窗口
|
|
||||||
self.market_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
|
|
||||||
self.toggle_market_monitor_btn.config(text="◀▶")
|
|
||||||
self.market_monitor_visible = True
|
|
||||||
|
|||||||
Reference in New Issue
Block a user