高阶 会员专享

Renaissance Technologies:量化信号发现

Read in English

全球业绩最佳的对冲基金如何从市场微观结构中提取阿尔法

⚠️ Medallion 的真实情况

Renaissance 的 Medallion Fund:在扣除 5% 管理费和 44% 业绩提成之后,1988 至 2018 年年化收益率达 66%。

扣费之前:年化约 80%。这是史上最出色的投资业绩记录,没有之一。

你为什么无法复制这一切:

  • 他们每天交易 10 万次以上,执行速度以微秒计
  • 服务器与交易所同址托管(延迟以纳秒计)
  • 300 多名物理学、数学和计算机科学博士
  • 自建数据源和定制的机器学习框架
  • 100 亿美元的基金容量(自 1993 年起不再对外部投资者开放)

你能复制的是: 他们的信号发现方法论。不是那套高频交易基础设施,而是系统化寻找优势的方法。

散户的现实预期:运用 Renaissance 的原则可实现 8-15% 的年化复合增长率 (不是 66%,但仍远胜买入持有)

🎯 你将学到什么

Renaissance 并不依赖某一个"秘密策略"。他们组合的是 数千个弱预测因子 其中每一个都只有微弱优势。你将学到:

  • 信号发现框架: 如何系统化地寻找并检验具有预测力的特征
  • 特征工程: 从价量数据中提取 50 多个信号(也就是 Renaissance 在看的东西)
  • 集成建模: 把弱预测因子(55% 准确率)组合成强模型(65% 以上准确率)
  • 交易成本建模: 为什么忽略成本会毁掉高频策略(以及如何把成本算进去)
  • 滚动前推检验: 避开困扰 99% 量化策略的过拟合问题
  • Python 实现: 包含 30 多个特征的完整信号发现引擎
  • 现实的业绩表现: 年化复合增长率 11.8%,Sharpe 比率 1.52,按日/按周再平衡(2015-2023 回测)

Renaissance 的优势:他们与众不同在哪里

起源故事

Jim Simons 起初并不是交易员。他是一位 为美国国家安全局破译苏联密码的数学家,后来成为伯克利大学专攻模式识别的教授。

1982 年,他把同样的模式识别技术用到了市场上。核心洞见是:

"市场是有模式的,它不是随机游走。但这些模式很弱、噪音很大,而且不断演化。你需要的是数学,不是直觉。"

— Jim Simons,据访谈内容转述

Renaissance 发现了什么

  1. 短期均值回归是真实存在的 (时间尺度是几分钟到几天,不是几个月)
  2. 市场微观结构会制造出可预测的低效 (订单流、买卖价差动态)
  3. 数千个弱信号胜过一个强信号 (集成方法)
  4. 优势衰减得很快 (策略的有效期是几个月到几年,不是几十年)
  5. 交易成本比阿尔法更重要 (0.1% 优势 - 0.08% 成本 = 0.02% 净优势)

他们如何交易(简化版)

时间跨度: 几分钟到 2 天(95% 以上的仓位在 48 小时内平掉)

信号数量: 同时评估 1000 多个预测特征

预测目标: 未来 1 小时的收益(不是下个月,也不是明年)

胜率: 约 50.75%(是的,仅比抛硬币好一点点)

交易频率: 每天在 100 多个市场上完成 10 万笔以上交易

微小优势在规模上的威力

情景: 胜率 50.75%,风险报酬比 1:1,每年 10 万笔交易

每笔交易的期望值:
EV = (胜率 × 平均盈利) - (败率 × 平均亏损)
EV = (0.5075 × $100) - (0.4925 × $100)
EV = $50.75 - $49.25 = 每交易 $100 赚 $1.50

年度收益(10 万笔交易,单笔平均 $100):
毛利:$1.50 × 100,000 = 在 $10M 的交易额上赚 $150,000(1.5%)

但加上杠杆后(Renaissance 约用 10 倍):
净值:1.5% × 10 = 年化收益 15%

若执行更优、信号更多、频率更高:
Medallion 实现 66% 的净收益(扣费后)

散户版的改造

你没法一天交易 10 万次。但你可以用 Renaissance 的 信号发现方法论 配合按日或按周再平衡:

  • 构建 20 到 50 个特征 来自价量数据(不是 1000 多个,但够用了)
  • 组合成集成模型 (随机森林、梯度提升)
  • 按日或按周交易 (不做日内 —— 你没有那套基础设施)
  • 把交易成本算进去 (Renaissance 每笔付 0.0001%,你要付 0.05-0.10%)

结果:年化复合增长率 8-15%,对比 Medallion 的 66%。依然非常出色,而且依然建立在他们的原则之上。

信号发现的理念

什么是"信号"?

信号: 任何由市场数据推导出、对未来收益具有 预测力 的特征。

示例:

  • RSI 低于 30 预示未来 5 天有 +0.3% 的收益(弱信号,准确率 52%)
  • 成交量放大到均值 2 倍以上预示均值回归(弱信号,准确率 53%)
  • 价格低于 20 日均线 2% 预示反弹(弱信号,准确率 54%)

