update
This commit is contained in:
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
[config]
|
[config]
|
||||||
miniqmtpath = D:/Programs/DTQMT_MN/userdata_mini
|
miniqmtpath = D:/Programs/DTQMT/userdata_mini
|
||||||
; account_no = 99082560
|
account_no = 99082560
|
||||||
account_no = 89009170
|
; account_no = 89009170
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -165,7 +165,8 @@ class QmtV(XtQuantTraderCallback):
|
|||||||
:param order: XtOrder对象
|
:param order: XtOrder对象
|
||||||
:return:
|
: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
|
# stockCode = order.stock_code
|
||||||
# ctrl:SFGridStrategy = self.stock_trade_ctrl[stockCode]
|
# ctrl:SFGridStrategy = self.stock_trade_ctrl[stockCode]
|
||||||
# # 如果存在对应的StockTradeController,则调用其onDataUpdate方法
|
# # 如果存在对应的StockTradeController,则调用其onDataUpdate方法
|
||||||
|
|||||||
@@ -16,27 +16,29 @@ class SFGridStrategy:
|
|||||||
|
|
||||||
def __init__(self, tradeTarget: model.SFGridTradeTarget):
|
def __init__(self, tradeTarget: model.SFGridTradeTarget):
|
||||||
self.tradeTarget:model.SFGridTradeTarget = tradeTarget
|
self.tradeTarget:model.SFGridTradeTarget = tradeTarget
|
||||||
self.enabledTrading(bool(tradeTarget.enabled)) # 修复类型兼容性问题
|
self.enabledTrading(tradeTarget.enabled) # type: ignore
|
||||||
|
|
||||||
event_bus.publish(EventTradeTargetUpdate, self.tradeTarget)
|
|
||||||
self.dataUpdateLock = threading.Lock()
|
self.dataUpdateLock = threading.Lock()
|
||||||
|
|
||||||
def updateGridIndex(self, grid_index: int):
|
def updateTradeTarget(self, inTradeTarget:model.SFGridTradeTarget):
|
||||||
"""更新网格索引"""
|
print(f'|- 标的{self.tradeTarget.targetName()}信息更新: START')
|
||||||
self.tradeTarget.grid_index = grid_index # type: ignore
|
self.dataUpdateLock.acquire()
|
||||||
self.refreshPlanPrice()
|
print(f'|- 标的{self.tradeTarget.targetName()}信息更新: LOCKED')
|
||||||
self.saveProxy()
|
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:
|
def enabledTrading(self, enabled: bool) -> model.SFGridTradeTarget:
|
||||||
self.tradeTarget.enabled = enabled # type: ignore
|
self.tradeTarget.enabled = enabled # type: ignore
|
||||||
self.saveProxy()
|
|
||||||
|
|
||||||
if enabled:
|
if enabled:
|
||||||
self.refreshPlanPrice()
|
|
||||||
print(f" |- 标的{self.tradeTarget.targetName()}交易启动, 持仓量:{self.tradeTarget.current_position}")
|
print(f" |- 标的{self.tradeTarget.targetName()}交易启动, 持仓量:{self.tradeTarget.current_position}")
|
||||||
if self.tradeTarget.status == 0: # 未建仓
|
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.tradeTarget.grid_index = 1 # pyright: ignore[reportAttributeAccessIssue]
|
||||||
|
self.refreshPlanPrice()
|
||||||
else: # 已建仓
|
else: # 已建仓
|
||||||
# 交易阶段,检查仓位,检查现有订单
|
# 交易阶段,检查仓位,检查现有订单
|
||||||
print(f" |- 标的{self.tradeTarget.targetName()}已有仓位或非初始状态 无需建初始仓 当前仓位: {self.tradeTarget.current_position} 状态: {self.tradeTarget.status}")
|
print(f" |- 标的{self.tradeTarget.targetName()}已有仓位或非初始状态 无需建初始仓 当前仓位: {self.tradeTarget.current_position} 状态: {self.tradeTarget.status}")
|
||||||
@@ -46,9 +48,10 @@ class SFGridStrategy:
|
|||||||
else:
|
else:
|
||||||
print(f' |- 仓位检查: 持仓需求不足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}, 交易启动失败')
|
print(f' |- 仓位检查: 持仓需求不足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}, 交易启动失败')
|
||||||
self.tradeTarget.enabled = False # type: ignore
|
self.tradeTarget.enabled = False # type: ignore
|
||||||
self.saveProxy()
|
|
||||||
else:
|
else:
|
||||||
print(f" |- 标的{self.tradeTarget.targetName()}交易监控暂停")
|
print(f" |- 标的{self.tradeTarget.targetName()}交易监控暂停")
|
||||||
|
|
||||||
|
self.saveProxy()
|
||||||
return self.tradeTarget
|
return self.tradeTarget
|
||||||
|
|
||||||
def isEnabled(self) -> bool:
|
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]
|
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]
|
orderPrice = inTradeTarget.getPriceGrid()[index]
|
||||||
orderType = xtconstant.STOCK_BUY
|
orderType = xtconstant.STOCK_BUY
|
||||||
orderRemark = OrderTypeInit
|
orderRemark = OrderTypeInit
|
||||||
@@ -104,7 +107,7 @@ class SFGridStrategy:
|
|||||||
print(f' |- 下网格{"多单" if orderType == xtconstant.STOCK_BUY else "空单"}')
|
print(f' |- 下网格{"多单" if orderType == xtconstant.STOCK_BUY else "空单"}')
|
||||||
self.tradeTarget.current_order_no = qmtv.orderAsync(
|
self.tradeTarget.current_order_no = qmtv.orderAsync(
|
||||||
str(self.tradeTarget.stock_code),
|
str(self.tradeTarget.stock_code),
|
||||||
config.grid_volume,
|
self.tradeTarget.grid_volume,
|
||||||
orderType,
|
orderType,
|
||||||
orderPrice,
|
orderPrice,
|
||||||
xtconstant.FIX_PRICE,
|
xtconstant.FIX_PRICE,
|
||||||
@@ -118,7 +121,7 @@ class SFGridStrategy:
|
|||||||
orderTypeName = "空单"
|
orderTypeName = "空单"
|
||||||
elif orderRemark == OrderTypeInit:
|
elif orderRemark == OrderTypeInit:
|
||||||
orderTypeName = "建仓单"
|
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:
|
finally:
|
||||||
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - release lock')
|
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - release lock')
|
||||||
self.saveProxy()
|
self.saveProxy()
|
||||||
@@ -126,21 +129,21 @@ class SFGridStrategy:
|
|||||||
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - END')
|
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - END')
|
||||||
|
|
||||||
|
|
||||||
def onAsyncOrderResponse(self, order:XtOrder):
|
# def onAsyncOrderResponse(self, order:XtOrder):
|
||||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:START')
|
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:START')
|
||||||
self.dataUpdateLock.acquire()
|
# self.dataUpdateLock.acquire()
|
||||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:LOCKED')
|
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:LOCKED')
|
||||||
try:
|
# try:
|
||||||
if order.strategy_name == self.getName():
|
# if order.strategy_name == self.getName():
|
||||||
self.tradeTarget.current_order_no = order.order_id
|
# self.tradeTarget.current_order_no = order.order_id
|
||||||
self.tradeTarget.current_order_type = order.order_remark
|
# self.tradeTarget.current_order_type = order.order_remark
|
||||||
self.saveProxy()
|
# self.saveProxy()
|
||||||
else:
|
# else:
|
||||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]: 不在策略监控范围内{order.strategy_name}')
|
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]: 不在策略监控范围内{order.strategy_name}')
|
||||||
finally:
|
# finally:
|
||||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:release lock')
|
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:release lock')
|
||||||
self.dataUpdateLock.release()
|
# self.dataUpdateLock.release()
|
||||||
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:END')
|
# print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:END')
|
||||||
|
|
||||||
def onOrderTrade(self, trade:XtTrade):
|
def onOrderTrade(self, trade:XtTrade):
|
||||||
print(f' |- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}]:START, {trade.order_id}')
|
print(f' |- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}]:START, {trade.order_id}')
|
||||||
@@ -192,20 +195,19 @@ class SFGridStrategy:
|
|||||||
print(f' |- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}]:END')
|
print(f' |- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}]:END')
|
||||||
|
|
||||||
def refreshPlanPrice(self):
|
def refreshPlanPrice(self):
|
||||||
if self.tradeTarget.status == 1:
|
if self.tradeTarget.status == 0:
|
||||||
buyIdx: int = self.tradeTarget.grid_index + 1 # pyright: ignore[reportAssignmentType]
|
self.tradeTarget.grid_index = 1 # type: ignore
|
||||||
sellIdx: int = self.tradeTarget.grid_index - 1
|
|
||||||
if self.tradeTarget.grid_index > 0: # 可以下空单
|
buyIdx: int = self.tradeTarget.grid_index + 1 # pyright: ignore[reportAssignmentType]
|
||||||
self.tradeTarget.plan_sell_price = float(self.tradeTarget.getPriceGrid()[sellIdx]) # pyright: ignore[reportAttributeAccessIssue]
|
sellIdx: int = self.tradeTarget.grid_index - 1
|
||||||
else:
|
if self.tradeTarget.grid_index > 0: # 可以下空单
|
||||||
self.tradeTarget.plan_sell_price = -1.0 # type: ignore
|
self.tradeTarget.plan_sell_price = float(self.tradeTarget.getPriceGrid()[sellIdx]) # pyright: ignore[reportAttributeAccessIssue]
|
||||||
if self.tradeTarget.grid_index < len(self.tradeTarget.getPriceGrid()) - 1:
|
|
||||||
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:
|
else:
|
||||||
self.tradeTarget.plan_buy_price = 10.0 # type: ignore
|
|
||||||
self.tradeTarget.plan_sell_price = -1.0 # type: ignore
|
self.tradeTarget.plan_sell_price = -1.0 # type: ignore
|
||||||
|
if self.tradeTarget.grid_index < len(self.tradeTarget.getPriceGrid()) - 1:
|
||||||
|
self.tradeTarget.plan_buy_price = float(self.tradeTarget.getPriceGrid()[buyIdx]) # pyright: ignore[reportAttributeAccessIssue]
|
||||||
|
else:
|
||||||
|
self.tradeTarget.plan_buy_price = -1.0 # pyright: ignore[reportAttributeAccessIssue]
|
||||||
self.saveProxy()
|
self.saveProxy()
|
||||||
|
|
||||||
def getName(self):
|
def getName(self):
|
||||||
|
|||||||
+274
-252
@@ -8,6 +8,7 @@ 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 import bus_events
|
||||||
from core.sfgrid.model import SFGridTradeTarget
|
from core.sfgrid.model import SFGridTradeTarget
|
||||||
import configparser
|
import configparser
|
||||||
import config
|
import config
|
||||||
@@ -25,6 +26,7 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
self.listening_stock = []
|
self.listening_stock = []
|
||||||
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)
|
||||||
|
eBus.event_bus.subscribe(bus_events.EventTradeTargetUpdate, self.onStrategyUpdate)
|
||||||
|
|
||||||
# 市场监控数据
|
# 市场监控数据
|
||||||
self.marketData: dict[str, Any] = {} # 存储市场数据 {stock_code: {stock_name, last_price, time}}
|
self.marketData: dict[str, Any] = {} # 存储市场数据 {stock_code: {stock_name, last_price, time}}
|
||||||
@@ -35,24 +37,45 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
# 创建界面
|
# 创建界面
|
||||||
self.create_ui()
|
self.create_ui()
|
||||||
|
|
||||||
self.start_refresh_thread()
|
|
||||||
|
|
||||||
|
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):
|
def onMarketDataUpdated(self, data):
|
||||||
# 收集所有市场数据用于市场监控
|
|
||||||
for stock_code, tickData in data.items():
|
for stock_code, tickData in data.items():
|
||||||
if stock_code in self.stockCodeIdMap:
|
if stock_code in self.stockCodeIdMap:
|
||||||
id:int = self.stockCodeIdMap[stock_code]
|
id:int = self.stockCodeIdMap[stock_code]
|
||||||
|
PrintLog(LogLevel.INFO, f'股票代码: {stock_code} in trade pool, 市场数据更新 {tickData["lastPrice"]}')
|
||||||
tradeTarget = self.tradeTargetData[id]
|
tradeTarget = self.tradeTargetData[id]
|
||||||
lastPrice = float("{:.3f}".format(tickData['lastPrice']))
|
lastPrice = float("{:.3f}".format(tickData['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 = self.strategy_ctrl[id]
|
||||||
stock_controller.onDataUpdate(tradeTarget)
|
stock_controller.onDataUpdate(tradeTarget)
|
||||||
else:
|
else:
|
||||||
# 非目标交易,发布市场数据更新事件用于市场监控
|
# 非目标交易,发布市场数据更新事件用于市场监控
|
||||||
lastPrice = tickData['lastPrice']
|
lastPrice = tickData['lastPrice']
|
||||||
if lastPrice == 10 or stock_code in self.listening_stock:
|
if lastPrice == 10 or stock_code in self.listening_stock:
|
||||||
|
PrintLog(LogLevel.INFO, f'股票代码: {stock_code} 监听中, 市场数据更新 {tickData["lastPrice"]}')
|
||||||
# 发布市场数据更新事件用于市场监控
|
# 发布市场数据更新事件用于市场监控
|
||||||
market_target = SFGridTradeTarget()
|
market_target = SFGridTradeTarget()
|
||||||
market_target.stock_code = stock_code
|
market_target.stock_code = stock_code
|
||||||
@@ -69,40 +92,25 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def init_trade_target_pool(self):
|
# 来自策略的数据更新
|
||||||
results = SFGridTradeTarget.select()
|
def onStrategyUpdate(self, target: SFGridTradeTarget):
|
||||||
for temp in results:
|
PrintLog(LogLevel.INFO, f'策略更新: {target.stock_code}-{target.stock_name}')
|
||||||
tradeTarget :SFGridTradeTarget = temp
|
self.updateTradeTarget(target)
|
||||||
id = tradeTarget.get_id()
|
|
||||||
|
|
||||||
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):
|
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.tradeTargetData[id] = target
|
||||||
self.stockCodeIdMap[target.stock_code] = target.get_id() # type: ignore
|
|
||||||
|
|
||||||
def start_refresh_thread(self):
|
if id not in self.strategy_ctrl:
|
||||||
"""启动刷新线程"""
|
self.stockCodeIdMap[target.stock_code] = id # type: ignore
|
||||||
self.refresh_thread = threading.Thread(target=self.refresh_loop, daemon=True)
|
self.strategy_ctrl[id] = SFGridStrategy(target) # pyright: ignore[reportArgumentType]
|
||||||
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秒刷新一次
|
|
||||||
|
|
||||||
|
# UI CREATE
|
||||||
def create_ui(self):
|
def create_ui(self):
|
||||||
"""创建UI界面"""
|
"""创建UI界面"""
|
||||||
# 主框架(使用self作为父容器)
|
# 主框架(使用self作为父容器)
|
||||||
@@ -114,30 +122,36 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
toolbar_frame.pack(fill=tk.X, pady=(0, 10))
|
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="➕ 添加标的",
|
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="🗑 删除标的",
|
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="🛠 交易设置",
|
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="🛠 网格修正",
|
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="▣ 实时监控",
|
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)
|
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:
|
def refresh_loop(self):
|
||||||
return
|
"""刷新循环"""
|
||||||
self.create_grid_correction_window(target)
|
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):
|
def create_tables_area(self, parent):
|
||||||
@@ -161,18 +175,6 @@ 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):
|
||||||
"""创建交易标的表格"""
|
"""创建交易标的表格"""
|
||||||
|
|
||||||
@@ -359,8 +361,6 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
self.get_trade_enabled_indicator(target.enabled) # type: ignore
|
self.get_trade_enabled_indicator(target.enabled) # type: ignore
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.trade_table.insert('', tk.END, values=values)
|
self.trade_table.insert('', tk.END, values=values)
|
||||||
|
|
||||||
|
|
||||||
@@ -399,155 +399,6 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
|
|
||||||
return None
|
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):
|
def refresh_table(self):
|
||||||
"""刷新表格数据"""
|
"""刷新表格数据"""
|
||||||
# 保存当前选中的项
|
# 保存当前选中的项
|
||||||
@@ -575,20 +426,6 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
# 刷新市场监控表格
|
# 刷新市场监控表格
|
||||||
self.populate_market_table()
|
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):
|
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=save_config).pack(side=tk.RIGHT, padx=5)
|
||||||
ttk.Button(button_frame, text="取消", command=config_window.destroy).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):
|
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}")
|
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):
|
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)
|
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:
|
try:
|
||||||
stock_name = qmtv.getInstrumentName(stock_code)
|
stock_name = qmtv.getInstrumentName(stock_code)
|
||||||
@@ -1108,16 +946,200 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
PrintLog(LogLevel.ERROR, f'新增交易标的失败 {stock_code} {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:
|
# try:
|
||||||
target = data.tradeTarget
|
# target = data.tradeTarget
|
||||||
grid_index = data.grid_index
|
# grid_index = data.grid_index
|
||||||
|
|
||||||
# 更新数据库中的网格索引
|
# # 更新数据库中的网格索引
|
||||||
target.grid_index = grid_index
|
# target.grid_index = grid_index
|
||||||
target.save()
|
# target.save()
|
||||||
|
|
||||||
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)}")
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
|
||||||
|
del self.tradeTargetData[id]
|
||||||
|
del self.strategy_ctrl[id]
|
||||||
|
del self.stockCodeIdMap[target.stock_code] # type: ignore
|
||||||
|
|
||||||
|
# 从数据库中删除
|
||||||
|
target.delete_instance()
|
||||||
|
# 添加日志
|
||||||
|
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 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