update
This commit is contained in:
+3
-3
@@ -1,5 +1,5 @@
|
||||
[config]
|
||||
miniqmtpath = D:/Programs/DTQMT_MN/userdata_mini
|
||||
; account_no = 99082560
|
||||
account_no = 89009170
|
||||
miniqmtpath = D:/Programs/DTQMT/userdata_mini
|
||||
account_no = 99082560
|
||||
; account_no = 89009170
|
||||
|
||||
|
||||
+2
-1
@@ -165,7 +165,8 @@ class QmtV(XtQuantTraderCallback):
|
||||
:param order: XtOrder对象
|
||||
:return:
|
||||
"""
|
||||
print(f'orderd {order.strategy_name}-{order.stock_code} {order.order_id} {order.order_volume}-{order.order_status}')
|
||||
pass
|
||||
# print(f'orderd {order.strategy_name}-{order.stock_code} {order.order_id} {order.order_volume}-{order.order_status}')
|
||||
# stockCode = order.stock_code
|
||||
# ctrl:SFGridStrategy = self.stock_trade_ctrl[stockCode]
|
||||
# # 如果存在对应的StockTradeController,则调用其onDataUpdate方法
|
||||
|
||||
@@ -16,27 +16,29 @@ class SFGridStrategy:
|
||||
|
||||
def __init__(self, tradeTarget: model.SFGridTradeTarget):
|
||||
self.tradeTarget:model.SFGridTradeTarget = tradeTarget
|
||||
self.enabledTrading(bool(tradeTarget.enabled)) # 修复类型兼容性问题
|
||||
|
||||
event_bus.publish(EventTradeTargetUpdate, self.tradeTarget)
|
||||
self.enabledTrading(tradeTarget.enabled) # type: ignore
|
||||
self.dataUpdateLock = threading.Lock()
|
||||
|
||||
def updateGridIndex(self, grid_index: int):
|
||||
"""更新网格索引"""
|
||||
self.tradeTarget.grid_index = grid_index # type: ignore
|
||||
self.refreshPlanPrice()
|
||||
self.saveProxy()
|
||||
def updateTradeTarget(self, inTradeTarget:model.SFGridTradeTarget):
|
||||
print(f'|- 标的{self.tradeTarget.targetName()}信息更新: START')
|
||||
self.dataUpdateLock.acquire()
|
||||
print(f'|- 标的{self.tradeTarget.targetName()}信息更新: LOCKED')
|
||||
try:
|
||||
self.tradeTarget = inTradeTarget
|
||||
finally:
|
||||
print(f'|- 标的{self.tradeTarget.targetName()}信息更新: UNLOCKED')
|
||||
self.dataUpdateLock.release()
|
||||
print(f'|- 标的{self.tradeTarget.targetName()}信息更新: END')
|
||||
|
||||
def enabledTrading(self, enabled: bool) -> model.SFGridTradeTarget:
|
||||
self.tradeTarget.enabled = enabled # type: ignore
|
||||
self.saveProxy()
|
||||
|
||||
if enabled:
|
||||
self.refreshPlanPrice()
|
||||
print(f" |- 标的{self.tradeTarget.targetName()}交易启动, 持仓量:{self.tradeTarget.current_position}")
|
||||
if self.tradeTarget.status == 0: # 未建仓
|
||||
print(f" |- 标的{self.tradeTarget.targetName()}初始状态, 设置网格序号 1")
|
||||
print(f" |- 标的{self.tradeTarget.targetName()}初始状态, 设置网格序号 1,")
|
||||
self.tradeTarget.grid_index = 1 # pyright: ignore[reportAttributeAccessIssue]
|
||||
self.refreshPlanPrice()
|
||||
else: # 已建仓
|
||||
# 交易阶段,检查仓位,检查现有订单
|
||||
print(f" |- 标的{self.tradeTarget.targetName()}已有仓位或非初始状态 无需建初始仓 当前仓位: {self.tradeTarget.current_position} 状态: {self.tradeTarget.status}")
|
||||
@@ -46,9 +48,10 @@ class SFGridStrategy:
|
||||
else:
|
||||
print(f' |- 仓位检查: 持仓需求不足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}, 交易启动失败')
|
||||
self.tradeTarget.enabled = False # type: ignore
|
||||
self.saveProxy()
|
||||
else:
|
||||
print(f" |- 标的{self.tradeTarget.targetName()}交易监控暂停")
|
||||
|
||||
self.saveProxy()
|
||||
return self.tradeTarget
|
||||
|
||||
def isEnabled(self) -> bool:
|
||||
@@ -76,7 +79,7 @@ class SFGridStrategy:
|
||||
|
||||
gridBasePrice = -1 if index>=len(inTradeTarget.getPriceGrid()) or index < 0 else inTradeTarget.getPriceGrid()[int(index)] # pyright: ignore[reportArgumentType]
|
||||
|
||||
if self.tradeTarget.enabled and self.tradeTarget.status == 0 and lastPrice <= config.grid_price[1]: # 已启用,未建仓,建仓
|
||||
if self.tradeTarget.enabled and self.tradeTarget.status == 0 and lastPrice <= inTradeTarget.getPriceGrid()[1]: # 已启用,未建仓,建仓
|
||||
orderPrice = inTradeTarget.getPriceGrid()[index]
|
||||
orderType = xtconstant.STOCK_BUY
|
||||
orderRemark = OrderTypeInit
|
||||
@@ -104,7 +107,7 @@ class SFGridStrategy:
|
||||
print(f' |- 下网格{"多单" if orderType == xtconstant.STOCK_BUY else "空单"}')
|
||||
self.tradeTarget.current_order_no = qmtv.orderAsync(
|
||||
str(self.tradeTarget.stock_code),
|
||||
config.grid_volume,
|
||||
self.tradeTarget.grid_volume,
|
||||
orderType,
|
||||
orderPrice,
|
||||
xtconstant.FIX_PRICE,
|
||||
@@ -118,7 +121,7 @@ class SFGridStrategy:
|
||||
orderTypeName = "空单"
|
||||
elif orderRemark == OrderTypeInit:
|
||||
orderTypeName = "建仓单"
|
||||
print(f' |- {orderTypeName}委托, 单号 {self.tradeTarget.current_order_no}, 网格基准价 {gridBasePrice}, 下单价 {orderPrice}, 下单量 {config.grid_volume}')
|
||||
print(f' |- {orderTypeName}委托, 单号 {self.tradeTarget.current_order_no}, 网格基准价 {gridBasePrice}, 下单价 {orderPrice}, 下单量 {self.tradeTarget.grid_volume}')
|
||||
finally:
|
||||
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - release lock')
|
||||
self.saveProxy()
|
||||
@@ -126,21 +129,21 @@ class SFGridStrategy:
|
||||
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - END')
|
||||
|
||||
|
||||
def onAsyncOrderResponse(self, order:XtOrder):
|
||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:START')
|
||||
self.dataUpdateLock.acquire()
|
||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:LOCKED')
|
||||
try:
|
||||
if order.strategy_name == self.getName():
|
||||
self.tradeTarget.current_order_no = order.order_id
|
||||
self.tradeTarget.current_order_type = order.order_remark
|
||||
self.saveProxy()
|
||||
else:
|
||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]: 不在策略监控范围内{order.strategy_name}')
|
||||
finally:
|
||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:release lock')
|
||||
self.dataUpdateLock.release()
|
||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:END')
|
||||
# def onAsyncOrderResponse(self, order:XtOrder):
|
||||
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:START')
|
||||
# self.dataUpdateLock.acquire()
|
||||
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:LOCKED')
|
||||
# try:
|
||||
# if order.strategy_name == self.getName():
|
||||
# self.tradeTarget.current_order_no = order.order_id
|
||||
# self.tradeTarget.current_order_type = order.order_remark
|
||||
# self.saveProxy()
|
||||
# else:
|
||||
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]: 不在策略监控范围内{order.strategy_name}')
|
||||
# finally:
|
||||
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:release lock')
|
||||
# self.dataUpdateLock.release()
|
||||
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:END')
|
||||
|
||||
def onOrderTrade(self, trade:XtTrade):
|
||||
print(f' |- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}]:START, {trade.order_id}')
|
||||
@@ -192,7 +195,9 @@ class SFGridStrategy:
|
||||
print(f' |- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}]:END')
|
||||
|
||||
def refreshPlanPrice(self):
|
||||
if self.tradeTarget.status == 1:
|
||||
if self.tradeTarget.status == 0:
|
||||
self.tradeTarget.grid_index = 1 # type: ignore
|
||||
|
||||
buyIdx: int = self.tradeTarget.grid_index + 1 # pyright: ignore[reportAssignmentType]
|
||||
sellIdx: int = self.tradeTarget.grid_index - 1
|
||||
if self.tradeTarget.grid_index > 0: # 可以下空单
|
||||
@@ -203,9 +208,6 @@ class SFGridStrategy:
|
||||
self.tradeTarget.plan_buy_price = float(self.tradeTarget.getPriceGrid()[buyIdx]) # pyright: ignore[reportAttributeAccessIssue]
|
||||
else:
|
||||
self.tradeTarget.plan_buy_price = -1.0 # pyright: ignore[reportAttributeAccessIssue]
|
||||
else:
|
||||
self.tradeTarget.plan_buy_price = 10.0 # type: ignore
|
||||
self.tradeTarget.plan_sell_price = -1.0 # type: ignore
|
||||
self.saveProxy()
|
||||
|
||||
def getName(self):
|
||||
|
||||
+272
-250
@@ -8,6 +8,7 @@ import time
|
||||
from core import constants
|
||||
import core.eventbus as eBus
|
||||
from core.logger import LogLevel, PrintLog
|
||||
from core.sfgrid import bus_events
|
||||
from core.sfgrid.model import SFGridTradeTarget
|
||||
import configparser
|
||||
import config
|
||||
@@ -25,6 +26,7 @@ class TradeTargetUI(ttk.Frame):
|
||||
self.listening_stock = []
|
||||
self.init_trade_target_pool()
|
||||
eBus.event_bus.subscribe(eBus.MarketDataUpdate, self.onMarketDataUpdated)
|
||||
eBus.event_bus.subscribe(bus_events.EventTradeTargetUpdate, self.onStrategyUpdate)
|
||||
|
||||
# 市场监控数据
|
||||
self.marketData: dict[str, Any] = {} # 存储市场数据 {stock_code: {stock_name, last_price, time}}
|
||||
@@ -35,24 +37,45 @@ class TradeTargetUI(ttk.Frame):
|
||||
# 创建界面
|
||||
self.create_ui()
|
||||
|
||||
self.start_refresh_thread()
|
||||
|
||||
def onMarketDataUpdated(self, data):
|
||||
def init_trade_target_pool(self):
|
||||
results = SFGridTradeTarget.select()
|
||||
for temp in results:
|
||||
tradeTarget :SFGridTradeTarget = temp
|
||||
id = tradeTarget.get_id()
|
||||
|
||||
status = "新建" if tradeTarget.status == 0 else "已建初始仓"
|
||||
tradeTarget.current_position = qmtv.getStockPosition(tradeTarget.stock_code) # type: ignore
|
||||
# 计算计划交易价格
|
||||
if tradeTarget.grid_index > 0:
|
||||
tradeTarget.plan_buy_price = tradeTarget.getPriceGrid()[tradeTarget.grid_index - 1] # type: ignore
|
||||
if tradeTarget.grid_index < len(tradeTarget.getPriceGrid()) - 1:
|
||||
tradeTarget.plan_sell_price = tradeTarget.getPriceGrid()[tradeTarget.grid_index + 1] # type: ignore
|
||||
tradeTarget.save()
|
||||
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
|
||||
PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {tradeTarget.stock_code}-{tradeTarget.stock_name}: {tradeTarget.plan_buy_price} {tradeTarget.plan_sell_price}') # type: ignore
|
||||
self.updateTradeTarget(tradeTarget)
|
||||
|
||||
PrintLog(LogLevel.INFO, f'- [成功]交易标的信息初始化, 共 {len(self.tradeTargetData)} 个标的')
|
||||
|
||||
|
||||
# 收集所有市场数据用于市场监控
|
||||
def onMarketDataUpdated(self, data):
|
||||
for stock_code, tickData in data.items():
|
||||
if stock_code in self.stockCodeIdMap:
|
||||
id:int = self.stockCodeIdMap[stock_code]
|
||||
PrintLog(LogLevel.INFO, f'股票代码: {stock_code} in trade pool, 市场数据更新 {tickData["lastPrice"]}')
|
||||
tradeTarget = self.tradeTargetData[id]
|
||||
lastPrice = float("{:.3f}".format(tickData['lastPrice']))
|
||||
PrintLog(LogLevel.INFO, f'股票代码: {stock_code} {id}, 市场数据更新 {lastPrice}')
|
||||
tradeTarget.market_price = lastPrice # type: ignore
|
||||
self.updateTradeTarget(tradeTarget)
|
||||
stock_controller: SFGridStrategy = self.strategy_ctrl[id]
|
||||
stock_controller = self.strategy_ctrl[id]
|
||||
stock_controller.onDataUpdate(tradeTarget)
|
||||
else:
|
||||
# 非目标交易,发布市场数据更新事件用于市场监控
|
||||
lastPrice = tickData['lastPrice']
|
||||
if lastPrice == 10 or stock_code in self.listening_stock:
|
||||
PrintLog(LogLevel.INFO, f'股票代码: {stock_code} 监听中, 市场数据更新 {tickData["lastPrice"]}')
|
||||
# 发布市场数据更新事件用于市场监控
|
||||
market_target = SFGridTradeTarget()
|
||||
market_target.stock_code = stock_code
|
||||
@@ -69,40 +92,25 @@ class TradeTargetUI(ttk.Frame):
|
||||
}
|
||||
|
||||
|
||||
def init_trade_target_pool(self):
|
||||
results = SFGridTradeTarget.select()
|
||||
for temp in results:
|
||||
tradeTarget :SFGridTradeTarget = temp
|
||||
id = tradeTarget.get_id()
|
||||
# 来自策略的数据更新
|
||||
def onStrategyUpdate(self, target: SFGridTradeTarget):
|
||||
PrintLog(LogLevel.INFO, f'策略更新: {target.stock_code}-{target.stock_name}')
|
||||
self.updateTradeTarget(target)
|
||||
|
||||
status = "新建" if tradeTarget.status == 0 else "已建初始仓"
|
||||
tradeTarget.current_position = qmtv.getStockPosition(tradeTarget.stock_code) # type: ignore
|
||||
tradeTarget.save()
|
||||
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
|
||||
self.strategy_ctrl[id] = stockTradeController # pyright: ignore[reportArgumentType]
|
||||
self.updateTradeTarget(tradeTarget)
|
||||
|
||||
PrintLog(LogLevel.INFO, f'- [成功]交易标的信息初始化, 共 {len(self.tradeTargetData)} 个标的')
|
||||
|
||||
def updateTradeTarget(self, target: SFGridTradeTarget):
|
||||
id = target.get_id()
|
||||
status = "新建" if target.status == 0 else "已建初始仓"
|
||||
PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {target.stock_code}-{target.stock_name} 当前持仓: {target.current_position} 网格索引: {target.grid_index} 基准价格 {target.getPriceGrid()[1]} 状态: {status} 启用交易线程: {'自动交易中' if target.enabled else '交易已停止'}')
|
||||
PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {target.stock_code}-{target.stock_name}: {target.plan_buy_price} {target.plan_sell_price}') # type: ignore
|
||||
# 更新或添加数据到本地缓存
|
||||
self.tradeTargetData[target.get_id()] = target
|
||||
self.stockCodeIdMap[target.stock_code] = target.get_id() # type: ignore
|
||||
self.tradeTargetData[id] = target
|
||||
|
||||
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秒刷新一次
|
||||
if id not in self.strategy_ctrl:
|
||||
self.stockCodeIdMap[target.stock_code] = id # type: ignore
|
||||
self.strategy_ctrl[id] = SFGridStrategy(target) # pyright: ignore[reportArgumentType]
|
||||
|
||||
# UI CREATE
|
||||
def create_ui(self):
|
||||
"""创建UI界面"""
|
||||
# 主框架(使用self作为父容器)
|
||||
@@ -114,30 +122,36 @@ class TradeTargetUI(ttk.Frame):
|
||||
toolbar_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
|
||||
# 工具栏按钮
|
||||
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)
|
||||
command=self.btnHandlerAddTradeTarget, 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)
|
||||
command=self.btnHandlerDelSelectedTradeTarget, width=12).pack(side=tk.LEFT, padx=2)
|
||||
ttk.Button(toolbar_frame, text="▶️ 启动交易",
|
||||
command=self.btnHandlerStartSelectedTrade, width=12).pack(side=tk.LEFT, padx=2)
|
||||
ttk.Button(toolbar_frame, text="⏸ 暂停交易",
|
||||
command=self.btnHandlerStopSelectedTrade, 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)
|
||||
command=self.btnHandlerTradeSettings, width=12).pack(side=tk.LEFT, padx=2)
|
||||
ttk.Button(toolbar_frame, text="🛠 网格修正",
|
||||
command=self.grid_correct, width=12).pack(side=tk.LEFT, padx=2)
|
||||
command=self.btnHandlerGridCorrect, width=12).pack(side=tk.LEFT, padx=2)
|
||||
ttk.Button(toolbar_frame, text="▣ 实时监控",
|
||||
command=self.toggle_market_monitor, width=12).pack(side=tk.RIGHT, padx=2)
|
||||
command=self.btnHandlerToggleMarketMonitor, width=12).pack(side=tk.RIGHT, padx=2)
|
||||
|
||||
# 表格区域
|
||||
self.create_tables_area(main_frame)
|
||||
|
||||
def grid_correct(self):
|
||||
# 启动刷新线程
|
||||
self.refresh_thread = threading.Thread(target=self.refresh_loop, daemon=True)
|
||||
self.refresh_thread.start()
|
||||
PrintLog(LogLevel.INFO, "UI刷新线程已启动")
|
||||
|
||||
target = self.get_selected_target()
|
||||
if not target:
|
||||
return
|
||||
self.create_grid_correction_window(target)
|
||||
|
||||
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_tables_area(self, parent):
|
||||
@@ -161,18 +175,6 @@ class TradeTargetUI(ttk.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):
|
||||
"""创建交易标的表格"""
|
||||
|
||||
@@ -359,8 +361,6 @@ class TradeTargetUI(ttk.Frame):
|
||||
self.get_trade_enabled_indicator(target.enabled) # type: ignore
|
||||
]
|
||||
|
||||
|
||||
|
||||
self.trade_table.insert('', tk.END, values=values)
|
||||
|
||||
|
||||
@@ -399,155 +399,6 @@ class TradeTargetUI(ttk.Frame):
|
||||
|
||||
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:
|
||||
PrintLog(LogLevel.INFO, f'启动标的交易 {target.targetName()}')
|
||||
target.enabled = True # type: ignore
|
||||
|
||||
id = target.get_id()
|
||||
if id in self.strategy_ctrl:
|
||||
tradeController: SFGridStrategy = self.strategy_ctrl[target.get_id()]
|
||||
tradeTarget = tradeController.enabledTrading(True)
|
||||
self.tradeTargetData[id] = tradeTarget
|
||||
else:
|
||||
PrintLog(LogLevel.INFO, f"\t创建标的交易控制器 {target.targetName()}")
|
||||
|
||||
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:
|
||||
PrintLog(LogLevel.INFO, f'暂停标的交易 {target.targetName()}')
|
||||
id = target.get_id()
|
||||
if id in self.strategy_ctrl:
|
||||
tradeController: SFGridStrategy = self.strategy_ctrl[target.get_id()]
|
||||
tradeTarget = tradeController.enabledTrading(False)
|
||||
orders = qmtv.queryPendingOrder(target.stock_code, tradeController.getName()) # type: ignore
|
||||
for order in orders:
|
||||
qmtv.xttrader.cancel_order_stock_async(qmtv.account, order.order_id)
|
||||
print(f'取消未成交订单 {len(orders)}')
|
||||
self.tradeTargetData[id] = tradeTarget
|
||||
else:
|
||||
print(f"标的交易控制器不存在 {target.stock_code} {target.stock_name}\n")
|
||||
# self.add_log("INFO", f"已暂停交易: {target.stock_code} - {target.stock_name}")
|
||||
# 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:
|
||||
id = target.get_id()
|
||||
try:
|
||||
# 从数据库中删除
|
||||
target.delete_instance()
|
||||
|
||||
del self.tradeTargetData[id]
|
||||
del self.strategy_ctrl[id]
|
||||
del self.stockCodeIdMap[target.stock_code] # type: ignore
|
||||
# 添加日志
|
||||
PrintLog(LogLevel.INFO, f"交易标的已删除,ID: {id} {target.targetName()}")
|
||||
except Exception as e:
|
||||
PrintLog(LogLevel.ERROR, f"删除交易标的失败 ID {id}: {str(e)}")
|
||||
PrintLog(LogLevel.INFO, f"已发送删除请求: {target.stock_code} - {target.stock_name}")
|
||||
|
||||
def add_trade_target(self):
|
||||
"""添加新的交易标的"""
|
||||
# 获取顶层窗口
|
||||
root = self.winfo_toplevel()
|
||||
|
||||
# 创建顶层窗口
|
||||
add_window = tk.Toplevel(root)
|
||||
add_window.title("添加交易标的")
|
||||
add_window.geometry("400x150")
|
||||
add_window.resizable(False, False)
|
||||
|
||||
# 设置窗口模态
|
||||
add_window.transient(root)
|
||||
add_window.grab_set()
|
||||
|
||||
# 居中显示
|
||||
root.update_idletasks()
|
||||
x = root.winfo_x() + (root.winfo_width() // 2) - 200
|
||||
y = root.winfo_y() + (root.winfo_height() // 2) - 75
|
||||
add_window.geometry(f"400x150+{x}+{y}")
|
||||
|
||||
# 创建输入框架
|
||||
input_frame = ttk.Frame(add_window, padding=20)
|
||||
input_frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# 股票代码输入
|
||||
ttk.Label(input_frame, text="股票代码:").grid(row=0, column=0, sticky=tk.W, pady=5)
|
||||
stock_code_entry = ttk.Entry(input_frame, width=30)
|
||||
stock_code_entry.grid(row=0, column=1, pady=5, padx=(10, 0))
|
||||
stock_code_entry.focus()
|
||||
|
||||
# 按钮框架
|
||||
button_frame = ttk.Frame(input_frame)
|
||||
button_frame.grid(row=1, column=0, columnspan=2, pady=20)
|
||||
|
||||
def confirm_add():
|
||||
stock_code = stock_code_entry.get().strip()
|
||||
if not stock_code:
|
||||
messagebox.showwarning("输入错误", "请输入股票代码")
|
||||
return
|
||||
|
||||
# 发布事件通知主控制器添加标的
|
||||
self.addTradeTarget(stock_code)
|
||||
add_window.destroy()
|
||||
|
||||
def cancel_add():
|
||||
add_window.destroy()
|
||||
|
||||
# 确认和取消按钮
|
||||
ttk.Button(button_frame, text="确认", command=confirm_add, width=10).pack(side=tk.LEFT, padx=5)
|
||||
ttk.Button(button_frame, text="取消", command=cancel_add, width=10).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 绑定回车键确认
|
||||
stock_code_entry.bind('<Return>', lambda event: confirm_add())
|
||||
|
||||
PrintLog(LogLevel.INFO, "点击添加交易标的按钮")
|
||||
|
||||
def refresh_table(self):
|
||||
"""刷新表格数据"""
|
||||
# 保存当前选中的项
|
||||
@@ -575,20 +426,6 @@ class TradeTargetUI(ttk.Frame):
|
||||
# 刷新市场监控表格
|
||||
self.populate_market_table()
|
||||
|
||||
def trade_settings(self):
|
||||
"""网格配置功能"""
|
||||
target = self.get_selected_target()
|
||||
if not target:
|
||||
return
|
||||
|
||||
# 检查标的的状态,status为1时仅可查看
|
||||
if target.status == 1:
|
||||
# 创建只读的网格配置查看窗口
|
||||
self.create_grid_view_window(target)
|
||||
else:
|
||||
# 创建可编辑的网格配置窗口
|
||||
self.create_grid_config_window(target)
|
||||
|
||||
def create_grid_view_window(self, target: SFGridTradeTarget):
|
||||
"""创建网格配置查看窗口(只读)"""
|
||||
# 获取顶层窗口
|
||||
@@ -882,6 +719,23 @@ class TradeTargetUI(ttk.Frame):
|
||||
ttk.Button(button_frame, text="保存", command=save_config).pack(side=tk.RIGHT, padx=5)
|
||||
ttk.Button(button_frame, text="取消", command=config_window.destroy).pack(side=tk.RIGHT, padx=5)
|
||||
|
||||
def decrease_grid_index(self, grid_index_var: tk.IntVar, target: SFGridTradeTarget, required_position_label: ttk.Label, position_status_label: ttk.Label):
|
||||
"""减少网格序号"""
|
||||
current_value = grid_index_var.get()
|
||||
if current_value > 0:
|
||||
grid_index_var.set(current_value - 1)
|
||||
# 同步更新需求持仓量和持仓状态
|
||||
self.update_required_position_and_status(grid_index_var.get(), target, required_position_label, position_status_label)
|
||||
|
||||
def increase_grid_index(self, grid_index_var: tk.IntVar, max_index: int, target: SFGridTradeTarget, required_position_label: ttk.Label, position_status_label: ttk.Label):
|
||||
"""增加网格序号"""
|
||||
current_value = grid_index_var.get()
|
||||
if current_value < max_index:
|
||||
grid_index_var.set(current_value + 1)
|
||||
# 同步更新需求持仓量和持仓状态
|
||||
self.update_required_position_and_status(grid_index_var.get(), target, required_position_label, position_status_label)
|
||||
|
||||
|
||||
def create_grid_correction_window(self, target: SFGridTradeTarget):
|
||||
"""创建网格修正窗口"""
|
||||
# 获取顶层窗口
|
||||
@@ -1043,23 +897,6 @@ class TradeTargetUI(ttk.Frame):
|
||||
PrintLog(LogLevel.INFO, f"网格修正已保存: {target.stock_code} - {target.stock_name}, 网格序号: {new_grid_index}")
|
||||
|
||||
|
||||
|
||||
def decrease_grid_index(self, grid_index_var: tk.IntVar, target: SFGridTradeTarget, required_position_label: ttk.Label, position_status_label: ttk.Label):
|
||||
"""减少网格序号"""
|
||||
current_value = grid_index_var.get()
|
||||
if current_value > 0:
|
||||
grid_index_var.set(current_value - 1)
|
||||
# 同步更新需求持仓量和持仓状态
|
||||
self.update_required_position_and_status(grid_index_var.get(), target, required_position_label, position_status_label)
|
||||
|
||||
def increase_grid_index(self, grid_index_var: tk.IntVar, max_index: int, target: SFGridTradeTarget, required_position_label: ttk.Label, position_status_label: ttk.Label):
|
||||
"""增加网格序号"""
|
||||
current_value = grid_index_var.get()
|
||||
if current_value < max_index:
|
||||
grid_index_var.set(current_value + 1)
|
||||
# 同步更新需求持仓量和持仓状态
|
||||
self.update_required_position_and_status(grid_index_var.get(), target, required_position_label, position_status_label)
|
||||
|
||||
def update_required_position_and_status(self, grid_index: int, target: SFGridTradeTarget, required_position_label: ttk.Label, position_status_label: ttk.Label):
|
||||
"""更新需求持仓量和持仓状态"""
|
||||
# 计算需求持仓量
|
||||
@@ -1071,7 +908,8 @@ class TradeTargetUI(ttk.Frame):
|
||||
self.update_position_status(current_position, required_position, position_status_label)
|
||||
|
||||
|
||||
def addTradeTarget(self, stock_code: str, gridIndex: int = 1):
|
||||
# 交易池管理
|
||||
def addTradeTarget(self, stock_code: str, gridIndex: int = 1): # 新增
|
||||
"""处理添加交易标的事件"""
|
||||
try:
|
||||
stock_name = qmtv.getInstrumentName(stock_code)
|
||||
@@ -1108,16 +946,200 @@ class TradeTargetUI(ttk.Frame):
|
||||
except Exception as e:
|
||||
PrintLog(LogLevel.ERROR, f'新增交易标的失败 {stock_code} {e}')
|
||||
|
||||
def update_trade_target_grid(self, data: GridFixData):
|
||||
"""更新交易标的网格信息"""
|
||||
# def update_trade_target_grid(self, data: GridFixData):
|
||||
# """更新交易标的网格信息"""
|
||||
# try:
|
||||
# target = data.tradeTarget
|
||||
# grid_index = data.grid_index
|
||||
|
||||
# # 更新数据库中的网格索引
|
||||
# target.grid_index = grid_index
|
||||
# target.save()
|
||||
|
||||
# PrintLog(LogLevel.INFO, f"网格修正已应用: {target.stock_code} - {target.stock_name}, 网格索引: {grid_index}")
|
||||
# except Exception as e:
|
||||
# PrintLog(LogLevel.ERROR, f"网格修正更新失败: {str(e)}")
|
||||
|
||||
# button handlers =============================================================================================
|
||||
def btnHandlerGridCorrect(self):
|
||||
|
||||
target = self.get_selected_target()
|
||||
if not target:
|
||||
return
|
||||
self.create_grid_correction_window(target)
|
||||
|
||||
|
||||
def btnHandlerToggleMarketMonitor(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 btnHandlerTradeSettings(self):
|
||||
"""网格配置功能"""
|
||||
target = self.get_selected_target()
|
||||
if not target:
|
||||
return
|
||||
|
||||
# 检查标的的状态,status为1时仅可查看
|
||||
if target.status == 1:
|
||||
# 创建只读的网格配置查看窗口
|
||||
self.create_grid_view_window(target)
|
||||
else:
|
||||
# 创建可编辑的网格配置窗口
|
||||
self.create_grid_config_window(target)
|
||||
|
||||
def btnHandlerStartSelectedTrade(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:
|
||||
PrintLog(LogLevel.INFO, f'启动标的交易 {target.targetName()}')
|
||||
target.enabled = True # type: ignore
|
||||
|
||||
id = target.get_id()
|
||||
if id in self.strategy_ctrl:
|
||||
tradeController: SFGridStrategy = self.strategy_ctrl[target.get_id()]
|
||||
tradeTarget = tradeController.enabledTrading(True)
|
||||
self.tradeTargetData[id] = tradeTarget
|
||||
else:
|
||||
PrintLog(LogLevel.INFO, f"\t创建标的交易控制器 {target.targetName()}")
|
||||
|
||||
def btnHandlerStopSelectedTrade(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:
|
||||
PrintLog(LogLevel.INFO, f'暂停标的交易 {target.targetName()}')
|
||||
id = target.get_id()
|
||||
if id in self.strategy_ctrl:
|
||||
tradeController: SFGridStrategy = self.strategy_ctrl[target.get_id()]
|
||||
tradeTarget = tradeController.enabledTrading(False)
|
||||
orders = qmtv.queryPendingOrder(target.stock_code, tradeController.getName()) # type: ignore
|
||||
for order in orders:
|
||||
qmtv.xttrader.cancel_order_stock_async(qmtv.account, order.order_id)
|
||||
print(f'取消未成交订单 {len(orders)}')
|
||||
self.tradeTargetData[id] = tradeTarget
|
||||
else:
|
||||
print(f"标的交易控制器不存在 {target.stock_code} {target.stock_name}\n")
|
||||
# self.add_log("INFO", f"已暂停交易: {target.stock_code} - {target.stock_name}")
|
||||
# messagebox.showinfo("暂停成功", f"已暂停 {target.stock_code} ({target.stock_name}) 的交易")
|
||||
|
||||
def btnHandlerDelSelectedTradeTarget(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:
|
||||
id = target.get_id()
|
||||
try:
|
||||
target = data.tradeTarget
|
||||
grid_index = data.grid_index
|
||||
|
||||
# 更新数据库中的网格索引
|
||||
target.grid_index = grid_index
|
||||
target.save()
|
||||
del self.tradeTargetData[id]
|
||||
del self.strategy_ctrl[id]
|
||||
del self.stockCodeIdMap[target.stock_code] # type: ignore
|
||||
|
||||
PrintLog(LogLevel.INFO, f"网格修正已应用: {target.stock_code} - {target.stock_name}, 网格索引: {grid_index}")
|
||||
# 从数据库中删除
|
||||
target.delete_instance()
|
||||
# 添加日志
|
||||
PrintLog(LogLevel.INFO, f"交易标的已删除,ID: {id} {target.targetName()}")
|
||||
except Exception as e:
|
||||
PrintLog(LogLevel.ERROR, f"网格修正更新失败: {str(e)}")
|
||||
PrintLog(LogLevel.ERROR, f"删除交易标的失败 ID {id}: {str(e)}")
|
||||
PrintLog(LogLevel.INFO, f"已发送删除请求: {target.stock_code} - {target.stock_name}")
|
||||
|
||||
def btnHandlerAddTradeTarget(self):
|
||||
"""添加新的交易标的"""
|
||||
# 获取顶层窗口
|
||||
root = self.winfo_toplevel()
|
||||
|
||||
# 创建顶层窗口
|
||||
add_window = tk.Toplevel(root)
|
||||
add_window.title("添加交易标的")
|
||||
add_window.geometry("400x150")
|
||||
add_window.resizable(False, False)
|
||||
|
||||
# 设置窗口模态
|
||||
add_window.transient(root)
|
||||
add_window.grab_set()
|
||||
|
||||
# 居中显示
|
||||
root.update_idletasks()
|
||||
x = root.winfo_x() + (root.winfo_width() // 2) - 200
|
||||
y = root.winfo_y() + (root.winfo_height() // 2) - 75
|
||||
add_window.geometry(f"400x150+{x}+{y}")
|
||||
|
||||
# 创建输入框架
|
||||
input_frame = ttk.Frame(add_window, padding=20)
|
||||
input_frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# 股票代码输入
|
||||
ttk.Label(input_frame, text="股票代码:").grid(row=0, column=0, sticky=tk.W, pady=5)
|
||||
stock_code_entry = ttk.Entry(input_frame, width=30)
|
||||
stock_code_entry.grid(row=0, column=1, pady=5, padx=(10, 0))
|
||||
stock_code_entry.focus()
|
||||
|
||||
# 按钮框架
|
||||
button_frame = ttk.Frame(input_frame)
|
||||
button_frame.grid(row=1, column=0, columnspan=2, pady=20)
|
||||
|
||||
def confirm_add():
|
||||
stock_code = stock_code_entry.get().strip()
|
||||
if not stock_code:
|
||||
messagebox.showwarning("输入错误", "请输入股票代码")
|
||||
return
|
||||
|
||||
# 发布事件通知主控制器添加标的
|
||||
self.addTradeTarget(stock_code)
|
||||
add_window.destroy()
|
||||
|
||||
def cancel_add():
|
||||
add_window.destroy()
|
||||
|
||||
# 确认和取消按钮
|
||||
ttk.Button(button_frame, text="确认", command=confirm_add, width=10).pack(side=tk.LEFT, padx=5)
|
||||
ttk.Button(button_frame, text="取消", command=cancel_add, width=10).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 绑定回车键确认
|
||||
stock_code_entry.bind('<Return>', lambda event: confirm_add())
|
||||
|
||||
PrintLog(LogLevel.INFO, "点击添加交易标的按钮")
|
||||
Reference in New Issue
Block a user