关键洞察: 每个信号都很弱(仅比随机好一点)。但 组合起来之后,它们就产生了强大的预测力。

Renaissance 的信号分类法

他们把信号分成 5 类:

1. 均值回归信号

前提: 价格短期内反应过度,随后回归均值

示例:

  • 与移动均线的偏离度(5 日、10 日、20 日)
  • RSI 超买/超卖
  • 布林带极值
  • 日内高低点与前一日的对比

时间跨度: 1 小时到 5 天

2. 动量信号

前提: 趋势会短期延续,然后反转

示例:

  • 1 日收益(昨天的赢家今天继续赢)
  • 3 日收益(但到第 7 天会反转)
  • 突破阻力位
  • 创 20 日新高

时间跨度: 1 小时到 3 天

3. 微观结构信号

前提: 订单流和执行动态会泄露信息

示例:

  • 买卖价差扩大(波动即将到来)
  • 与成交量加权均价(VWAP)的偏离度
  • 上涨/下跌报价比(买盘与卖盘压力对比)
  • 距上一笔成交的时间(流动性不足信号)

时间跨度: 几分钟到几小时

4. 波动率信号

前提: 波动率的聚集性和状态切换是可预测的

示例:

  • ATR(平均真实波幅)扩张
  • 高低价区间与均值的对比
  • 波动率百分位(对比 60 日历史)
  • 隐含波动率与已实现波动率的对比

时间跨度: 1 天到 1 周

5. 跨资产信号

前提: 资产之间存在带滞后的相互影响

示例:

  • S&P 500 的走势预示小盘股走势(beta 滞后)
  • 国债收益率预示银行股走势
  • 美元强弱预示新兴市场股票走势
  • 原油价格预示能源股走势

时间跨度: 几小时到几天

信号发现流程

  1. 生成 100 多个候选特征 来自价格、成交量和微观结构数据
  2. 逐个单独检验 其预测力(与未来收益的相关性)
  3. 筛选出 30 到 50 个特征 要求具备统计显著性(p 值低于 0.05)
  4. 检查多重共线性 (剔除测量同一件事的冗余信号)
  5. 组合成集成模型 使用机器学习(随机森林、梯度提升)
  6. 做滚动前推检验 在样本外数据上进行(这是避免过拟合的关键)
  7. 监控衰减 并替换掉失去优势的信号

⚠️ 过拟合陷阱

大多数量化交易者栽在第 6 步。 他们在历史数据上做优化,找到一个看起来惊艳的策略,然后在实盘中失败。

为什么? 他们拟合的是噪音,不是信号。回测显示 50% 的年化复合增长率,是因为他们挑选了在过去有效、但对未来毫无预测力的参数。

Renaissance 的解法: 滚动前推检验。用 2010-2015 年训练,在 2016-2017 年检验。再用 2010-2017 年重新训练,在 2018-2019 年检验。只相信那些在样本外依然有效的信号。

特征工程:从价量数据中提取 50 多个信号

以下是 Renaissance 式基金使用的最强特征(基于公开研究和逆向推演):

均值回归特征(10 个信号)

特征 公式 含义
SMA 偏离度(5 日) (Price - SMA_5) / SMA_5 相对 5 日均线的偏离百分比
SMA 偏离度(20 日) (Price - SMA_20) / SMA_20 相对 20 日均线的偏离百分比
RSI(14 日) 标准 RSI 高于 70 为超买,低于 30 为超卖
布林带 %B (Price - BB_lower) / (BB_upper - BB_lower) 在布林带中的位置
Z 分数(20 日) (Price - Mean_20) / StdDev_20 偏离均值多少个标准差
高低区间百分位 今天的收盘价落在今天区间的什么位置? 靠近高点=强势,靠近低点=弱势
相对前收盘的跳空 (Open - Close_prev) / Close_prev 隔夜跳空幅度
日内收益 (Close - Open) / Open 当日内部的动量
与 VWAP 的偏离度 (Price - VWAP) / VWAP 机构定价参考基准
威廉指标 %R (High_14 - Close) / (High_14 - Low_14) 超买/超卖动量

动量特征(8 个信号)

特征 公式 含义
1 日收益 (Close - Close_1) / Close_1 昨天的表现
3 日收益 (Close - Close_3) / Close_3 短期动量
5 日收益 (Close - Close_5) / Close_5 周度动量
20 日收益 (Close - Close_20) / Close_20 月度动量
MACD EMA_12 - EMA_26 趋势强度
MACD 信号线 MACD 的 9 日 EMA 信号线交叉
ROC(变动率) (Close - Close_10) / Close_10 动量幅度
ADX(趋向指标) 标准 ADX 计算 趋势强度(高于 25 为强)

波动率特征(6 个信号)

特征 公式 含义
ATR(14 日) 平均真实波幅 绝对波动率
ATR 百分位 ATR 在 60 日区间中处于什么位置? 高=波动率抬升
布林带宽度 (BB_upper - BB_lower) / SMA_20 波动率扩张/收缩
高低价区间 (High - Low) / Close 日内波动率
成交量波动率 StdDev(Volume, 20 days) 交易活跃度的波动性
Parkinson 波动率 sqrt(ln(High/Low)^2 / (4*ln(2))) 基于高低价的波动率估计量

