找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3742

积分

0

好友

516

主题
发表于 16 小时前 | 查看: 1| 回复: 0

2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含300篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!

做因子研究,最大的痛点是什么?往往不是“不会写代码”,而是“想法验证的速度太慢”。每次都要重新写一遍数据分组、滚动窗口、截面对齐的代码,既容易出错,又难以复用。使用表达式引擎的核心价值就在于解决这个问题,它让你能专注于因子的数学逻辑本身。

它的主要优势可以总结为三点:

  1. 将研究目标从繁琐的工程细节中解耦,让你只关注因子的数学结构。
  2. 把重复的分组、窗口、对齐逻辑都交给引擎去处理,减少手动编写带来的错误。
  3. 统一了因子的定义格式,让因子复现、对比和批量迭代变得前所未有的容易。

想想看,传统的方式可能是每个因子都手写一长段 Pandas 或 DataFrame 的操作逻辑,代码又长又难以统一。而表达式的方式,一句 Rank(Ts_Mean(Close, 10)) 就能清晰表达一个复杂的因子,让你直接进入验证和迭代环节。

AKQuant 因子表达式的特色

AKQuant 的因子表达式引擎在设计上有一些鲜明的特色,理解它们能帮助你更好地使用:

  1. 高性能执行:底层基于 Rust + Polars 构建,充分利用了 Lazy 执行模式和并行计算能力。
  2. 分区语义清晰:它明确区分了 TS(时序)、CS(截面)、EL(元素级)三类计算分区。遇到像 Rank(Ts_Mean(...)) 这样的跨分区嵌套,引擎会自动拆步执行,你不用担心背后的复杂性。
  3. 工程化与可调试:对于复杂的表达式,你可以先验证内层结果,再叠加外层逻辑,这种分步调试的方式让定位问题变得非常直接。
  4. 批量计算友好run_batch 方法专门为统一样本池下的多因子并行评估而设计,效率很高。
  5. 时区与数据规范明确:默认时区为 Asia/Shanghai,如果你处理的是非 A 股市场数据,记得要显式设置时区。

5分钟实战:从零到第一个可用因子

我们来快速跑通一个完整的流程。整个过程的核心就是:准备数据 -> 创建引擎 -> 执行表达式。

首先,我们需要准备数据。这里以 A 股日线数据为例,使用 akshare 获取数据,并用 AKQuant 的 ParquetDataCatalog 进行本地化管理。

import akshare as ak
import pandas as pd
from akquant.data import ParquetDataCatalog
from akquant.factor import FactorEngine

# 初始化一个本地数据目录
catalog = ParquetDataCatalog("./data_catalog")

# 假设我们准备两只股票的数据
symbols = ["sh600000", "sz300750"]
for symbol in symbols:
    df = ak.stock_zh_a_daily(
        symbol=symbol,
        start_date="20230101",
        end_date="20230601",
        adjust="hfq",
    )
    df["symbol"] = symbol
    df["date"] = pd.to_datetime(df["date"])
    df.set_index("date", inplace=True)
    catalog.write(symbol, df)

# 创建因子引擎
engine = FactorEngine(catalog)

数据准备好后,就可以开始玩转因子表达式了。建议按照“由简入繁、先单后批”的顺序来操作。

# 1. 单层表达式:先确认基础计算是否正确
df_ts = engine.run("Ts_Mean(Close, 5)")

# 2. 嵌套表达式:再确认跨分区语义是否如预期
df_nested = engine.run("Rank(Ts_Mean(Close, 5))")

# 3. 批量表达式:最后进行规模化计算,一次评估多个因子
df_batch = engine.run_batch(
    [
        "Ts_Mean(Close, 5)",
        "Rank(Volume)",
        "Rank(Ts_Corr(Close, Volume, 10))",
    ]
)
print(df_batch)

运行批量计算后,你会得到一个结构清晰的 DataFrame,类似下面这样:

