This commit is contained in:
2026-06-24 11:02:42 +08:00
parent c59c1b9fc4
commit ee8fc60eae
7 changed files with 105 additions and 16 deletions
+15 -1
View File
@@ -11,13 +11,27 @@ EventPrintLog = "print_log" # 打印日志
class EventBus:
def __init__(self):
self.listeners = {} # 管理各种event的订阅情况
self.last_events = {} # 存储每个事件的最后一次值,用于"重播"给新订阅者
def subscribe(self, event_type, listener):
def subscribe(self, event_type, listener, replay=True):
"""订阅事件
Args:
event_type: 事件类型
listener: 回调函数
replay: 是否自动重播最近一次事件状态(默认True)
"""
if event_type not in self.listeners:
self.listeners[event_type] = []
self.listeners[event_type].append(listener)
# 新订阅者自动收到最近一次事件状态(如果存在)
if replay and event_type in self.last_events:
listener(self.last_events[event_type])
def publish(self, event_type, data):
# 存储最后一次事件值
self.last_events[event_type] = data
if event_type in self.listeners:
for listener in self.listeners[event_type]:
listener(data)
+15 -8
View File
@@ -545,11 +545,19 @@ class RealQmtV:
PrintLog(LogLevel.ERROR, f'- [市场数据订阅失败-{e}]')
def _on_market_data(self, datas: dict):
"""xtquant 行情回调 — 收到行情即标记市场活跃"""
"""xtquant 行情回调 — 收到行情即标记市场活跃(但需满足 09:15 后才激活)"""
self.lastMarketDataUpdateTimestamp = time.time()
if not self.isMarketActive:
self.isMarketActive = True
eBus.event_bus.publish(eBus.EventMarketActiveSwitch, True)
# 检查当前时间是否已过 09:15(集合竞价结束后才激活市场状态)
import zoneinfo
beijing_tz = zoneinfo.ZoneInfo("Asia/Shanghai")
now = datetime.datetime.now(beijing_tz)
t = now.time()
activation_time = datetime.time(9, 15)
if t >= activation_time:
self.isMarketActive = True
eBus.event_bus.publish(eBus.EventMarketActiveSwitch, True)
PrintLog(LogLevel.INFO, f'- [行情] 市场激活 (时间 {t.strftime("%H:%M:%S")} >= 09:15)')
eBus.event_bus.publish(eBus.MarketDataUpdate, datas)
def _is_trading_time(self) -> bool:
@@ -567,17 +575,16 @@ class RealQmtV:
return (morning_start <= t <= morning_end) or (afternoon_start <= t <= afternoon_end)
def _market_data_watchdog(self):
"""行情活跃监控 — 超过 120 秒无行情 且 非交易时间 则标记市场不活跃"""
"""行情活跃监控 — 超过 120 秒无行情 则标记市场不活跃(无论是否交易时间)"""
while True:
time.sleep(15)
if self.isMarketActive:
elapsed = time.time() - self.lastMarketDataUpdateTimestamp
# 非交易时间直接标记休市;交易时间内才用超时判断
if not self._is_trading_time() or elapsed > 120:
# 只有超过 120 秒无行情才标记不活跃,不再区分交易时间
if elapsed > 120:
self.isMarketActive = False
eBus.event_bus.publish(eBus.EventMarketActiveSwitch, False)
if not self._is_trading_time():
PrintLog(LogLevel.INFO, '- [行情] 非交易时间,市场标记为不活跃')
PrintLog(LogLevel.INFO, f'- [行情] 超过 {elapsed:.0f} 秒无数据,市场标记为不活跃')
def stopMarketDataSubscription(self):
"""停止市场数据订阅"""
+20 -6
View File
@@ -54,6 +54,11 @@ class SFGridStrategy:
"""
self.tradeTarget: model.SFGridTradeTarget = tradeTarget
# 数据更新锁:保护 orderGrid 和 tradeTarget 的并发访问
# QMT 回调在独立线程中触发,必须在可能触发回调的操作之前创建
# 注意:这个锁必须在订阅事件之前创建,防止事件在初始化期间触发
self.dataUpdateLock = threading.Lock()
# 订阅事件总线:监听订单创建、成交、失败三种事件
event_bus.subscribe(eBus.MarketOrderCreated, self.onOrderCreateAsync)
event_bus.subscribe(eBus.MarketOrderTraded, self.onOrderTrade)
@@ -75,10 +80,6 @@ class SFGridStrategy:
# 加载券商侧已存在的未成交订单,恢复到 orderGrid 中
self.loadExistOrders()
# 数据更新锁:保护 orderGrid 和 tradeTarget 的并发访问
# QMT 回调在独立线程中触发,必须在可能触发回调的操作之前创建
self.dataUpdateLock = threading.Lock()
# 根据数据库中的 enabled 字段决定是否启动交易
self.enabledTrading(tradeTarget.enabled) # type: ignore
@@ -122,6 +123,9 @@ class SFGridStrategy:
当市场数据从不可用变为可用时,如果策略已启用则刷新网格订单。
"""
PrintLog(LogLevel.INFO,
f'|- [市场状态切换] 标的{self.tradeTarget.targetName()} '
f'isActive={isActive}, enabled={self.tradeTarget.enabled}')
if isActive and self.tradeTarget.enabled:
self.refreshGridOrder()
@@ -300,7 +304,12 @@ class SFGridStrategy:
self.tradeTarget.enabled = False # type: ignore
# 刷新网格订单(空仓只挂买单,有仓买卖对冲)
self.refreshGridOrder()
# 只有市场活跃时才下单,收盘后不再尝试下单
if qmtv.isMarketActive:
self.refreshGridOrder()
else:
PrintLog(LogLevel.INFO,
f' |- 市场已休市,跳过刷新网格订单')
else:
# ── 停用交易: 取消所有未成交订单 ──
@@ -465,7 +474,12 @@ class SFGridStrategy:
PrintLog(LogLevel.INFO,
f' 手续费 : {trade.commission:.3f}')
# 4. 刷新网格订单:在新的 grid_index 位置重新挂买卖单
self.refreshGridOrder()
# 只有市场活跃时才下单,收盘后不再尝试下单
if qmtv.isMarketActive:
self.refreshGridOrder()
else:
PrintLog(LogLevel.INFO,
f'|- 成交后市场已休市,跳过刷新网格订单')
finally:
self.dataUpdateLock.release()
+3
View File
@@ -1279,8 +1279,11 @@ class QmtApp:
self._refresh_ui()
def _on_market_active(self, is_active: bool):
from core.logger import PrintLog, LogLevel
PrintLog(LogLevel.INFO, f'[UI] 收到市场状态切换事件 is_active={is_active}')
self._update_market_status(is_active)
self.page.update()
PrintLog(LogLevel.INFO, f'[UI] 市场状态已更新,_market_label={self._market_label.value}')
def _update_market_status(self, active: bool):
"""更新顶栏市场状态指示灯"""