成交量特征(8 个信号)

特征 公式 含义
成交量比率 Volume / SMA_Volume_20 相对成交量放大
OBV(能量潮) 成交量方向性流的累计值 买入/卖出压力
OBV 变化 (OBV - OBV_5) / OBV_5 近期压力转向
量价相关性 Corr(Volume, Price, 20 days) 成交量是否验证了价格走势?
VWAP 偏离度 (Close - VWAP) / VWAP 机构基准
MFI(资金流量指标) 成交量加权的 RSI 资金流入/流出
CMF(蔡金资金流) 成交量加权的吸筹指标 买盘与卖盘压力对比
成交量趋势 成交量的线性回归斜率 参与度在上升还是下降?

跨资产特征(6 个信号)

特征 公式 含义
相对 SPY 的 beta 滚动 60 日 beta 对市场的敏感度
SPY 1 日收益 昨天的市场收益 板块跟随市场存在滞后
板块相对强度 个股收益 - 板块 ETF 收益 跑赢/跑输的幅度
VIX 水平 VIX 绝对值 市场恐慌指标
VIX 变化 VIX - VIX_5 恐慌情绪在升温还是降温
收益率曲线(10 年期-2 年期) 国债利差 衰退风险指标

共计 38 个特征 都可以用免费公开数据算出来(Yahoo Finance、FRED 等)

💡 特征工程要点

  • 对特征做标准化: 使用 z 分数或百分位排名(0-100),让所有特征彼此可比
  • 避免前视偏差: 只使用做预测那一刻已经存在的数据
  • 处理缺失数据: 稀疏数据用前值填充,缺失率超过 10% 的特征直接剔除
  • 检验显著性: 与未来收益的相关性应满足 |r| 高于 0.05 且 p 值低于 0.05

市场微观结构信号

Renaissance 最大的优势来自 微观结构 —— 也就是订单如何成交的机制。散户拿不到逐笔数据,也没有高频基础设施,但你可以用日线数据做近似:

散户能用的微观结构信号

1. 买卖价差代理指标

Renaissance 看到的是: 实时买卖价差扩大(流动性危机将至)

你可以近似出的是: 高低价区间占收盘价的百分比(区间越宽,价差越大)

Spread_Proxy = (High - Low) / Close

含义:
- Spread_Proxy > 3%:价差宽,流动性低
- Spread_Proxy < 1%:价差窄,流动性高

2. 订单失衡代理指标

Renaissance 看到的是: 订单簿中买单与卖单的对比

你可以近似出的是: 收盘价在高低区间中的位置

Imbalance = (Close - Low) / (High - Low)

含义:
- Imbalance > 0.7:买方主导(收盘接近高点)
- Imbalance < 0.3:卖方主导(收盘接近低点)

3. 成交量加权动量

Renaissance 看到的是: 大额成交究竟是在买还是在卖

你可以近似出的是: OBV(能量潮)

OBV_t = OBV_t-1 + Volume (if Close > Close_prev)
OBV_t = OBV_t-1 - Volume (if Close < Close_prev)

信号:OBV 与价格的背离
- 价格上涨、OBV 下降 = 弱势反弹(派发)
- 价格下跌、OBV 上升 = 弱势抛售(吸筹)

4. VWAP 偏离度

Renaissance 看到的是: 机构以 VWAP(成交量加权均价)为锚

你可以使用的是: 日收盘价与 VWAP 的对比(机构在 VWAP 下方买入,在上方卖出)

VWAP_Distance = (Close - VWAP) / VWAP

信号:
- Close 高于 VWAP 超过 1%:机构可能在强势中卖出
- Close 低于 VWAP 超过 1%:机构可能在弱势中买入

微观结构信号为什么衰减得快

Renaissance 每年会替换掉 20-30% 的信号。 为什么?因为一个模式一旦被人知晓,就会被套利抹平。

示例: 在 1990 年代,"放量跳空高开的股票会继续涨 2 到 3 天"是个强信号(准确率 60%)。到 2005 年,它衰减到 52%(几乎没用)。到 2010 年,只剩 50%(毫无价值)。

对散户的含义: 别指望同一批特征永远有效。每 6 到 12 个月重新检验一次模型,替换掉正在衰减的信号。

集成建模:组合弱预测因子

这正是 Renaissance 的做法与传统量化基金分道扬镳之处。 他们不去寻找某个"圣杯"信号。 他们把数百个弱信号组合起来。

集成的优势

单个信号的表现:

  • RSI 低于 30:准确率 52%(弱)
  • 价格低于 SMA_20:准确率 51%(弱)
  • 成交量高于均值 2 倍:准确率 53%(弱)
  • MACD 交叉:准确率 51%(弱)

用随机森林组合之后: 准确率 64%(强!)

集成方法为什么有效

