多策略组合系统的架构演进:从单策略到全天候

多策略组合系统的架构演进:从单策略到全天候

阶段一:单策略裸奔

最早的量化系统就是一根筋:一个策略,一个进程,定时拉数据、算信号、发单。

python 复制代码
import time
import pandas as pd
from datetime import datetime

class SingleStrategyRunner:
    def __init__(self, strategy, broker):
        self.strategy = strategy
        self.broker = broker

    def run(self):
        while True:
            now = datetime.now()
            # 收盘后计算信号
            if now.hour == 15 and now.minute == 5:
                data = self.broker.get_history(days=120)
                signal = self.strategy.generate_signal(data)
                if signal == 1:
                    self.broker.buy(target=0.95)
                elif signal == -1:
                    self.broker.sell_all()
            time.sleep(30)

问题清单:

  • 策略逻辑与执行逻辑耦合,改一行代码要停整个进程
  • 没有状态管理,进程挂了持仓状态全靠券商接口反查
  • 单点故障,一次异常退出就彻底躺平
  • 无法回测与实盘共用同一套代码,两边逻辑经常漂移

这个阶段的核心矛盾是耦合。策略、执行、调度、状态全混在一起,任何一处改动都可能引发连锁反应。

阶段二:多策略并行------解耦与隔离

当手里攒了三五个策略,第一反应是"多开几个进程"。但这只是把单点故障变成了多点故障。

真正的演进方向是事件驱动 + 策略隔离。核心思路:把每个策略封装成独立单元,通过统一的事件总线通信,状态由中央账本管理。

python 复制代码
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Dict
import queue
import threading

@dataclass
class SignalEvent:
    strategy_id: str
    symbol: str
    direction: int  # 1 买, -1 卖, 0 平
    weight: float
    timestamp: float

class Strategy(ABC):
    def __init__(self, strategy_id: str):
        self.strategy_id = strategy_id

    @abstractmethod
    def on_data(self, data: pd.DataFrame) -> list[SignalEvent]:
        ...

class StrategyEngine:
    """每个策略跑在独立线程,产出信号事件"""
    def __init__(self, bus: queue.Queue):
        self.bus = bus
        self.strategies: Dict[str, Strategy] = {}
        self._stop = threading.Event()

    def register(self, strategy: Strategy):
        self.strategies[strategy.strategy_id] = strategy

    def _run_one(self, strategy: Strategy):
        while not self._stop.is_set():
            data = self._fetch_data(strategy.strategy_id)
            if data is None or data.empty:
                time.sleep(1)
                continue
            for event in strategy.on_data(data):
                self.bus.put(event)
            time.sleep(5)

    def start(self):
        for s in self.strategies.values():
            threading.Thread(target=self._run_one, args=(s,), daemon=True).start()

    def _fetch_data(self, sid: str):
        # 实际项目替换为行情源
        return None

中央账本负责汇总所有策略的目标仓位,做净额处理:

python 复制代码
class PortfolioLedger:
    """汇总多策略信号,输出净目标仓位"""
    def __init__(self):
        self.target: Dict[str, float] = {}
        self.lock = threading.Lock()

    def apply(self, event: SignalEvent):
        with self.lock:
            key = event.symbol
            delta = event.direction * event.weight
            self.target[key] = self.target.get(key, 0.0) + delta
            # 单标的敞口上限
            self.target[key] = max(-1.0, min(1.0, self.target[key]))

    def snapshot(self) -> Dict[str, float]:
        with self.lock:
            return dict(self.target)

这一阶段解决的问题:

  • 策略之间互不干扰,一个策略抛异常不影响其他策略
  • 信号与执行分离,执行层只认净目标仓位
  • 回测和实盘共用 Strategy 接口,逻辑不再漂移

新暴露的问题:

  • 多策略同时满仓时,总仓位可能超过 100%,杠杆失控
  • 策略之间相关性高,回撤叠加,组合并没有真正分散
  • 没有资金分配机制,谁先发信号谁占坑

阶段三:资金分配与风险预算

多策略并行不等于多策略组合。组合的关键在于资金如何在策略之间分配。

最朴素的做法是等权,但等权忽略了策略的波动率差异。更合理的是风险平价(Risk Parity):让每个策略对组合的风险贡献相等。