shape: (781, 5)
┌─────────────────────┬──────────┬───────────┬──────────┬──────────┐
│ date                ┆ symbol   ┆ factor_0  ┆ factor_1 ┆ factor_2 │
│ ---                 ┆ ---      ┆ ---       ┆ ---      ┆ ---      │
│ datetime[ms]        ┆ str      ┆ f64       ┆ f64      ┆ f64      │
╞═════════════════════╪══════════╪═══════════╪══════════╪══════════╡
│ 2023-01-03 00:00:00 ┆ sh600000 ┆ null      ┆ 0.8      ┆ null     │
│ 2023-01-03 00:00:00 ┆ sh600036 ┆ null      ┆ 1.0      ┆ null     │
│ 2023-01-03 00:00:00 ┆ sh600519 ┆ null      ┆ 0.2      ┆ null     │
│ 2023-01-03 00:00:00 ┆ sh603843 ┆ null      ┆ 0.6      ┆ null     │
│ 2023-01-03 00:00:00 ┆ sz300750 ┆ null      ┆ 0.4      ┆ null     │
│ …                   ┆ …        ┆ …         ┆ …        ┆ …        │
│ 2023-12-27 00:00:00 ┆ sh600519 ┆ 13479.5   ┆ 0.5      ┆ 0.5      │
│ 2023-12-28 00:00:00 ┆ sh600036 ┆ 159.256   ┆ 1.0      ┆ 1.0      │
│ 2023-12-28 00:00:00 ┆ sh600519 ┆ 13568.274 ┆ 0.5      ┆ 0.5      │
│ 2023-12-29 00:00:00 ┆ sh600036 ┆ 159.498   ┆ 1.0      ┆ 1.0      │
│ 2023-12-29 00:00:00 ┆ sh600519 ┆ 13657.63  ┆ 0.5      ┆ 0.5      │
└─────────────────────┴──────────┴───────────┴──────────┴──────────┘

你看,核心的使用代码其实非常简洁:

from akquant.factor import FactorEngine
engine = FactorEngine(catalog)

# 单因子验证
df = engine.run("Rank(Ts_Mean(Close, 10))")

# 批量并行
df_batch = engine.run_batch([
    "Ts_Mean(Close, 5)",
    "Rank(Volume)",
    "If(Close > Open, 1, 0)",
])

建议你按照这个顺序来落地你的研究:

  1. 准备好 catalog,确保里面包含 symboldate 以及 OHLCV 等关键数据列。
  2. run 方法跑单个表达式,先观察结果是否符合你的逻辑预期。
  3. 确认无误后,再使用 run_batch 在统一的样本池和区间内进行多因子批量计算和对比。

语法规则速览

变量与常量

  • 列名不区分大小写:Closeclose 是等价的。
  • 数值常量直接写就行,比如 10.5
  • 变量名必须能映射到你数据中的真实列名。如果你的数据列叫 ClosePrice,那么在表达式中写 Close 会导致执行失败。

运算符

  • 算术运算+, -, *, /
  • 比较运算>, <, >=, <=, ==, !=
  • 逻辑组合:可以在条件中使用 & (与), | (或)

示例:

If((Close > Open) & (Volume > Ts_Mean(Volume, 20)), 1, 0)

三类核心分区语义

这是理解表达式的关键:

  • TS (时序):按 symbol 分组,在时间序列上滚动计算。
  • CS (截面):按 date 分组,在横截面上对所有股票进行计算。
  • EL (元素级):逐元素变换,不引入任何窗口或分组。

Rank(Ts_Mean(...)) 这样的写法,就是一个 CS 算子嵌套了一个 TS 算子。引擎会自动拆解执行步骤,并物化中间结果,你无需手动处理。

算子速查手册

为了方便查阅,这里列出了常用的算子。

时序算子 (TS)

算子 别名 说明 示例
Ts_Mean(X, d) Mean 过去 d 期移动平均 Ts_Mean(Close, 5)
Ts_Std(X, d) Std 过去 d 期移动标准差 Ts_Std(Close, 20)
Ts_Max(X, d) Max 过去 d 期最大值 Ts_Max(High, 10)
Ts_Min(X, d) Min 过去 d 期最小值 Ts_Min(Low, 10)
Ts_Sum(X, d) Sum 过去 d 期求和 Ts_Sum(Volume, 5)
Ts_ArgMax(X, d) ArgMax 过去 d 期最大值出现的距今天数 Ts_ArgMax(Close, 5)
Ts_ArgMin(X, d) ArgMin 过去 d 期最小值出现的距今天数 Ts_ArgMin(Close, 5)
Ts_Rank(X, d) - 当前值在过去 d 期中的分位排名 Ts_Rank(Close, 5)
Delta(X, d) - X(t) - X(t-d) Delta(Close, 1)
Delay(X, d) Ref 滞后 d 期数值 Delay(Close, 1)
Ts_Corr(X, Y, d) Corr 过去 d 期相关系数 Ts_Corr(Close, Volume, 20)
Ts_Cov(X, Y, d) Cov 过去 d 期协方差 Ts_Cov(Close, Open, 20)

