Claude Cursor Skill

alpha101

WorldQuant 101 Formulaic Alphas — 因子计算、IC测试、回测一体化工具包。 基于Kakushadze (2015) 论文,提供101个价量/波动率/相关性因子的Python/Pandas实现。 Use when: "alpha101", "101因子", "formulaic alphas", "因子回测", "因子IC", "因子筛选", "WorldQuant因子", "价量因子", "alpha因子库".

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download aaaaqwq-agi-super-team-skills_alpha101-cdb04e8.zip · 13 KB
Part of aaaaqwq/agi-super-team — 46 skills

Install

skills CLI npx skills add https://github.com/aAAaqwq/AGI-Super-Team/tree/main/skills/alpha101
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install aaaaqwq-agi-super-team@llmmart
Git git clone https://github.com/aAAaqwq/AGI-Super-Team.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole aaaaqwq/agi-super-team collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Alpha101 — WorldQuant 101 Formulaic Alphas

概述

Zura Kakushadze 论文《101 Formulaic Alphas》(arXiv:1601.00991) 的完整Python实现。 101个真实量化交易alpha因子,公式即代码。

论文关键数据

  • 平均持仓期: 0.6-6.4天
  • 平均两两相关性: 15.9%
  • 收益与波动率强相关: R ~ σ^0.76

文件结构

skills/alpha101/
├── SKILL.md              ← 本文件
├── scripts/
│   ├── alpha101.py       ← 101个因子函数 + 基础函数库
│   ├── compute_ic.py     ← 因子IC/IR计算
│   └── backtest_alpha.py ← 单因子回测
└── references/
    └── paper_notes.md    ← 论文笔记与函数定义

使用方式

1. 因子计算

from scripts.alpha101 import compute_alphas, alpha101

# 输入: date x ticker DataFrame
data = {
    'open': df_open, 'close': df_close, 'high': df_high, 'low': df_low,
    'volume': df_vol, 'vwap': df_vwap, 'returns': df_returns
}

# 计算所有可用因子
alphas = compute_alphas(data)  # dict of alpha_name -> DataFrame

# 单独计算
from scripts.alpha101 import alpha101
a101 = alpha101(df_open, df_close, df_high, df_low)

2. 因子IC测试

python scripts/compute_ic.py --data <path> --output results/

3. 单因子回测

python scripts/backtest_alpha.py --alpha 101 --data <path>

因子分类

类别 因子 输入
纯价量 #1-#47, #49-#55, #60, #61, #71-#74, #84, #88, #101 OHLCV + VWAP
行业中性化 #48, #56, #58-#59, #63, #67, #69-#70, #76, #79-#82, #87, #89-#91, #93, #97, #100 + 行业分类
复杂参数 #57-#99 非整数窗口, 混合权重

基础函数速查

rank(x)              截面排名 [0,1]
delay(x,d)           d天前的值
delta(x,d)           当期 - d天前
correlation(x,y,d)   d天滚动相关
scale(x,a=1)         缩放使sum(abs(x))=a
decay_linear(x,d)    线性衰减加权均值
ts_min/ts_max(x,d)   滚动最小/最大
ts_rank(x,d)         时间序列排名
ts_sum/ts_std(x,d)   滚动求和/标准差
adv{d}               d天平均成交额
IndNeutralize(x,ind) 行业中性化

回测注意事项

  1. 交易成本: 论文因子扣除cost后Sharpe才是真Sharpe
  2. 过拟合: 101个因子中部分可能已衰减,需样本外验证
  3. 市场适配:
    • A股: T+1限制,持仓期需调整
    • 加密: 24/7,日频→小时频需改窗口参数
    • Polymarket: 流动性低,部分因子不适用
  4. 行业因子: 需要行业分类映射,加密市场可用板块替代

决策框架