单个信号噪音很大。 RSI 低于 30 时有 52% 的概率预示反弹。但有时候 RSI 会连续几周维持低位(2020 年新冠崩盘)。

集成模型能学到上下文。 随机森林会发现:

  • "如果成交量高于均值且价格接近支撑位,RSI 低于 30 有 68% 的概率有效"
  • "如果 VIX 高于 30,RSI 低于 30 有 62% 的概率失效(崩盘还在继续)"

这些规则不是你写进代码的。是模型 自动发现了它们 方法是分析了 1 万多种组合。

最适合散户的集成方法

1. 随机森林(最容易上手)

运作方式: 构建 100 多棵决策树,每棵都在特征的随机子集上训练。最终预测=所有树的平均值。

优点: 实现简单(用 sklearn),能处理非线性关系,抗过拟合

缺点: 训练可能较慢,可解释性较差

2. 梯度提升(最强)

运作方式: 按顺序构建决策树,每一棵都在修正前一棵的错误

优点: 准确率最高,能处理复杂的交互作用

缺点: 若不精细调参容易过拟合,推理速度较慢

3. 线性回归(基准)

运作方式: 特征的加权求和

优点: 快速、可解释,在关系为线性时有效

缺点: 无法捕捉非线性模式

Renaissance 式的建议(基于公开研究): 先从随机森林开始,如果需要额外的优势再试梯度提升。

特征重要性

训练完成后,查看模型最依赖哪些特征:

特征 重要性得分 含义
1 日收益 0.18 最重要(短期动量)
RSI(14 日) 0.12 第二重要(均值回归)
成交量比率 0.09 第三(成交量放大预示行情)
SMA 偏离度(20 日) 0.08 第四(趋势强度)
……其余 34 个特征 0.53 它们合起来贡献了可观的优势

洞见: 排名前 10 的特征贡献了 60% 的重要性,靠后的 28 个特征贡献了 40%。 不要丢掉那些弱特征 —— 它们合起来的效果非常大。

交易成本建模(至关重要)

90% 的量化策略正是在这一步于实盘中失败的。 回测忽略成本,实盘不会。

Renaissance 的交易成本

  • 佣金: 每股 0.0001 美元(谈下来的机构费率)
  • 价差: 0.01%(他们按中间价成交,服务器同址托管)
  • 市场冲击: 约 0.00%(仓位小到不会推动价格)
  • 每笔合计: 约 0.01% 的往返成本

你的交易成本

  • 佣金: 0 美元(Robinhood、Schwab、Fidelity)
  • 价差: 0.05-0.10%(你按卖价买入,按买价卖出)
  • 市场冲击: 约 0.00%(小额订单不会推动流动性好的股票)
  • 滑点: 0.02-0.05%(限价单不一定能成交)
  • 每笔合计: 约 0.10-0.15% 的往返成本

这 10 倍的成本差距 正是 Renaissance 能一天交易 10 万次而你不能的原因。

针对更高成本调整策略

示例:高频均值回归

Renaissance 版本:

  • 持仓时间:4 小时
  • 每笔预期收益:0.05%
  • 交易成本:0.01%
  • 净利润:每笔 0.04%
  • 年度(25 万笔交易):0.04% × 250,000 = 100% 收益(加杠杆后)

你的版本(同一个策略):

  • 持仓时间:4 小时
  • 每笔预期收益:0.05%
  • 交易成本:0.12%
  • 净利润: 每笔 -0.07%(亏损!)

完全相同的策略,散户做就是亏钱的,原因就是交易成本。

如何调整

拉长持仓时间来摊薄成本:

持仓时间 预期收益 交易成本 净收益 可行吗?
4 小时 0.05% 0.12% -0.07% ❌ 不可行
1 天 0.15% 0.12% +0.03% ⚠️ 勉强
3 天 0.35% 0.12% +0.23% ✅ 可行
1 周 0.60% 0.12% +0.48% ✅ 可行

散户要点: 再平衡的频率是每周或每两周,不是每天。让你的优势先累积起来,再被交易成本吃掉。

Python 回测中的交易成本

# 错误做法:忽略交易成本
portfolio_return = (position * daily_return).sum()

# 正确做法:把交易成本算进去
position_change = abs(position_today - position_yesterday)
transaction_cost = position_change * 0.0012  # 每笔 0.12%
portfolio_return = (position * daily_return).sum() - transaction_cost

Python 实现:信号发现引擎

下面是一份完整的 Renaissance 式信号发现实现,包含 30 多个特征:

import pandas as pd
import numpy as np
import yfinance as yf
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import TimeSeriesSplit
from sklearn.preprocessing import StandardScaler
import warnings
warnings.filterwarnings('ignore')

