市场水温算法设计方案:多因子热力累积模型v2

市场水温算法设计方案:多因子热力累积模型 v2

Market Temperature — Heat Accumulation Index (HAI)

一、背景与动机

1.1 核心观察

在加密市场中存在一个"水温"现象:

  • 当锚定资产(BTC)价格持续运行在高位时,ALT 资产的 Beta 系数、相关性、跟随性会发生相变
  • 高温持续 → ALT 表现暴躁:Beta 跳变、跟随性增强、波动幅度夸张
  • 低温持续 → ALT 表现迟钝:Beta 低迷、跟随性差、情绪摆烂

关键特征:不是瞬时温度决定行为,而是温度在高位维持足够长时间后,行情才发生质变。

1.2 在量化金融中的对应

这个现象在学术界对应 Market Regime Detection(市场状态识别)

方法 优点 缺点 适用性
HMM(隐马尔可夫) 学术主流,概率输出,可在线化(Particle Filter HMM) 状态数需预设,标准实现需离线训练 可作为长期升级路径,但当前架构不需要
Markov Switching 经济学标准,Hamilton (1989) 计算较重,宏观频率,非 5m 级别 不适合:时间尺度不匹配
Hawkes Process(自激点过程) 天然建模"热量累积→爆发",金融传染领域前沿 实现复杂,参数估计需 MLE 可作为 P3 升级路径
Absorption Ratio (Kritzman 2010) PCA 测量系统性风险集中度,前沿 需多币种收益率矩阵 纳入因子 6
深度学习(VAE/Transformer) 理论上限高 需训练数据/GPU,黑箱 不适合:过度工程
多因子 Leaky Integrator 增量计算,可解释,零训练,自然均衡 权重需调优(本方案用自适应权重解决) 最适合:完美匹配现有架构

1.3 选择理由