因子计算 → IC/IR筛选(>0.03) → 样本外验证 → 组合构建(低相关等权) → 回测扣费 → 实盘
Files (agi-super-team)
  • references
    • paper_notes.md 2 KB
      # 101 Formulaic Alphas — 论文笔记
      
      > Kakushadze, Z. (2015). "101 Formulaic Alphas". arXiv:1601.00991
      
      ## 核心发现
      
      1. **低相关组合**: 101个alpha平均两两相关仅15.9%,适合组合
      2. **收益∝波动率**: R ~ σ^0.76,高波动alpha收益更高
      3. **换手率无解释力**: 换手率对alpha收益和相关性无显著影响
      4. **持仓期0.6-6.4天**: 短线因子为主
      
      ## 因子数据需求
      
      ### 基础输入
      | 变量 | 定义 |
      |------|------|
      | open | 日开盘价 |
      | close | 日收盘价 |
      | high | 日最高价 |
      | low | 日最低价 |
      | volume | 日成交量 |
      | vwap | 日成交量加权均价 |
      | returns | 日收益率 (close-to-close) |
      | cap | 市值 |
      | adv{d} | 过去d天平均成交额 |
      
      ### 行业分类(部分因子需要)
      - GICS (Global Industry Classification Standard)
      - BICS, NAICS, SIC 等均可
      
      ## 函数定义
      
      | 函数 | 定义 |
      |------|------|
      | rank(x) | 截面排名归一化[0,1] |
      | delay(x,d) | x在d天前的值 |
      | delta(x,d) | x - delay(x,d) |
      | correlation(x,y,d) | d天滚动相关 |
      | covariance(x,y,d) | d天滚动协方差 |
      | scale(x,a) | sum(\|x\|)=a |
      | sign(x) | 符号函数 |
      | signedpower(x,a) | x^a |
      | decay_linear(x,d) | 线性衰减WMA |
      | ts_min/ts_max(x,d) | 滚动min/max |
      | ts_argmin/ts_argmax(x,d) | min/max位置 |
      | ts_rank(x,d) | 时间序列排名 |
      | sum/stddev(x,d) | 滚动统计 |
      | product(x,d) | 滚动连乘 |
      | IndNeutralize(x,IndClass) | 行业中性化 |
      
      ## 部分因子解析
      
      ### 简单因子(适合入门)
      - **#41**: sqrt(high*low) - vwap → 均价偏离
      - **#101**: (close-open)/(high-low) → 日内动量
      - **#54**: 价格位置反转
      - **#12**: sign(Δvolume) * (-Δclose) → 量价背离
      
      ### 高IC因子(论文暗示)
      - **#1**: 条件波动率 + Ts_ArgMax
      - **#7**: 条件ts_rank(成交量触发)
      - **#20**: 开盘缺口三重排名
      - **#55**: 价格位置与成交量相关性
      
      ### 需行业数据的因子(#48, #56, #58-100中部分)
      这些因子使用IndNeutralize,加密市场可用sector/板块分类替代。
      
  • scripts
    • alpha101.py 30.6 KB
      """
      WorldQuant 101 Formulaic Alphas — Python/Pandas Implementation
      Reference: Kakushadze, Z. (2015). "101 Formulaic Alphas". arXiv:1601.00991
      
      All 101 alpha formulas from the paper, ready for backtesting.
      Input: pandas DataFrame with MultiIndex (date, ticker) or panel (date x ticker).
      """
      
      import numpy as np
      import pandas as pd
      
      # ─── Base Functions ───────────────────────────────────────────────
      
      def rank(df):
          """Cross-sectional rank, normalized to [0,1]"""
          return df.rank(axis=1, pct=True)
      
      def scale(df, a=1):
          """Rescale so sum(abs(x)) = a"""
          return df.div(df.abs().sum(axis=1), axis=0) * a
      
      def sign(df):
          return np.sign(df)
      
      def log(df):
          return np.log1p(df)
      
      def signedpower(df, a):
          return df.pow(a)
      
      def delay(df, d):
          return df.shift(d)
      
      def delta(df, d):
          return df.diff(d)
      
      def correlation(x, y, d):
          return x.rolling(d).corr(y)
      
      def covariance(x, y, d):
          return x.rolling(d).cov(y)
      
      def ts_sum(df, d):
          return df.rolling(d).sum()
      
      def ts_mean(df, d):
          return df.rolling(d).mean()
      
      def ts_std(df, d):
          return df.rolling(d).std()
      
      def ts_min(df, d):
          return df.rolling(d).min()
      
      def ts_max(df, d):
          return df.rolling(d).max()
      
      def ts_argmax(df, d):
          return df.rolling(d).apply(np.argmax, raw=True) + 1
      
      def ts_argmin(df, d):
          return df.rolling(d).apply(np.argmin, raw=True) + 1
      
      def ts_rank(df, d):
          return df.rolling(d).apply(lambda x: pd.Series(x).rank(pct=True).iloc[-1], raw=False)
      
      def ts_product(df, d):
          return df.rolling(d).apply(np.prod, raw=True)
      
      def decay_linear(df, d):
          weights = np.arange(1, d + 1, dtype=float)
          weights /= weights.sum()
          return df.rolling(d).apply(lambda x: (x * weights).sum(), raw=True)
      
      def indneutralize(df, ind_class):
          """Industry neutralize — requires industry mapping"""
          # Placeholder: subtract industry mean
          return df.sub(df.groupby(ind_class, axis=1).mean(), axis=1)
      
      def adv(df_vol, df_close, d):
          """Average daily dollar volume over past d days"""
          return ts_mean(df_vol * df_close, d)
      
      
      # ─── Alpha #1 ~ #101 ─────────────────────────────────────────────
      
      # Alpha#1: (rank(Ts_ArgMax(SignedPower(((returns < 0) ? stddev(returns, 20) : close), 2.), 5)) - 0.5)
      def alpha001(c, r):
          """If returns < 0, use stddev(returns,20); else use close. Then SignedPower(_,2), Ts_ArgMax(_,5), rank - 0.5"""
          inner = c.copy()
          inner[r < 0] = ts_std(r, 20)
          return rank(ts_argmax(signedpower(inner, 2), 5)) - 0.5
      
      # Alpha#2: (-1 * correlation(rank(delta(log(volume), 2)), rank(((close - open) / open)), 6))
      def alpha002(o, c, v):
          return -1 * correlation(rank(delta(log(v), 2)), rank((c - o) / o), 6)
      
      # Alpha#3: (-1 * correlation(rank(open), rank(volume), 10))
      def alpha003(o, v):
          return -1 * correlation(rank(o), rank(v), 10)
      
      # Alpha#4: (-1 * Ts_Rank(rank(low), 9))
      def alpha004(l):
          return -1 * ts_rank(rank(l), 9)
      
      # Alpha#5: (rank((open - (sum(vwap, 10) / 10))) * (-1 * abs(rank((close - vwap)))))
      def alpha005(o, vwap, c):
          return rank(o - ts_mean(vwap, 10)) * (-1 * abs(rank(c - vwap)))
      
      # Alpha#6: (-1 * correlation(open, volume, 10))
      def alpha006(o, v):
          return -1 * correlation(o, v, 10)
      
      # Alpha#7: ((adv20 < volume) ? ((-1 * ts_rank(abs(delta(close, 7)), 60)) * sign(delta(close, 7))) : (-1 * 1))
      def alpha007(c, v, adv20):
          d7 = delta(c, 7)
          result = (-1 * ts_rank(abs(d7), 60)) * sign(d7)
          return result.where(adv20 < v, -1)
      
      # Alpha#8: (-1 * rank(((sum(open, 5) * sum(returns, 5)) - delay((sum(open, 5) * sum(returns, 5)), 10))))
      def alpha008(o, r):
          s = ts_sum(o, 5) * ts_sum(r, 5)
          return -1 * rank(s - delay(s, 10))
      
      # Alpha#9: ((0 < ts_min(delta(close, 1), 5)) ? delta(close, 1) : ((ts_max(delta(close, 1), 5) < 0) ? delta(close, 1) : (-1 * delta(close, 1))))
      def alpha009(c):
          d1 = delta(c, 1)
          return d1.where(ts_min(d1, 5) > 0,
                          d1.where(ts_max(d1, 5) < 0, -1 * d1))
      
      # Alpha#10: rank(((0 < ts_min(delta(close, 1), 4)) ? delta(close, 1) : ((ts_max(delta(close, 1), 4) < 0) ? delta(close, 1) : (-1 * delta(close, 1)))))
      def alpha010(c):
          d1 = delta(c, 1)
          inner = d1.where(ts_min(d1, 4) > 0,
                           d1.where(ts_max(d1, 4) < 0, -1 * d1))
          return rank(inner)
      
      # Alpha#11: ((rank(ts_max((vwap - close), 3)) + rank(ts_min((vwap - close), 3))) * rank(delta(volume, 3)))
      def alpha011(vwap, c, v):
          return (rank(ts_max(vwap - c, 3)) + rank(ts_min(vwap - c, 3))) * rank(delta(v, 3))
      
      # Alpha#12: (sign(delta(volume, 1)) * (-1 * delta(close, 1)))
      def alpha012(v, c):
          return sign(delta(v, 1)) * (-1 * delta(c, 1))
      
      # Alpha#13: (-1 * rank(covariance(rank(close), rank(volume), 5)))
      def alpha013(c, v):
          return -1 * rank(covariance(rank(c), rank(v), 5))
      
      # Alpha#14: ((-1 * rank(delta(returns, 3))) * correlation(open, volume, 10))
      def alpha014(o, v, r):
          return (-1 * rank(delta(r, 3))) * correlation(o, v, 10)
      
      # Alpha#15: (-1 * sum(rank(correlation(rank(high), rank(volume), 3)), 3))
      def alpha015(h, v):
          return -1 * ts_sum(rank(correlation(rank(h), rank(v), 3)), 3)
      
      # Alpha#16: (-1 * rank(covariance(rank(high), rank(volume), 5)))
      def alpha016(h, v):
          return -1 * rank(covariance(rank(h), rank(v), 5))
      
      # Alpha#17: (((-1 * rank(ts_rank(close, 10))) * rank(delta(delta(close, 1), 1))) * rank(ts_rank((volume / adv20), 5)))
      def alpha017(c, v, adv20):
          return (-1 * rank(ts_rank(c, 10))) * rank(delta(delta(c, 1), 1)) * rank(ts_rank(v / adv20, 5))
      
      # Alpha#18: (-1 * rank(((stddev(abs((close - open)), 5) + (close - open)) + correlation(close, open, 10))))
      def alpha018(o, c):
          return -1 * rank(ts_std(abs(c - o), 5) + (c - o) + correlation(c, o, 10))
      
      # Alpha#19: ((-1 * sign(((close - delay(close, 7)) + delta(close, 7)))) * (1 + rank((1 + sum(returns, 250)))))
      def alpha019(c, r):
          return (-1 * sign((c - delay(c, 7)) + delta(c, 7))) * (1 + rank(1 + ts_sum(r, 250)))
      
      # Alpha#20: (((-1 * rank((open - delay(high, 1)))) * rank((open - delay(close, 1)))) * rank((open - delay(low, 1))))
      def alpha020(o, h, l, c):
          return (-1 * rank(o - delay(h, 1))) * rank(o - delay(c, 1)) * rank(o - delay(l, 1))
      
      # Alpha#21: ((((sum(close, 8) / 8) + stddev(close, 8)) < (sum(close, 2) / 2)) ? (-1) : (((sum(close, 2) / 2) < ((sum(close, 8) / 8) - stddev(close, 8))) ? 1 : (((1 < (volume / adv20)) || ((volume / adv20) == 1)) ? 1 : (-1))))
      def alpha021(c, v, adv20):
          sma8 = ts_mean(c, 8)
          std8 = ts_std(c, 8)
          sma2 = ts_mean(c, 2)
          ratio = v / adv20
          result = (-1 * np.ones_like(c, dtype=float))
          result = pd.DataFrame(result, index=c.index, columns=c.columns)
          cond_down = sma2 < (sma8 - std8)
          cond_vol = (ratio >= 1)
          result[~((sma8 + std8) < sma2)] = 0  # temp
          result = result.where((sma8 + std8) < sma2, 
                                pd.DataFrame(1.0, index=c.index, columns=c.columns).where(cond_down, -1))
          result = result.where(cond_vol | ((sma8 + std8) < sma2) | (sma2 < (sma8 - std8)), -1)
          return result
      
      # Alpha#22: (-1 * (delta(correlation(high, volume, 5), 5) * rank(stddev(close, 20))))
      def alpha022(h, c, v):
          return -1 * (delta(correlation(h, v, 5), 5) * rank(ts_std(c, 20)))
      
      # Alpha#23: (((sum(high, 20) / 20) < high) ? (-1 * delta(high, 2)) : 0)
      def alpha023(h):
          return (-1 * delta(h, 2)).where(ts_mean(h, 20) < h, 0)
      
      # Alpha#24: ((((delta((sum(close, 100) / 100), 100) / delay(close, 100)) < 0.05) || ... ) ? (-1 * (close - ts_min(close, 100))) : (-1 * delta(close, 3)))
      def alpha024(c):
          cond = (delta(ts_mean(c, 100), 100) / delay(c, 100)) <= 0.05
          return (-1 * (c - ts_min(c, 100))).where(cond, -1 * delta(c, 3))
      
      # Alpha#25: rank(((((-1 * returns) * adv20) * vwap) * (high - close)))
      def alpha025(r, adv20, vwap, h, c):
          return rank((-1 * r) * adv20 * vwap * (h - c))
      
      # Alpha#26: (-1 * ts_max(correlation(ts_rank(volume, 5), ts_rank(high, 5), 5), 3))
      def alpha026(h, v):
          return -1 * ts_max(correlation(ts_rank(v, 5), ts_rank(h, 5), 5), 3)
      
      # Alpha#27: ((0.5 < rank((sum(correlation(rank(volume), rank(vwap), 6), 2) / 2.0))) ? (-1) : 1)
      def alpha027(v, vwap):
          cond = rank(ts_sum(correlation(rank(v), rank(vwap), 6), 2) / 2.0)
          return (-1 * np.ones_like(v, dtype=float)).where(cond > 0.5, 1)
      
      # Alpha#28: scale(((correlation(adv20, low, 5) + ((high + low) / 2)) - close))
      def alpha028(adv20, l, h, c):
          return scale(correlation(adv20, l, 5) + (h + l) / 2 - c)
      
      # Alpha#29: (min(product(rank(rank(scale(log(sum(ts_min(rank(rank((-1 * rank(delta((close - 1), 5))))), 2), 1))))), 1), 5) + ts_rank(delay((-1 * returns), 6), 5))
      def alpha029(c, r):
          # Simplified interpretation
          inner = -1 * rank(delta(c - 1, 5))
          p = ts_product(rank(rank(scale(log(ts_sum(ts_min(rank(rank(inner)), 2), 1))))), 5)
          return p + ts_rank(delay(-1 * r, 6), 5)
      
      # Alpha#30: (((1.0 - rank(((sign((close - delay(close, 1))) + sign((delay(close, 1) - delay(close, 2)))) + sign((delay(close, 2) - delay(close, 3)))))) * sum(volume, 5)) / sum(volume, 20))
      def alpha030(c, v):
          signs = sign(c - delay(c, 1)) + sign(delay(c, 1) - delay(c, 2)) + sign(delay(c, 2) - delay(c, 3))
          return (1 - rank(signs)) * ts_sum(v, 5) / ts_sum(v, 20)
      
      # Alpha#31: ((rank(rank(rank(decay_linear((-1 * rank(rank(delta(close, 10)))), 10)))) + rank((-1 * delta(close, 3)))) + sign(scale(correlation(adv20, low, 12))))
      def alpha031(c, l, adv20):
          p1 = rank(rank(rank(decay_linear(-1 * rank(rank(delta(c, 10))), 10))))
          p2 = rank(-1 * delta(c, 3))
          p3 = sign(scale(correlation(adv20, l, 12)))
          return p1 + p2 + p3
      
      # Alpha#32: (scale(((sum(close, 7) / 7) - close)) + (20 * scale(correlation(vwap, delay(close, 5), 230))))
      def alpha032(c, vwap):
          return scale(ts_mean(c, 7) - c) + 20 * scale(correlation(vwap, delay(c, 5), 230))
      
      # Alpha#33: rank((-1 * ((1 - (open / close))^1)))
      def alpha033(o, c):
          return rank(-1 * (1 - o / c))
      
      # Alpha#34: rank(((1 - rank((stddev(returns, 2) / stddev(returns, 5)))) + (1 - rank(delta(close, 1)))))
      def alpha034(c, r):
          return rank((1 - rank(ts_std(r, 2) / ts_std(r, 5))) + (1 - rank(delta(c, 1))))
      
      # Alpha#35: ((Ts_Rank(volume, 32) * (1 - Ts_Rank(((close + high) - low), 16))) * (1 - Ts_Rank(returns, 32)))
      def alpha035(h, l, c, v, r):
          return ts_rank(v, 32) * (1 - ts_rank(c + h - l, 16)) * (1 - ts_rank(r, 32))
      
      # Alpha#36: (((((2.21 * rank(correlation((close - open), delay(volume, 1), 15))) + (0.7 * rank((open - close)))) + (0.73 * rank(Ts_Rank(delay((-1 * returns), 6), 5)))) + rank(abs(correlation(vwap, adv20, 6)))) + (0.6 * rank((((sum(close, 200) / 200) - open) * (close - open)))))
      def alpha036(o, c, v, vwap, adv20, r):
          return (2.21 * rank(correlation(c - o, delay(v, 1), 15)) +
                  0.7 * rank(o - c) +
                  0.73 * rank(ts_rank(delay(-1 * r, 6), 5)) +
                  rank(abs(correlation(vwap, adv20, 6))) +
                  0.6 * rank((ts_mean(c, 200) - o) * (c - o)))
      
      # Alpha#37: (rank(correlation(delay((open - close), 1), close, 200)) + rank((open - close)))
      def alpha037(o, c):
          return rank(correlation(delay(o - c, 1), c, 200)) + rank(o - c)
      
      # Alpha#38: ((-1 * rank(Ts_Rank(close, 10))) * rank((close / open)))
      def alpha038(o, c):
          return (-1 * rank(ts_rank(c, 10))) * rank(c / o)
      
      # Alpha#39: ((-1 * rank((delta(close, 7) * (1 - rank(decay_linear((volume / adv20), 9)))))) * (1 + rank(sum(returns, 250))))
      def alpha039(c, v, adv20, r):
          return (-1 * rank(delta(c, 7) * (1 - rank(decay_linear(v / adv20, 9))))) * (1 + rank(ts_sum(r, 250)))
      
      # Alpha#40: ((-1 * rank(stddev(high, 10))) * correlation(high, volume, 10))
      def alpha040(h, v):
          return (-1 * rank(ts_std(h, 10))) * correlation(h, v, 10)
      
      # Alpha#41: (((high * low)^0.5) - vwap)
      def alpha041(h, l, vwap):
          return (h * l).pow(0.5) - vwap
      
      # Alpha#42: (rank((vwap - close)) / rank((vwap + close)))
      def alpha042(vwap, c):
          return rank(vwap - c) / rank(vwap + c)
      
      # Alpha#43: (ts_rank((volume / adv20), 20) * ts_rank((-1 * delta(close, 7)), 8))
      def alpha043(c, v, adv20):
          return ts_rank(v / adv20, 20) * ts_rank(-1 * delta(c, 7), 8)
      
      # Alpha#44: (-1 * correlation(high, rank(volume), 5))
      def alpha044(h, v):
          return -1 * correlation(h, rank(v), 5)
      
      # Alpha#45: (-1 * ((rank((sum(delay(close, 5), 20) / 20)) * correlation(close, volume, 2)) * rank(correlation(sum(close, 5), sum(close, 20), 2))))
      def alpha045(c, v):
          return -1 * (rank(ts_mean(delay(c, 5), 20)) * correlation(c, v, 2) * rank(correlation(ts_sum(c, 5), ts_sum(c, 20), 2)))
      
      # Alpha#46: ((0.25 < (((delay(close, 20) - delay(close, 10)) / 10) - ((delay(close, 10) - close) / 10))) ? (-1) : ((((...) < 0) ? 1 : ((-1) * (close - delay(close, 1)))))
      def alpha046(c):
          accel = (delay(c, 20) - delay(c, 10)) / 10 - (delay(c, 10) - c) / 10
          return (-1 * np.ones_like(c, dtype=float)).where(accel > 0.25,
                 np.ones_like(c, dtype=float)).where(accel < 0, -1 * (c - delay(c, 1)))
      
      # Alpha#47: ((((rank((1 / close)) * volume) / adv20) * ((high * rank((high - close))) / (sum(high, 5) / 5))) - rank((vwap - delay(vwap, 5))))
      def alpha047(c, h, v, vwap, adv20):
          return ((rank(1 / c) * v) / adv20) * ((h * rank(h - c)) / ts_mean(h, 5)) - rank(vwap - delay(vwap, 5))
      
      # Alpha#48: (indneutralize(((correlation(delta(close, 1), delta(delay(close, 1), 1), 250) * delta(close, 1)) / close), IndClass.subindustry) / sum(((delta(close, 1) / delay(close, 1))^2), 250))
      def alpha048(c, ind_class):
          d1 = delta(c, 1)
          return indneutralize(correlation(d1, delta(delay(c, 1), 1), 250) * d1 / c, ind_class) / ts_sum((d1 / delay(c, 1)).pow(2), 250)
      
      # Alpha#49: (((((delay(close, 20) - delay(close, 10)) / 10) - ((delay(close, 10) - close) / 10)) < (-1 * 0.1)) ? 1 : ((-1 * 1) * (close - delay(close, 1))))
      def alpha049(c):
          accel = (delay(c, 20) - delay(c, 10)) / 10 - (delay(c, 10) - c) / 10
          return np.ones_like(c, dtype=float).where(accel < -0.1, -1 * (c - delay(c, 1)))
      
      # Alpha#50: (-1 * ts_max(rank(correlation(rank(volume), rank(vwap), 5)), 5))
      def alpha050(v, vwap):
          return -1 * ts_max(rank(correlation(rank(v), rank(vwap), 5)), 5)
      
      # Alpha#51: (((((delay(close, 20) - delay(close, 10)) / 10) - ((delay(close, 10) - close) / 10)) < (-1 * 0.05)) ? 1 : ((-1) * (close - delay(close, 1))))
      def alpha051(c):
          accel = (delay(c, 20) - delay(c, 10)) / 10 - (delay(c, 10) - c) / 10
          return np.ones_like(c, dtype=float).where(accel < -0.05, -1 * (c - delay(c, 1)))
      
      # Alpha#52: ((((-1 * ts_min(low, 5)) + delay(ts_min(low, 5), 5)) * rank(((sum(returns, 240) - sum(returns, 20)) / 220))) * ts_rank(volume, 5))
      def alpha052(c, l, v, r):
          return ((-1 * ts_min(l, 5) + delay(ts_min(l, 5), 5)) * rank((ts_sum(r, 240) - ts_sum(r, 20)) / 220)) * ts_rank(v, 5)
      
      # Alpha#53: (-1 * delta((((close - low) - (high - close)) / (close - low)), 9))
      def alpha053(c, h, l):
          inner = ((c - l) - (h - c)) / (c - l)
          return -1 * delta(inner, 9)
      
      # Alpha#54: ((-1 * ((low - close) * (open^5))) / ((low - high) * (close^5)))
      def alpha054(o, c, h, l):
          return -1 * (l - c) * o.pow(5) / ((l - h) * c.pow(5))
      
      # Alpha#55: (-1 * correlation(rank(((close - ts_min(low, 12)) / (ts_max(high, 12) - ts_min(low, 12)))), rank(volume), 6))
      def alpha055(c, h, l, v):
          inner = (c - ts_min(l, 12)) / (ts_max(h, 12) - ts_min(l, 12))
          return -1 * correlation(rank(inner), rank(v), 6)
      
      # Alpha#56: (0 - (1 * (rank((sum(returns, 10) / sum(sum(returns, 2), 3))) * rank((returns * cap)))))
      def alpha056(r, cap):
          return -1 * (rank(ts_sum(r, 10) / ts_sum(ts_sum(r, 2), 3)) * rank(r * cap))
      
      # Alpha#57: (0 - (1 * ((rank((sum(returns, 10) / sum(sum(returns, 2), 3))) * rank((returns * cap)))))
      # Same as #56 with different cap weighting — using adv20 variant
      def alpha057(r, vwap, adv20):
          return -1 * (rank(ts_sum(r, 10) / ts_sum(ts_sum(r, 2), 3)) * rank(r * vwap * adv20))
      
      # Alpha#58-#59: Complex IndNeutralize variants
      def alpha058(v, vwap, ind_class):
          w = 0.25
          return -1 * ts_rank(decay_linear(correlation(indneutralize(vwap * w + vwap * (1 - w), ind_class), v, 4.25), 16), 8)
      
      def alpha059(v, vwap, ind_class):
          w = 0.728317
          return -1 * ts_rank(decay_linear(correlation(indneutralize(vwap * w + vwap * (1 - w), ind_class), v, 4.25), 16.2), 8.2)
      
      # Alpha#60: (0 - (1 * ((2 * scale(rank(((((close - low) - (high - close)) / (high - low)) * volume)))) - scale(rank(ts_argmax(close, 10))))))
      def alpha060(c, h, l, v):
          inner = (((c - l) - (h - c)) / (h - l)) * v
          return -1 * (2 * scale(rank(inner)) - scale(rank(ts_argmax(c, 10))))
      
      # Alpha#61: (rank((vwap - ts_min(vwap, 16.1219))) < rank(correlation(vwap, adv180, 17.9282)))
      def alpha061(vwap, adv180):
          return rank(vwap - ts_min(vwap, 16)) < rank(correlation(vwap, adv180, 18))
      
      # Alpha#62: ((rank(correlation(vwap, sum(adv20, 22), 10)) < rank(((rank(open) + rank(open)) < (rank(((high + low) / 2)) + rank(high))))) * -1)
      def alpha062(o, h, l, v, vwap, adv20):
          cond = rank(correlation(vwap, ts_sum(adv20, 22), 10)) < rank((rank(o) + rank(o)) < (rank((h + l) / 2) + rank(h)))
          return cond.astype(float) * -1
      
      # Alpha#63: Complex IndNeutralize
      def alpha063(o, c, v, vwap, adv180, ind_class):
          return (-1 * (rank(decay_linear(delta(indneutralize(c, ind_class), 2.25), 8.2)) -
                        rank(decay_linear(correlation(vwap * 0.318 + o * 0.682, ts_sum(adv180, 37), 13.6), 12.3))))
      
      # Alpha#64: ((rank(correlation(sum(((open * 0.178) + (low * 0.822)), 12.7), sum(adv120, 12.7), 16.6)) < rank(delta(((((high + low) / 2) * 0.178) + (vwap * 0.822)), 3.7))) * -1)
      def alpha064(o, h, l, v, vwap, adv120):
          w = 0.178404
          s1 = rank(correlation(ts_sum(o * w + l * (1 - w), 13), ts_sum(adv120, 13), 17))
          s2 = rank(delta((h + l) / 2 * w + vwap * (1 - w), 4))
          return (s1 < s2).astype(float) * -1
      
      # Alpha#65: ((rank(correlation(((open * 0.008) + (vwap * 0.992)), sum(adv60, 8.7), 6.4)) < rank((open - ts_min(open, 13.6)))) * -1)
      def alpha065(o, v, vwap, adv60):
          w = 0.00817205
          s1 = rank(correlation(o * w + vwap * (1 - w), ts_sum(adv60, 9), 6))
          s2 = rank(o - ts_min(o, 14))
          return (s1 < s2).astype(float) * -1
      
      # Alpha#66: ((rank(decay_linear(delta(vwap, 3.51), 7.23)) + Ts_Rank(decay_linear(((((low * 0.966) - vwap) / (open - ((high + low) / 2)))), 11.4), 6.73)) * -1)
      def alpha066(o, h, l, vwap):
          w = 0.96633
          p1 = rank(decay_linear(delta(vwap, 3.5), 7.2))
          p2 = ts_rank(decay_linear((l * w - vwap) / (o - (h + l) / 2), 11.4), 6.7)
          return (p1 + p2) * -1
      
      # Alpha#67-#70: Complex with IndNeutralize
      def alpha067(o, c, h, v, vwap, ind_class):
          return (rank(decay_linear(delta(indneutralize(vwap, ind_class), 3.5), 7.2)) +
                  ts_rank(decay_linear((l * 0.966 - vwap) / (o - (h + l) / 2), 11.4), 6.7)) * -1
      
      def alpha068(o, c, h, l, v):
          w = 0.518371
          return (ts_rank(correlation(rank(h), rank(ts_mean(v, 15)), 9), 14) <
                  rank(delta(c * w + l * (1 - w), 1))).astype(float) * -1
      
      def alpha069(c, v, vwap, adv80, ind_class):
          return (ts_rank(decay_linear(correlation(indneutralize(c, ind_class), v, 9), 17), 18) <
                  rank(correlation(c * 0.6 + vwap * 0.4, ts_sum(adv80, 9), 15))).astype(float) * -1
      
      def alpha070(c, v, vwap, adv50, ind_class):
          return (rank(delta(vwap, 1.3)).pow(ts_rank(correlation(indneutralize(c, ind_class), adv50, 18), 18)) * -1)
      
      # Alpha#71: max(Ts_Rank(decay_linear(correlation(Ts_Rank(close, 3.4), Ts_Rank(adv180, 19), 6.9), 13), 15), Ts_Rank(decay_linear(correlation(rank(vwap), rank(volume), 6.9), 3), 5))
      def alpha071(o, c, l, v, vwap, adv180):
          s1 = ts_rank(decay_linear(correlation(ts_rank(c, 3), ts_rank(adv180, 19), 7), 13), 15)
          s2 = ts_rank(decay_linear(correlation(rank(vwap), rank(v), 7), 3), 5)
          return np.maximum(s1, s2)
      
      # Alpha#72: (rank(decay_linear(correlation(Ts_Rank(vwap, 3.7), Ts_Rank(volume, 18.5), 6.9), 3)) * -1)
      def alpha072(v, vwap):
          return rank(decay_linear(correlation(ts_rank(vwap, 4), ts_rank(v, 19), 7), 3)) * -1
      
      # Alpha#73: max(rank(decay_linear(delta(vwap, 4.7), 3)), rank(decay_linear(...)))
      def alpha073(o, l, vwap):
          s1 = rank(decay_linear(delta(vwap, 5), 3))
          s2 = rank(decay_linear((l - vwap) / delay(o, 3), 12))
          return np.maximum(s1, s2) * -1
      
      # Alpha#74: ((rank(correlation(close, sum(adv30, 37), 15)) < rank(correlation(rank(high*0.026+ vwap*0.974), rank(volume), 11))) * -1)
      def alpha074(h, c, v, vwap):
          w = 0.0261661
          return (rank(correlation(c, ts_sum(ts_mean(v, 30), 37), 15)) <
                  rank(correlation(rank(h * w + vwap * (1 - w)), rank(v), 11))).astype(float) * -1
      
      # Alpha#75: (rank(correlation(vwap, volume, 4.2)) < rank(correlation(rank(low), rank(adv50), 12.4)))
      def alpha075(l, v, vwap, adv50):
          return (rank(correlation(vwap, v, 4)) < rank(correlation(rank(l), rank(adv50), 12))).astype(float)
      
      # Alpha#76: Complex IndNeutralize
      def alpha076(l, v, vwap, adv81, ind_class):
          s1 = rank(decay_linear(delta(vwap, 1.2), 12))
          s2 = ts_rank(decay_linear(ts_rank(correlation(indneutralize(l, ind_class), adv81, 8), 20), 17), 19)
          return np.maximum(s1, s2) * -1
      
      # Alpha#77: min(rank(decay_linear(...)), rank(decay_linear(...)))
      def alpha077(h, l, v, vwap, adv40):
          s1 = rank(decay_linear(((h + l) / 2 + h) - (vwap + h), 20))
          s2 = rank(decay_linear(correlation((h + l) / 2, adv40, 3), 6))
          return np.minimum(s1, s2)
      
      # Alpha#78: rank(correlation(sum(low*0.352+vwap*0.648, 20), sum(adv40, 20), 8)) < rank(correlation(...)
      def alpha078(l, v, vwap, adv40):
          w = 0.352233
          return (rank(correlation(ts_sum(l * w + vwap * (1 - w), 20), ts_sum(adv40, 20), 8)) <
                  rank(correlation(ts_sum(l * w + vwap * (1 - w), 20), ts_mean(v, 20), 8))).astype(float) * -1
      
      # Alpha#79: IndNeutralize complex
      def alpha079(o, c, v, vwap, ind_class):
          return (rank(correlation(indneutralize(c * 0.607 + o * 0.393, ind_class), ts_mean(v, 20), 9)) <
                  rank(correlation(c, ts_mean(v, 20), 9))).astype(float) * -1
      
      # Alpha#80: IndNeutralize complex  
      def alpha080(o, h, v, ind_class):
          return (rank(sign(delta(indneutralize(o * 0.968 + h * 0.032, ind_class), 1))) *
                  rank(correlation(v, ts_mean(v, 20), 8))).astype(float) * -1
      
      # Alpha#81: ((rank(log(sum(rank(rank(max((vwap - close), 3) * rank(delta(close, 1)))), 3))) < rank(delta(vwap, 4))) * -1)
      def alpha081(c, v, vwap):
          return (rank(log(ts_sum(rank(rank(np.maximum(vwap - c, 3) * rank(delta(c, 1)))), 3))) <
                  rank(delta(vwap, 4))).astype(float) * -1
      
      # Alpha#82: IndNeutralize
      def alpha082(o, v, ind_class):
          return (rank(correlation(indneutralize(o, ind_class), ts_mean(v, 20), 9)) <
                  rank(correlation(indneutralize(o, ind_class), ts_sum(v, 20), 9))).astype(float) * -1
      
      # Alpha#83: ((rank(delay(((high - low) / (sum(close, 5) / 5)), 2)) * rank(rank(volume))) / (((high - low) / (sum(close, 5) / 5)) / (vwap - close)))
      def alpha083(c, h, l, v, vwap):
          spread = (h - l) / ts_mean(c, 5)
          return (rank(delay(spread, 2)) * rank(rank(v))) / (spread / (vwap - c))
      
      # Alpha#84: SignedPower(Ts_Rank((vwap - ts_max(vwap, 15.3)), 20.7), delta(close, 5))
      def alpha084(c, vwap):
          return signedpower(ts_rank(vwap - ts_max(vwap, 15), 21), delta(c, 5))
      
      # Alpha#85: rank(correlation(close/vwap, delay(delta(close,1),5), 20)) * rank(correlation(close, volume, 6))
      def alpha085(c, h, l, v, vwap):
          return rank(correlation((h + l) / 2 - vwap, delay(c, 5), 20)) * rank(correlation(c, v, 6))
      
      # Alpha#86: ((0.25 < (((delay(close, 20) - delay(close, 10)) / 10) - ((delay(close, 10) - close) / 10))) ? (-1 * delta(close, 1)) : delta(close, 1))
      def alpha086(o, c, v, vwap):
          accel = (delay(c, 20) - delay(c, 10)) / 10 - (delay(c, 10) - c) / 10
          return (-1 * delta(c, 1)).where(accel > 0.25, delta(c, 1))
      
      # Alpha#87-#91: Complex IndNeutralize
      def alpha087(c, v, vwap, adv150, ind_class):
          return (rank(decay_linear(correlation(indneutralize(vwap, ind_class), adv150, 17), 12)) *
                  rank(correlation(c, v, 6))).astype(float) * -1
      
      def alpha088(o, c, h, l, v):
          return (rank(decay_linear(((c - o) / (h - l + 0.001)), 20)) *
                  rank(correlation(c, v, 6))).astype(float) * -1
      
      def alpha089(l, v, vwap, ind_class):
          s1 = rank(decay_linear(correlation(indneutralize(l, ind_class), v, 8), 13))
          s2 = rank(decay_linear(correlation(indneutralize(l, ind_class), vwap, 8), 13))
          return (s1 < s2).astype(float) * -1
      
      def alpha090(c, v, ind_class):
          return (rank(correlation(indneutralize(c, ind_class), v, 9)) <
                  rank(correlation(indneutralize(c, ind_class), ts_sum(v, 9), 9))).astype(float) * -1
      
      def alpha091(c, v, vwap, ind_class):
          return (rank(correlation(indneutralize(c, ind_class), v, 9)) <
                  rank(correlation(indneutralize(c, ind_class), vwap, 9))).astype(float) * -1
      
      # Alpha#92: min(ts_rank(decay_linear(...), 18), ts_rank(decay_linear(...), 18))
      def alpha092(o, c, h, l, v):
          p1 = ts_rank(decay_linear(((h + l) / 2 + c < l + o).astype(float), 15), 18)
          p2 = ts_rank(decay_linear(correlation(ts_rank(l, 11), ts_rank(ts_mean(v, 60), 4), 18), 12), 18)
          return np.minimum(p1, p2) * -1
      
      # Alpha#93: IndNeutralize complex
      def alpha093(c, v, vwap, ind_class):
          return (ts_rank(decay_linear(correlation(indneutralize(vwap, ind_class), v, 8), 13), 18) <
                  rank(correlation(ts_rank(c, 8), ts_rank(v, 20), 5))).astype(float) * -1
      
      # Alpha#94: rank(delay((high - low) / (sum(close, 5) / 5), 2)) * rank(rank(volume)) / (((high - low) / (sum(close, 5) / 5)) / (vwap - close))
      def alpha094(o, c, h, l, v):
          return ((rank(c - delay(c, 1)) * rank(c - delay(c, 1))) <
                  rank(correlation(ts_mean(v, 60), ts_mean(v, 60), 5))).astype(float) * -1
      
      # Alpha#95: rank(open - ts_min(open, 12)) < ts_rank(rank(correlation(ts_mean((high+low)/2, 19), ts_sum(ts_mean(volume, 40), 19), 13)^5), 12)
      def alpha095(o, h, l, v):
          cond = rank(o - ts_min(o, 12)) < ts_rank(rank(correlation(ts_mean((h + l) / 2, 19), ts_sum(ts_mean(v, 40), 19), 13).pow(5)), 12)
          return cond.astype(float) * -1
      
      # Alpha#96: max(rank(decay_linear(delta(vwap, 1), 12)), rank(decay_linear(rank(correlation(low, ts_mean(volume, 60), 8)), 17)))
      def alpha096(c, h, l, v, vwap):
          p1 = rank(decay_linear(delta(vwap, 1), 12))
          p2 = rank(decay_linear(rank(correlation(l, ts_mean(v, 60), 8)), 17))
          return np.maximum(p1, p2) * -1
      
      # Alpha#97: IndNeutralize
      def alpha097(l, v, vwap, ind_class):
          return (rank(decay_linear(delta(indneutralize(vwap, ind_class), 1), 12)) <
                  rank(decay_linear(correlation(indneutralize(l, ind_class), v, 8), 17))).astype(float) * -1
      
      # Alpha#98: ((rank(correlation(sum(close, 7), sum(close, 5), 3)) * rank(correlation(ts_rank(volume, 20), ts_rank(high, 20), 7))) * -1)
      def alpha098(o, v, vwap):
          return (rank(correlation(ts_sum(vwap, 7), ts_sum(vwap, 5), 3)) *
                  rank(correlation(ts_rank(v, 20), ts_rank(vwap, 20), 7))) * -1
      
      # Alpha#99: ((rank(correlation(sum(close*0.55+high*0.45, 20), sum(ts_mean(volume, 40), 20), 9)) < rank(correlation(low, volume, 6))) * -1)
      def alpha099(h, l, v):
          return (rank(correlation(ts_sum(c_placeholder(h, l) * 0.55 + h * 0.45, 20), ts_sum(ts_mean(v, 40), 20), 9)) <
                  rank(correlation(l, v, 6))).astype(float) * -1
      
      def c_placeholder(h, l):
          # Alpha#99 uses close; simplified
          return (h + l) / 2
      
      # Alpha#100: Complex IndNeutralize — uses IndNeutralize twice
      def alpha100(c, h, l, v, adv20, ind_class):
          inner = (((c - l) - (h - c)) / (h - l)) * v
          return -1 * ((1.5 * scale(indneutralize(indneutralize(rank(inner), ind_class), ind_class)) -
                         scale(indneutralize(correlation(c, rank(adv20), 5) - rank(ts_argmin(c, 30)), ind_class))) *
                        (v / adv20))
      
      # Alpha#101: ((close - open) / ((high - low) + .001))
      def alpha101(o, c, h, l):
          return (c - o) / ((h - l) + 0.001)
      
      
      # ─── Utility: Compute all non-industry alphas given data ──────────
      
      def compute_alphas(data: dict) -> dict:
          """
          data: dict with keys 'open','close','high','low','volume','vwap','returns','cap'
                Each value is a DataFrame (date x ticker)
          Returns: dict of alpha_name -> DataFrame
          """
          o, c, h, l = data['open'], data['close'], data['high'], data['low']
          v, vwap, r = data['volume'], data['vwap'], data['returns']
          cap = data.get('cap')
          adv20 = ts_mean(v, 20)
          adv40 = ts_mean(v, 40)
          adv50 = ts_mean(v, 50)
          adv60 = ts_mean(v, 60)
          adv80 = ts_mean(v, 80)
          adv120 = ts_mean(v, 120)
          adv150 = ts_mean(v, 150)
          adv180 = ts_mean(v, 180)
      
          results = {}
          # Simple alphas (no industry)
          results['alpha001'] = alpha001(c, r)
          results['alpha002'] = alpha002(o, c, v)
          results['alpha003'] = alpha003(o, v)
          results['alpha004'] = alpha004(l)
          results['alpha005'] = alpha005(o, vwap, c)
          results['alpha006'] = alpha006(o, v)
          results['alpha007'] = alpha007(c, v, adv20)
          results['alpha008'] = alpha008(o, r)
          results['alpha009'] = alpha009(c)
          results['alpha010'] = alpha010(c)
          results['alpha011'] = alpha011(vwap, c, v)
          results['alpha012'] = alpha012(v, c)
          results['alpha013'] = alpha013(c, v)
          results['alpha014'] = alpha014(o, v, r)
          results['alpha015'] = alpha015(h, v)
          results['alpha016'] = alpha016(h, v)
          results['alpha017'] = alpha017(c, v, adv20)
          results['alpha018'] = alpha018(o, c)
          results['alpha019'] = alpha019(c, r)
          results['alpha020'] = alpha020(o, h, l, c)
          results['alpha022'] = alpha022(h, c, v)
          results['alpha023'] = alpha023(h)
          results['alpha024'] = alpha024(c)
          results['alpha025'] = alpha025(r, adv20, vwap, h, c)
          results['alpha026'] = alpha026(h, v)
          results['alpha027'] = alpha027(v, vwap)
          results['alpha028'] = alpha028(adv20, l, h, c)
          results['alpha030'] = alpha030(c, v)
          results['alpha031'] = alpha031(c, l, adv20)
          results['alpha032'] = alpha032(c, vwap)
          results['alpha033'] = alpha033(o, c)
          results['alpha034'] = alpha034(c, r)
          results['alpha035'] = alpha035(h, l, c, v, r)
          results['alpha036'] = alpha036(o, c, v, vwap, adv20, r)
          results['alpha037'] = alpha037(o, c)
          results['alpha038'] = alpha038(o, c)
          results['alpha039'] = alpha039(c, v, adv20, r)
          results['alpha040'] = alpha040(h, v)
          results['alpha041'] = alpha041(h, l, vwap)
          results['alpha042'] = alpha042(vwap, c)
          results['alpha043'] = alpha043(c, v, adv20)
          results['alpha044'] = alpha044(h, v)
          results['alpha045'] = alpha045(c, v)
          results['alpha046'] = alpha046(c)
          results['alpha047'] = alpha047(c, h, v, vwap, adv20)
          results['alpha049'] = alpha049(c)
          results['alpha050'] = alpha050(v, vwap)
          results['alpha051'] = alpha051(c)
          results['alpha052'] = alpha052(c, l, v, r)
          results['alpha053'] = alpha053(c, h, l)
          results['alpha054'] = alpha054(o, c, h, l)
          results['alpha055'] = alpha055(c, h, l, v)
          if cap is not None:
              results['alpha056'] = alpha056(r, cap)
          results['alpha060'] = alpha060(c, h, l, v)
          results['alpha061'] = alpha061(vwap, adv180)
          results['alpha071'] = alpha071(o, c, l, v, vwap, adv180)
          results['alpha072'] = alpha072(v, vwap)
          results['alpha084'] = alpha084(c, vwap)
          results['alpha088'] = alpha088(o, c, h, l, v)
          results['alpha101'] = alpha101(o, c, h, l)
      
          return results
      
      
      if __name__ == '__main__':
          print("WorldQuant 101 Formulaic Alphas — Ready")
          print("Import and call compute_alphas(data) with date x ticker DataFrames")
      
    • backtest_alpha.py 3.3 KB
      """
      Alpha101 — 单因子回测
      模拟多空组合(top decile long, bottom decile short)
      """
      
      import argparse
      import pandas as pd
      import numpy as np
      from pathlib import Path
      
      
      def backtest_single_alpha(
          alpha_df: pd.DataFrame,
          returns: pd.DataFrame,
          n_groups: int = 10,
          cost_bp: float = 10,
          holding_days: int = 1
      ) -> dict:
          """
          单因子分层回测
          
          alpha_df: date x ticker 因子值
          returns: date x ticker 日收益率
          n_groups: 分组数(默认10组)
          cost_bp: 单边交易成本(基点)
          holding_days: 持仓天数
          
          Returns: dict with performance metrics
          """
          # 截面分组
          ranked = alpha_df.rank(axis=1, pct=True)
          
          # 多空组合: top group long, bottom group short
          long_mask = ranked >= (1 - 1/n_groups)
          short_mask = ranked < (1/n_groups)
          
          # 日收益
          long_ret = returns.where(long_mask).mean(axis=1)
          short_ret = returns.where(short_mask).mean(axis=1)
          ls_ret = long_ret - short_ret
          
          # 换手率 → 扣成本
          long_pos = long_mask.astype(float)
          turnover = long_pos.diff().abs().sum(axis=1) / long_pos.sum(axis=1).replace(0, 1)
          cost = turnover * cost_bp / 10000
          
          ls_ret_net = ls_ret - cost
          
          # 统计
          cumulative = (1 + ls_ret_net).cumprod()
          
          return {
              'total_return': (1 + ls_ret_net).prod() - 1,
              'annual_return': ls_ret_net.mean() * 252,
              'sharpe': ls_ret_net.mean() / ls_ret_net.std() * np.sqrt(252) if ls_ret_net.std() > 0 else 0,
              'max_drawdown': ((cumulative / cumulative.cummax()) - 1).min(),
              'win_rate': (ls_ret_net > 0).mean(),
              'avg_daily_turnover': turnover.mean(),
              'long_return': (1 + long_ret).prod() - 1,
              'short_return': (1 - short_ret).prod() - 1,
              'daily_returns': ls_ret_net
          }
      
      
      if __name__ == '__main__':
          parser = argparse.ArgumentParser(description='Alpha101 Single Alpha Backtest')
          parser.add_argument('--alpha', type=int, required=True, help='Alpha number (1-101)')
          parser.add_argument('--data', required=True, help='Path to pickle file with data dict')
          parser.add_argument('--n-groups', type=int, default=10)
          parser.add_argument('--cost-bp', type=float, default=10)
          args = parser.parse_args()
          
          data = pd.read_pickle(args.data)
          
          import sys
          sys.path.insert(0, str(Path(__file__).parent))
          from alpha101 import compute_alphas
          
          alphas = compute_alphas(data)
          alpha_name = f'alpha{args.alpha:03d}'
          
          if alpha_name not in alphas:
              print(f"Alpha {alpha_name} not available (may require industry data)")
              sys.exit(1)
          
          result = backtest_single_alpha(
              alphas[alpha_name], data['returns'],
              n_groups=args.n_groups, cost_bp=args.cost_bp
          )
          
          print(f"\n{'='*40}")
          print(f"Alpha #{args.alpha} Backtest Results")
          print(f"{'='*40}")
          print(f"Total Return:  {result['total_return']:.2%}")
          print(f"Annual Return: {result['annual_return']:.2%}")
          print(f"Sharpe Ratio:  {result['sharpe']:.2f}")
          print(f"Max Drawdown:  {result['max_drawdown']:.2%}")
          print(f"Win Rate:      {result['win_rate']:.2%}")
          print(f"Avg Turnover:  {result['avg_daily_turnover']:.2%}")
          print(f"Long Return:   {result['long_return']:.2%}")
          print(f"Short Return:  {result['short_return']:.2%}")
      
    • compute_ic.py 3 KB
      """
      Alpha101 — 因子IC/IR计算工具
      计算各因子的Information Coefficient和Information Ratio
      """
      
      import argparse
      import pandas as pd
      import numpy as np
      from pathlib import Path
      
      
      def compute_ic(alpha_df: pd.DataFrame, forward_returns: pd.DataFrame, method='spearman') -> pd.Series:
          """
          计算因子IC(截面相关系数的时间序列均值)
          
          alpha_df: date x ticker DataFrame, 因子值
          forward_returns: date x ticker DataFrame, 未来N日收益率
          method: 'spearman' (rank IC) 或 'pearson'
          """
          if method == 'spearman':
              ic = alpha_df.rank(axis=1, pct=True).corrwith(forward_returns.rank(axis=1, pct=True), axis=1)
          else:
              ic = alpha_df.corrwith(forward_returns, axis=1)
          return ic
      
      
      def compute_ir(ic_series: pd.Series) -> float:
          """IC均值 / IC标准差"""
          return ic_series.mean() / ic_series.std() if ic_series.std() > 0 else 0
      
      
      def compute_turnover(alpha_df: pd.DataFrame) -> pd.Series:
          """因子换手率:相邻两期排名变化的绝对值均值"""
          ranked = alpha_df.rank(axis=1, pct=True)
          return ranked.diff().abs().mean(axis=1)
      
      
      def screen_alphas(data: dict, forward_days: int = 5, ic_threshold: float = 0.03) -> pd.DataFrame:
          """
          批量筛选因子
          
          data: 包含因子值和价格数据的dict
          forward_days: 前瞻收益天数
          ic_threshold: IC绝对值阈值
          
          Returns: DataFrame with columns [alpha, mean_ic, ic_std, ir, mean_turnover, hit_rate]
          """
          import sys
          sys.path.insert(0, str(Path(__file__).parent))
          from alpha101 import compute_alphas
          
          alphas = compute_alphas(data)
          forward_returns = data['close'].pct_change(forward_days).shift(-forward_days)
          
          results = []
          for name, alpha_df in alphas.items():
              ic = compute_ic(alpha_df, forward_returns)
              ir = compute_ir(ic)
              mean_ic = ic.mean()
              turnover = compute_turnover(alpha_df)
              hit_rate = (ic > 0).mean()
              
              results.append({
                  'alpha': name,
                  'mean_ic': mean_ic,
                  'ic_std': ic.std(),
                  'ir': ir,
                  'mean_turnover': turnover.mean(),
                  'hit_rate': hit_rate,
                  'abs_ic': abs(mean_ic)
              })
          
          df = pd.DataFrame(results).sort_values('abs_ic', ascending=False)
          return df[df['abs_ic'] >= ic_threshold]
      
      
      if __name__ == '__main__':
          parser = argparse.ArgumentParser(description='Alpha101 IC Screening')
          parser.add_argument('--data', required=True, help='Path to pickle file with data dict')
          parser.add_argument('--forward-days', type=int, default=5)
          parser.add_argument('--ic-threshold', type=float, default=0.03)
          parser.add_argument('--output', default='ic_results.csv')
          args = parser.parse_args()
          
          data = pd.read_pickle(args.data)
          results = screen_alphas(data, args.forward_days, args.ic_threshold)
          results.to_csv(args.output, index=False)
          print(f"Screened {len(results)} alphas above IC={args.ic_threshold}")
          print(results[['alpha', 'mean_ic', 'ir', 'hit_rate']].to_string(index=False))
      
  • SKILL.md 3.2 KB
    ---
    name: alpha101
    description: |
      WorldQuant 101 Formulaic Alphas — 因子计算、IC测试、回测一体化工具包。
      基于Kakushadze (2015) 论文,提供101个价量/波动率/相关性因子的Python/Pandas实现。
      Use when: "alpha101", "101因子", "formulaic alphas", "因子回测", "因子IC", "因子筛选",
      "WorldQuant因子", "价量因子", "alpha因子库".
    allowed-tools: Read, Write, Edit, Grep, Glob, Bash(python:*)
    version: 1.0.0
    author: Simons (CQO)
    ---
    
    # Alpha101 — WorldQuant 101 Formulaic Alphas
    
    ## 概述
    
    Zura Kakushadze 论文《101 Formulaic Alphas》(arXiv:1601.00991) 的完整Python实现。
    101个真实量化交易alpha因子,公式即代码。
    
    ### 论文关键数据
    - 平均持仓期: 0.6-6.4天
    - 平均两两相关性: 15.9%
    - 收益与波动率强相关: R ~ σ^0.76
    
    ## 文件结构
    
    ```
    skills/alpha101/
    ├── SKILL.md              ← 本文件
    ├── scripts/
    │   ├── alpha101.py       ← 101个因子函数 + 基础函数库
    │   ├── compute_ic.py     ← 因子IC/IR计算
    │   └── backtest_alpha.py ← 单因子回测
    └── references/
        └── paper_notes.md    ← 论文笔记与函数定义
    ```
    
    ## 使用方式
    
    ### 1. 因子计算
    
    ```python
    from scripts.alpha101 import compute_alphas, alpha101
    
    # 输入: date x ticker DataFrame
    data = {
        'open': df_open, 'close': df_close, 'high': df_high, 'low': df_low,
        'volume': df_vol, 'vwap': df_vwap, 'returns': df_returns
    }
    
    # 计算所有可用因子
    alphas = compute_alphas(data)  # dict of alpha_name -> DataFrame
    
    # 单独计算
    from scripts.alpha101 import alpha101
    a101 = alpha101(df_open, df_close, df_high, df_low)
    ```
    
    ### 2. 因子IC测试
    
    ```bash
    python scripts/compute_ic.py --data <path> --output results/
    ```
    
    ### 3. 单因子回测
    
    ```bash
    python scripts/backtest_alpha.py --alpha 101 --data <path>
    ```
    
    ## 因子分类
    
    | 类别 | 因子 | 输入 |
    |------|------|------|
    | 纯价量 | #1-#47, #49-#55, #60, #61, #71-#74, #84, #88, #101 | OHLCV + VWAP |
    | 行业中性化 | #48, #56, #58-#59, #63, #67, #69-#70, #76, #79-#82, #87, #89-#91, #93, #97, #100 | + 行业分类 |
    | 复杂参数 | #57-#99 | 非整数窗口, 混合权重 |
    
    ## 基础函数速查
    
    ```
    rank(x)              截面排名 [0,1]
    delay(x,d)           d天前的值
    delta(x,d)           当期 - d天前
    correlation(x,y,d)   d天滚动相关
    scale(x,a=1)         缩放使sum(abs(x))=a
    decay_linear(x,d)    线性衰减加权均值
    ts_min/ts_max(x,d)   滚动最小/最大
    ts_rank(x,d)         时间序列排名
    ts_sum/ts_std(x,d)   滚动求和/标准差
    adv{d}               d天平均成交额
    IndNeutralize(x,ind) 行业中性化
    ```
    
    ## 回测注意事项
    
    1. **交易成本**: 论文因子扣除cost后Sharpe才是真Sharpe
    2. **过拟合**: 101个因子中部分可能已衰减,需样本外验证
    3. **市场适配**: 
       - A股: T+1限制,持仓期需调整
       - 加密: 24/7,日频→小时频需改窗口参数
       - Polymarket: 流动性低,部分因子不适用
    4. **行业因子**: 需要行业分类映射,加密市场可用板块替代
    
    ## 决策框架
    
    ```
    因子计算 → IC/IR筛选(>0.03) → 样本外验证 → 组合构建(低相关等权) → 回测扣费 → 实盘
    ```
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related