截面算子 (CS)

算子 说明 示例
Rank(X) 同一交易日(横截面)上的百分比排名,结果在 0~1 之间 Rank(Close)
Scale(X) 同一交易日(横截面)上的归一化,使得 sum(abs(X)) = 1 Scale(Close)

逻辑与数学算子 (EL)

算子 说明 示例
If(Cond, A, B) 条件判断 If(Close > Open, 1, -1)
Sign(X) 符号函数(返回 1, 0, -1) Sign(Close - Open)
Abs(X) 绝对值 Abs(Close - Open)
Log(X) 自然对数 Log(Volume)
SignedPower(X, e) 保持符号的幂运算 SignedPower(Close, 2)

排障手册(按优先级排序)

遇到问题别慌,按以下顺序检查,大概率能快速解决。

结果全是 NaN

优先检查这三项:

  1. 窗口期 d 是否大于可用的历史数据长度(数据 Warmup 不足)。
  2. 原始数据列本身是否含有大量 NaN 值。
  3. 表达式中的列名是否与数据中的真实列名完全匹配。

嵌套表达式计算明显变慢

这通常是因为跨分区(如 TS 套 CS)嵌套触发了引擎的拆步物化操作。
处理方式

  1. 先单独运行内层的表达式(比如先跑 Ts_Mean(Close, 5))。
  2. 确认内层结果合理后,再套上外层算子(比如再跑 Rank(上一步结果))。
  3. 在批量计算场景下,使用 run_batch 本身已经过优化。

窗口语义与预期不一致

如果你的数据存在停牌或缺失日期,引擎的滚动窗口是按数据行(而非自然日历日)推进的。这可能导致窗口的实际时间跨度与你的“自然日”理解有偏差。建议先对数据进行交易日对齐处理。

列名看着对却报错

引擎会进行列名大小写归一化处理(Closeclose 视为一列),但它不会自动猜测别名。ClosePriceClose 在它看来就是两个完全不同的列。

同一策略在不同市场回测结果漂移

先检查时区和数据本地化设置:

  1. 默认时区是 Asia/Shanghai
  2. 处理非 A 股数据时,必须显式设置 timezone 参数。
  3. 确保你的时间列已经用 tz_localize 等方法赋予了正确的时区信息。

性能优化建议

  1. 先单后批:始终先用 run 调通单个因子逻辑,再用 run_batch 进行扩展。
  2. 简化嵌套:减少不必要的深层嵌套,优先采用逻辑清晰、易于解释的算子组合结构。
  3. 精简数据:只加载计算所需的最小数据列,有效降低 IO 和内存开销。
  4. 清洗数据:提前处理停牌和缺失日期,可以减少因窗口偏差带来的反复调试。

从“会写因子”到“会做研究流水线”

真正高效的因子研究,其目标不是一天内写出 100 个不同的公式,而是建立一套稳定的、可重复的研究流水线。这套流水线应该包括:

  • 用单表达式快速验证因子逻辑的正确性。
  • 在统一的样本池和时间内,批量评估大量因子的表现。
  • 对表现异常的因子,有标准化的排查和诊断流程。
  • 将经过验证的、可解释的因子组合沉淀下来,形成你自己的策略特征库。

AKQuant 的因子表达式引擎,其最大价值正是在于此:它将你的时间和精力,从重复编写“胶水代码”中解放出来,让你能够重新聚焦于“研究因子本身”这个核心创造性工作上。对于希望提升研究效率的量化从业者来说,掌握这样一款工具至关重要。如果你对利用 Python 进行智能 & 数据分析和建模感兴趣,欢迎到技术社区交流更多实战心得。


加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取本文完整研究解析、代码实现细节。




上一篇:我在MWC高通展台,看到了AI跨终端生态落地的关键一步
下一篇:CUDA Agent:基于大规模智能体强化学习,自动生成高性能CUDA内核
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-4 20:12 , Processed in 0.487486 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表