From 1618cad5a0900bd8512aa42196917027e789082e Mon Sep 17 00:00:00 2001 From: "GDP\\solonot" Date: Wed, 19 Nov 2025 16:28:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E9=83=A8=E5=88=86=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E8=AE=A2=E5=8D=95=E6=9F=A5=E8=AF=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/qmt.py | 7 +- core/sfgrid/sfgrid_strategy.py | 6 +- core/sfgrid/sfgrid_ui.py | 177 ++------------------------------- 3 files changed, 16 insertions(+), 174 deletions(-) diff --git a/core/qmt.py b/core/qmt.py index 6814d91..1dd8f6f 100644 --- a/core/qmt.py +++ b/core/qmt.py @@ -54,17 +54,14 @@ class QmtV(XtQuantTraderCallback): def getStockPosition(self, stock_code: str): - volume = 0 print(f'获取股票持仓: {stock_code}, {self.xttrader.connected}, {self.account.account_id if self.account else None}') # pyright: ignore[reportAttributeAccessIssue] positions = self.xttrader.query_stock_positions(self.account) if positions: for temp in positions: pos:XtPosition = temp if pos.stock_code == stock_code: - volume = pos.volume - break - - return volume + return pos + return None def queryPendingOrder(self, stock_code:str, tag: str) -> list[XtOrder]: diff --git a/core/sfgrid/sfgrid_strategy.py b/core/sfgrid/sfgrid_strategy.py index 80676ad..c6e382e 100644 --- a/core/sfgrid/sfgrid_strategy.py +++ b/core/sfgrid/sfgrid_strategy.py @@ -36,6 +36,10 @@ class SFGridStrategy: gridIdx = int(order.order_remark.split(',')[1]) self.orderGrid[gridIdx] = order.order_id PrintLog(LogLevel.INFO, f'|- 标的[{self.tradeTarget.targetName()}] 初始化: 加载现有订单, grid-{gridIdx} order_id:{self.orderGrid[gridIdx]}') + + def printPendingOrder(self): + for idx, order_id in self.orderGrid.items(): + PrintLog(LogLevel.DEBUG, f" {idx} : {order_id}") def onMarketActiveSwitch(self, isActive: bool): if isActive and self.tradeTarget.enabled: @@ -165,7 +169,7 @@ class SFGridStrategy: self.dataUpdateLock.release() def onOrderTrade(self, trade:XtTrade): # TODO 委托成交通知,处理成交后网格切换 - remark = response.order_remark.split(',') + remark = trade.order_remark.split(',') if trade.strategy_name != self.getName() or len(remark) < 3 or self.tradeTarget.stock_code != trade.stock_code: return PrintLog(LogLevel.INFO, f'|- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}] : {trade.order_id}') diff --git a/core/sfgrid/sfgrid_ui.py b/core/sfgrid/sfgrid_ui.py index 4417809..c9c8582 100644 --- a/core/sfgrid/sfgrid_ui.py +++ b/core/sfgrid/sfgrid_ui.py @@ -21,6 +21,7 @@ class TradeTargetUI(ttk.Frame): self.stockCodeIdMap:dict[str, int] = {} self.strategy_ctrl:dict[int, SFGridStrategy] = {} # stock_code->trade_target self.targetMarketPrice: dict[int, float] = {} + self.targetAvgPrice: dict[int, float] = {} self.listening_stock = [] # 监控价格,默认值为10 self.monitor_price = 10.0 @@ -45,7 +46,9 @@ class TradeTargetUI(ttk.Frame): results = SFGridTradeTarget.select() for temp in results: tradeTarget:SFGridTradeTarget = temp - tradeTarget.current_position = qmtv.getStockPosition(tradeTarget.stock_code) # type: ignore + pos = qmtv.getStockPosition(tradeTarget.stock_code) + tradeTarget.current_position = 0 if pos is None else pos.volume # type: ignore + self.targetAvgPrice[tradeTarget.get_id()] = pos.avg_price if pos is not None else 0.0 self.updateTradeTarget(tradeTarget, True) # 初始化的时候 PrintLog(LogLevel.INFO, f'- [成功]交易标的信息初始化, 共 {len(self.tradeTargetData)} 个标的') @@ -69,10 +72,6 @@ class TradeTargetUI(ttk.Frame): # 使用用户设置的监控价格替代硬编码的10 if lastPrice == self.monitor_price or stock_code in self.listening_stock: # 发布市场数据更新事件用于市场监控 - market_target = SFGridTradeTarget() - market_target.stock_code = stock_code - market_target.stock_name = qmtv.getInstrumentName(stock_code) # type: ignore - market_target.market_price = lastPrice # type: ignore if stock_code not in self.listening_stock: self.listening_stock.append(stock_code) # 更新市场监控数据用于UI显示 @@ -343,7 +342,7 @@ class TradeTargetUI(ttk.Frame): f"{self.targetMarketPrice[id]:.3f}" if id in self.targetMarketPrice else '-', # "市场价" target.current_position, # "当前持仓" '-' if target.init_price is None else f"{target.init_price:.3f}", # "建仓成本" - '-', # if target.plan_buy_price is None else f"{target.plan_buy_price:.3f}", # "平均成本" + f"{self.targetAvgPrice[id]:.3f}" if id in self.targetMarketPrice else '-', # "平均成本" target.grid_match_count, # "网格匹配次数" f"{target.grid_total_profit:.3f}", # "网格收益" self.get_trade_enabled_indicator(target.enabled) # type: ignore @@ -368,7 +367,8 @@ class TradeTargetUI(ttk.Frame): values = self.trade_table.item(item)['values'] ctrl = self.strategy_ctrl[values[0]] PrintLog(LogLevel.DEBUG, f"双击查看详情: {values[0]} - {values[1]}") - PrintLog(LogLevel.DEBUG, f"双击查看详情 - 订单网格: \n{ctrl.orderGrid}") + PrintLog(LogLevel.DEBUG, f"双击查看详情 - 订单网格") + ctrl.printPendingOrder() def get_selected_target(self): """获取选中的交易标的""" @@ -725,133 +725,6 @@ class TradeTargetUI(ttk.Frame): 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): - """创建网格修正窗口""" - # 获取顶层窗口 - root = self.winfo_toplevel() - - # 创建顶层窗口 - correction_window = tk.Toplevel(root) - correction_window.title(f"网格修正 - {target.stock_code} ({target.stock_name})") - correction_window.geometry("500x400") - correction_window.resizable(False, False) - - # 设置窗口模态 - correction_window.transient(root) - correction_window.grab_set() - - # 居中显示 - root.update_idletasks() - x = root.winfo_x() + (root.winfo_width() // 2) - 250 - y = root.winfo_y() + (root.winfo_height() // 2) - 200 - correction_window.geometry(f"500x400+{x}+{y}") - - # 创建主框架 - main_frame = ttk.Frame(correction_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) - - # 创建修正选项框架 - options_frame = ttk.LabelFrame(main_frame, text="修正选项", padding=10) - options_frame.pack(fill=tk.X, pady=(0, 10)) - - # 网格序号 - grid_index_frame = ttk.Frame(options_frame) - grid_index_frame.pack(fill=tk.X, pady=5) - - ttk.Label(grid_index_frame, text="网格序号:", width=12).pack(side=tk.LEFT) - - # 网格序号调整控件 - grid_index_var = tk.IntVar(value=getattr(target, 'grid_index')) - - # 当前持仓量 - position_frame = ttk.Frame(options_frame) - position_frame.pack(fill=tk.X, pady=5) - - current_position_value = getattr(target, 'current_position') - ttk.Label(position_frame, text="当前持仓量:", width=12).pack(side=tk.LEFT) - ttk.Label(position_frame, text=str(current_position_value), width=10, anchor=tk.CENTER).pack(side=tk.LEFT, padx=5) - - # 需求持仓量 - required_position_frame = ttk.Frame(options_frame) - required_position_frame.pack(fill=tk.X, pady=5) - - grid_index_value = getattr(target, 'grid_index') - required_position = grid_index_value * target.grid_volume - ttk.Label(required_position_frame, text="需求持仓量:", width=12).pack(side=tk.LEFT) - required_position_label = ttk.Label(required_position_frame, text=str(required_position), width=10, anchor=tk.CENTER) - required_position_label.pack(side=tk.LEFT, padx=5) - - # 持仓量状态提示 - position_status_frame = ttk.Frame(options_frame) - position_status_frame.pack(fill=tk.X, pady=5) - - position_status_label = ttk.Label(position_status_frame, text="", foreground="red") - position_status_label.pack(side=tk.LEFT, padx=(12, 0)) - - # 初始化持仓量状态 - self.update_position_status(current_position_value, required_position, position_status_label) - - # 网格序号显示和按钮 - grid_index_label = ttk.Label(grid_index_frame, textvariable=grid_index_var, width=10, anchor=tk.CENTER) - grid_index_label.pack(side=tk.LEFT, padx=5) - - # 减少按钮 - ttk.Button(grid_index_frame, text="-", width=3, - command=lambda: self.decrease_grid_index(grid_index_var, target, required_position_label, position_status_label)).pack(side=tk.LEFT, padx=(5, 5)) - - # 增加按钮 - ttk.Button(grid_index_frame, text="+", width=3, - command=lambda: self.increase_grid_index(grid_index_var, len(target.getPriceGrid())-1, target, required_position_label, position_status_label)).pack(side=tk.LEFT, padx=(5, 0)) - - # 当前价格(实时更新) - price_frame = ttk.Frame(options_frame) - price_frame.pack(fill=tk.X, pady=5) - - ttk.Label(price_frame, text="当前价格:", width=12).pack(side=tk.LEFT) - current_price_var = tk.StringVar(value="-") - current_price_label = ttk.Label(price_frame, textvariable=current_price_var, width=10, anchor=tk.CENTER) - current_price_label.pack(side=tk.LEFT, padx=5) - - # 保存按钮框架 - button_frame = ttk.Frame(main_frame) - button_frame.pack(fill=tk.X, pady=(10, 0)) - - # 保存按钮 - ttk.Button(button_frame, text="确认修正", - command=lambda: self.save_grid_correction(correction_window, target, grid_index_var.get())).pack(side=tk.RIGHT, padx=5) - - # 取消按钮 - ttk.Button(button_frame, text="取消", - command=correction_window.destroy).pack(side=tk.RIGHT, padx=5) - - # 监听市场数据更新 - def on_market_data_update(updated_target: SFGridTradeTarget): - if updated_target.get_id() == target.get_id(): - current_price_var.set(f"{updated_target.market_price:.3f}" if updated_target.market_price else "-") - - # 订阅市场数据更新事件 - eBus.event_bus.subscribe(eBus.MarketDataUpdate, on_market_data_update) - - # 窗口关闭时取消订阅 - def on_window_close(): - if eBus.MarketDataUpdate in eBus.event_bus.listeners and on_market_data_update in eBus.event_bus.listeners[eBus.MarketDataUpdate]: - eBus.event_bus.listeners[eBus.MarketDataUpdate].remove(on_market_data_update) - correction_window.destroy() - - correction_window.protocol("WM_DELETE_WINDOW", on_window_close) - - # 初始化当前价格 - if target.market_price is not None: - current_price_var.set(f"{target.market_price:.3f}") def update_position_status(self, current_position: int, required_position: int, status_label: ttk.Label): """更新持仓量状态提示""" @@ -860,32 +733,6 @@ class TradeTargetUI(ttk.Frame): else: shortage = required_position - current_position status_label.config(text=f"还需补充 {shortage} 手仓位", foreground="red") - - def save_grid_correction(self, window, target: SFGridTradeTarget, new_grid_index: int): - """保存网格修正""" - # 更新网格序号 - setattr(target, 'grid_index', new_grid_index) - - # 重新计算需求持仓量 - required_position = new_grid_index * target.grid_volume - - # 检查持仓量是否满足要求 - current_position = getattr(target, 'current_position') - if current_position < required_position: - shortage = required_position - current_position - result = messagebox.askyesno( - "持仓量不足", - f"当前持仓量({current_position}手)小于需求持仓量({required_position}手)\n" - f"还需补充 {shortage} 手仓位,是否继续保存?" - ) - if not result: - return - - # 关闭窗口 - window.destroy() - - # 添加日志 - PrintLog(LogLevel.INFO, f"网格修正已保存: {target.stock_code} - {target.stock_name}, 网格序号: {new_grid_index}") def update_required_position_and_status(self, grid_index: int, target: SFGridTradeTarget, required_position_label: ttk.Label, position_status_label: ttk.Label): @@ -920,15 +767,9 @@ class TradeTargetUI(ttk.Frame): new_target = SFGridTradeTarget.create( stock_name=stock_name, stock_code=stock_code, - market_price=0.0, - current_position=pos, + current_position="0" if pos is None else str(pos.volume), grid_index=gridIndex, - last_trade_price=0.0, - plan_buy_price=0.0, - plan_sell_price=0.0, - current_order_price=0.0, - current_order_no='', - current_order_type='', + init_price=0.0, status=-1 ) # 更新标的池