本系统已有多个可复用的构建组件:

  • Rogers-Satchell 波动率估计(momentum_filter.py
  • BOCPD 趋势概率(momentum_filter.py
  • Huber-CUSUM 急动检测(momentum_filter.py
  • Efficiency Ratio 趋势强度(momentum_filter.py
  • Volume EMA 量能放大(momentum_filter.py
  • 多币种协整分析(analysis_core.py — 可用于提取 Realized Correlation)

无需引入新的外部依赖或数据源,只需将已有信号聚合为市场级温度指标。

1.4 与 v1 方案的关键差异

维度 v1 方案 v2 方案(本文)
因子权重 固定硬编码 0.25/0.25/0.20/0.15/0.15 Inverse-Vol 自适应权重
归一化 混合(部分百分位,部分直接映射) 全部统一滚动百分位
热力累积 二元模式(只累积或只衰减) Leaky Integrator(始终衰减 + 高温注入)
累积器归一化 Sigmoid(参数敏感) 累积值滚动百分位(自适应)
Regime 输出 硬三态分类 模糊隶属度(概率分布 → 平滑调参)
因子数 5 个(纯 BTC 价量) 6 个(新增 Realized Correlation)
HMM 评估 完全否定 客观评估,列为长期升级路径

二、算法架构

2.1 整体结构

┌───────────────────────────────────────────────────┐
│              BTC 5m K 线数据(WebSocket)             │
│         + ALT 收益率相关性数据(已有分析结果)          │
└──────────────────────┬────────────────────────────┘
                       │
          ┌────────────▼────────────────┐
          │    第一层:瞬时温度计          │
          │  (6 因子自适应加权 → 0~100)   │
          │                              │
          │  RS波动率      × w₁(自适应)   │
          │  BOCPD趋势概率 × w₂(自适应)   │
          │  CUSUM急动幅度  × w₃(自适应)   │
          │  ER趋势强度    × w₄(自适应)   │
          │  量能放大比     × w₅(自适应)   │
          │  Realized Corr × w₆(自适应)   │
          │                              │
          │  权重 = 1/σᵢ 归一化           │
          └────────────┬────────────────┘
                       │ instant_temp (0~100)
          ┌────────────▼────────────────┐
          │    第二层:Leaky Integrator   │
          │  (始终衰减 + 高温注入)        │
          │  (自然均衡点,无需硬上限)      │
          │                              │
          │  h(t) = λ·h(t-1) + g·Δ(t)   │
          │  半衰期 ≈ 11.5 小时          │
          └────────────┬────────────────┘
                       │ heat_percentile (0~100)
          ┌────────────▼────────────────┐
          │    第三层:模糊 Regime 输出   │
          │                              │
          │  p_cold + p_warm + p_hot = 1 │
          │  策略参数 = Σ pᵢ × paramᵢ   │
          │  (平滑过渡,无硬切换)         │
          └─────────────────────────────┘

2.2 与现有系统的关系

┌─ Beta 体制过滤器(配对级,已实现)──────────────────┐
│  β_divergence = max(0, β_short - β_long)           │
│  → 检测单个配对的 β 结构性变化                       │
│  → 拦截协整失效期的入场                              │
└──────────────────────────────────────────────────┘

┌─ momentum_filter(配对级过滤)─────────────────────┐
│  Layer 0: BOCPD   → 单个配对的机制切换              │
│  Layer 1: ER      → 单个配对的趋势强度              │
│  Layer 2: CUSUM   → 单个配对的急动检测              │
│  Layer 3: 平稳性   → 单个配对的均值回复能力           │
└──────────────────────────────────────────────────┘

┌─ market_temperature(市场级过滤)── 本方案 ─────────┐
│  聚焦 BTC + 跨币种相关性,输出全局温度概率分布         │
│  影响所有 ALT 配对的入场/出场参数                     │
└──────────────────────────────────────────────────┘

决策流程:
  should_enter()
    ├─ ① market_temperature.regime_probs → 市场级参数调整
    ├─ ② beta_regime.check()            → 配对级 β 拦截
    └─ ③ momentum_filter.check()        → 配对级动量过滤

三、第一层:瞬时温度计

3.1 六因子定义

因子 1:Rogers-Satchell 波动率

已有实现位置:momentum_filter.py_update_rs_vol()

# Rogers-Satchell 波动率(OHLC 估计,不受漂移影响)
rs_var = log(H/C) * log(H/O) + log(L/C) * log(L/O)
rs_vol = sqrt(EMA(rs_var, span=rs_span))

归一化:滚动百分位排名(过去 7 天 = 2016 根 5m bar)

rs_score = rolling_percentile(rs_vol, window=2016)  # 输出 0~100

选择理由

  • 基础风险度量,最稳定的温度底色
  • Rogers-Satchell 不受漂移影响(比 Parkinson 更适合有趋势的市场)

因子 2:BOCPD 趋势概率

已有实现位置:momentum_filter.py_bocpd_trend_probability()

# Bayesian Online Changepoint Detection (Adams & MacKay, 2007)
# 输出:P(|μ| > drift_threshold | data)
trend_prob = bocpd.trend_probability  # 0.0 ~ 1.0

归一化:滚动百分位排名

bocpd_score = rolling_percentile(trend_prob, window=2016)  # 输出 0~100

选择理由

  • 直接测量 regime shift 概率
  • 在线贝叶斯推断,无滞后
  • 已在 momentum_filter Layer 0 中验证可靠

v1→v2 变更:v1 直接映射 trend_prob * 100,导致分布偏斜,与百分位归一化因子的有效权重不一致。v2 统一用百分位。

因子 3:CUSUM 急动幅度

已有实现位置:momentum_filter.py_huber_cusum_check()

# 取双向 CUSUM 最大值,反映最大偏离幅度
cusum_max = max(S_pos, S_neg)

归一化:滚动百分位排名

cusum_score = rolling_percentile(cusum_max, window=2016)  # 输出 0~100

选择理由

  • 捕捉急剧价格变动(暴涨/暴跌)
  • Huber 裁剪已内置,对插针鲁棒
  • 与波动率互补:波动率是"震幅",CUSUM 是"方向性冲击"

因子 4:Efficiency Ratio

已有实现位置:momentum_filter.py_er_check()

# Kaufman ER = 位移 / 路径长度
displacement = abs(close[-1] - close[-er_window])
volatility = sum(abs(close[i] - close[i-1]) for i in range(er_window))
er = displacement / volatility  # 0.0 ~ 1.0

归一化:滚动百分位排名

er_score = rolling_percentile(er, window=2016)  # 输出 0~100

选择理由

  • 测量趋势的"纯度"——ER 高说明价格单向运动
  • 与水温概念对应:高 ER = 市场有方向 = 温度升高

v1→v2 变更:v1 直接映射 er * 100。ER 分布通常集中在 0.05~0.30,直接映射时 score 永远在 5~30 之间,实际贡献远低于标称权重。v2 统一用百分位消除此偏差。

因子 5:量能放大比

已有实现位置:momentum_filter.py 中成交量 EMA

# 当前成交量 / EMA 成交量
vol_ratio = current_volume / ema_volume

归一化:滚动百分位排名

volume_score = rolling_percentile(vol_ratio, window=2016)  # 输出 0~100

选择理由

  • 量价验证:无量上涨是虚火,放量上涨才是真热
  • 与其他因子互补:唯一的非价格维度

因子 6:Realized Correlation(新增)

数据来源:analysis_core.py 中已有多币种收益率数据

# 计算 top-N ALT 与 BTC 的滚动相关系数均值
# 当所有 ALT 相关性趋向 1.0 → 市场系统性风险上升 → 配对策略最危险
def _compute_realized_correlation(self, btc_returns: deque, alt_returns_dict: dict) -> float:
    """计算 BTC 与活跃 ALT 的平均相关性

    Args:
        btc_returns: BTC 5m 收益率序列 (至少 60 根)
        alt_returns_dict: {symbol: deque[returns]} 各 ALT 的 5m 收益率

    Returns:
        mean_corr: 平均相关系数 (0.0 ~ 1.0)
    """
    if len(btc_returns) < 60 or len(alt_returns_dict) < 3:
        return 0.5  # 数据不足时返回中性值

    btc = np.array(btc_returns)
    correlations = []
    for sym, alt_ret in alt_returns_dict.items():
        if len(alt_ret) < 60:
            continue
        alt = np.array(alt_ret)[-len(btc):]
        if len(alt) < 60:
            continue
        corr = np.corrcoef(btc[-60:], alt[-60:])[0, 1]
        if not np.isnan(corr):
            correlations.append(abs(corr))

    if len(correlations) < 3:
        return 0.5

    return float(np.mean(correlations))

归一化:滚动百分位排名

corr_score = rolling_percentile(mean_corr, window=2016)  # 输出 0~100

选择理由

  • 最直接的配对交易风险信号:当所有 ALT 相关性 → 1.0 时,配对交易的均值回复假设最容易失效
  • 这是 Absorption Ratio (Kritzman et al., 2010) 的简化版,计算成本极低
  • 其他 5 个因子都是 BTC 自身的价量指标,Realized Correlation 是唯一的跨资产结构指标
  • 直接预测配对策略表现,而不是间接通过 BTC 波动推断

3.2 自适应加权:Inverse-Volatility Weighting

为什么不用固定权重?

固定权重的核心问题:

  1. 不同市场周期下各因子预测力差异巨大
  2. 因子之间存在非线性相关性(RS vol 和 CUSUM 高度相关),固定权重会双重计算
  3. 真正"最优权重"是时变的

Inverse-Vol Weighting 原理

各因子的有效贡献应当均衡。如果某因子近期波动很大(贡献不稳定),就降低其权重;波动小(信号稳定)的因子权重提高。

class AdaptiveWeights:
    """Inverse-Volatility 自适应因子权重

    原理:
    - 波动大的因子 → 信号不稳定 → 降权
    - 波动小的因子 → 信号稳定 → 加权
    - 每个因子的权重 = 1/σᵢ,然后归一化使 Σwᵢ = 1
    """

    def __init__(self, n_factors: int = 6, vol_window: int = 288):
        """
        Args:
            n_factors: 因子数量
            vol_window: 计算因子波动率的窗口 (288 = 1天的 5m bar)
        """
        self.n_factors = n_factors
        self.vol_window = vol_window
        self._buffers: list[deque] = [deque(maxlen=vol_window) for _ in range(n_factors)]
        self._default_weights = [1.0 / n_factors] * n_factors  # 预热期等权

    def update(self, scores: list[float]) -> list[float]:
        """输入各因子百分位得分,返回自适应权重

        Args:
            scores: 各因子百分位得分 [f1, f2, ..., f6],每个 0~100

        Returns:
            weights: 归一化权重 [w1, w2, ..., w6],Σ = 1.0
        """
        assert len(scores) == self.n_factors

        for i, s in enumerate(scores):
            self._buffers[i].append(s)

        # 预热期:数据不足时用等权
        if any(len(buf) < 30 for buf in self._buffers):
            return self._default_weights.copy()

        # 计算各因子近期标准差
        stds = []
        for buf in self._buffers:
            data = list(buf)
            n = len(data)
            mean = sum(data) / n
            var = sum((x - mean) ** 2 for x in data) / max(n - 1, 1)
            stds.append(max(var ** 0.5, 1e-6))  # 防除零

        # Inverse-Vol: weight_i = 1/σ_i
        inv_vols = [1.0 / s for s in stds]
        total = sum(inv_vols)
        weights = [iv / total for iv in inv_vols]

        return weights

特性

  • 预热期(<30 根 bar)自动使用等权 1/6 ≈ 0.167
  • 正常运行后动态调整,贡献均衡化
  • 无需人工调参,完全自适应
  • 计算量极低:每次 update 仅 O(n_factors) 操作

与 v1 固定权重的对比

场景 v1 固定权重 v2 Inverse-Vol
ER 长期低波 权重 0.15(标称),实际贡献偏低 自动加权(低波 = 稳定信号)
CUSUM 和 RS 高相关 双重计算,有效权重 0.45 相关因子同波 → 自然降权
某因子在当前市场周期失效 仍保持固定权重 失效因子波动大 → 自动降权

3.3 加权合成

def compute_instant_temp(self, scores: list[float]) -> float:
    """计算瞬时温度

    Args:
        scores: 6 个因子的百分位得分,各 0~100
                [rs, bocpd, cusum, er, volume, corr]

    Returns:
        instant_temp: 加权温度 0~100
    """
    weights = self._adaptive_weights.update(scores)
    instant_temp = sum(w * s for w, s in zip(weights, scores))
    return instant_temp  # 0~100

3.4 归一化策略:统一滚动百分位

核心改进:所有 6 个因子统一使用滚动百分位归一化

v1 的问题:
  RS vol    → 滚动百分位 → 分布均匀 [0, 100]
  BOCPD     → 直接 ×100  → 分布偏斜 (大部分时间 10~30)
  CUSUM     → 滚动百分位 → 分布均匀 [0, 100]
  ER        → 直接 ×100  → 分布偏斜 (大部分时间 5~30)
  Volume    → 滚动百分位 → 分布均匀 [0, 100]

  ⇒ BOCPD 和 ER 的"有效权重"远低于标称值
  ⇒ 实际温度主要由 RS/CUSUM/Volume 三个因子驱动
  ⇒ 标称权重 0.25/0.25/0.20/0.15/0.15 名不副实

v2 的解决:
  所有因子 → 滚动百分位 → 分布均匀 [0, 100]
  ⇒ 每个因子的贡献与权重一致
  ⇒ Inverse-Vol 自适应权重才能真正发挥作用

滚动窗口选择:7 天(2016 根 5m bar)

  • 太短(1 天):百分位波动剧烈,温度抖动
  • 太长(30 天):对近期变化不敏感
  • 7 天:平衡稳定性与敏感性,覆盖一个完整的周周期

实现方式:增量排序缓冲区(deque + bisect),O(log n) 插入 + O(log n) 查询

from collections import deque
import bisect

class RollingPercentile:
    """增量滚动百分位计算器"""

    def __init__(self, window: int = 2016):
        self.window = window
        self.values = deque(maxlen=window)    # FIFO 原始值
        self.sorted_vals = []                  # 排序副本

    def update(self, value: float) -> float:
        """添加新值,返回当前百分位 (0~100)"""
        # 移除最旧值
        if len(self.values) == self.window:
            old = self.values[0]
            idx = bisect.bisect_left(self.sorted_vals, old)
            self.sorted_vals.pop(idx)

        # 添加新值
        self.values.append(value)
        bisect.insort(self.sorted_vals, value)

        # 计算百分位
        rank = bisect.bisect_right(self.sorted_vals, value)
        return (rank / len(self.sorted_vals)) * 100

四、第二层:Leaky Integrator 热力累积器

4.1 核心思想

瞬时温度只告诉你"现在热不热",但水温模型的关键洞察是:

持续高温一段时间后,市场性格才会改变

4.2 v1 模型的物理缺陷

v1 的热力累积器:

# v1(有缺陷)
if temp > base_temp:
    heat += delta      # 只加不减
else:
    heat *= decay      # 只减不加

问题

  • 当 temp = 80 时,热量只增不衰,会线性无限增长直到碰上 max_heat 天花板
  • 现实中应该是始终衰减 + 高温时额外注入
  • 需要 max_heat 硬上限 + sigmoid 归一化来补丁,但 sigmoid 参数敏感

4.3 v2 模型:Leaky Integrator(漏积分器)

class LeakyHeatAccumulator:
    """Leaky Integrator 热力累积器

    物理模型:
    - 每个时间步热量都在衰减(散热)
    - 高温时额外注入热量(加热)
    - 当注入 = 衰减时达到自然均衡点
    - 无需硬上限,系统自然收敛

    公式:
        h(t) = λ × h(t-1) + g × max(0, T(t) - T_base)

    均衡点(ΔT = T - T_base 恒定时):
        h_eq = g × ΔT / (1 - λ)

    示例(λ=0.995, g=0.15, ΔT=30):
        h_eq = 0.15 × 30 / (1 - 0.995) = 900
    """

    def __init__(
        self,
        base_temp: float = 50.0,       # 基准温度(低于此不注入)
        decay_factor: float = 0.995,    # 每根 5m bar 的衰减系数
        gain: float = 0.15,            # 注入增益
    ):
        self.base_temp = base_temp
        self.decay_factor = decay_factor
        self.gain = gain
        self.heat = 0.0

    def update(self, instant_temp: float) -> float:
        """每根 5m K 线调用一次

        Args:
            instant_temp: 瞬时温度 (0~100)

        Returns:
            heat: 当前累积热量(无上限,由滚动百分位归一化)
        """
        excess = max(0.0, instant_temp - self.base_temp)
        self.heat = self.decay_factor * self.heat + self.gain * excess
        return self.heat

4.4 参数设计

基准温度 base_temp = 50

  • 百分位 50 = 中位数 = "正常"水平
  • 低于 50 的温度不会注入热量,但热量仍在衰减
  • 含义:只有"高于正常"的状态才会推动 regime 变化

衰减系数 decay_factor = 0.995

半衰期计算:
  ln(0.5) / ln(0.995) = 138.3 根 5m bar
  138.3 × 5min = 691.5 min ≈ 11.5 小时

含义

  • BTC 从 80° 突然降到 40°
  • ALT 的"暴躁状态"还会持续约 11.5 小时才衰减一半
  • 这符合观察:ALT 的反应是迟缓的,不会立刻冷却

注入增益 gain = 0.15

与 v1 的 accumulation_rate = 1.0 不同,v2 的 gain 更小,因为 Leaky Integrator 同时在衰减和注入。

均衡分析

当 instant_temp 恒定为 T 时,均衡热量:
  h_eq = gain × max(0, T - base_temp) / (1 - decay_factor)

示例:
  T=50 (中位温度): h_eq = 0.15 × 0  / 0.005 = 0      (无热量)
  T=60 (温和偏高): h_eq = 0.15 × 10 / 0.005 = 300
  T=70 (明显偏高): h_eq = 0.15 × 20 / 0.005 = 600
  T=80 (高温):     h_eq = 0.15 × 30 / 0.005 = 900
  T=90 (极高温):   h_eq = 0.15 × 40 / 0.005 = 1200
  T=100 (最高):    h_eq = 0.15 × 50 / 0.005 = 1500

热量有自然上界(不需要 max_heat 硬上限),且上界由物理参数决定而非人为钳位。

4.5 热量归一化:滚动百分位(替代 Sigmoid)

v1 使用 Sigmoid 归一化,但存在参数敏感问题(midpoint 设在 30% 处导致过早饱和)。

v2 使用滚动百分位归一化热量值,与第一层因子归一化策略一致:

self._heat_percentile = RollingPercentile(window=2016)

def normalized_heat(self) -> float:
    """将累积热量归一化到 0~100"""
    return self._heat_percentile.update(self.heat)

优势

  • 与第一层归一化策略一致,无额外参数
  • 自动适应不同市场环境下的热量分布
  • 不存在 Sigmoid 的参数敏感问题(midpoint、k 值)
  • 低温区和高温区分辨率自然均衡

4.6 Leaky Integrator 动力学示意

瞬时温度 (instant_temp)
100│          ╱╲
   │         ╱  ╲      ╱╲
 80│────────╱────╲────╱──╲──── 高温线
   │       ╱      ╲  ╱    ╲
 50│──────╱────────╲╱──────── 基准线(注入阈值)
   │     ╱                    ← 低于基准:无注入,仅衰减
  0└─────────────────────────── 时间

累积热量 (heat) — 注意:始终在衰减
100│                    ╱────  ← 持续高温:注入 > 衰减,稳步上升
   │                   ╱
 70│──────────────────╱────── 高隶属度区
   │           ╱╲    ╱
   │          ╱  ╲  ╱         ← 第一次高温不够持久:
 30│─────────╱────╲╱─────────    注入停止后,衰减占主导
   │        ╱      ↘            热量自然下降(无硬切换)
   │       ╱
  0└─────────────────────────── 时间
          ↑              ↑
       第一波高温      第二波持续高温
       (注入后衰减)    (注入持续 > 衰减
                        热量稳步累积)

4.7 v1 vs v2 累积器对比

特性 v1 Binary Accumulator v2 Leaky Integrator
高温时 只加不减(线性增长) 加减同时(收敛到均衡)
低温时 只减不加(指数衰减) 只减不加(指数衰减)
上界 需要 max_heat 硬上限 自然上界 = g×ΔT/(1-λ)
归一化 Sigmoid(参数敏感) 滚动百分位(自适应)
物理含义 不符合热力学 标准散热+加热模型
均衡分析 无法解析 h_eq = g×ΔT/(1-λ)

五、第三层:模糊 Regime 分类与平滑策略调整

5.1 v1 硬分类的问题

v1 使用硬阈值 30/70 + 滞后:

heat=69 → WARM (正常参数)
heat=71 → HOT  (仓位减半,阈值×1.5)

问题

  1. heat 从 69→71 的微小变化导致策略参数跳变
  2. 阈值 30/70 缺乏统计依据
  3. heat=31 和 heat=69 都是 WARM,但风险差异巨大

5.2 v2 方案:模糊隶属度

用 softmax 输出各 regime 的概率分布,策略参数连续平滑调整:

import math

class FuzzyRegimeClassifier:
    """模糊 Regime 分类器

    输出 (p_cold, p_warm, p_hot) 概率分布,
    策略参数为各 regime 参数的加权平均。

    核心公式 (log-linear softmax):
        score_cold = -(heat / tau)
        score_warm = -((heat - 50)² / (2 × sigma²))
        score_hot  = (heat - 100) / tau

        p_i = exp(score_i) / Σ exp(score_j)

    参数语义:
        tau:   控制 cold/hot 端的灵敏度(越小越尖锐)
        sigma: 控制 warm 区间的宽度(越大越宽)
    """

    def __init__(self, tau: float = 15.0, sigma: float = 18.0):
        self.tau = tau
        self.sigma = sigma

    def classify(self, heat_percentile: float) -> dict[str, float]:
        """返回各 regime 的隶属度

        Args:
            heat_percentile: 归一化热量 (0~100)

        Returns:
            {"cold": p, "warm": p, "hot": p},Σ = 1.0
        """
        h = heat_percentile

        # 各 regime 的 log 得分
        score_cold = -(h / self.tau)
        score_warm = -((h - 50.0) ** 2) / (2.0 * self.sigma ** 2)
        score_hot = (h - 100.0) / self.tau

        # Softmax(数值稳定版)
        scores = [score_cold, score_warm, score_hot]
        max_score = max(scores)
        exps = [math.exp(s - max_score) for s in scores]
        total = sum(exps)

        return {
            "cold": exps[0] / total,
            "warm": exps[1] / total,
            "hot": exps[2] / total,
        }

参数含义

tau = 15.0:
  heat=0  → p_cold ≈ 0.95, p_warm ≈ 0.05, p_hot ≈ 0.00
  heat=20 → p_cold ≈ 0.65, p_warm ≈ 0.34, p_hot ≈ 0.01
  heat=50 → p_cold ≈ 0.06, p_warm ≈ 0.88, p_hot ≈ 0.06  (warm 主导)
  heat=80 → p_cold ≈ 0.00, p_warm ≈ 0.34, p_hot ≈ 0.66
  heat=100→ p_cold ≈ 0.00, p_warm ≈ 0.05, p_hot ≈ 0.95

sigma = 18.0:
  warm 概率在 heat=32~68 之间 > 0.5
  自然对应 v1 的 30~70 区间,但过渡平滑

5.3 平滑策略参数调整

# ── 各 Regime 的策略参数 ──
_REGIME_PARAMS = {
    "cold": {
        "zscore_entry_multiplier": 1.3,      # 入场阈值 ×1.3
        "cointegration_min_passed": 4,        # 协整要求提高
        "position_size_multiplier": 0.7,      # 仓位缩小 30%
        "max_concurrent_positions": 3,        # 最大持仓数
    },
    "warm": {
        "zscore_entry_multiplier": 1.0,       # 正常参数
        "cointegration_min_passed": 3,
        "position_size_multiplier": 1.0,
        "max_concurrent_positions": 3,
    },
    "hot": {
        "zscore_entry_multiplier": 1.5,       # 入场阈值 ×1.5
        "cointegration_min_passed": 5,        # 协整要求大幅提高
        "position_size_multiplier": 0.5,      # 仓位减半
        "max_concurrent_positions": 2,        # 最大持仓数减少
    },
}


def get_blended_adjustments(regime_probs: dict[str, float]) -> dict:
    """根据 regime 概率分布计算混合策略参数

    Args:
        regime_probs: {"cold": p, "warm": p, "hot": p}

    Returns:
        混合后的策略参数字典

    Example:
        regime_probs = {"cold": 0.0, "warm": 0.34, "hot": 0.66}
        → zscore_entry_multiplier = 0.34 × 1.0 + 0.66 × 1.5 = 1.33
        → position_size_multiplier = 0.34 × 1.0 + 0.66 × 0.5 = 0.67
    """
    result = {}
    for param_key in _REGIME_PARAMS["warm"]:
        blended = sum(
            regime_probs[regime] * _REGIME_PARAMS[regime][param_key]
            for regime in ("cold", "warm", "hot")
        )
        result[param_key] = blended

    # 主导 regime(用于日志和告警)
    dominant = max(regime_probs, key=regime_probs.get)
    result["dominant_regime"] = dominant
    result["dominant_prob"] = regime_probs[dominant]

    return result

关键优势

v1 (硬分类):
  heat=69 → WARM → multiplier=1.0
  heat=71 → HOT  → multiplier=1.5  (跳变!)

v2 (模糊):
  heat=69 → p_warm=0.42, p_hot=0.56 → multiplier = 0.42×1.0 + 0.56×1.5 = 1.26
  heat=71 → p_warm=0.38, p_hot=0.60 → multiplier = 0.38×1.0 + 0.60×1.5 = 1.28
  → 平滑过渡,无跳变

5.4 防抖机制

模糊分类已内在平滑,但仍需防止高频微小振荡导致参数频繁变化:

class TemperatureOutputSmoother:
    """温度输出平滑器

    对模糊分类结果做 EMA 平滑,防止因瞬时温度微小波动
    导致策略参数频繁变化。
    """

    def __init__(self, alpha: float = 0.1):
        """
        Args:
            alpha: EMA 衰减因子 (0.1 = 约 10 根 bar 的平滑窗口)
        """
        self.alpha = alpha
        self._smoothed: dict[str, float] | None = None

    def smooth(self, regime_probs: dict[str, float]) -> dict[str, float]:
        if self._smoothed is None:
            self._smoothed = regime_probs.copy()
        else:
            for key in regime_probs:
                self._smoothed[key] = (
                    self.alpha * regime_probs[key]
                    + (1.0 - self.alpha) * self._smoothed[key]
                )
        # 重新归一化(EMA 可能导致微小数值偏差)
        total = sum(self._smoothed.values())
        return {k: v / total for k, v in self._smoothed.items()}

5.5 日志中的 Regime 标签

虽然策略参数用模糊值,但日志和告警仍使用主导 regime 标签:

dominant = max(regime_probs, key=regime_probs.get)
# 日志: regime=HOT(0.66) warm=0.34 cold=0.00

Regime 切换告警的触发条件改为:主导 regime 发生变化


六、集成设计

6.1 新增文件

src/trading/market_temperature.py   # ~250 行,核心实现
    ├─ RollingPercentile            # 滚动百分位计算器
    ├─ AdaptiveWeights              # Inverse-Vol 自适应权重
    ├─ LeakyHeatAccumulator         # Leaky Integrator 热力累积
    ├─ FuzzyRegimeClassifier        # 模糊 Regime 分类
    ├─ TemperatureOutputSmoother    # 输出平滑
    └─ MarketTemperature            # 顶层聚合类

6.2 修改文件

src/trading/config.py               # ~20 行,新增配置参数
src/trading/strategy.py             # ~40 行,集成温度判断
src/services/realtime_kline_service_base.py  # ~25 行,驱动温度更新

6.3 配置参数

# trading/config.py → StrategyParams 新增

# --- 市场水温 ---
market_temperature_enabled: bool = True
market_temperature_base_temp: float = 50.0        # 基准温度
market_temperature_decay: float = 0.995            # 衰减系数(半衰期≈11.5h)
market_temperature_gain: float = 0.15              # Leaky Integrator 注入增益
market_temperature_percentile_window: int = 2016   # 百分位窗口(7天×288根/天)
market_temperature_vol_window: int = 288           # 因子权重波动率窗口(1天)
market_temperature_regime_tau: float = 15.0        # Regime softmax 温度参数
market_temperature_regime_sigma: float = 18.0      # Regime warm 区间宽度
market_temperature_smooth_alpha: float = 0.1       # 输出 EMA 平滑系数

设计要点

  • 不再需要 cold_thresholdhot_thresholdhysteresis — 模糊分类自动处理
  • 不再需要 max_heat — Leaky Integrator 有自然上界
  • 不再需要 weights dict — Inverse-Vol 自适应计算
  • 新增 gainregime_tauregime_sigmasmooth_alpha

6.4 数据流集成

realtime_kline_service_base.py
│
├─ _on_candle_update(symbol="BTC/USDC:USDC", timeframe="5m")
│     │
│     ├─ [已有] momentum_filter._update_rs_vol(btc_candle)
│     ├─ [已有] momentum_filter._bocpd_trend_probability(btc_data)
│     ├─ [已有] momentum_filter._huber_cusum_check(btc_data)
│     ├─ [已有] momentum_filter._er_check(btc_data)
│     │
│     └─ [新增] market_temperature.update(
│              rs_vol=..., bocpd_prob=...,
│              cusum_max=..., er=..., vol_ratio=...,
│              realized_corr=...,
│           )
│           → 内部自动:归一化 → 自适应加权 → Leaky Integrate → 模糊分类 → 平滑
│
├─ _process_analysis_result(symbol="ALT/USDC:USDC")
│     └─ strategy.should_enter()
│           ├─ [新增] market_temperature.get_adjustments() → 混合参数
│           ├─ [已有] beta_regime.check()                  → 配对级 β 拦截
│           └─ [已有] momentum_filter.check()              → 配对级过滤

6.5 strategy.py 集成点

# strategy.py :: AdaptiveBollingerStrategy._check_entry()

def _check_entry(self, key, z4h, adaptive_z, timestamp, current_above, params, latest_price=None):

    # === 第一关:市场水温(市场级) ===
    if self._market_temp and self._market_temp.enabled:
        adjustments = self._market_temp.get_adjustments()

        # 平滑混合后的阈值倍数
        effective_zscore_threshold = (
            params.adaptive_threshold * adjustments["zscore_entry_multiplier"]
        )

        # 协整通过数要求(取整)
        min_passed = round(adjustments["cointegration_min_passed"])

        # 仓位大小倍数(传递给 executor)
        position_multiplier = adjustments["position_size_multiplier"]

        # 日志
        dominant = adjustments["dominant_regime"]
        dominant_p = adjustments["dominant_prob"]
        logger.info(
            f"🌡️ 水温调整 | {pair_label} | "
            f"regime={dominant}({dominant_p:.0%}) | "
            f"阈值×{adjustments['zscore_entry_multiplier']:.2f} "
            f"仓位×{adjustments['position_size_multiplier']:.2f}"
        )
    else:
        effective_zscore_threshold = params.adaptive_threshold
        position_multiplier = 1.0

    # === 第二关:Beta 体制过滤(配对级,已有) ===
    if params.beta_regime_enabled:
        beta_state = self._beta_regime.check(key, ...)
        if beta_state.hard_block:
            return None
        effective_zscore_threshold *= beta_state.threshold_scale

    # === 后续:原有逻辑不变 ===
    ...

七、监控与告警

7.1 飞书推送

主导 Regime 切换时推送告警:

🌡️ 市场水温变化

Regime 主导: WARM → HOT (p=0.72)
概率分布: cold=0.02 warm=0.26 hot=0.72
瞬时温度: 82.3
累积热量百分位: 78.1

因子得分 (百分位):
  RS波动率:      91.2 (w=0.18)
  BOCPD趋势:     78.5 (w=0.15)
  CUSUM急动:      85.3 (w=0.14)
  ER趋势:        67.2 (w=0.17)
  量能放大:       72.8 (w=0.16)
  Realized Corr:  88.4 (w=0.20)  ← 相关性飙升

策略调整 (混合):
  入场阈值: ×1.38
  仓位: ×0.63
  协整通过: 5 (取整)

7.2 日志记录

每根 5m bar 记录温度快照(DEBUG 级别):

[MarketTemp] instant=72.3 heat=847.5 heat_pct=78.1 regime=HOT(0.72) | w=[0.18,0.15,0.14,0.17,0.16,0.20]

主导 Regime 切换记录(INFO 级别):

[MarketTemp] REGIME SHIFT: WARM → HOT (p=0.72, duration_in_warm=4.2h) | heat_pct=78.1

7.3 数据库存储(可选)

可将温度数据写入 analysis_results 表的扩展字段,用于回测分析:

ALTER TABLE analysis_results ADD COLUMN IF NOT EXISTS market_temp DOUBLE PRECISION;
ALTER TABLE analysis_results ADD COLUMN IF NOT EXISTS market_regime VARCHAR(10);
ALTER TABLE analysis_results ADD COLUMN IF NOT EXISTS market_regime_prob DOUBLE PRECISION;

八、回测验证方案

8.1 验证目标

  1. Regime 识别是否与主观判断一致:对照 BTC 历史行情,验证 HOT/COLD 主导概率划分是否合理
  2. 对策略收益的影响:在 HOT regime 收紧参数 vs 不收紧,对比胜率和盈亏比
  3. 参数敏感性:衰减系数、gain、tau、sigma 的变化对结果的影响
  4. v2 vs v1 对比:统一归一化 + Leaky Integrator 的改进是否体现在回测结果中

8.2 验证方法

# 使用历史 klines 数据回放
for candle in historical_btc_5m_candles:
    temp.update(candle)
    probs = temp.regime_probs
    adjustments = temp.get_adjustments()

# 对比:
# A 组:不使用水温过滤的原始策略
# B 组:v1 水温过滤(硬分类)
# C 组:v2 水温过滤(模糊分类 + Leaky Integrator)
# 统计:胜率、盈亏比、最大回撤、夏普比率

8.3 关键检验指标

指标 期望效果
HOT 主导期 C 组回撤 显著低于 A 组,与 B 组持平或更优
WARM 主导期 ABC 三组表现 基本一致(水温不干预正常交易)
COLD 主导期 C 组假信号 少于 A 组(收紧入场过滤了弱信号)
整体夏普比率 C ≥ B ≥ A
Regime 切换时参数跳变 C 组无跳变(模糊平滑),B 组有跳变
相关性飙升期拦截率 C 组 > B 组(Realized Corr 因子贡献)

8.4 因子贡献度分析

回测中记录各因子权重随时间的变化,验证 Inverse-Vol 自适应权重是否合理:

# 预期行为:
# - 在趋势行情中:ER 波动小 → ER 权重上升(信号稳定)
# - 在震荡行情中:ER 波动大 → ER 权重下降(信号不稳定)
# - CUSUM 和 RS 高相关时:两者同波 → 各自权重被压制 → 避免双重计算

九、实现优先级

优先级 组件 预估代码量
P0 MarketTemperature 核心类(RollingPercentile + AdaptiveWeights + LeakyHeatAccumulator + FuzzyRegimeClassifier + Smoother) ~200 行
P0 配置参数 (StrategyParams 新增字段) ~20 行
P0 strategy.py 集成 ~40 行
P0 service_base.py 驱动 ~25 行
P1 Realized Correlation 计算 ~40 行
P1 飞书告警推送 ~35 行
P1 日志输出 ~15 行
P2 数据库存储(回测用) ~20 行
P2 回测验证脚本 ~150 行

总计核心实现:~285 行新代码 + ~65 行修改


十、风险与局限

10.1 已知局限

局限 影响 缓解措施
百分位窗口冷启动 启动前 7 天百分位不准 使用历史数据预填充
Inverse-Vol 权重冷启动 前 30 根 bar 使用等权 30 根 = 2.5 小时,影响极小
Realized Corr 需要多币种 活跃 ALT < 3 时退化 数据不足时返回中性值 0.5
仅基于 BTC + 跨币种相关性 部分 ALT 有独立行情 可扩展为 BTC + ETH 双锚定
无 Funding Rate 数据 温度计缺少杠杆/情绪维度 后续补充为第 7 个因子

10.2 后续演进方向

优先级 方向 方法 预期效果
P1 资金费率因子 接入 Hyperliquid funding rate API,作为第 7 个温度因子 补充市场杠杆和情绪维度
P1 OI 变化率因子 接入 Hyperliquid open interest API,作为第 8 个温度因子 补充资金流入/流出速度
P2 Exponential Gradient 调权 替代 Inverse-Vol,用在线凸优化动态学习最优权重 (Cesa-Bianchi & Lugosi, 2006) 权重不仅均衡,还能追踪最优
P2 多锚定资产 BTC + ETH 双温度计,取加权平均 覆盖独立于 BTC 的 ETH 生态行情
P2 配对级温度修正 特定 ALT 对 BTC 温度的敏感度不同(β 调节) 个性化温度调整
P3 Hawkes Process 用自激点过程替代 Leaky Integrator,建模"高温事件提升未来高温概率" 学术最优的热力累积模型
P3 Online HMM 用 Particle Filter HMM 替代整体三层架构 统一概率框架,自动学习状态数和转移概率
P3 回测自动调参 基于历史数据的贝叶斯优化(而非网格搜索) 高效参数空间探索

附录 A:学术参考

方法 参考文献 本方案的应用
Rogers-Satchell Volatility Rogers & Satchell (1991) 因子 1:OHLC 波动率估计
BOCPD Adams & MacKay (2007) "Bayesian Online Changepoint Detection" 因子 2:在线变点检测
Huber-CUSUM Page (1954) + Huber (1964) 因子 3:鲁棒急动检测
Kaufman Efficiency Ratio Kaufman (1995) "Smarter Trading" 因子 4:趋势纯度
Absorption Ratio Kritzman et al. (2010) "Principal Components as a Measure of Systemic Risk" 因子 6 的理论基础
Inverse-Volatility Weighting Risk Parity 文献 (Qian, 2005) 自适应因子权重
Leaky Integrator 信号处理标准模型 热力累积器
Softmax Fuzzy Classification 模糊逻辑 + 统计学习 模糊 Regime 输出
Exponential Gradient Cesa-Bianchi & Lugosi (2006) "Prediction, Learning, and Games" P2 升级路径
Hawkes Process Bacry et al. (2015) "Hawkes processes in finance" P3 升级路径
Online HMM Cappé (2011) "Online EM Algorithm for HMMs" P3 升级路径

附录 B:参数速查表

参数 默认值 含义 调优方向
base_temp 50.0 注入阈值(百分位中位数) 一般不需调整
decay_factor 0.995 衰减系数(半衰期≈11.5h) ↓ 更快冷却 ↑ 更慢冷却
gain 0.15 注入增益 ↑ 升温更快 ↓ 升温更慢
percentile_window 2016 百分位窗口(7天) ↓ 更敏感 ↑ 更稳定
vol_window 288 权重波动率窗口(1天) ↓ 权重更敏感 ↑ 权重更稳定
regime_tau 15.0 Softmax 温度(cold/hot 灵敏度) ↓ 更尖锐 ↑ 更平滑
regime_sigma 18.0 Warm 区间宽度 ↓ warm 更窄 ↑ warm 更宽
smooth_alpha 0.1 输出 EMA 平滑 ↑ 更快响应 ↓ 更平滑

均衡分析快查

h_eq = gain × (T - base_temp) / (1 - decay_factor)

gain=0.15, decay=0.995:
  T=60 → h_eq=300,  T=70 → h_eq=600,  T=80 → h_eq=900
  T=90 → h_eq=1200, T=100 → h_eq=1500

gain=0.20, decay=0.995 (更快升温):
  T=60 → h_eq=400,  T=70 → h_eq=800,  T=80 → h_eq=1200

gain=0.15, decay=0.990 (更快冷却, 半衰期≈5.8h):
  T=60 → h_eq=150,  T=70 → h_eq=300,  T=80 → h_eq=450

Read more

AMI的优越性

世界模型(World Models)的具体例子 如下,我按类型分类,便于理解。每类都附带实际实现、演示效果和应用场景。 1. Yann LeCun / Meta 的 JEPA 系列(最直接对应“世界模型”概念) 这些是 LeCun 主张的非生成式抽象预测世界模型代表。 * I-JEPA(Image JEPA,2023) 输入一张图像,模型把不同区域(context 和 target)编码成抽象表示,然后预测 target 的表示(不在像素级别重建)。 例子:给定一张遮挡了部分物体的图片,模型能预测“被遮挡物体的大致位置和属性”,构建对物体持久性和空间关系的理解。 这是一个“原始世界模型”,能学习物理常识(如物体不会凭空消失)。 * V-JEPA / V-JEPA 2(Video JEPA,

By SHI XIAOLONG

什么是:“世界模型(World Models)”

世界模型(World Models) 是人工智能领域的一个核心概念,尤其在 Yann LeCun 等研究者推动的下一代 AI 架构中占据中心位置。它指的是 AI 系统在内部构建的对现实世界的抽象模拟或内部表示,让机器能够像人类或动物一样“理解”物理世界、预测未来、规划行动。 简单比喻 想象你闭上眼睛也能“看到”房间里的物体会如何移动、碰撞或掉落——这就是你大脑里的世界模型。AI 的世界模型就是类似的“数字孪生”(digital twin)或“内部模拟器”:它不是简单记住数据,而是学习世界的动态、因果关系和物理直觉(如重力、物体持久性、遮挡、因果等)。 为什么需要世界模型? 当前主流的大型语言模型(LLM) 擅长处理文本(统计模式预测),但存在根本局限: * 缺乏对物理世界的真正理解 → 容易“幻觉”、无法可靠规划。 * 样本效率低 → 人类/

By SHI XIAOLONG

K线周期可配置化设计方案

K线周期可配置化设计方案 1. 背景与目标 当前 Beta 套利策略的 K 线周期硬编码为 "1h",分散在多个文件中。需要: 1. 将 K 线周期从 1h 改为 2h 2. 提取为环境变量 BETA_ARB_KLINE_INTERVAL,使其可在 .env 中配置 2. 影响范围分析 2.1 需要修改的文件(共 6 个) 文件 硬编码位置 修改内容 src/trading/config.py BetaArbConfig dataclass 新增 kline_interval 字段,

By SHI XIAOLONG

对于空间环境、“信息/逻辑”(比如代码、结构、表达)秩序追求的心理特征分析

一、为什么是“空间 + 信息”同时强化? 因为你当年面对的是“双重失控”: 1️⃣ 外部世界是脏乱 + 失序的 * 空间被污染 * 行为无边界 * 基本生活秩序崩塌 👉 所以你现在会强烈要求: * 桌面干净 * 房间有序 * 物品可控 这是在修复:“物理世界必须是可控的” 2️⃣ 人的行为和逻辑也是混乱的 * 没有规则 * 没有底线 * 没有理性 👉 所以你现在会特别在意: * 表达是否清晰 * 逻辑是否自洽 * 结构是否优雅 * 代码是否干净 这是在修复:“认知世界必须是合理的” 二、你其实构建了一个“高纯度系统” 你现在的偏好,本质上是: 👉 低噪音 + 高结构 + 强控制感 具体表现就是: * 空间:极简、整洁、可预测 * 信息:清晰、压缩、无冗余 这类人有一个很明显的优势: 👉 处理复杂问题时,

By SHI XIAOLONG