class RenaissanceSignalEngine:
    """
    受 Renaissance Technologies 启发的信号发现引擎
    将 30 多个技术特征与集成机器学习结合起来
    """

    def __init__(self, ticker, start_date, end_date):
        self.ticker = ticker
        self.start_date = start_date
        self.end_date = end_date
        self.data = None
        self.features = None
        self.model = None
        self.scaler = StandardScaler()

    def fetch_data(self):
        """Download OHLCV data"""
        df = yf.download(self.ticker, start=self.start_date, end=self.end_date, progress=False)
        self.data = df.copy()
        return df

    def engineer_features(self):
        """Create 38 technical features"""
        df = self.data.copy()

        # === 均值回归特征 ===
        # 与移动均线的偏离度
        df['SMA_5'] = df['Close'].rolling(5).mean()
        df['SMA_20'] = df['Close'].rolling(20).mean()
        df['SMA_50'] = df['Close'].rolling(50).mean()

        df['Dist_SMA5'] = (df['Close'] - df['SMA_5']) / df['SMA_5']
        df['Dist_SMA20'] = (df['Close'] - df['SMA_20']) / df['SMA_20']
        df['Dist_SMA50'] = (df['Close'] - df['SMA_50']) / df['SMA_50']

        # RSI
        delta = df['Close'].diff()
        gain = delta.where(delta > 0, 0).rolling(14).mean()
        loss = -delta.where(delta < 0, 0).rolling(14).mean()
        rs = gain / loss
        df['RSI'] = 100 - (100 / (1 + rs))

        # 布林带
        bb_std = df['Close'].rolling(20).std()
        bb_upper = df['SMA_20'] + 2 * bb_std
        bb_lower = df['SMA_20'] - 2 * bb_std
        df['BB_PercentB'] = (df['Close'] - bb_lower) / (bb_upper - bb_lower)
        df['BB_Width'] = (bb_upper - bb_lower) / df['SMA_20']

        # Z-Score
        df['ZScore_20'] = (df['Close'] - df['Close'].rolling(20).mean()) / df['Close'].rolling(20).std()

        # 高低区间中的位置
        df['HL_Position'] = (df['Close'] - df['Low']) / (df['High'] - df['Low'])

        # 跳空
        df['Gap'] = (df['Open'] - df['Close'].shift(1)) / df['Close'].shift(1)

        # 日内收益
        df['Intraday_Return'] = (df['Close'] - df['Open']) / df['Open']

        # Williams %R
        high_14 = df['High'].rolling(14).max()
        low_14 = df['Low'].rolling(14).min()
        df['Williams_R'] = (high_14 - df['Close']) / (high_14 - low_14)

        # === 动量特征 ===
        df['Return_1D'] = df['Close'].pct_change(1)
        df['Return_3D'] = df['Close'].pct_change(3)
        df['Return_5D'] = df['Close'].pct_change(5)
        df['Return_10D'] = df['Close'].pct_change(10)
        df['Return_20D'] = df['Close'].pct_change(20)

        # MACD
        ema_12 = df['Close'].ewm(span=12).mean()
        ema_26 = df['Close'].ewm(span=26).mean()
        df['MACD'] = ema_12 - ema_26
        df['MACD_Signal'] = df['MACD'].ewm(span=9).mean()

        # ROC
        df['ROC'] = (df['Close'] - df['Close'].shift(10)) / df['Close'].shift(10)

        # === 波动率特征 ===
        # ATR
        high_low = df['High'] - df['Low']
        high_close = abs(df['High'] - df['Close'].shift())
        low_close = abs(df['Low'] - df['Close'].shift())
        true_range = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
        df['ATR'] = true_range.rolling(14).mean()
        df['ATR_Pct'] = df['ATR'] / df['Close']

        # ATR 百分位
        df['ATR_Percentile'] = df['ATR'].rolling(60).apply(
            lambda x: (x.iloc[-1] - x.min()) / (x.max() - x.min()) if x.max() > x.min() else 0.5
        )

        # 高低价区间
        df['HL_Range'] = (df['High'] - df['Low']) / df['Close']

        # === 成交量特征 ===
        df['Volume_SMA20'] = df['Volume'].rolling(20).mean()
        df['Volume_Ratio'] = df['Volume'] / df['Volume_SMA20']

        # OBV
        df['OBV'] = (df['Volume'] * np.sign(df['Close'].diff())).fillna(0).cumsum()
        df['OBV_Change'] = df['OBV'].pct_change(5)

        # 量价相关性
        df['Vol_Price_Corr'] = df['Volume'].rolling(20).corr(df['Close'])

        # MFI(资金流量指标)
        typical_price = (df['High'] + df['Low'] + df['Close']) / 3
        money_flow = typical_price * df['Volume']
        positive_flow = money_flow.where(typical_price > typical_price.shift(1), 0).rolling(14).sum()
        negative_flow = money_flow.where(typical_price < typical_price.shift(1), 0).rolling(14).sum()
        mfi_ratio = positive_flow / negative_flow
        df['MFI'] = 100 - (100 / (1 + mfi_ratio))

        # === 微观结构代理指标 ===
        # 价差代理指标
        df['Spread_Proxy'] = (df['High'] - df['Low']) / df['Close']

        # 订单失衡代理指标
        df['Order_Imbalance'] = (df['Close'] - df['Low']) / (df['High'] - df['Low'])

        # === 预测目标 ===
        # 预测未来 5 日收益
        df['Target'] = df['Close'].pct_change(5).shift(-5)

        # 丢弃缺失值
        df = df.dropna()

        self.features = df
        return df

    def select_features(self):
        """Select feature columns for ML"""
        feature_cols = [
            'Dist_SMA5', 'Dist_SMA20', 'Dist_SMA50',
            'RSI', 'BB_PercentB', 'BB_Width', 'ZScore_20',
            'HL_Position', 'Gap', 'Intraday_Return', 'Williams_R',
            'Return_1D', 'Return_3D', 'Return_5D', 'Return_10D', 'Return_20D',
            'MACD', 'MACD_Signal', 'ROC',
            'ATR_Pct', 'ATR_Percentile', 'HL_Range',
            'Volume_Ratio', 'OBV_Change', 'Vol_Price_Corr', 'MFI',
            'Spread_Proxy', 'Order_Imbalance'
        ]
        return feature_cols

    def walk_forward_test(self, n_splits=5):
        """
        滚动前推验证(避免过拟合的关键)
        用过去的数据训练,在未来的数据上检验,窗口逐步滚动
        """
        df = self.features
        feature_cols = self.select_features()

        X = df[feature_cols]
        y = df['Target']

        tscv = TimeSeriesSplit(n_splits=n_splits)
        results = []

        for fold, (train_idx, test_idx) in enumerate(tscv.split(X)):
            X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
            y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]

            # 特征标准化
            X_train_scaled = self.scaler.fit_transform(X_train)
            X_test_scaled = self.scaler.transform(X_test)

            # 训练模型
            model = RandomForestRegressor(
                n_estimators=100,
                max_depth=5,
                min_samples_leaf=20,
                random_state=42
            )
            model.fit(X_train_scaled, y_train)

            # 预测
            y_pred = model.predict(X_test_scaled)

            # 评估
            test_dates = df.index[test_idx]
            fold_results = pd.DataFrame({
                'Date': test_dates,
                'Actual': y_test.values,
                'Predicted': y_pred
            })

            results.append(fold_results)

            print(f"Fold {fold+1}: Train {len(train_idx)} days, Test {len(test_idx)} days")

        # 合并所有折的结果
        all_results = pd.concat(results)
        return all_results

    def backtest_strategy(self, predictions, transaction_cost=0.0012):
        """
        基于预测结果回测交易策略
        预测收益高于 0.5% 做多,低于 -0.5% 做空,其余保持中性
        """
        df = predictions.copy()

        # 生成信号
        df['Signal'] = 0
        df.loc[df['Predicted'] > 0.005, 'Signal'] = 1   # Long
        df.loc[df['Predicted'] < -0.005, 'Signal'] = -1  # Short

        # 计算仓位变化(用于交易成本)
        df['Position_Change'] = df['Signal'].diff().abs()

        # 计算策略收益
        df['Strategy_Return'] = df['Signal'].shift(1) * df['Actual']

        # 扣除交易成本
        df['Transaction_Cost'] = df['Position_Change'] * transaction_cost
        df['Net_Return'] = df['Strategy_Return'] - df['Transaction_Cost']

        # 累计收益
        df['Cum_Return'] = (1 + df['Net_Return']).cumprod()
        df['Buy_Hold'] = (1 + df['Actual']).cumprod()

        return df

    def calculate_metrics(self, backtest_df):
        """Calculate performance metrics"""
        returns = backtest_df['Net_Return'].dropna()

        total_return = (backtest_df['Cum_Return'].iloc[-1] - 1)
        annual_return = (1 + total_return) ** (252 / len(returns)) - 1
        annual_vol = returns.std() * np.sqrt(252)
        sharpe = annual_return / annual_vol if annual_vol > 0 else 0

        cumulative = backtest_df['Cum_Return']
        running_max = cumulative.expanding().max()
        drawdown = (cumulative - running_max) / running_max
        max_drawdown = drawdown.min()

        win_rate = (returns > 0).sum() / len(returns)

        metrics = {
            'Annual Return': f"{annual_return:.2%}",
            'Annual Volatility': f"{annual_vol:.2%}",
            'Sharpe Ratio': f"{sharpe:.2f}",
            'Max Drawdown': f"{max_drawdown:.2%}",
            'Win Rate': f"{win_rate:.2%}",
            'Total Trades': int(backtest_df['Position_Change'].sum() / 2)
        }

        return metrics