python 复制代码
import numpy as np

def risk_parity_weights(returns: pd.DataFrame, lookback: int = 60) -> pd.Series:
    """
    returns: 每列一个策略的日收益率
    返回: 各策略权重,使风险贡献相等
    """
    recent = returns.tail(lookback).dropna()
    cov = recent.cov().values
    n = cov.shape[0]

    # 迭代求解风险平价权重
    w = np.ones(n) / n
    for _ in range(500):
        port_var = w @ cov @ w
        marginal = cov @ w
        rc = w * marginal / np.sqrt(port_var)  # 风险贡献
        target = rc.mean()
        w = w * (target / rc)
        w = w / w.sum()
    return pd.Series(w, index=returns.columns)


def apply_vol_target(weights: pd.Series, returns: pd.DataFrame,
                     target_vol: float = 0.15) -> pd.Series:
    """在风险平价基础上叠加波动率目标"""
    port_ret = (returns.tail(60) * weights).sum(axis=1)
    realized = port_ret.std() * np.sqrt(252)
    scale = target_vol / realized if realized > 0 else 1.0
    return weights * min(scale, 2.0)  # 杠杆上限 2 倍

在 PortfolioLedger 里引入权重层:

python 复制代码
class WeightedLedger(PortfolioLedger):
    def __init__(self, weights: Dict[str, float]):
        super().__init__()
        self.weights = weights  # strategy_id -> 资金权重

    def apply(self, event: SignalEvent):
        w = self.weights.get(event.strategy_id, 0.0)
        scaled = SignalEvent(
            strategy_id=event.strategy_id,
            symbol=event.symbol,
            direction=event.direction,
            weight=event.weight * w,
            timestamp=event.timestamp,
        )
        super().apply(scaled)

架构对比:

维度 阶段一 阶段二 阶段三
策略隔离 无 线程级 线程级 + 权重隔离
资金管理 全仓 净额封顶 风险平价 + 波动目标
故障影响 全局 单策略 单策略
回撤特征 单策略回撤 相关性叠加 风险分散

阶段四:全天候------状态机与动态切换

到了这一步,系统能跑多策略、能分配资金,但还有一个致命问题:策略有生命周期。

趋势策略在震荡市里连续亏损,套利策略在流动性枯竭时失效。如果一直给所有策略分配资金,等于在无效环境里持续送钱。

全天候的核心不是"永远满仓",而是根据市场状态动态调整策略权重。

python 复制代码
from enum import Enum

class Regime(Enum):
    TREND_UP = "trend_up"
    TREND_DOWN = "trend_down"
    RANGE = "range"
    HIGH_VOL = "high_vol"

class RegimeDetector:
    """基于波动率和趋势强度识别市场状态"""
    def __init__(self, lookback: int = 20):
        self.lookback = lookback

    def detect(self, prices: pd.Series) -> Regime:
        ret = prices.pct_change().tail(self.lookback)
        vol = ret.std() * np.sqrt(252)

        # 趋势强度:ADX 简化版
        ma_fast = prices.tail(5).mean()
        ma_slow = prices.tail(20).mean()
        trend_strength = abs(ma_fast - ma_slow) / ma_slow

        if vol > 0.35:
            return Regime.HIGH_VOL
        if trend_strength > 0.03:
            return Regime.TREND_UP if ma_fast > ma_slow else Regime.TREND_DOWN
        return Regime.RANGE


# 策略在不同状态下的权重系数
REGIME_MULTIPLIER = {
    Regime.TREND_UP:   {"trend": 1.5, "mean_rev": 0.3, "arb": 1.0},
    Regime.TREND_DOWN: {"trend": 1.5, "mean_rev": 0.3, "arb": 1.0},
    Regime.RANGE:      {"trend": 0.3, "mean_rev": 1.5, "arb": 1.0},
    Regime.HIGH_VOL:   {"trend": 0.5, "mean_rev": 0.5, "arb": 0.8},
}

把状态检测接入权重层,形成闭环:

