# coding:utf-8 from core.sfgrid.model import TradeTarget from core.eventbus import ActionDisableMarketData, ActionEnableMarketData, ActionEventAddTradeTarget, ActionEventDeleteTradeTarget, ActionEventDisableTrade, ActionEventEnableTrade, EventTradeTargetUpdate, MarketDataUpdate, MarketDataEnabled, MarketDataDisabled, ResultEventTradeDisabled, ResultEventTradeEnabled, ResultEventTradeTargetDeleted, ActionEventGridFix, event_bus from xtquant.xttrader import XtQuantTrader import time from peewee import ModelSelect import core.sfgrid.model as model import sfgrid_config from core.sfgrid.sfgrid_strategy import SFGridStrategy from core.util import getInstrumentName, getStockPosition, queryPendingOrder from xtquant.xttrader import XtQuantTrader from xtquant.xttype import StockAccount, XtAsset, XtOrder, XtPosition, XtTrade from xtquant import xtdata from xtquant.xttrader import XtQuantTraderCallback import datetime import core.sfgrid.ui as ui from core.logger import PrintLog, LogLevel from core.sfgrid.objects import GridFixData # 量化核心控制对象 class MainController(XtQuantTraderCallback): def __init__(self, account_no: str, miniQmtPath: str): super().__init__() self.registerEventHandler() # self.appUi = ui.TradeTargetUI() xtdata.enable_hello = False session_id = int(time.time()) self.xt_trader: XtQuantTrader = XtQuantTrader(miniQmtPath, session_id) self.xt_trader.register_callback(self) self.xt_trader.start() self.xt_trader.connect() PrintLog(LogLevel.INFO, f'- [{'成功' if self.xt_trader.connected else '失败'}]市场交易连接: {miniQmtPath}') if self.xt_trader.connected == False: self.inited: bool = False return else: self.inited = True self.account= StockAccount(account_no, 'STOCK') PrintLog(LogLevel.INFO, f'- [成功]交易账号对象初始化完成, 账号: {self.account.account_id}') # pyright: ignore[reportAttributeAccessIssue] subscribe_result = self.xt_trader.subscribe(self.account) PrintLog(LogLevel.INFO, f'- [{'成功' if subscribe_result == 0 else '失败'}:{subscribe_result}]交易状态订阅') if subscribe_result == 0: self.inited = True else: self.inited = False return self.listening_stock = [] self.stock_trade_ctrl = {} self.init_instrument_pool(self.xt_trader, self.account) # type: ignore self.seq = None PrintLog(LogLevel.INFO, '- [成功]三疯交易系统初始化完成') self.startMarketData() def registerEventHandler(self): event_bus.subscribe(ActionEventEnableTrade, self.onEnableTrade) event_bus.subscribe(ActionEventDisableTrade, self.onDisableTrade) event_bus.subscribe(ActionEnableMarketData, self.onMarketDataEnabled) event_bus.subscribe(ActionDisableMarketData, self.onMarketDataDisabled) event_bus.subscribe(ActionEventAddTradeTarget, self.onAddTradeTarget) event_bus.subscribe(ActionEventDeleteTradeTarget, self.onDeleteTradeTarget) event_bus.subscribe(ActionEventGridFix, self.onGridFix) def onDeleteTradeTarget(self, id: int): """处理删除交易标的事件""" self.del_trade_target(id) # 发布删除完成事件 event_bus.publish(ResultEventTradeTargetDeleted, id) def onAddTradeTarget(self, stock_code: str): """处理添加交易标的事件""" self.add_trade_target(stock_code) def onMarketDataEnabled(self, data): """处理市场数据监听启用事件""" self.startMarketData() def onMarketDataDisabled(self, data): """处理市场数据监听禁用事件""" self.stopMarketData() def onEnableTrade(self, id: int): self.start_stock_trade(id) def onDisableTrade(self, id: int): self.pause_stock_trade(id) def onGridFix(self, data: GridFixData): """处理网格修正事件""" self.update_trade_target_grid(data) def update_trade_target_grid(self, data: GridFixData): """更新交易标的网格信息""" try: target = data.tradeTarget grid_index = data.grid_index # 更新数据库中的网格索引 target.grid_index = grid_index target.save() # 更新内存中的交易标的 if target.get_id() in self.instrument_pool: self.instrument_pool[target.get_id()] = target # 更新交易控制器中的网格信息 if target.stock_code in self.stock_trade_ctrl: trade_controller: SFGridStrategy = self.stock_trade_ctrl[target.stock_code] trade_controller.updateGridIndex(grid_index) # type: ignore PrintLog(LogLevel.INFO, f"网格修正已应用: {target.stock_code} - {target.stock_name}, 网格索引: {grid_index}") except Exception as e: PrintLog(LogLevel.ERROR, f"网格修正更新失败: {str(e)}") def startMarketData(self): PrintLog(LogLevel.INFO, '- 启动市场数据订阅') # self.seq = xtdata.subscribe_whole_quote(['SH', 'SZ'], callback=self.onDataUpdate) if self.seq == -1: PrintLog(LogLevel.ERROR, '- 市场数据订阅失败') else: event_bus.publish(MarketDataEnabled, True) PrintLog(LogLevel.INFO, f'- 市场数据订阅成功, 订阅号={self.seq}') def stopMarketData(self): PrintLog(LogLevel.INFO, '- 停止市场数据订阅') if self.seq is not None and self.seq > 0: xtdata.unsubscribe_quote(self.seq) event_bus.publish(MarketDataDisabled, False) def add_trade_target(self, stock_code: str): try: stock_name = getInstrumentName(stock_code) if not stock_name: PrintLog(LogLevel.ERROR, f'无法获取股票代码 {stock_code} 的名称,请检查代码是否正确') return # 检查是否已存在该标的 existing_target = model.TradeTarget.get_or_none(model.TradeTarget.stock_code == stock_code) if existing_target: PrintLog(LogLevel.INFO, f'交易标的 {stock_code} {stock_name} 已存在') return new_target = model.TradeTarget.create( stock_name=stock_name, stock_code=stock_code, market_price=0.0, current_position=0, grid_index=0, 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='' ) new_target.save() PrintLog(LogLevel.INFO, f'新增交易标的 {stock_code} {stock_name}, {new_target.id}') # 刷新标的持仓 pos = getStockPosition(stock_code, self.xt_trader, self.account) # type: ignore model.TradeTarget.update(current_position=pos).where(model.TradeTarget.stock_code == stock_code).execute() # 更新标的池 self.refresh_targets() # 添加交易控制器 stockTradeController = SFGridStrategy(new_target, self.xt_trader, self.account) # type: ignore self.stock_trade_ctrl[stock_code] = stockTradeController except Exception as e: PrintLog(LogLevel.ERROR, f'新增交易标的失败 {stock_code} {e}') def del_trade_target(self, id:int): try: # 检查标的是否存在 if id not in self.instrument_pool: PrintLog(LogLevel.ERROR, f"交易标的 ID {id} 不存在") return target: model.TradeTarget = self.instrument_pool[id] # 如果存在交易控制器,先停止交易 if target.stock_code in self.stock_trade_ctrl: # 停止交易控制器 del self.stock_trade_ctrl[target.stock_code] # 从数据库中删除 target.delete_instance() # 从内存中删除 del self.instrument_pool[id] # 刷新标的池 self.refresh_targets() PrintLog(LogLevel.INFO, f"已删除交易标的: {target.stock_code} - {target.stock_name}") except Exception as e: PrintLog(LogLevel.ERROR, f"删除交易标的失败 ID {id}: {str(e)}") def init_instrument_pool(self, xtTrader:XtQuantTrader, account:StockAccount): self.refresh_targets() for id in self.instrument_pool: target:TradeTarget = self.instrument_pool[id] status = "新建" if target.status == 0 else "已建初始仓" PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {target.stock_code}-{target.stock_name} 当前持仓: {getStockPosition(target.stock_code, self.xt_trader, self.account)} 网格索引: {target.grid_index} 基准价格 {sfgrid_config.grid_price[target.grid_index]} 状态: {status} 启用交易线程: {'自动交易中' if target.enabled else '交易已停止'}') # type: ignore tradeTarget:model.TradeTarget = self.instrument_pool[id] tradeTarget.current_position = getStockPosition(tradeTarget.stock_code, xtTrader, account) # type: ignore result = tradeTarget.save() PrintLog(LogLevel.INFO, f' |- 同步[{target.stock_code}-{target.stock_name}]持仓信息[{'成功' if result == 1 else '失败'}]') stockTradeController = SFGridStrategy(tradeTarget, self.xt_trader, self.account) # type: ignore self.stock_trade_ctrl[tradeTarget.stock_code] = stockTradeController event_bus.publish(EventTradeTargetUpdate, tradeTarget) PrintLog(LogLevel.INFO, f'- [成功]交易标的信息初始化, 共 {len(self.instrument_pool)} 个标的') def refresh_targets(self): # 更新标的池 results:ModelSelect = model.TradeTarget.select() self.instrument_pool: dict[int, model.TradeTarget] = {} for temp in results: result :model.TradeTarget = temp self.instrument_pool[result.get_id()] = result def print_position_info(self): positions:list[XtPosition] = self.xt_trader.query_stock_positions(self.account) if positions: PrintLog(LogLevel.INFO, "\n- 持仓信息") for temp in positions: pos : XtPosition = temp if pos.volume <=0: continue PrintLog(LogLevel.INFO, f"股票代码: {pos.stock_code}-{getInstrumentName(pos.stock_code)}") PrintLog(LogLevel.INFO, f"总持仓: {pos.volume}") PrintLog(LogLevel.INFO, f"可用持仓: {pos.can_use_volume}") PrintLog(LogLevel.INFO, f"持仓成本: {pos.avg_price}") PrintLog(LogLevel.INFO, "---") else: PrintLog(LogLevel.INFO, "\n当前无持仓") def print_account_info(self): temp = self.xt_trader.query_stock_asset(self.account) asset: XtAsset = temp # type: ignore PrintLog(LogLevel.INFO, f"=== 账户信息 {self.account.account_id} ===") # type: ignore PrintLog(LogLevel.INFO, f"可用资金: {asset.cash}") PrintLog(LogLevel.INFO, f"总资产: {asset.total_asset}") PrintLog(LogLevel.INFO, f"证券市值: {asset.market_value}") def print_stock_orders(self): orders = self.xt_trader.query_stock_orders(self.account, cancelable_only=True) if orders: PrintLog(LogLevel.INFO, "\n=== 委托信息 ===") for order in orders: PrintLog(LogLevel.INFO, f"委托编号: {order.order_id}") PrintLog(LogLevel.INFO, f"股票代码: {order.stock_code} {getInstrumentName(order.stock_code)}") PrintLog(LogLevel.INFO, f"委托方向: {order.offset_flag} ") PrintLog(LogLevel.INFO, f"委托价格: {order.price}") PrintLog(LogLevel.INFO, f"委托数量: {order.order_volume}") PrintLog(LogLevel.INFO, f"已成交数量: {order.traded_volume}") PrintLog(LogLevel.INFO, f"委托状态: {order.order_status} ") PrintLog(LogLevel.INFO, "---") else: PrintLog(LogLevel.INFO, "\n当前无委托记录") # 初始化指定标的交易控制器 def start_stock_trade(self, id: int): tradeTarget: TradeTarget = self.instrument_pool[id] # check existing thread if tradeTarget.stock_code in self.stock_trade_ctrl: tradeController: SFGridStrategy = self.stock_trade_ctrl[tradeTarget.stock_code] tradeTarget = tradeController.enabledTrading(True) self.instrument_pool[id] = tradeTarget event_bus.publish(ResultEventTradeEnabled, tradeTarget) else: PrintLog(LogLevel.INFO, f"\t创建标的交易控制器 {tradeTarget.stock_code} {getInstrumentName(tradeTarget.stock_code)}") def pause_stock_trade(self, id: int): localTarget: model.TradeTarget = self.instrument_pool[id] print(f'暂停标的交易 {localTarget.stock_code} - enabled {localTarget.enabled}') if localTarget.stock_code in self.stock_trade_ctrl: tradeController: SFGridStrategy = self.stock_trade_ctrl[localTarget.stock_code] tradeTarget = tradeController.enabledTrading(False) orders = queryPendingOrder(localTarget.stock_code, tradeController.getName(), self.xt_trader, self.account) # type: ignore for order in orders: self.xt_trader.cancel_order_stock_async(self.account, order.order_id) print(f'取消未成交订单 {len(orders)}') self.instrument_pool[id] = tradeTarget event_bus.publish(ResultEventTradeDisabled, tradeTarget) else: print(f"标的交易控制器不存在 {localTarget.stock_code} {localTarget.stock_name}\n")