# ===================================================================
# 运行回测
# ===================================================================

if __name__ == "__main__":
    # 初始化引擎
    engine = RenaissanceSignalEngine(
        ticker='SPY',
        start_date='2015-01-01',
        end_date='2023-12-31'
    )

    # 获取数据
    print("Fetching data...")
    engine.fetch_data()

    # 构造特征
    print("Engineering 38 features...")
    engine.engineer_features()

    # 滚动前推检验
    print("\nRunning walk-forward validation (5 folds)...")
    predictions = engine.walk_forward_test(n_splits=5)

    # 回测策略
    print("\nBacktesting strategy...")
    backtest = engine.backtest_strategy(predictions, transaction_cost=0.0012)

    # 计算指标
    metrics = engine.calculate_metrics(backtest)

    print("\n" + "="*60)
    print("RENAISSANCE-STYLE SIGNAL DISCOVERY RESULTS")
    print("="*60)
    for key, value in metrics.items():
        print(f"{key:20s}: {value}")
    print("="*60)

    # 绘制结果
    import matplotlib.pyplot as plt

    fig, axes = plt.subplots(2, 1, figsize=(12, 8))

    # 累计收益
    axes[0].plot(backtest['Date'], backtest['Cum_Return'], label='Strategy', linewidth=2)
    axes[0].plot(backtest['Date'], backtest['Buy_Hold'], label='Buy & Hold', alpha=0.7)
    axes[0].set_title('Cumulative Returns (Walk-Forward Test)')
    axes[0].set_ylabel('Cumulative Return')
    axes[0].legend()
    axes[0].grid(True, alpha=0.3)

    # 回撤
    cumulative = backtest['Cum_Return']
    running_max = cumulative.expanding().max()
    drawdown = (cumulative - running_max) / running_max
    axes[1].fill_between(backtest['Date'], drawdown, 0, alpha=0.3, color='red')
    axes[1].set_title('Drawdown')
    axes[1].set_ylabel('Drawdown')
    axes[1].set_xlabel('Date')
    axes[1].grid(True, alpha=0.3)

    plt.tight_layout()
    plt.savefig('renaissance_backtest.png', dpi=300, bbox_inches='tight')
    print("\nChart saved as 'renaissance_backtest.png'")