python 复制代码
class AllWeatherLedger(WeightedLedger):
    def __init__(self, base_weights, strategy_types, detector):
        super().__init__(base_weights)
        self.strategy_types = strategy_types  # strategy_id -> "trend"/"mean_rev"/"arb"
        self.detector = detector
        self.current_regime = Regime.RANGE

    def update_regime(self, prices: pd.Series):
        new_regime = self.detector.detect(prices)
        if new_regime != self.current_regime:
            self.current_regime = new_regime
            self._rebalance_weights()

    def _rebalance_weights(self):
        mult = REGIME_MULTIPLIER[self.current_regime]
        raw = {
            sid: self.weights[sid] * mult.get(self.strategy_types[sid], 1.0)
            for sid in self.weights
        }
        total = sum(raw.values())
        self.weights = {k: v / total for k, v in raw.items()}

全天候架构的四个层次:

复制代码
┌─────────────────────────────────────┐
│  数据层:行情 + 状态检测             │
├─────────────────────────────────────┤
│  策略层:独立线程,事件驱动输出信号   │
├─────────────────────────────────────┤
│  组合层:风险平价 + 状态权重调整      │
├─────────────────────────────────────┤
│  执行层:净额下单 + 状态持久化        │
└─────────────────────────────────────┘

工程上的几个坑

1. 状态持久化。 进程重启后必须恢复持仓、权重、当前市场状态。用 SQLite 或 Redis 都行,关键是要有 snapshot() 和 restore() 对称接口。

2. 时钟对齐。 多策略跑在不同线程,信号时间戳必须统一到同一时钟源,否则净额计算会出现时序错乱。

3. 回测与实盘的一致性。 状态检测、权重计算、净额处理这三层必须在回测里完整复现。很多组合回测好看实盘拉胯,就是因为回测只跑了策略层,没跑组合层。

4. 权重调整的频率。 状态检测太频繁会导致权重抖动,交易成本爆炸。实践中用日频检测、周频调权,配合最小变动阈值。

演进路线总结

从单策略到全天候,本质是不断把隐式假设显式化的过程:

  • 阶段一假设"只有一个策略",阶段二把它变成"多策略隔离"
  • 阶段二假设"资金平均分配",阶段三把它变成"风险预算"
  • 阶段三假设"策略永远有效",阶段四把它变成"状态自适应"

每一步演进都不是为了架构好看,而是因为上一层的假设在实盘中被打脸了。架构演进的驱动力永远来自真实的亏损和故障,而不是设计模式。

代码层面,核心是保持 Strategy 接口稳定,让组合层和执行层可以独立演化。策略作者只关心 on_data 返回什么信号,不需要知道资金怎么分配、状态怎么切换。这种分层是系统能持续演进的前提。

更多内容请关注本站。

相关推荐
梦帮科技14 小时前
Kali Linux Scapy 畸形报文构造学——深度解剖非法 TCP Flags 与反常序列号
linux·爬虫·神经网络·算法·数学建模·系统架构·模拟退火算法
quantdash_cc18 小时前
量化数据清洗应该放在数据层还是策略层?从系统架构看清职责边界
开发语言·python·数据分析·系统架构·量化交易·股票数据·quantdash
ESDWAN1 天前
跨境投资与全球资产托管数字化解决方案:系统架构、数据治理与合规风控实践
大数据·金融·系统架构·云计算
带金箍的至尊宝1 天前
系统架构设计师笔记 08:UML 用例图、类图、时序图
笔记·系统架构·uml
9624561 天前
餐饮 SaaS 优惠券系统架构演进(八)PriceQuote 版本化与回放测试:Golden Case、Shadow Diff、策略灰度与历史价格审计
系统架构
wuyk5551 天前
样章-第03讲-FreeRTOS在16KB-RAM上的裁剪与陷阱
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网·系统架构
一切皆是因缘际会2 天前
同源世界系统:兼备量子系统与信息几何功能星际通信零延迟
人工智能·ai·系统架构·信息与通信·星际通信·量子系统·信息几何
微三云生态系统架构师-彭丹2 天前
GEO信源矩阵分发系统?多平台内容投喂与AI引用率提升
系统架构·大模型应用·geo系统·内容矩阵·geo代运营服务·信源分发·ai引用率
Jmyd01232 天前
智慧文博3D扫描系统架构:从采集到平台的全链路方案
3d·系统架构·3d扫描·智慧文博