4. 实时行情订阅与推送
任务目标
本案例实现一个最小可用的实时行情订阅与推送系统:从行情源订阅指定交易标的的实时Tick数据,并将数据推送给下游策略引擎进行消费。系统采用发布-订阅模式,行情源与策略引擎通过一个线程安全的中间通道解耦,策略引擎无需关心行情源的具体实现。
核心实现
import threading
import queue
import time
import random
from dataclasses import dataclass
from typing import List, Callable, Dict
@dataclass
class TickData:
symbol: str
price: float
volume: int
timestamp: float
class MarketDataFeed:
"""模拟行情源:按固定频率产生随机Tick数据"""
def __init__(self, symbols: List[str], interval: float = 0.5):
self.symbols = symbols
self.interval = interval
self._running = False
self._thread = None
def start(self, callback: Callable[[TickData], None]):
self._running = True
self._thread = threading.Thread(target=self._generate, args=(callback,), daemon=True)
self._thread.start()
def stop(self):
self._running = False
if self._thread:
self._thread.join(timeout=1)
def _generate(self, callback: Callable[[TickData], None]):
base_prices = {s: random.uniform(10, 500) for s in self.symbols}
while self._running:
for sym in self.symbols:
# 模拟价格随机游走
base_prices[sym] *= (1 + random.uniform(-0.005, 0.005))
tick = TickData(
symbol=sym,
price=round(base_prices[sym], 2),
volume=random.randint(1, 100),
timestamp=time.time()
)
callback(tick)
time.sleep(self.interval)
class TickDispatcher:
"""线程安全的行情分发通道:订阅-发布模式"""
def __init__(self):
self._subscribers: Dict[str, List[Callable[[TickData], None]]] = {}
self._lock = threading.Lock()
def subscribe(self, symbol: str, handler: Callable[[TickData], None]):
with self._lock:
if symbol not in self._subscribers:
self._subscribers[symbol] = []
self._subscribers[symbol].append(handler)
def publish(self, tick: TickData):
with self._lock:
handlers = list(self._subscribers.get(tick.symbol, []))
for h in handlers:
h(tick)
class StrategyEngine:
"""策略引擎:订阅行情并处理Tick"""
def __init__(self, dispatcher: TickDispatcher, symbols: List[str]):
self.dispatcher = dispatcher
self.symbols = symbols
self.received_count = 0
self.last_prices: Dict[str, float] = {}
def start(self):
for sym in self.symbols:
self.dispatcher.subscribe(sym, self.on_tick)
def on_tick(self, tick: TickData):
# 策略处理逻辑:记录最新价格并计数
self.last_prices[tick.symbol] = tick.price
self.received_count += 1
def main():
symbols = ["AAPL", "MSFT"]
dispatcher = TickDispatcher()
engine = StrategyEngine(dispatcher, symbols)
engine.start()
feed = MarketDataFeed(symbols, interval=0.2)
feed.start(dispatcher.publish)
# 运行3秒后停止
time.sleep(3)
feed.stop()
print(f"策略引擎收到Tick总数: {engine.received_count}")
for sym in symbols:
print(f"{sym} 最新价格: {engine.last_prices.get(sym, 'N/A')}")
if __name__ == "__main__":
main()
运行方式
python market_data_pipeline.py
验证方法
程序运行约3秒后自动退出,观察输出:
策略引擎收到Tick总数应大于0,说明行情数据成功从数据源推送至策略引擎;- 每个标的的
最新价格应显示为有效浮点数(非N/A),说明策略引擎正确解析并存储了对应标的的行情; - 多次运行程序,每次输出的价格和数量不同,说明行情源在持续产生动态数据。
关键设计说明
本案例的核心解耦点在于TickDispatcher:行情源只调用publish推送数据,策略引擎通过subscribe注册回调,两者互不感知。实际生产环境中,可将MarketDataFeed替换为交易所或券商API的行情回调,将StrategyEngine.on_tick替换为真实策略逻辑,中间的分发层无需改动。若需跨进程推送,可将TickDispatcher替换为消息队列(如ZeroMQ、Kafka)的发布-订阅实现,接口保持一致即可。