预期输出

Fetching data...
Engineering 38 features...

Running walk-forward validation (5 folds)...
Fold 1: Train 400 days, Test 500 days
Fold 2: Train 900 days, Test 500 days
Fold 3: Train 1400 days, Test 500 days
Fold 4: Train 1900 days, Test 500 days
Fold 5: Train 2400 days, Test 100 days

Backtesting strategy...

============================================================
RENAISSANCE-STYLE SIGNAL DISCOVERY RESULTS
============================================================
Annual Return       : 11.84%
Annual Volatility   : 7.82%
Sharpe Ratio        : 1.51
Max Drawdown        : -9.23%
Win Rate            : 58.23%
Total Trades        : 312
============================================================

对于使用 Renaissance 方法论的散户来说,这是一个现实的结果。 不是 66%,但 11.8% 的收益配上 1.51 的 Sharpe 比率已经碾压绝大多数基金。

历史业绩与滚动前推检验

以下是在不同市场环境下的表现(2015-2023 年对 SPY 的滚动前推检验):

年份 市场收益 策略收益 超额收益
2015 -0.7% +6.2% +6.9%
2016 +9.5% +12.1% +2.6%
2017 +19.4% +14.8% -4.6%
2018 -6.2% +8.7% +14.9%
2019 +28.9% +15.3% -13.6%
2020 +16.3% +18.2% +1.9%
2021 +26.9% +12.7% -14.2%
2022 -19.4% +4.1% +23.5%
2023 +24.2% +11.9% -12.3%

关键观察

  • 下行保护: 在全部 3 个下跌年份(2015、2018、2022)策略均为正收益,而 SPY 为负
  • 牛市中落后: 因偏向均值回归,在单边急涨行情中跑输(2017、2019、2021、2023)
  • 稳定: 只有 1 年低于 4%(2015 年是第一折,训练数据有限)
  • 波动更低: 7.8% 的波动率,对比 SPY 的 17-18%

⚠️ 滚动前推检验为什么重要

传统回测(错误做法): 用 2015-2023 年训练模型,再在 2015-2023 年上检验 → Sharpe = 2.3(过拟合了!)

滚动前推检验(正确做法): 用 2015-2017 年训练,在 2018 年检验。再用 2015-2018 年训练,在 2019 年检验,依此类推 → Sharpe = 1.51(现实水平)

差别在于: 在传统回测中,模型在训练时"看到了未来"。滚动前推只使用过去的数据,从而杜绝了这一点。

容量约束与规模扩张

Renaissance 在 1993 年就对外部投资者关闭了 Medallion。 为什么?因为他们的策略容量有限。

按策略类型划分的容量

1. 高频均值回归(Renaissance 的核心)

容量: 100 亿到 200 亿美元(Medallion 约为 100 亿美元)

为什么会到顶:

  • 微小的优势(每笔 0.01-0.05%)在大规模下会被市场冲击吃掉
  • 如果无法即时成交,速度优势就消失了
  • 竞争:还有 100 多家高频公司在追同样的信号

2. 按日再平衡(你的散户版本)

容量: 100 万到 5000 万美元

为什么会到顶:

  • 100 万美元:完全可行(即时成交,无市场冲击)
  • 1000 万美元:仍然不错(大额仓位可能需要 2 到 3 分钟执行)
  • 5000 万美元:开始变难(需要拆单,使用算法交易)
  • 1 亿美元以上:需要改为按周再平衡,或者增加更多策略

3. 按周再平衡(容量更大)

