查看全课大纲(第 3 / 52 节)
- 1.量化分析基本概念:从咖啡馆到华尔街
- 2.量化交易员学习路线:从零基础到策略开发
- 3.量化策略生命周期:数据分析的五大流程
- 4.量化数据类型:数值型与类别型数据
- 5.量化沙盒搭建(1):Python与Anaconda安装
- 6.量化沙盒搭建(2):Jupyter Notebook与库配置
- 7.本地多源数据:量化历史回测的基石
- 8.TXT数据流操作:处理金融交易日志
- 9.JSON数据流操作:解析股票行情API响应
- 10.CSV行情文件操作:读取与存储历史K线
- 11.Excel报表操作:清洗财报与财务指标
- 12.量化数据库:SQL常用查询语法
- 13.量化数据库:Python连接与写入
- 14.量化多表连接:关联股票信息与行情
- 15.实战:小象咖啡店营业数据与个股财务指标的多源数据分析
- 16.量化数据采集:爬虫工作原理与合规
- 17.量化爬虫核心:URL管理与去重策略
- 18.量化网页下载:防反爬与请求头伪装
- 19.网页解析(1):使用XPath定位财经新闻
- 20.网页解析(2):使用CSS选择器与ItemLoader
- 21.Scrapy框架:构建工业级量化爬虫
- 22.Scrapy实战(1):新建项目与数据模型定义
- 23.Scrapy实战(2):编写财经爬虫逻辑
- 24.Scrapy实战(3):启用数据管道清洗行情
- 25.Scrapy实战(4):防反爬中间件配置
- 26.实战:构建每日热门个股行情自动采集系统
- 27.NumPy与SciPy:加速量化矩阵计算
- 28.NumPy数组:构建股票价格矩阵
- 29.数组切片:提取特定股票与交易日数据
- 30.NumPy常用方法:计算均值与波动率
- 31.向量化计算:摆脱缓慢的For循环
- 32.通用函数ufunc:计算对数收益率与累计收益
- 33.实战:股票组合资产收益率与协方差矩阵计算
- 34.Pandas Series:管理单只股票收盘价
- 35.Pandas DataFrame:构建多只股票行情面板
- 36.Pandas Index:时间序列索引与对齐
- 37.DataFrame索引操作:loc与iloc定位行情
- 38.索引总结:量化多字段筛选与定位
- 39.运算与对齐:合并不同交易日的数据
- 40.Pandas map:对股票代码与状态做映射
- 41.Pandas apply:滚动计算技术指标
- 42.排序操作:按市盈率与收益率对股票排名
- 43.数据清洗:填充与剔除停牌股票缺失值
- 44.数据清洗:剔除重复报价与异常交易数据
- 45.常用统计:量化最大回撤与分位数风险评估
- 46.极值定位:寻找历史最高价与最低价的发生日
- 47.风险与累计收益:计算年化波动率与累计回报
- 48.实战:小象咖啡店营业数据与热门个股时间序列清洗分析
- 49.Series索引操作:按位置与标签访问行情数据
- 50.文件读写操作:CSV行情数据导入导出
- 51.数据清洗:替换异常报价数据
- 52.常用统计:sum/mean/median/count聚合指标
量化策略生命周期:数据分析的五大流程
约 12 分钟
小象实战讲义 · 量化交易Python入门:从咖啡馆到华尔街
一个量化策略从想法到落地,中间要经历哪些环节?本节把完整生命周期拆解为数据分析的五大流程——问题定义、数据采集、清洗处理、分析建模、结论落地,并说明每个环节的产出物与常见坑。理解这条主线后,后面章节的每个工具你都知道它服务于流程的哪一步,而不是孤立地学语法。
💡 核心导读
本节将系统介绍数据分析的标准流程,从数据获取到最终报告生成的全生命周期。通过完整的Python代码演示,您将掌握:数据采集与清洗的核心方法、探索性分析(EDA)的典型技术、建模分析的关键步骤,以及如何通过可视化呈现分析结果。重点学习Pandas数据透视、Matplotlib/seaborn可视化,以及scikit-learn的快速建模技巧。
数据分析标准流程
1. 数据采集与输入
import pandas as pd
import numpy as np
# 构造一个简单的股票收盘价序列
data = {
'date': pd.date_range('2024-01-01', periods=6),
'symbol': ['AAPL', 'AAPL', 'AAPL', 'GOOGL', 'GOOGL', 'GOOGL'],
'close': [175.2, 176.5, np.nan, 142.3, 143.8, 145.0],
'volume': [78000000, 82000000, 79000000, 32000000, 31000000, 33000000]
}
df = pd.DataFrame(data)
print(f"数据维度: {df.shape}")
print("\n前3行数据示例:")
print(df.head(3).to_string())输出示例(ASCII表格格式):
数据维度: (6, 4)
前3行数据示例:
date symbol close volume
0 2024-01-01 AAPL 175.2 78000000
1 2024-01-02 AAPL 176.5 82000000
2 2024-01-03 AAPL NaN 790000002. 数据清洗与预处理
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
# 构造一个简单的金融数据集用于演示数据清洗
np.random.seed(42) # 固定随机种子,保证输出确定性
dates = pd.date_range('2024-01-01', periods=10)
data = {
'date': dates,
'returns': np.random.randn(10) * 0.02 + 0.001, # 日收益率
'volume': np.random.randint(1000000, 5000000, 10), # 成交量
'volatility': np.random.rand(10) * 0.05 + 0.01 # 波动率
}
df = pd.DataFrame(data)
# 引入一个异常值
df.loc[5, 'returns'] = 0.15 # 一个异常高的收益率
df_clean = df.copy()
# 处理异常值:将收益率限制在[-0.1, 0.1]之间
df_clean['returns'] = df_clean['returns'].clip(lower=-0.1, upper=0.1)
# 标准化两个特征
scaler = StandardScaler()
df_clean[['volume', 'volatility']] = scaler.fit_transform(df_clean[['volume', 'volatility']])
print("\n清洗后统计摘要(部分):")
print(df_clean[['returns', 'volume', 'volatility']].describe().loc[['mean', 'std']].to_string())清洗后统计摘要(部分):
returns volume volatility
mean 0.020329 -1.110223e-16 1.665335e-16
std 0.031141 1.054093e+00 1.054093e+003. 探索性分析(EDA)
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
# 构造并处理一个简单的金融特征数据集
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=8)
data = {
'date': dates,
'momentum': np.random.randn(8) * 0.1 + 0.02,
'value': np.random.randn(8) * 0.08 + 0.01,
'size': np.random.randn(8) * 0.12 - 0.01,
'target_return': np.random.randn(8) * 0.02 + 0.005
}
df = pd.DataFrame(data)
df_clean = df.copy()
# 标准化特征
scaler = StandardScaler()
df_clean[['momentum', 'value', 'size']] = scaler.fit_transform(df_clean[['momentum', 'value', 'size']])
fig1, ax1 = plt.subplots(figsize=(8, 6))
corr_matrix = df_clean[['momentum', 'value', 'size', 'target_return']].corr()
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', ax=ax1, fmt='.2f')
ax1.set_title('因子相关性矩阵')
print("已生成特征相关性热图。")
plt.close(fig1)
fig2, ax2 = plt.subplots(figsize=(8, 4))
df_clean[['momentum', 'value']].boxplot(vert=False, ax=ax2)
ax2.set_title('关键因子分布(动量、价值)')
print("已生成关键特征箱线图。")
plt.close(fig2)已生成特征相关性热图。
已生成关键特征箱线图。4. 建模与分析
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
# 构造一个简单的多因子预测收益率的数据集
np.random.seed(42)
n_samples = 20
data = {
'factor_momentum': np.random.randn(n_samples) * 0.1,
'factor_value': np.random.randn(n_samples) * 0.08,
'factor_quality': np.random.randn(n_samples) * 0.06,
'next_day_return': 0.005 + 0.3 * np.random.randn(n_samples) * 0.02 # 目标变量
}
df = pd.DataFrame(data)
# 数据清洗与标准化
df_clean = df.copy()
scaler = StandardScaler()
df_clean[['factor_momentum', 'factor_value', 'factor_quality']] = scaler.fit_transform(df_clean[['factor_momentum', 'factor_value', 'factor_quality']])
X = df_clean.drop('next_day_return', axis=1)
y = df_clean['next_day_return']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
print(f"模型R2分数: {model.score(X_test, y_test):.3f}")
print("因子系数(重要性):")
for name, coef in zip(X.columns, model.coef_):
print(f"- {name}: {coef:.4f}")模型R2分数: -0.825
因子系数(重要性):
- factor_momentum: -0.0006
- factor_value: 0.0001
- factor_quality: -0.00195. 结果可视化
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
# 构造、处理并建模
np.random.seed(42)
n_samples = 20
data = {
'factor_A': np.random.randn(n_samples) * 0.1,
'factor_B': np.random.randn(n_samples) * 0.08,
'target_return': 0.005 + 0.3 * np.random.randn(n_samples) * 0.02
}
df = pd.DataFrame(data)
df_clean = df.copy()
scaler = StandardScaler()
df_clean[['factor_A', 'factor_B']] = scaler.fit_transform(df_clean[['factor_A', 'factor_B']])
X = df_clean.drop('target_return', axis=1)
y = df_clean['target_return']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(y_test, y_pred, alpha=0.7)
y_min, y_max = y_test.min(), y_test.max()
ax.plot([y_min, y_max], [y_min, y_max], 'r--', lw=2)
ax.set_xlabel('真实收益率')
ax.set_ylabel('预测收益率')
ax.set_title('收益率预测结果对比')
print("已生成预测结果对比散点图。")
plt.close(fig)已生成预测结果对比散点图。📝 动手练一练
第 1 题 · 概念应用
小象在分析一个“动量反转”策略时,发现直接使用原始日度收益率序列计算出的20日动量因子,在回测中表现异常优秀。但当他将数据按日期排序后,仔细检查了数据清洗前的原始行情数据,发现其中一些股票在特定日期有长时间的停牌(缺失值)。以下哪一种缺失值处理方式会在动量因子中引入未来函数(前视偏差),是他必须避免的?
- A. 在计算20日收益率(动量)时,直接跳过缺失日,用停牌日之后第一个有效交易日的价格进行计算,以保证计算窗口长度固定为20个交易日。
- B. 在数据清洗阶段,使用
.bfill()(后向填充),用停牌日之后第一个有效交易日的价格填充缺失的收盘价,然后再计算动量因子。 - C. 在数据清洗阶段,将停牌日的收益率视为0(即假定当日无损益),然后计算动量因子。
- D. 在数据清洗阶段,使用
.ffill()(前向填充),用停牌日前最后一个有效交易日的价格填充缺失的收盘价,然后计算动量因子。
👉 点击查看参考答案
答案:B
推理过程:判断关键在于该处理是否使用了计算时点之后才能知道的价格。选项B的后向填充(.bfill())拿停牌之后的复牌价回填历史缺口,直接把未来信息注入因子,是典型的未来函数,会严重夸大回测性能,必须避免。其余三项都不使用未来价格:选项A跳过缺失日会造成窗口长度与日期对齐的口径问题,但不引入未来信息;选项C把停牌日收益率记为 0 是一种建模假设(假定当日无损益),可能扭曲收益率分布,但同样没有偷看未来;选项D的前向填充用停牌前最后一个有效价格,模拟“停牌期间无法交易、价格保持不变”,是量化回测处理停牌缺失值的标准做法。
💡 小象贴士:时刻警惕“未来函数”!在数据清洗和特征计算中,任何使用到未来信息(如用明天的价格填充今天的缺失值)的操作,都会导致回测结果严重失真,产生无法在实际中复现的虚假收益。
第 2 题 · 代码填空
在构建因子库时,需要对原始因子值进行“去极值”处理,以防止少数异常值(如财报公布日的暴涨暴跌)对后续标准化和模型训练产生过大影响。一种常见的方法是使用“MAD(Median Absolute Deviation,中位数绝对偏差)法”。请补全下面使用MAD法对因子序列进行去极值处理的代码。
import pandas as pd
import numpy as np
# 模拟一个包含异常值的因子序列(例如,市值因子)
np.random.seed(2024)
factor_values = np.random.randn(100) * 10 + 50 # 均值为50,标准差为10的正态分布
factor_values[10] = 200 # 第11个位置插入一个巨大的正异常值
factor_values[90] = -100 # 第91个位置插入一个巨大的负异常值
series = pd.Series(factor_values, name='factor_raw')
print("原始序列描述:")
print(series.describe())
# MAD去极值处理
median = series.median()
mad = (series - median).abs().median() # 计算MAD
# 定义合理的上下限,通常取中位数 ± n倍 MAD,n常取3或5
n = 5
upper_limit = median + n * 1.4826 * mad # 1.4826是正态分布下MAD与标准差的换算系数
lower_limit = median - n * 1.4826 * mad
# 将超出上下限的值替换为限值本身
series_winsorized = series.______(lower=lower_limit, upper=upper_limit)
print(f"\n中位数: {median:.2f}, MAD: {mad:.2f}")
print(f"下限: {lower_limit:.2f}, 上限: {upper_limit:.2f}")
print("\n去极值后序列描述:")
print(series_winsorized.describe())👉 点击查看参考答案
空位应填:clip
补全后的完整可运行代码块:
import pandas as pd
import numpy as np
# 模拟一个包含异常值的因子序列(例如,市值因子)
np.random.seed(2024)
factor_values = np.random.randn(100) * 10 + 50 # 均值为50,标准差为10的正态分布
factor_values[10] = 200 # 第11个位置插入一个巨大的正异常值
factor_values[90] = -100 # 第91个位置插入一个巨大的负异常值
series = pd.Series(factor_values, name='factor_raw')
print("原始序列描述:")
print(series.describe())
# MAD去极值处理
median = series.median()
mad = (series - median).abs().median() # 计算MAD
# 定义合理的上下限,通常取中位数 ± n倍 MAD,n常取3或5
n = 5
upper_limit = median + n * 1.4826 * mad # 1.4826是正态分布下MAD与标准差的换算系数
lower_limit = median - n * 1.4826 * mad
# 将超出上下限的值替换为限值本身
series_winsorized = series.clip(lower=lower_limit, upper=upper_limit)
print(f"\n中位数: {median:.2f}, MAD: {mad:.2f}")
print(f"下限: {lower_limit:.2f}, 上限: {upper_limit:.2f}")
print("\n去极值后序列描述:")
print(series_winsorized.describe())运行结果示例:
原始序列描述:
count 100.000000
mean 50.239623
std 23.516533
min -100.000000
25% 42.946634
50% 50.269396
75% 58.220359
max 200.000000
Name: factor_raw, dtype: float64
中位数: 50.27, MAD: 7.88
下限: -8.11, 上限: 108.65
去极值后序列描述:
count 100.000000
mean 50.245010
std 12.935806
min -8.111193
25% 42.946634
50% 50.269396
75% 58.220359
max 108.649985
Name: factor_raw, dtype: float64可以看到,经过clip方法去极值处理后,序列的样本标准差从 23.52 回落到 12.94(生成数据时总体标准差设定为 10;未收敛到 10 是因为只有 100 个样本、且上下限内仍有偏度),人为置入的 -100 与 200 被限制到 -8.11 与 108.65,极端异常值的影响被有效控制。
本章小结
通过本节的实践,我们完成了从原始数据到建模分析的完整流程。以下是需要重点掌握的技能点:
📋 行动清单
- 使用Pandas完成数据加载与清洗,尝试对
AveBedrms字段进行缺失值填充 - 用Seaborn绘制至少3种不同类型的图表(如pairplot、violinplot等)
- 修改建模代码,尝试用随机森林(RandomForestRegressor)替代线性回归并比较结果
—— 小象教研组