很多人觉得基本面分析和量化交易是两个世界。
基本面投资者看财报、看行业、做价值投资。 量化交易者看数据、做回测、做高频策略。
但实际上,基本面数据完全可以量化。
PE、ROE、营收增速、负债率...这些财务指标都可以变成因子,进入量化模型。
今天我们聊聊如何用Rust处理财务报表数据,并构建基本面因子。
A股常见的财务数据接口(Tushare、Choice、Wind等)通常返回结构化表格数据。我们先定义一个最简洁的财务记录结构体,用于反序列化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use serde::Deserialize;
use chrono::NaiveDate; // 建议使用日期类型而非字符串
/// 单条财务报表记录(简化版,实际可能包含更多字段)
#[derive(Debug, Deserialize, Clone)]
pub struct FinancialData {
/// 股票代码(ts_code),如 "600519.SH"
pub ts_code: String,
/// 报告期(通常为财报发布日期或截止日期)
/// 建议后续解析为 NaiveDate
pub report_date: String,
/// 营业收入(元)
pub revenue: Option<f64>,
/// 净利润(元)
pub profit: Option<f64>,
/// 总资产(元)
pub assets: Option<f64>,
/// 总负债(元)
pub liabilities: Option<f64>,
/// 净资产(归母权益)
pub equity: Option<f64>,
/// 每股收益(EPS) - 用于计算PE
pub eps: Option<f64>,
/// 当日收盘价(用于PE计算,注意匹配日期)
pub close: Option<f64>,
}
1
2
3
4
5
6
7
8
9
fn fill_missing(df: &DataFrame) -> DataFrame {
df.lazy()
.with_columns([
col("revenue").fill_null(lit(0.0)),
col("profit").fill_null(lit(0.0)),
])
.collect()
.unwrap()
}
为什么这样处理?
1
2
3
4
5
6
7
8
9
fn remove_outliers(df: &DataFrame) -> DataFrame {
df.lazy()
.with_columns([
col("pe")
.clip(lit(0.0), lit(100.0)) // PE超过100视为异常
])
.collect()
.unwrap()
}
1
2
3
4
5
6
7
8
9
10
/// 计算静态PE = 收盘价 / EPS(TTM或最新单季)
/// 注意:实际中常用TTM PE或动态PE
fn calculate_pe(df: &DataFrame) -> DataFrame {
df.lazy()
.with_columns([
(col("close") / col("eps")).alias("pe")
])
.collect()
.unwrap()
}
1
2
3
4
5
6
7
8
9
/// ROE = 净利润 / 平均净资产(简化用期末净资产)
fn calculate_roe(df: &DataFrame) -> DataFrame {
df.lazy()
.with_columns([
(col("profit") / col("equity")).alias("roe")
])
.collect()
.unwrap()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/// 计算单季度/年度营收同比增速
/// 前提:数据已按 ts_code + report_date 排序
fn calculate_revenue_growth(df: &DataFrame) -> DataFrame {
df.lazy()
.sort(["ts_code", "report_date"])
.with_columns([
col("revenue")
.pct_change()
.alias("revenue_growth")
])
.collect()
.unwrap()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// 简单线性加权多因子打分(示例)
/// 实际中常使用 z-score + IC 加权 / 机器学习融合
fn calculate_composite_score(df: &DataFrame) -> DataFrame {
df.lazy()
// 标准化各因子
.with_columns([
((col("pe") - col("pe").mean()) / col("pe").std()).alias("pe_zscore"),
((col("roe") - col("roe").mean()) / col("roe").std()).alias("roe_zscore"),
])
// 合成综合得分
.with_columns([
(col("pe_zscore") * lit(-0.3) + col("roe_zscore") * lit(0.7))
.alias("composite_score")
])
.collect()
.unwrap()
}
基本面因子的问题:
基本面量化是长期有效的策略方向,关键在于数据的准确性和因子的有效性。
下一篇,期货跨期套利策略:Polars处理多合约价差矩阵
敬请期待。
(全文完)