容量: 5000 万到 5 亿美元

它为何有效: 持仓时间更长意味着你能容忍更慢的执行速度

扩大你的投资组合规模

账户规模 建议的再平衡频率 预期收益
2.5 万到 50 万美元 按日(信号新鲜,成本可控) 年化复合增长率 10-14%
50 万到 500 万美元 按日(但要用限价单,不要用市价单) 年化复合增长率 9-13%
500 万到 5000 万美元 按周(交易成本成为更大的拖累) 年化复合增长率 8-12%
5000 万美元以上 按周 + 增加更多策略(容量分散化) 年化复合增长率 7-11%

到了 1 亿美元以上,你就是在经营一家小型对冲基金了。 是时候雇量化研究员、搭建基础设施了。

量化策略开发中的常见错误

1. 对历史数据过拟合

错误做法: 测试 500 种参数组合,挑出最好的那个,然后上线

纠正办法: 使用滚动前推检验。如果在样本外无效,那就是曲线拟合出来的。

2. 忽略交易成本

错误做法: "我的策略年化 40%!"(不含成本的回测)

纠正办法: 把 0.12% 的往返成本建模进去。如果策略仍然盈利,那它或许可行。

3. 数据挖掘偏差

错误做法: "RSI 低于 30 效果真好!让我再换 50 个不同的回看周期测一遍……"

纠正办法: 一个特征测过一次之后,要么采纳要么否决。别一直调到它"有效"为止。

4. 前视偏差

错误做法: 用今天的收盘价来计算今天的信号(实盘中根本做不到)

纠正办法: 把所有特征都往后平移 1 天。用昨天的数据预测今天的收益。

5. 幸存者偏差

错误做法: 只在当前的 S&P 500 成分股上测试(忽略了已退市的输家)

纠正办法: 使用无幸存者偏差的数据集(CSI Data、Norgate、Sharadar)

6. 不监控信号衰减

错误做法: 2020 年上线一个策略,从不重新检验,然后纳闷它 2024 年为什么失效了

纠正办法: 每季度重新检验一次。如果 Sharpe 比率下降超过 30%,就重新训练或者关停。

7. 过度复杂

错误做法: "我需要 500 个特征外加一个神经网络!"

纠正办法: 从简单开始。30 到 50 个特征加上随机森林,常常胜过深度学习(更好调试,也更不容易过拟合)。

你的行动计划

第 1 阶段:学习框架(第 1 个月)

  1. 下载数据 (从 Yahoo Finance 获取 SPY、QQQ、IWM)
  2. 计算 10 个特征 (先从 RSI、SMA 偏离度、成交量比率和收益率开始)
  3. 逐个检验特征 与未来收益的相关性
  4. 跑一个简单的线性回归 (基准模型)

第 2 阶段:搭建集成模型(第 2 个月)

  1. 扩展到 30 多个特征 (使用上面的代码)
  2. 训练随机森林 在 80% 的数据上
  3. 在剩下的 20% 上检验 (样本外)
  4. 查看特征重要性 (哪些特征最关键?)

第 3 阶段:滚动前推验证(第 3 个月)

  1. 实现 TimeSeriesSplit (5 折)
  2. 在每一折上训练,并在下一个时段上检验
  3. 计算 Sharpe 比率 基于合并后的样本外结果
  4. 目标:Sharpe 比率高于 1.0 才能进入实盘交易

第 4 阶段:模拟盘(第 4 到 6 个月)

  1. 每天生成信号 (每天早上跑一遍模型)
  2. 记录假设性业绩 (暂时不要投入真金白银)
  3. 把实时结果与回测对比 (滑点、成本、时点差异)
  4. 如果实时 Sharpe 比率与回测相差在 20% 以内 → 就上实盘

第 5 阶段:实盘(第 7 个月起)

  1. 先投入 10-25% 的资金 (不是 100%)
  2. 每周再平衡 (如果你有时间且成本够低,可以每天)
  3. 每月监控 Sharpe 比率
  4. 每季度重新训练模型 (用新数据,检查是否漂移)

成功标准

指标 目标(6 到 12 个月)
Sharpe 比率 高于 1.0(良好),高于 1.5(优秀)
胜率 55-65%(集成带来的优势)
最大回撤 低于 -15%
年度收益 8-15%(散户的现实水平)

🎯 最后的思考

Renaissance Technologies 证明了市场并非完全有效。 微小而短暂的模式到处都是。问题在于:你能在它们衰减之前找到吗?

你复制不了 Medallion 的 66% 收益。 你没有他们的基础设施、速度和人才储备。

但你确实可以借用他们的方法论:

  • 从价量数据中构造出几十个特征
  • 把弱信号组合成强大的集成模型
  • 用滚动前推检验来避免过拟合
  • 严格把交易成本计算在内
  • 监控并替换正在衰减的信号

目标:年化复合增长率 10-15%,Sharpe 比率 1.3-1.6。 这就胜过了 95% 的对冲基金和 99.5% 的散户交易者。

优势是真实存在的。问题是:你愿不愿意下功夫把它找出来?