零基础-Python-量化交易入门笔记-全-
零基础 Python 量化交易入门笔记(全)
001:课程内容与大纲介绍 📚
在本节课中,我们将从整体上了解《零基础Python金融分析与量化交易实战课程》的内容安排、学习风格以及核心模块。本课程旨在通过纯实战的方式,带领大家掌握使用Python进行金融数据分析与量化交易策略开发的全过程。

课程核心模块概述
我们的课程内容主要围绕以下三大核心模块展开:
上一节我们介绍了课程的整体目标,本节中我们来详细看看这三个模块的具体内容。
以下是三大核心模块的详细介绍:


-
Python数据分析与处理
本模块将讲解如何使用Python处理和分析数据。核心内容包括数据的读取、清洗、转换以及基础分析。我们将重点学习两个关键工具包:用于数值计算的NumPy和用于数据分析的Pandas。 -
金融数据分析
本模块将过渡到实际的金融领域。我们将使用真实的股票数据,学习如何对时间序列数据进行建模、统计分析和可视化。目标是掌握从金融数据中提取有价值信息的方法。 -
量化交易策略
本模块将深入量化交易实战。我们将学习经典的量化交易策略,并介绍如何在回测平台上实现策略。核心是学习如何将策略思想转化为代码,并在历史数据上进行回测,以评估策略的收益、风险等指标,例如计算超额收益。
课程特色与学习工具
我们的课程是零基础起步的纯实战课程,重点在于代码实现。所有讲解都将围绕实际数据和代码展开。
在讲解过程中,我们会使用以下主要工具:

- Jupyter Notebook:用于演示Python工具包的使用和数据分析案例。
- 量化回测平台:用于实现和测试交易策略。在平台上,我们可以编写策略代码,在历史数据上运行回测,并获取详细的交易报告,包括每日持仓、买卖点和最终收益等。
为了方便大家练习,课程将提供所有涉及到的数据和代码。
详细课程大纲
以下是课程三个模块的详细大纲介绍。

上一节我们了解了课程使用的工具,本节中我们来看看每个模块具体涵盖的知识点。
以下是各模块的具体学习内容:


第一模块:Python必备工具包实战
- 目标:为零基础学员打好Python数据分析基础。
- 内容:Python环境配置、核心语法,以及
NumPy和Pandas工具包的详细教学与实战。
第二模块:Python金融数据分析
- 目标:将Python技能应用于金融数据。
- 内容:使用真实股票数据进行实战分析,包括数据建模、统计,并完成在量化平台上的第一个回测项目。

第三模块:深入量化交易
- 目标:开发并改进量化交易策略。
- 内容:学习经典量化策略,掌握策略设计、数据处理、回测分析与策略优化方法,以获得更优的收益结果。


总结

本节课中,我们一起学习了《零基础Python金融分析与量化交易实战课程》的总体框架。我们明确了课程包含Python数据处理、金融数据分析和量化交易策略三大实战模块,并了解了课程纯代码、重实战的学习风格以及将使用的主要工具。从下一节课开始,我们将正式进入第一模块的学习,从Python基础开始,逐步构建金融数据分析与量化交易的能力。
002:Prophet股价预测任务概述 📈

在本节课中,我们将学习如何使用Facebook开源的Prophet框架进行时间序列预测,并应用于股价分析与预测任务。我们将了解Prophet的核心概念、优势以及基本使用流程。

什么是股价分析与预测
股价分析与预测是指对股票价格随时间变化的规律进行研究,并预测其未来走势。股票价格本质上是一个带有日期和数值的数据序列。因此,股价预测是一个典型的时间序列预测任务。
之前我们介绍过ARIMA模型,但其使用难度较大,不够便捷。本节我们将介绍一个由Facebook开源的时间序列预测工具——Prophet,它使用起来非常方便。
Prophet框架简介
Prophet是一个功能强大的时间序列预测框架,其官方网站提供了详细的文档和教程。它的设计目标是让预测变得简单而有效。
时间序列通常受多种成分影响:
- 趋势:宏观、长期且持续的作用。
- 周期性:序列呈现出的周期性波动。
- 季节性:在特定季节(如春夏秋冬)表现出的规律性影响。
- 随机因素:不确定的随机过程。
- 节假日与突发事件:特殊日期或事件对序列的冲击。
Prophet框架的亮点在于它能方便地封装模型,用户只需调节少量参数即可轻松构建预测模型。它支持按小时、天、周、月等多种粒度进行预测,并能自动处理周期性、季节性、节假日效应、异常值和缺失值。

核心概念:趋势转折点
Prophet中一个非常重要的参数是趋势转折点。转折点是指序列中发生剧烈变化的位置,例如股价突然大幅上涨或下跌。模型通过识别历史数据中的转折点,可以更好地拟合训练数据。

然而,转折点也可能带来过拟合的风险。模型可能过度捕捉训练数据中特有的剧烈波动,而这些波动未必会在未来的测试数据中重现。因此,转折点的设置是Prophet模型调参时需要重点考虑的部分。


Prophet的算法基础

Prophet的算法核心结合了三种经典的时间序列模型思想:
- 自回归模型:基于自身过去的数据来预测未来数据。
- 移动平均模型。
- 整合模型。


这些思想与ARIMA模型类似,但Prophet通过更好的封装,提供了更简单易用的接口。


基本使用流程

Prophet提供了Python和R两种接口。对于Python用户,其使用流程与Scikit-learn的API风格相似,非常直观。
以下是使用Prophet进行预测的基本步骤:

-
准备数据:数据需要包含两列:
ds(时间戳)和y(要预测的指标)。这是标准的输入格式。# 示例数据格式 # ds y # 2023-01-01 100 # 2023-01-02 102 -
创建并训练模型:实例化
Prophet类并调用fit方法进行训练。from prophet import Prophet model = Prophet() model.fit(df) -
构建未来时间框:使用
make_future_dataframe函数创建需要预测的未来时间段。future = model.make_future_dataframe(periods=365) # 预测未来365天

- 进行预测:调用
predict方法得到预测结果。结果包含预测值yhat以及置信区间的上下限yhat_lower和yhat_upper。forecast = model.predict(future)

- 可视化结果:Prophet内置了便捷的可视化功能,可以轻松绘制预测趋势、季节性成分等。
fig1 = model.plot(forecast) fig2 = model.plot_components(forecast)
任务与数据准备
本节课的任务是进行股价分析与预测。我们将使用一个工具库来获取股票数据集。在运行示例代码前,需要确保网络连接并安装必要的工具。
以下是需要安装的库:
pip install yfinance:用于获取股票数据。pip install prophet:Prophet预测框架本身。matplotlib:用于绘图(通常已安装)。
如果使用pip install安装失败,可以尝试以下方法:
- 访问Python官方包索引网站搜索对应的
.whl文件进行安装。 - 或者下载库的源代码,在解压目录下执行
python setup.py install命令进行安装。
代码结构与演示
本节课的核心代码和功能函数已封装在一个类中。我们将通过Jupyter Notebook演示完整的预测流程和结果,并通过集成开发环境的调试功能,逐步深入代码内部,理解Prophet每一步的具体操作。

本节课中,我们一起学习了Facebook Prophet框架在时间序列预测中的应用。我们了解了其核心优势、基本概念(如趋势转折点)以及从数据准备、模型训练到预测可视化的完整流程。Prophet以其简单易用的特点,成为了时间序列预测任务中的一个强大工具。
003:股票数据获取与初步分析 📈
在本节课中,我们将学习如何获取股票数据,并进行初步的可视化分析。我们将以微软(MSFT)的股票为例,演示从数据读取到绘制趋势图的完整流程。
数据读取与查看
首先,我们需要读取股票数据。这里我们使用 yfinance 库来获取微软(MSFT)的历史股价数据。
import yfinance as yf
import pandas as pd
# 读取微软股票数据
stock_symbol = 'MSFT'
data = yf.download(stock_symbol)
执行上述代码后,我们会得到一个包含多个指标的 DataFrame。数据中包含了日期(Date)、开盘价(Open)、收盘价(Close)等。我们主要关注的是收盘价(Close),这是我们后续要预测的目标变量。

为了确保代码正常运行,请检查是否已正确安装 yfinance、pandas 和 matplotlib 库。如果代码报错,通常是因为缺少这些库,需要先安装它们。

数据预处理
读取数据后,我们需要进行一些预处理,以便后续分析。
# 重置索引,将日期列变为普通列
data = data.reset_index()
# 提取关键列:日期(DS)和目标变量(Y)
data['DS'] = pd.to_datetime(data['Date'])
data['Y'] = data['Close']
# 计算每日价格变化
data['daily_change'] = data['Close'] - data['Open']
通过以上步骤,我们得到了一个清晰的数据集,其中 DS 是日期,Y 是收盘价。我们还计算了每日的价格变化(收盘价减去开盘价)。
数据统计信息
接下来,我们可以计算一些基本的统计信息,例如数据的时间范围、最高价、最低价等。
# 计算起始和结束日期
start_date = data['DS'].min()
end_date = data['DS'].max()
# 计算最高价和最低价
max_price = data['Y'].max()
min_price = data['Y'].min()
# 找到最高价和最低价对应的日期
max_price_date = data.loc[data['Y'] == max_price, 'DS'].values[0]
min_price_date = data.loc[data['Y'] == min_price, 'DS'].values[0]
这些统计信息有助于我们了解数据的整体情况,例如股票的历史表现和波动范围。
数据可视化
在时间序列分析中,可视化是非常重要的一步。通过绘制股票价格随时间变化的趋势图,我们可以直观地观察其走势。
以下是绘制股票价格趋势图的代码:
import matplotlib.pyplot as plt
# 绘制收盘价趋势图
plt.figure(figsize=(12, 6))
plt.plot(data['DS'], data['Y'], color='red', linewidth=2, label='Close Price')
plt.xlabel('Date')
plt.ylabel('Price')
plt.title('Microsoft Stock Price Trend (1986-2018)')
plt.grid(True)
plt.legend()
plt.show()
通过这张图,我们可以看到微软股票从1986年到2018年的整体趋势。虽然中间有波动,但整体呈上升趋势。
多指标可视化
除了收盘价,我们还可以同时可视化多个指标,例如交易量(Volume)和每日价格变化(daily_change)。
# 绘制交易量和每日价格变化
fig, ax1 = plt.subplots(figsize=(12, 6))
# 绘制每日价格变化
ax1.plot(data['DS'], data['daily_change'], color='red', linewidth=2, label='Daily Change')
ax1.set_xlabel('Date')
ax1.set_ylabel('Daily Change', color='red')
ax1.tick_params(axis='y', labelcolor='red')

# 创建第二个Y轴,绘制交易量
ax2 = ax1.twinx()
ax2.plot(data['DS'], data['Volume'], color='blue', linewidth=2, label='Volume')
ax2.set_ylabel('Volume', color='blue')
ax2.tick_params(axis='y', labelcolor='blue')


plt.title('Microsoft Stock: Daily Change and Volume')
plt.show()


通过这张图,我们可以同时观察股票的价格变化和交易量。例如,在某些日期,交易量会突然增加,这可能与市场事件相关。

模拟投资收益
为了增加趣味性,我们可以模拟一个简单的投资场景:假设在某个起始日期买入股票,在终止日期卖出,计算收益情况。
def calculate_profit(data, start_date, end_date, shares=100):
# 筛选指定日期范围内的数据
mask = (data['DS'] >= start_date) & (data['DS'] <= end_date)
filtered_data = data.loc[mask].copy()
# 计算买入价和卖出价
buy_price = filtered_data.iloc[0]['Close']
sell_price = filtered_data.iloc[-1]['Close']
# 计算总收益
profit = (sell_price - buy_price) * shares
return profit


# 示例:计算从1986年到2018年的收益
profit = calculate_profit(data, start_date, end_date, shares=100)
print(f"总收益: ${profit:.2f}")


这个函数可以帮助我们快速计算在不同时间段内投资的收益情况。例如,如果在1986年买入微软股票并持有到2018年,收益会非常可观。


总结


本节课中,我们一起学习了如何获取股票数据、进行预处理、计算统计信息以及绘制可视化图表。通过以上步骤,我们可以对时间序列数据有一个初步的了解,并为后续的预测分析打下基础。


在下一节中,我们将进一步探讨如何利用这些数据进行时间序列预测。
004:使用fbprophet进行预测实例 📈


在本节课中,我们将学习如何使用Facebook开发的fbprophet库来构建一个时间序列预测模型。我们将从数据准备、模型构建、可视化分析到未来预测,完整地走一遍流程,并理解其中的核心概念。


模型构建与初步可视化

上一节我们介绍了时间序列的基本概念,本节中我们来看看如何使用fbprophet构建模型。


首先,执行创建模型的操作后,fbprophet会帮助我们完成建模和预测,并生成可视化图表。

在生成的图表中:
- 黑色点代表观测值,即我们的历史数据。
- 绿色点代表模型拟合(预测)出的值。
- 浅绿色区域代表预测的置信区间。
这样,一个基本的时间序列模型就构建完成了。
分析模型的季节性规律
构建完模型后,我们可以利用fbprophet框架自带的函数来深入分析模型发现的规律。

该框架内置了plot_components函数,无需我们自己编写。训练完模型后,此函数可以展示数据中发现的多种趋势。

以下是该函数能展示的主要趋势成分:
- 整体趋势:数据随年份变化的长期趋势。
- 周内趋势:数据在一周内的变化规律。
- 年内趋势:数据在一年中各个月份的变化规律。

代码实现详解
现在,让我们具体看一下建模的代码是如何实现的。



核心建模步骤如下:

-
模型实例化:调用
Prophet()创建模型实例。在实例化时可以指定多种参数。model = Prophet( yearly_seasonality=True, weekly_seasonality=True, changepoint_prior_scale=0.05 )yearly_seasonality,weekly_seasonality等参数控制是否绘制相应的季节性成分。changepoint_prior_scale是一个关键参数,用于控制模型对趋势变化的灵活度(敏感度),默认值为0.05。此参数我们后续会详细讨论。- 还可以通过
holidays参数传入节假日信息。
-
添加自定义季节性:例如,可以添加一个以30.5天为周期的季节性成分。
model.add_seasonality(name='monthly', period=30.5, fourier_order=5)

-
拟合模型:使用历史数据对模型进行训练。可以通过
training_years参数指定使用最近多少年的数据进行训练。model.fit(history_data) -
构建未来时间框架:使用
make_future_dataframe方法生成待预测的时间点。periods参数指定需要预测的未来天数。future = model.make_future_dataframe(periods=0) # periods=0 表示只对历史数据进行拟合,不预测未来


- 进行预测:对生成的时间框架进行预测。
预测结果forecast = model.predict(future)forecast是一个DataFrame,会自动添加以下几列:yhat:预测值。yhat_lower:预测区间的下限。yhat_upper:预测区间的上限。

- 绘制结果:将观测值、预测值及置信区间绘制在同一张图上。
黑色点表示观测值,绿色线表示预测值,浅绿色区域表示置信区间。fig1 = model.plot(forecast)


季节性分解结果解读


执行 model.plot_components(forecast) 后,我们可以得到分解图。


- 整体趋势图:展示了从2015年到2018年,数据整体的变化情况,总体呈上升趋势。
- 月内趋势图:展示了数据在一个月内的典型波动。例如,月初可能上升,月末可能下降。
- 年内趋势图:展示了数据在一年中各个月份的典型水平。例如,图中可能显示12月和1月的数值上浮最大,而7月、9月和10月是下降的。
这些规律为我们提供了分析思路。例如,如果这是股价数据,根据模型发现的“月底下跌”趋势,那么在月底时或许应该谨慎操作。
突变点分析

fbprophet 模型的一个重要功能是自动检测“突变点”。突变点是指时间序列趋势发生显著变化的点,例如从上升转为下降,或增长速率突然加快/减慢。





模型通过观察时间序列的二阶导数来识别这些点。





在分析突变点的图表中:
- 绿色竖线:标识二阶导数大于零的突变点,意味着在该点趋势转为加速上升或减速下降(积极变化)。
- 红色竖线:标识二阶导数小于零的突变点,意味着在该点趋势转为减速上升或加速下降(消极变化)。



我们可以通过以下代码获取和可视化突变点:
# 从模型中获取所有突变点的日期
changepoints = model.changepoints
# 计算突变点处的趋势变化率(近似二阶导)
trend_deltas = model.params['delta'].mean(0)
# 根据变化率正负进行分类并绘图
进行未来预测
最后,我们来看如何用训练好的模型预测未来。这与拟合历史数据的步骤类似,关键区别在于 make_future_dataframe 的 periods 参数。

例如,设置 periods=180,表示我们希望预测未来180天的数据。
future = model.make_future_dataframe(periods=180)
forecast = model.predict(future)
fig = model.plot(forecast)



在结果图中:
- 黑色点(观测值)截止于历史数据的最后日期(例如2018年1月26日)。
- 绿色线(预测值)会向后延伸180天,显示出模型对未来趋势的推断。
- 预测部分同样带有置信区间。
模型预测的未来趋势可能相对保守或平缓,这主要受 changepoint_prior_scale 参数影响。

核心参数:changepoint_prior_scale
changepoint_prior_scale 参数是控制模型灵活度的关键。


- 值调大:模型会更灵活,对数据中的波动更敏感,趋势线可能包含更多的突变点,预测未来时可能更“奔放”,波动更大。
- 值调小:模型会更保守和平滑,倾向于认为趋势稳定,突变点少,预测未来时更“平缓”。
调整这个参数,可以在模型对历史数据的拟合程度与对未来预测的泛化能力之间进行权衡。

总结

本节课中我们一起学习了使用 fbprophet 库进行时间序列预测的完整流程:



- 模型构建与拟合:实例化
Prophet模型,并通过fit方法用历史数据训练模型。 - 可视化分析:使用
plot和plot_components函数可视化拟合结果及季节性、趋势性分解。 - 突变点检测:理解并利用模型自动识别趋势发生显著变化的点。
- 未来预测:通过指定
periods参数,生成未来时间点并进行预测。 - 参数调节:认识了核心参数
changepoint_prior_scale的作用,它用于控制模型对趋势变化的敏感度,从而影响预测结果的保守或激进程度。

fbprophet 是一个功能强大且易于使用的时间序列预测工具,通过本教程的实例,我们已经掌握了其基本用法和核心概念。
005:亚马逊股价趋势 📈
在本节课中,我们将学习如何评估和改进一个基础的股票价格预测模型。我们将以亚马逊(AMZN)的股价数据为例,分析模型的预测效果,并探讨如何通过调整参数来提升模型的准确性。
模型回顾与数据准备
上一节我们构建了一个基础的股价预测模型。本节中,我们来看看如何评估和改进这个模型。
首先,我们导入必要的库并加载新的数据。这次我们使用亚马逊(股票代码:AMZN)的股价数据进行分析。亚马逊的股价从1997年5月22日的低点,上涨至我们获取数据时(示例中为某年1月23日)的1362.54美元,整体呈现上升趋势。
以下是加载数据和构建基础模型的代码:
# 导入库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from fbprophet import Prophet
# 加载亚马逊股价数据
df = pd.read_csv('AMZN.csv')
df['ds'] = pd.to_datetime(df['Date'])
df['y'] = df['Close']
# 构建基础模型
model = Prophet()
model.fit(df)

构建的基础模型预测结果如下图所示,其中黑点代表真实股价,绿点代表模型预测值。我们发现,真实数据波动较大,而模型的预测趋势则更为平缓。这在一定程度上是合理的,因为一个完全拟合所有突变点的模型可能过拟合,而平稳的预测趋势通常更具鲁棒性。
模型评估方法
在得到基础预测后,我们需要评估模型的性能。评估主要关注两个方面:
- 预测误差:计算预测值与真实值之间的差异。
- 趋势判断准确性:模型是否能正确预测股价的上升或下降趋势。
以下是评估步骤的分解:
首先,划分训练集和测试集。我们以2017年1月24日为界,此日期前的三年数据(2014-2017)作为训练集,此日期后的一年数据(2017-2018)作为测试集。
# 划分训练集和测试集
train = df[df['ds'] < '2017-01-24']
test = df[df['ds'] >= '2017-01-24']
接着,使用训练集拟合模型,并预测测试集时间段(未来365天)的股价。
# 训练模型
model = Prophet()
model.fit(train)
# 创建未来日期数据框并进行预测
future = model.make_future_dataframe(periods=365)
forecast = model.predict(future)
然后,我们计算关键评估指标。预测误差可以通过平均绝对误差等公式计算:


误差公式:error = |y_true - y_pred|

趋势判断则通过比较相邻日期的价格差来完成。如果今日价格高于昨日,则记为上升趋势(1),反之记为下降趋势(0)。通过比较预测趋势和真实趋势的符号是否一致,来计算趋势预测的正确率。

# 计算趋势方向
def get_trend_direction(series):
return np.sign(np.diff(series))
# 获取预测和真实的趋势方向
pred_trend = get_trend_direction(forecast['yhat'].values)
true_trend = get_trend_direction(test['y'].values)
# 计算趋势预测准确率
trend_accuracy = np.mean(pred_trend == true_trend)
运行评估代码后,我们得到如下结果:模型在测试集上的平均预测误差约为160美元,上升趋势预测正确率约为56%,下降趋势预测正确率约为44%。这些指标表明,当前的基础模型预测能力有限,效果接近于随机猜测。

结果分析与改进方向
从预测图表可以直观看出,模型的预测值(蓝色线)与真实股价(黑色线)之间存在显著差距,且模型预测的曲线过于平缓,未能有效捕捉真实数据中的波动和上升势头。
因此,我们接下来的目标是调整模型参数,以提升其捕捉数据波动和趋势的能力。可能的改进方向包括:
- 在Prophet模型中引入季节性、节假日效应等额外因素。
- 调整趋势变化点的先验尺度,让模型更能适应突变。
- 尝试不同的模型或集成方法。

本节课中我们一起学习了如何评估一个时间序列预测模型的性能,包括计算预测误差和趋势判断准确率。我们以亚马逊股价为例,发现基础模型的预测效果有较大提升空间。在下一节中,我们将具体探讨如何通过调参来优化模型,使其能更好地拟合数据特征。
006:突变点调参 🎯
在本节课中,我们将学习如何调整 Prophet 模型中的一个关键参数——突变点(Change Point)的权重,并观察其对预测结果的影响。我们将通过实验对比不同权重值下的模型表现,并学习如何选择最优参数。
概述
上一节我们介绍了 Prophet 模型的基本使用。本节中,我们来看看如何调整模型参数以优化预测效果。核心在于理解并调整突变点(Change Point)的权重参数 changepoint_prior_scale。
突变点权重参数解析
突变点权重参数 changepoint_prior_scale 用于指定模型对数据中突变趋势的重视程度。其默认值为 0.05。
- 权重大(例如 0.2):模型会更努力地拟合训练数据中的突变点,使预测曲线更贴合历史数据的波动。但这可能导致过拟合,即模型过于“记忆”训练数据,在未知数据上表现变差。
- 权重小(例如 0.001):模型对突变点不敏感,预测趋势会更为平缓和保守。这可能导致欠拟合,即模型未能充分学习数据中的有效模式。
实验:对比不同权重的影响
以下是实验步骤,我们将对比 changepoint_prior_scale 为 0.001, 0.05, 0.1, 0.2 时的模型表现。
- 准备数据与模型:加载股票历史数据,并循环为每个权重值创建一个 Prophet 模型。
- 进行预测:对每个模型进行未来 180 天的预测。
- 可视化结果:将真实数据点与不同权重下的预测曲线绘制在同一张图中进行对比。
# 示例代码结构
import pandas as pd
from prophet import Prophet
# 假设 df 是包含‘ds’和‘y’两列的历史数据
# df = pd.read_csv(‘your_stock_data.csv’)
param_grid = [0.001, 0.05, 0.1, 0.2]
forecasts = []
for param in param_grid:
m = Prophet(changepoint_prior_scale=param)
m.fit(df)
future = m.make_future_dataframe(periods=180)
forecast = m.predict(future)
forecasts.append(forecast)
# 接下来进行绘图对比...
实验结果分析:
- 蓝色线(权重=0.001):预测曲线非常平缓,几乎是一条直线,未能捕捉数据中的上升和下降突变,属于明显的欠拟合。
- 红色线(权重=0.05,默认值):预测趋势有所改善,能反映部分数据波动,但整体仍偏保守。
- 黄色线(权重=0.2):预测曲线与历史数据的拟合度最高,能敏锐地捕捉到突变点。但这也意味着过拟合风险增加。
参数评估与选择
仅仅观察拟合曲线不够客观,我们需要量化评估。通常,我们关注模型在测试集(未来数据)上的误差。
以下是评估不同参数下训练误差和测试误差的步骤:
- 划分训练集和测试集。
- 用不同参数在训练集上拟合模型。
- 在测试集上计算预测误差(如均方误差)。
- 选择测试误差最小的参数作为最优参数。
在我们的实验中,当把 changepoint_prior_scale 从 0.2 继续调大到 0.7 时,测试误差达到了最低点(例如 66)。因此,对于当前数据集,0.7 是比默认值 0.05 更优的选择。
核心结论:调参的目标是找到使测试误差最小化的参数值,以平衡欠拟合与过拟合。
模型评估与应用
使用优化后的参数(changepoint_prior_scale=0.7)重新训练模型并进行预测。


- 预测效果提升:对比优化前后模型对某个特定日期的预测值,发现新模型的预测误差更小。
- 预测未来:模型可以方便地预测未来任意时间点的值,并给出不确定性区间(上下界)。
future_dates = m.make_future_dataframe(periods=90) # 预测未来90天 forecast = m.predict(future_dates) # forecast 中包含 ‘yhat’, ‘yhat_lower’, ‘yhat_upper’ 等列


探索性应用:模拟股票交易



我们可以将预测结果用于简单的策略回测,例如:
- 策略:如果模型预测明日股价上涨(
yhat_tomorrow > yhat_today),则今日买入,明日卖出。 - 回测:在一段历史时期内模拟此策略,计算累计收益。
- 注意:这只是一个简化演示。实际金融市场极其复杂,此类基于历史价格的预测策略通常无法稳定盈利,且过去表现不代表未来。
总结
本节课中我们一起学习了 Prophet 模型的核心调参技巧:
- 理解参数:
changepoint_prior_scale控制模型对数据突变的灵敏度,需要在欠拟合与过拟合之间取得平衡。 - 实验方法:通过网格搜索不同的参数值,并可视化预测趋势来直观感受其影响。
- 科学评估:依赖测试集误差这个量化指标来选择最优参数,而不是单纯追求对训练数据的高拟合度。
- 持续探索:Prophet 功能丰富,建议结合官方文档和实际动手测试,以深入掌握其各项特性。

要深入掌握任何工具,最好的方法就是:阅读官方文档了解原理,然后亲手编写代码进行实验。
007:连续指标变化情况分析 📈


在本节课中,我们将要学习如何分析金融时间序列的连续变化情况,特别是如何计算和累加增长率,以理解初始投资在连续时间内的价值变化。
概述
上一节我们介绍了如何计算简单的差异值和增长率。本节中我们来看看,当我们需要分析一个连续时间序列的整体变化,例如计算初始投资经过一系列涨跌后的最终价值时,会遇到什么问题以及如何解决。

简单增长率累加的问题
首先,我们思考一个问题:能否将一段时间内每天的增长率简单相加,来得到这段时间的总增长率?
例如,假设一只股票:
- 第一天价格:
100 - 第二天价格:
50 - 第三天价格:
75
以下是计算过程:
- 第二天的增长率:
(50 - 100) / 100 = -0.5 - 第三天的增长率:
(75 - 50) / 50 = 0.5 - 简单累加增长率:
-0.5 + 0.5 = 0

累加结果为零,似乎表示股票“没涨没跌”。但观察原始数据,股价从100跌至75,实际上是下跌的。这说明简单增长率不能直接相加来反映整体变化。

解决方案:使用对数计算连续复合增长率

对于连续时间的计算,我们需要借助数学中的对数。核心思想是:对数的加法等价于其内部数值的乘法。
我们重新计算上面的例子,但使用对数:
- 计算每日价格比的对数:
log(后一天价格 / 前一天价格) - 第二天:
log(50 / 100) - 第三天:
log(75 / 50) - 将对数结果累加:
log(50/100) + log(75/50)
根据对数运算法则,这等价于:
log( (50/100) * (75/50) ) = log(75/100)


最终结果 log(75/100) 直接反映了第三天价格与第一天价格的比值关系。将这个对数值通过指数函数还原,就能得到从第一天到第三天的实际复合增长倍数。


这种方法的意义在于:无论中间过程如何涨跌,将对数化的每日变化率累加,最终结果直接等价于首日与末日价格的整体比较。这完美解决了简单增长率无法累加的问题。


在Python/Pandas中的实现


接下来,我们看看如何在代码中实现上述计算。假设我们有一个名为 data 的Pandas DataFrame,其中包含多只股票每日的收盘价。

步骤1:计算每日对数收益率


首先,我们需要计算“今日价格/昨日价格”的对数值。

# 计算每日的对数收益率
# shift(1) 将数据整体向下移动一行,从而得到“前一天”的价格
returns = np.log(data / data.shift(1))
以下是关键函数和步骤说明:
data.shift(1):将data中的每一列数据向下移动一行。这样,每一行的数据就变成了它前一行的数据(即“前一天”的价格)。data / data.shift(1):计算今日价格与昨日价格的比值。np.log(...):计算比值的自然对数,得到每日的连续复合增长率(对数收益率)。


步骤2:计算累积收益率

我们的目标是知道:假设最初投资1单位(如1元),经过一系列日期后,价值变成了多少。这需要对每日的对数收益率进行累积求和,然后通过指数函数还原。

# 计算累积收益率
# cumsum() 实现累积求和
cumulative_returns = returns.cumsum()
# 通过指数函数 exp 还原为实际的价值倍数
normalized_price = np.exp(cumulative_returns)

以下是关键函数和步骤说明:
returns.cumsum():对returns中的每一列进行累积求和。例如,对于序列[a, b, c],cumsum()的结果是[a, a+b, a+b+c]。这相当于累加了从起始日到当前日的所有对数收益率。np.exp(cumulative_returns):对累积求和后的对数收益率取指数。因为log(最终价格/初始价格) = 累积对数收益率,所以最终价格/初始价格 = exp(累积对数收益率)。这样,normalized_price中的每个值就代表了“以第一天的价格为1单位基准,当前日期对应的价值”。


步骤3:可视化结果
我们可以将 normalized_price 绘制出来,直观地看到每只股票从起始日开始,1单位投资的价值增长曲线。


# 绘制累积收益率曲线
normalized_price.plot(figsize=(16, 8))

这张图清晰地展示了不同股票长期的投资回报情况。例如,可能显示亚马逊的1元投资在期末变成了约10元,而苹果的变成了约4元。

核心操作总结

本节课中我们一起学习了金融时间序列分析中几个核心的计算操作:


- 计算差异值:使用
.diff()方法可以方便地计算相邻数据的差值。 - 计算简单增长率:公式为
(当期值 - 上期值) / 上期值。这种方法直观,但增长率不能直接相加来衡量整体变化。 - 计算连续复合增长率(对数收益率):
- 公式:
log(当期值 / 上期值) - 优点:可以对数收益率进行累加,累加结果直接对应首末期的整体变化。这是分析长期连续变化的有力工具。
- 公式:
- 计算累积收益率:
- 步骤:先计算每日对数收益率,然后使用
.cumsum()进行累积求和,最后用np.exp()还原为实际的价值倍数。 - 意义:得到以起始点为基准(标准化为1)的资产价格序列,完美展示资产的长期增长轨迹。
- 步骤:先计算每日对数收益率,然后使用



通过掌握这些方法,你就能对金融时间序列进行基本的趋势分析和量化评估。
008:短均与长均计算实例 📈

在本节课中,我们将学习如何通过计算股票的短期和长期移动平均线,来构建一个简单的分析策略。这个策略的核心是识别“黄金交叉”和“死亡交叉”,以辅助判断买入和卖出的时机。

概述

我们将使用苹果公司的股票数据作为示例。整个流程分为三步:首先计算短期移动平均线,然后计算长期移动平均线,最后将结果可视化,并识别关键的交叉点。


计算短期移动平均线
上一节我们介绍了策略的目标,本节中我们来看看如何计算第一个指标——短期移动平均线。
移动平均线的计算方法是取指定时间窗口内股价的平均值。对于短期均线,通常使用5天(即一周的交易日)作为窗口。但由于我们的示例数据时间跨度较长,为了更清晰地展示,我们将短期窗口设置为10天。
以下是计算短期移动平均线的步骤:


- 选择我们要分析的股票数据,这里使用苹果公司(AAPL)的股价。
- 使用Pandas的
.rolling()方法创建一个滚动窗口对象。 - 对窗口内的数据计算平均值(
.mean()),得到短期移动平均线。


对应的核心代码如下:
# 假设 df 是包含苹果股价的DataFrame,列名为 ‘AAPL‘
short_window = 10
df[‘M1‘] = df[‘AAPL‘].rolling(window=short_window).mean()

这段代码创建了一个名为 M1 的新列,它存储了以10天为窗口计算的股价移动平均值。


计算长期移动平均线


在计算出短期均线后,接下来我们需要计算长期移动平均线作为对比基准。
长期移动平均线通常使用20天或30天(约一个月)作为窗口。为了与短期均线形成明显对比,我们这里选择30天作为长期窗口。
计算长期移动平均线的方法与短期类似:
- 使用相同的股价数据列。
- 创建一个更大的滚动窗口(例如30天)。
- 计算该窗口内的平均值。


对应的核心代码如下:

long_window = 30
df[‘M2‘] = df[‘AAPL‘].rolling(window=long_window).mean()

这段代码创建了名为 M2 的新列,存储了30天长期移动平均值。


数据可视化与交叉点识别
现在我们已经有了短期(M1)和长期(M2)两条移动平均线,本节我们将它们与原始股价一起绘制在图表中,并学习如何识别“黄金交叉”与“死亡交叉”。


“死亡交叉”指短期均线从上方向下穿越长期均线,通常被视为下跌信号。“黄金交叉”则指短期均线从下方向上穿越长期均线,通常被视为上涨信号。
为了更直观地展示交叉点,我们还可以增加一个信号列。当短期均线大于长期均线时,标记为 1;反之则标记为 -1。这个信号的变化点就对应着交叉的发生。


以下是生成信号和绘制综合图表的核心步骤:

- 使用
np.where函数比较M1和M2,生成信号列position。 - 使用Matplotlib绘制股价、两条均线以及信号线。为了让信号线(取值仅为1或-1)清晰可见,我们使用双Y轴进行绘制。

对应的核心代码如下:

import numpy as np
import matplotlib.pyplot as plt

# 生成交易信号
df[‘position‘] = np.where(df[‘M1‘] > df[‘M2‘], 1, -1)
# 创建图形和主Y轴
fig, ax1 = plt.subplots(figsize=(12,6))
ax1.plot(df[‘AAPL‘], label=‘AAPL Price‘, color=‘blue‘, alpha=0.5)
ax1.plot(df[‘M1‘], label=‘Short MA (10-day)‘, color=‘green‘)
ax1.plot(df[‘M2‘], label=‘Long MA (30-day)‘, color=‘red‘)
ax1.set_xlabel(‘Date‘)
ax1.set_ylabel(‘Price‘)
ax1.legend(loc=‘upper left‘)
# 创建次Y轴用于显示信号
ax2 = ax1.twinx()
ax2.plot(df[‘position‘], label=‘Signal (1/-1)‘, color=‘purple‘, linestyle=‘--‘)
ax2.set_ylabel(‘Signal‘)
ax2.legend(loc=‘upper right‘)


plt.title(‘AAPL Stock Price with Moving Averages and Trading Signal‘)
plt.show()

在生成的图表中,您可以观察到:
- 当绿色短期均线下穿红色长期均线,同时紫色信号线从
1变为-1时,发生“死亡交叉”。 - 当绿色短期均线上穿红色长期均线,同时紫色信号线从
-1变为1时,发生“黄金交叉”。
总结


本节课中我们一起学习了如何构建一个基础的股票移动平均线分析策略。

我们首先介绍了“黄金交叉”和“死亡交叉”的概念。接着,我们使用Pandas的滚动窗口功能,分别计算了短期(10日)和长期(30日)移动平均线。然后,我们通过Matplotlib将股价和两条均线可视化,并利用 np.where 函数生成交易信号,清晰地标识出了两个交叉点。


这个实例展示了如何利用Pandas进行简单的金融数据分析和计算,并通过可视化直观地呈现分析结果。在实际应用中,您可以调整窗口参数(如5日、20日),并尝试结合其他指标来优化策略。
009:10.10.4-指标相关情况分析 📊

在本节课中,我们将学习如何使用Python进行回归分析,以探索两个金融指标(标普500指数和VIX恐慌指数)之间的相互关系。我们将通过数据提取、可视化图表绘制以及散点图矩阵分析,直观地展示变量间的关联性。





数据提取与初步观察 🔍


上一节我们介绍了数据处理的基础,本节中我们来看看如何提取特定指标进行分析。

首先,我们需要从数据集中提取我们关心的两个指标列:SPX(标普500指数)和VIX(恐慌指数)。



以下是提取数据的代码:
data2 = data2[['SPX', 'VIX']]
print(data2.head())


执行代码后,我们得到了只包含这两列的数据集,便于后续的观察和分析。




绘制双轴折线图 📈

提取数据后,最直观的方法是绘制图表来观察关系。但将两个量级不同的指标画在同一坐标系下并不清晰。



因此,我们采用双Y轴的方法,将两个指标绘制在同一张图上,但使用不同的坐标轴刻度。



以下是绘制双轴折线图的代码:
ax = data2['SPX'].plot(figsize=(10,6), legend=True)
ax2 = ax.twinx()
data2['VIX'].plot(ax=ax2, legend=True, color='g')
ax.figure.legend()

为了更清晰地观察短期趋势,我们可以只选取一段时间的数据(例如2010年至2012年)进行绘图。画图过程通常是迭代的:先画出基础图形,再根据问题逐步调整参数(如图形大小、数据范围),使结果更清晰。

绘制散点图矩阵 🔄


除了折线图,散点图是观察两个变量关系的另一种有效方式。我们可以计算指标的日收益率,并绘制散点图矩阵来观察分布与相关性。
以下是计算对数收益率并绘制散点图矩阵的代码:
import numpy as np
returns = np.log(data2 / data2.shift(1))
pd.plotting.scatter_matrix(returns,
alpha=0.2,
diagonal='kde', # 对角线绘制核密度估计图
figsize=(10, 10))


在这个矩阵图中:
- 对角线上的图是每个变量自身的核密度估计(KDE)图,它比直方图更平滑,展示了数据的分布形态。
- 非对角线上的图是两两变量之间的散点图,可以直观看到
SPX收益率与VIX收益率呈现负相关关系(一个上升时,另一个倾向于下降)。


通过散点图矩阵,我们可以更系统地观察变量自身的分布以及两两之间的关系。





总结 📝



本节课中我们一起学习了如何分析两个指标间的相关性。
- 我们首先从数据集中提取了目标指标数据。
- 然后,我们通过绘制双Y轴折线图,在同一图形中对比了不同量级指标的变化趋势。
- 最后,我们通过计算收益率并绘制散点图矩阵,更全面地分析了变量自身的分布(使用KDE图)以及变量间的负相关关系。


这些可视化方法是数据探索性分析(EDA)中非常实用的工具,能帮助我们快速、直观地理解数据间的关系。
010:回归方程与相关系数实例 📈


在本节课中,我们将学习如何使用Python的NumPy和Pandas库,基于金融时间序列数据构建回归方程、计算相关系数,并进行可视化分析。我们将从处理数据开始,逐步完成回归模型的建立、绘图,并探讨相关系数随时间变化的动态分析。
构建回归方程
上一节我们介绍了数据的基本处理,本节中我们来看看如何构建两个金融指标之间的回归方程。构建回归方程的方法很多,许多工具包都可以完成。这里我们使用NumPy库中的polyfit函数来计算回归系数。


polyfit函数可以帮助我们计算一个回归模型的系数。它需要传入几个参数:


- 第一个参数 (x): 自变量数据,即指标X。
- 第二个参数 (y): 因变量数据,即指标Y。
- 第三个参数 (deg): 回归方程的阶数。


这个参数deg决定了回归方程的形式。例如,当deg=1时,我们得到的是一次线性回归方程,其形式为:
Y = k * X + b
这包含了X的一次项(kX)和零次项(常数b)。

如果指定deg=2,则方程会包含X的二次项、一次项和常数项,可以拟合曲线。但在当前任务中,我们只关注线性关系,因此指定deg=1即可。

在计算之前,必须确保数据中没有空值(NaN),否则会导致计算错误。我们可以使用Pandas的dropna方法进行清理。

以下是构建回归方程的核心代码步骤:
# 假设 df 是包含‘X’和‘Y’两列数据的DataFrame
# 1. 清理数据中的空值
df.dropna(inplace=True)


# 2. 提取自变量X和因变量Y
x_data = df[‘X’].values
y_data = df[‘Y’].values

# 3. 使用polyfit计算回归系数 (k, b)
coefficients = np.polyfit(x_data, y_data, deg=1)
k, b = coefficients
执行上述代码后,我们就得到了回归方程的系数k和b,从而确定了具体的回归方程。


可视化回归结果
得到回归方程后,我们可以将其可视化,与原始数据散点图叠加,直观地观察拟合效果。

以下是绘制散点图和回归线的步骤:


首先,我们需要用回归方程计算出对应于每个X值的预测Y值。然后使用Matplotlib进行绘图。

import matplotlib.pyplot as plt


# 1. 绘制原始数据散点图
plt.scatter(x_data, y_data, s=16, label=‘原始数据’)


# 2. 计算回归线对应的Y值
y_pred = k * x_data + b # 利用求得的k和b计算预测值


# 3. 在同一图中绘制回归线
plt.plot(x_data, y_pred, color=‘red’, label=‘回归线’)

plt.legend()
plt.show()
通过这张图,我们可以清晰地看到两个指标之间的线性关系趋势。需要注意的是,在金融分析中,我们常常对增长率、收益率等处理后的序列进行回归,而不是原始价格,这一点在应用时需要根据具体问题判断。

计算相关系数

除了回归方程,衡量两个变量之间线性关系强度的常用指标是相关系数。在Pandas中,计算相关系数非常简单。

对于一个包含多列数据的DataFrame,可以直接使用.corr()方法计算所有列两两之间的相关系数矩阵。

# 计算DataFrame中所有列的相关系数矩阵
correlation_matrix = df[[‘X‘, ’Y‘]].corr()
print(correlation_matrix)
对角线上的值永远是1(变量与自身的完全相关),而非对角线上的值则显示了‘X’和‘Y’之间的相关系数。这个矩阵是对称的。



附加题:相关系数的动态分析

上一节我们计算了整体的相关系数,本节中我们来看看如何分析相关系数随时间的变化情况。这是一个更深入的分析,可以揭示两个指标间关系的稳定性或周期性。
思路是使用滚动窗口。我们随着时间推移,在每一个时间点,取最近一段时间(窗口)的数据来计算这两个指标的相关系数,从而得到一个随时间变化的相关系数序列。
以下是实现动态相关系数计算与可视化的代码:

# 设置滚动窗口的大小,例如250个交易日
window_size = 250


# 计算指标X相对于指标Y的滚动相关系数
rolling_corr = df[‘X’].rolling(window=window_size).corr(df[‘Y’])


# 绘制滚动相关系数随时间的变化图
rolling_corr.plot(figsize=(11, 6))
plt.title(‘滚动相关系数变化图 (窗口大小={})’.format(window_size))
plt.ylabel(‘相关系数’)
plt.show()
这张图展示了在每一个时间点上,基于过去一定窗口期数据计算出的相关系数。通过它,我们可以观察两个金融指标间的关联性是持续稳定,还是在某些市场阶段发生了显著变化。



总结

本节课中我们一起学习了金融时间序列分析中的几个核心操作:

- 构建回归方程:使用NumPy的
polyfit函数计算线性回归系数,并理解了方程阶数(deg)的含义。 - 结果可视化:将回归直线与原始数据散点图结合绘制,直观评估拟合效果。
- 计算相关系数:使用Pandas的
.corr()方法快速计算变量间的线性相关强度。 - 动态相关分析:通过滚动窗口计算相关系数的时间序列,深入分析变量间关系的动态演变。


建议大家在课后亲自动手实践一遍代码,并尝试修改参数(如回归阶数、滚动窗口大小),以加深对金融数据分析方法的理解。这些技能是进行更复杂量化分析的基础。
011:双均线策略与金叉死叉介绍




在本节课中,我们将学习量化交易中的一个基础策略——双均线策略。我们将了解什么是移动平均线,以及如何利用短期均线与长期均线的交叉点(金叉与死叉)来制定买卖决策。



双均线策略的基本概念




上一节我们介绍了量化交易的基本思路,本节中我们来看看一个具体的策略:双均线策略。



“双均线”指的是两条移动平均线。移动平均线是一种技术分析工具,用于平滑价格数据,以识别趋势方向。在股票数据中,我们每天都能获得一个收盘价。如果只看单个时间点的价格,很难把握整体的变化趋势。为了从整体上描述价格的变化趋势,我们引入了移动平均线的概念。



以下是移动平均线的核心思想:它不再关注单日的价格,而是计算一段时间内价格的平均值,并将这些平均值连接成线。这样得到的曲线比原始价格曲线更平滑,能更好地反映趋势。



短均线与长均线



“双”意味着有两条线,通常一条是短均线,另一条是长均线。




- 短均线:计算周期较短,例如5天(代表一周的交易日)或10天的收盘价平均值。它能反映价格的短期变化趋势。
- 公式:
短均线值 = (过去N个交易日的收盘价之和) / N,其中N较小(如5)。
- 公式:
- 长均线:计算周期较长,例如20天(约一个月)或60天(约一个季度)的收盘价平均值。它能反映价格的长期变化趋势。
- 公式:
长均线值 = (过去M个交易日的收盘价之和) / M,其中M较大(如20)。
- 公式:



短均线和长均线唯一的区别在于计算平均值时所选取的时间窗口大小不同:短均线窗口小,对价格变化更敏感;长均线窗口大,走势更平缓,趋势性更强。



金叉与死叉:买卖信号



做出这两条线之后,它们有什么用呢?关键在于观察两条线的交叉情况。



下图展示了两条均线,我们假设红色线为短均线,蓝色线为长均线。








图中标出了两个关键点:买点和卖点。



- 买点与黄金交叉:
- 在买点位置,短均线从下方向上穿越长均线。
- 这表示短期趋势开始走强并超过长期趋势,市场可能即将进入上涨阶段。
- 因此,这个交叉点被视为一个买入信号,称为 “黄金交叉”。



- 卖点与死亡交叉:
- 在卖点位置,短均线从上方向下穿越长均线。
- 这表示短期趋势开始走弱并跌破长期趋势,市场可能即将进入下跌阶段。
- 因此,这个交叉点被视为一个卖出信号,称为 “死亡交叉”。



策略逻辑与应用



现在我们已经知道了买点(金叉)和卖点(死叉)。对于一个交易数据集,如果我们能分析出哪里是金叉,哪里是死叉,就可以设计一个简单的交易策略。



以下是该策略的核心逻辑:
- 当出现黄金交叉时,执行买入操作。
- 当出现死亡交叉时,执行卖出操作。



理论上,如果每一次交叉信号都能被准确预测并执行,那么通过“低买高卖”或“高卖低买”的循环,投资者可以实现持续的收益。这就是基于双均线交叉的交易策略。



本节课中我们一起学习了双均线策略的基础知识,包括短均线、长均线的概念,以及金叉(买点)和死叉(卖点)的信号含义。在接下来的课程中,我们将使用Python代码来演示如何实际计算移动平均线,并找出这些交叉点,从而将这一策略付诸实践。
012:买点与卖点可视化分析 📈



在本节课中,我们将学习如何利用Python进行股票数据的可视化分析,核心是计算短期与长期移动平均线,并基于它们的交叉点来识别潜在的买入和卖出信号。我们将从数据预处理开始,逐步完成计算、标记和可视化,最终理解双均线策略的基本原理。


数据准备与预处理


首先,我们需要导入必要的工具包并加载数据。以下是实现这一步骤的代码:
import pandas as pd
import matplotlib.pyplot as plt


# 读取数据,指定第一列为时间索引
data = pd.read_csv('data.csv', index_col=0, parse_dates=True)


数据加载后,我们选择苹果公司的股票数据作为分析案例,并处理数据中可能存在的缺失值,以确保后续计算的准确性。

以下是具体操作步骤:
- 选择特定股票数据:从数据集中提取苹果公司的股价列。
- 处理缺失值:使用
dropna()方法移除包含缺失值的行。

# 选择苹果公司的股价数据
apple_data = data[['AAPL']].copy()
# 去除缺失值
apple_data = apple_data.dropna()
print(apple_data.head())


计算移动平均线

上一节我们准备好了干净的数据,本节中我们来看看如何计算关键的短期和长期移动平均线。移动平均线是技术分析中常用的指标,用于平滑股价波动,识别趋势。

首先,我们需要定义短期和长期的时间窗口。考虑到我们的数据集时间跨度较长(约7-8年),为了在图表中更清晰地观察交叉点,我们将窗口设置得比通常的5日和20日更大一些。
# 定义短期和长期窗口
short_window = 40
long_window = 100

接下来,我们使用 rolling() 和 mean() 方法计算移动平均线,并将结果作为新列添加到数据框中。

# 计算短期移动平均线 (SMA)
apple_data['SMA_short'] = apple_data['AAPL'].rolling(window=short_window).mean()
# 计算长期移动平均线 (SMA)
apple_data['SMA_long'] = apple_data['AAPL'].rolling(window=long_window).mean()
# 查看计算结果
print(apple_data.tail())


可视化均线与交叉点


计算完移动平均线后,我们可以通过绘图直观地观察股价与两条均线的关系,并识别交叉点。

以下是生成可视化图表的代码:


# 绘制股价与移动平均线
plt.figure(figsize=(14, 7))
plt.plot(apple_data['AAPL'], label='Apple Price', alpha=0.5)
plt.plot(apple_data['SMA_short'], label=f'Short SMA ({short_window} days)', color='red')
plt.plot(apple_data['SMA_long'], label=f'Long SMA ({long_window} days)', color='green')
plt.title('Apple Stock Price with Moving Averages')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()
plt.show()

在生成的图表中,我们可以分析:
- 黄金交叉:当短期均线从下方上穿长期均线时,通常被视为买入信号📈。
- 死亡交叉:当短期均线从上方下穿长期均线时,通常被视为卖出信号📉。


图表中的这些交叉点,就是我们策略中需要关注的关键位置。
标记买卖信号位置

为了量化分析,我们需要在数据中明确标记出短期均线相对于长期均线的位置关系。这有助于我们程序化地识别买卖点。


首先,由于计算移动平均线时,前 long_window-1 个数据点没有值,我们需要再次清理数据中的缺失值。

# 去除因计算移动平均线产生的缺失值
apple_data_clean = apple_data.dropna()


接着,我们创建一个新的标志列。当短期均线高于长期均线时,标记为1(可能持有或考虑买入);否则标记为0(可能卖出或观望)。
# 创建位置标志位
apple_data_clean['Position'] = np.where(apple_data_clean['SMA_short'] > apple_data_clean['SMA_long'], 1, 0)
print(apple_data_clean[['AAPL', 'SMA_short', 'SMA_long', 'Position']].head())

我们也可以将股价和这个买卖位置标志在同一张图上进行可视化,以便更直观地对照。

# 创建带有次坐标轴的可视化
fig, ax1 = plt.subplots(figsize=(14, 7))

ax1.plot(apple_data_clean['AAPL'], label='Price', color='blue', alpha=0.6)
ax1.plot(apple_data_clean['SMA_short'], label=f'Short SMA', color='red', linewidth=1)
ax1.plot(apple_data_clean['SMA_long'], label=f'Long SMA', color='green', linewidth=1)
ax1.set_xlabel('Date')
ax1.set_ylabel('Price')
ax1.legend(loc='upper left')
# 创建次坐标轴来显示买卖位置
ax2 = ax1.twinx()
ax2.plot(apple_data_clean['Position'], label='Position (Buy/Sell Signal)', color='purple', alpha=0.3, drawstyle='steps-post')
ax2.set_ylabel('Position Signal')
ax2.legend(loc='upper right')

plt.title('Stock Price, Moving Averages, and Trading Signals')
plt.show()

理解双均线策略逻辑
最后,我们来探讨一下基于双均线交叉策略的核心逻辑。这种策略的目标不是预测最低点买入、最高点卖出,而是通过跟踪趋势来获取收益。
其基本思想是:
- 买入信号:当出现“黄金交叉”(短期均线上穿长期均线),表明上涨趋势可能开始,执行买入操作。
- 卖出信号:当出现“死亡交叉”(短期均线下穿长期均线),表明下跌趋势可能开始,执行卖出操作。
通过这种方式,策略试图在主要上涨趋势中持有股票,而在主要下跌趋势中离场或做空,从而规避大的下跌风险,捕捉主要的上涨波段。即使无法买在最低、卖在最高,但长期坚持纪律性的操作,理论上可以超越简单的“买入并持有”策略。


本节课中我们一起学习了如何对股票数据进行可视化分析,重点是利用双移动平均线识别买卖信号。我们从数据预处理开始,逐步完成了计算移动平均线、可视化图表分析以及标记买卖信号位置。理解双均线交叉策略的逻辑是构建自动化交易系统的重要基础。在后续课程中,可以在此基础上回测策略的历史表现,并优化参数。
013:14.14.3-策略收益效果分析 📈

在本节课中,我们将学习如何计算和比较一个交易策略的最终收益。我们将基于之前构建的双均线策略,计算其在一段时间内的累计收益,并与“什么都不做”的原始股价增长进行对比。

上一节我们介绍了如何通过双均线交叉来生成交易信号(position)。本节中,我们来看看如何将这些信号转化为具体的收益,并评估策略的有效性。
计算原始收益率
首先,我们需要计算股价的日收益率。收益率是衡量投资回报的基本指标,计算公式为:当日收盘价除以前一日收盘价。


以下是计算收益率的步骤:
- 获取股价数据。
- 使用
shift(1)方法将数据向下移动一行,以获取前一天的股价。 - 用当日股价除以前一日股价,得到每日的增长率。

# 假设 data[‘close’] 是包含收盘价的列
returns = data[‘close’] / data[‘close’].shift(1)
执行这段代码后,returns 列就存储了每日的股价增长率。


计算策略收益率

接下来,我们根据交易信号来调整收益率。我们的策略核心是:当 position 为 1 时,我们跟随市场走势;当 position 为 -1 时,我们进行反向操作(即做空或卖出)。
以下是计算策略收益率的逻辑:
- 将交易信号
position也进行shift(1)操作,以确保我们使用的是前一天生成的信号来指导今天的交易。 - 将调整后的
position与原始returns相乘。当position为 -1 时,收益方向与市场相反。

# 假设 position 是之前生成的交易信号列
strategy_returns = position.shift(1) * returns
这样,strategy_returns 就代表了遵循我们策略后,每日的实际收益率。


处理缺失值与累计收益
在计算开始时,由于 shift 操作,数据首行会产生缺失值(NaN)。我们需要先处理这些缺失值。


以下是处理缺失值并计算累计收益的步骤:
- 使用
dropna()方法删除含有缺失值的行。 - 对处理后的收益率序列进行求和,得到对数空间下的总收益。
- 由于我们之前计算的是乘法关系的增长率,在求和后需要通过指数函数
np.exp()将其还原为最终的累计收益倍数。

# 删除缺失值
returns_clean = returns.dropna()
strategy_returns_clean = strategy_returns.dropna()
# 计算累计收益(先求和,再指数还原)
cumulative_return = np.exp(returns_clean.sum())
cumulative_strategy_return = np.exp(strategy_returns_clean.sum())
结果对比与分析


最后,我们对比两种情况的最终收益。

执行计算后,我们可能得到类似这样的结果:
- 原始收益:1 元本金最终变为约 4 元。
- 策略收益:1 元本金最终变为约 5.8 元。

这个对比表明,在我们构建的这个特定双均线策略和测试数据下,策略收益超过了单纯持有股票的收益。这初步验证了策略在历史数据上的有效性。

本节课中我们一起学习了如何量化分析一个交易策略的收益。我们首先计算了股价的原始日收益率,然后结合交易信号计算出策略的日收益率,最后通过处理缺失值、求和与指数还原,得到了策略与基准的最终累计收益,并进行了对比。这个过程是量化策略回测中评估绩效的关键步骤。
014:15.15.4-均线调参实例 📈



在本节课中,我们将学习如何通过调整移动平均线的参数来优化交易策略。我们将使用Python工具遍历不同的参数组合,评估每种组合的表现,从而找到更优的策略配置。
上一节我们介绍了双均线策略的基本原理和回测方法。本节中,我们来看看如何通过调整短期和长期移动平均线的参数来优化策略表现。
参数空间定义与遍历 🔄

为了找到最佳的参数组合,我们需要定义一个参数空间并进行遍历。以下是定义参数空间和导入必要工具的方法。
首先,我们需要导入用于生成参数组合的工具 product。

from itertools import product


product 函数可以帮助我们生成两个参数空间的所有可能组合。例如,我们可以为短期均线(SMA1)和长期均线(SMA2)分别指定一个取值范围。


# 定义短期均线(SMA1)的参数空间,从20到60,步长为4
sma1_range = range(20, 61, 4)
# 定义长期均线(SMA2)的参数空间,从180到280,步长为10
sma2_range = range(180, 281, 10)


接下来,我们使用 product 函数生成所有参数组合,并通过循环进行遍历。


# 遍历所有参数组合
for sma1, sma2 in product(sma1_range, sma2_range):
# 在这里执行策略计算
pass


数据准备与策略计算 📊


在遍历每个参数组合时,我们需要重新加载数据并计算相应的指标。以下是数据准备和策略计算的步骤。

首先,重新加载苹果公司的股价数据。

import pandas as pd

# 加载苹果公司股价数据
data = pd.read_csv('apple_stock_price.csv')


加载数据后,需要进行数据清洗,例如处理缺失值。

# 去除缺失值
data = data.dropna()
接着,计算每日收益率以及指定参数下的短期和长期移动平均线。
# 计算每日收益率
data['returns'] = data['Close'].pct_change()
# 计算短期移动平均线(SMA1)
data['SMA1'] = data['Close'].rolling(window=sma1).mean()
# 计算长期移动平均线(SMA2)
data['SMA2'] = data['Close'].rolling(window=sma2).mean()
# 再次去除因计算移动平均线产生的缺失值
data = data.dropna()
然后,根据双均线策略生成交易信号。


# 生成交易信号:当短期均线上穿长期均线时买入(1),下穿时卖出(-1)
data['position'] = 0
data.loc[data['SMA1'] > data['SMA2'], 'position'] = 1
data.loc[data['SMA1'] < data['SMA2'], 'position'] = -1


最后,计算策略的累积收益,并与基准(买入并持有)收益进行比较。

# 计算策略的每日收益
data['strategy'] = data['position'].shift(1) * data['returns']
# 计算累积收益
cumulative_returns = (data['returns'] + 1).cumprod()
cumulative_strategy = (data['strategy'] + 1).cumprod()


# 计算策略相对于基准的超额收益
outperformance = cumulative_strategy.iloc[-1] - cumulative_returns.iloc[-1]


结果收集与展示 📈

我们需要将每次遍历的结果收集起来,以便后续分析和比较。以下是创建结果表格的方法。


首先,初始化一个空的DataFrame来存储结果。


# 初始化结果表格
results = pd.DataFrame()

在每次循环中,将当前参数组合及其对应的策略表现添加到结果表格中。
# 在循环内部,构建当前参数组合的结果行
result_row = pd.DataFrame({
'SMA1': [sma1],
'SMA2': [sma2],
'Cumulative_Returns': [cumulative_returns.iloc[-1]],
'Cumulative_Strategy': [cumulative_strategy.iloc[-1]],
'Outperformance': [outperformance]
})

# 将当前结果行追加到总结果表格中,忽略索引以保持整洁
results = pd.concat([results, result_row], ignore_index=True)

遍历结束后,我们可以查看结果表格,分析不同参数组合的表现。

# 展示前10行结果
print(results.head(10))

通过观察 Outperformance 列,我们可以判断哪些参数组合使得策略表现优于简单的买入持有策略。正值表示策略表现更好,负值则表示不如基准。




本节课中我们一起学习了如何通过遍历不同的移动平均线参数来优化双均线交易策略。我们定义了参数空间,使用 product 函数进行组合遍历,在每次循环中重新计算策略指标,并收集结果进行比较。这个方法可以帮助我们系统地寻找更有效的策略参数,是量化策略开发中的重要步骤。
015:回测收益率指标解读 📈
在本节课中,我们将学习量化策略评估中最基础且核心的指标——回测收益率。我们将了解它的定义、计算方法,并通过Python代码演示如何从原始数据中计算出这一指标,帮助初学者理解策略表现的基本衡量方式。

上一节我们介绍了策略评估指标的重要性,本节中我们来看看最基础的指标——回测收益率。
什么是回测收益率?
当我们完成一个交易策略的构建后,需要在历史数据中进行测试,以判断策略的盈亏情况。但“赚”或“赔”是定性描述,我们需要一个定量的指标来回答“赚了多少”这个问题。回测收益率就是这样一个指标,它衡量了策略在整个回测周期内,相对于初始本金的收益百分比。


其核心计算公式如下:
回测收益率 = (期末资产总值 / 期初资产总值) - 1

其中,期末资产总值代表策略结束时的总价值,期初资产总值代表策略开始时的初始本金。

使用Python计算回测收益率


接下来,我们将通过Python代码,一步步演示如何从股票价格数据中计算回测收益率。我们假设数据中的收盘价序列代表了策略每日的资产净值。

首先,导入必要的工具包并加载数据。
import pandas as pd
import matplotlib.pyplot as plt

# 设置中文字体,方便后续图表显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 读取数据,假设第一列是日期
data = pd.read_csv('your_stock_data.csv', index_col=0)
# 查看数据前几行
print(data.head())
假设我们的数据 data 中包含一列名为 ‘close’ 的收盘价数据,我们将以此模拟策略的资产净值走势。
以下是计算回测收益率的具体步骤:
- 计算累积净值曲线:计算每个时间点的资产净值相对于期初净值的比值。
- 计算最终收益率:取期末的净值比值,并减去1,得到最终的回测收益率。
# 步骤1:计算每个时间点相对于期初的净值比
# 假设我们使用‘close’列作为净值
net_value_ratio = data['close'] / data['close'].iloc[0]

# 可视化净值增长曲线
net_value_ratio.plot(figsize=(10, 6))
plt.title('策略净值增长曲线 (相对于期初)')
plt.ylabel('净值比率')
plt.xlabel('日期')
plt.grid(True)
plt.show()

# 步骤2:计算最终的回测收益率
final_return = net_value_ratio.iloc[-1] - 1


# 将结果整理成DataFrame以便查看
result_df = pd.DataFrame({
‘回测收益率’: [final_return]
}, index=[‘最终结果’])

print(result_df)
执行以上代码后,你将得到策略在整个回测周期内的净值变化曲线,以及一个具体的回测收益率数值。例如,如果结果显示为0.042,则意味着策略获得了4.2%的总收益。


指标解读与注意事项


- 正负含义:回测收益率为正表示盈利,为负表示亏损。
- 相对性:该指标是相对于期初本金的收益率,是一个相对值,而非绝对的盈利金额。
- 局限性:单独看回测收益率不足以全面评估策略。它没有考虑风险(如下文将介绍的波动率、最大回撤),也没有与市场基准(如大盘指数)进行比较。一个高收益的策略可能伴随着无法承受的高风险。

本节课中我们一起学习了回测收益率。它是评估交易策略盈利能力的起点,计算公式为 (期末价值/期初价值 - 1)。我们通过Python演示了如何从价格数据中计算并可视化这一指标。记住,回测收益率是一个重要但单一的指标,在后续课程中,我们将结合其他风险与收益指标,对策略进行更全面的评估。
016:年化指标分析 📈


在本节课中,我们将要学习如何计算和解读量化策略分析中的两个核心收益指标:回测收益率与年化收益率。理解这两个指标对于评估一个交易策略的长期表现至关重要。
上一节我们介绍了如何计算策略的最终收益,本节中我们来看看如何将这些收益转化为更标准化的年化指标,以便进行横向比较。


理解回测收益率


回测收益率衡量的是策略在整个回测周期内的总盈亏情况。它计算的是从开始到结束,你的投资组合价值变化的百分比。

公式如下:
回测收益率 = (最终价值 - 初始价值) / 初始价值

这个指标直接反映了策略的最终效果。例如,有的策略可能实现了翻倍收益(收益率 > 100%),而有的策略则可能出现亏损(收益率为负值)。这直观地说明了“投资有风险”,策略并非稳赚不赔。

引入年化收益率

理解了总收益后,我们面临第二个问题:如何比较不同时间长度的策略表现?一个运行了10年的策略和一个运行了2年的策略,仅看总收益率是不公平的。这时就需要年化收益率。
年化收益率将总收益标准化,转化为假设每年都能获得相同收益率的等效年度收益率。这使得不同时间跨度的策略之间具有可比性。

以下是年化收益率的计算公式:

公式:
年化收益率 = (1 + 总收益率) ^ (250 / N) - 1


其中:
- 总收益率:即我们上面计算出的回测收益率。
- N:策略回测的总交易日天数。
- 250:通常假设一年有250个交易日(这是一个市场惯例近似值)。
计算示例


假设我们有一个策略,其回测数据总天数 N = 730 天(大约2年),计算出的总收益率为 R。
我们可以用Python代码来计算年化收益率:


# 假设 total_return 是计算好的总收益率
total_return = 0.5 # 例如,总收益为50%
N = 730 # 回测总交易日
trading_days_per_year = 250

# 计算年化收益率
annualized_return = (1 + total_return) ** (trading_days_per_year / N) - 1
print(f"年化收益率: {annualized_return:.2%}")

执行计算后,我们会得到每个策略对应的年化收益率。通常,年化收益率会比总收益率数值小,因为它将收益分摊到了每一年。例如,一个在3.4年内获得113%总收益的策略,其年化收益率可能约为20%左右。


观察年化收益率能让我们更清晰地看到策略的持续盈利能力,而不仅仅是最终结果。

核心指标总结


本节课中我们一起学习了量化策略分析中两个最关键的收益指标:
- 回测收益率:反映策略在整个测试周期内的最终总盈亏,是评估策略效果的起点。
- 年化收益率:将总收益标准化为年度比率,用于公平比较不同时间长度策略的盈利能力,是更常用的核心评估指标。


理解并计算这两个指标,是判断一个交易策略是否具备长期有效性的基础。在后续的分析中,我们还将结合风险指标(如最大回撤、夏普比率等)来更全面地评估策略。
017:最大回撤区间


概述
在本节课中,我们将要学习一个重要的风险指标——最大回撤。我们将了解它的定义、计算方式以及在策略评估中的意义。
什么是最大回撤?
上一节我们介绍了策略分析的基本框架,本节中我们来看看最大回撤这个具体指标。最大回撤描述的是在投资过程中,资产净值从最高点到后续最低点的最大跌幅所持续的那个区间。它衡量的是投资者可能经历的最糟糕、最“惨”的阶段。
公式:最大回撤 = max[(Pi - Pj) / Pi],其中 Pi 是第 i 天的价格,Pj 是 i 天之后某一天的价格。
这个指标的核心是评估风险,而非直接评估收益。一个策略可能最终盈利,但如果在过程中经历了巨大的回撤,其风险也相应很高。

最大回撤的计算方法
理解了概念后,我们来看看如何通过代码计算最大回撤。计算的核心思路是:遍历所有可能的价格点对,找出跌幅最大的那个区间。

以下是计算最大回撤的关键步骤:



- 计算累积最大值:首先,我们需要知道到每一天为止,历史上的最高价格是多少。这可以通过
pandas.Series.cummax()函数实现。 - 计算每日潜在回撤:用每一天的“历史最高价”减去当天的“实际价格”,得到每一天的潜在下跌幅度。
- 计算回撤比率:将上一步得到的下跌幅度,除以对应的“历史最高价”(即公式中的
Pi),得到每日的回撤比率。 - 找出最大值:在所有每日回撤比率中,找出最大值,这就是整个时间段内的最大回撤。

让我们通过一个简化的代码示例来演示这个过程:



import pandas as pd


# 假设‘close’是包含每日收盘价的Pandas Series
close_prices = df['close']


# 1. 计算累积最大值
cumulative_max = close_prices.cummax()


# 2. & 3. 计算每日回撤比率
daily_drawdown = (cumulative_max - close_prices) / cumulative_max


# 4. 找出最大回撤
max_drawdown = daily_drawdown.max()
print(f"最大回撤为:{max_drawdown:.2%}")
这段代码会输出一个百分比,例如“3.07%”,这表示在该投资周期内,投资者承受的最大账面损失为3.07%。



总结
本节课中我们一起学习了最大回撤。我们了解到,最大回撤是一个关键的风险评估指标,它量化了投资策略在历史上经历的最糟糕阶段。通过计算价格序列的累积最大值并与当日价格比较,我们可以准确地计算出这个值。记住,一个优秀的策略不仅要看最终收益,还必须严格控制其最大回撤,以保证投资过程的稳健性。
018:夏普比率的作用 📈
在本节课中,我们将要学习一个重要的金融风险调整收益指标——夏普比率。我们将了解它的定义、计算公式,并通过一个简单的例子来理解它在投资决策中的应用。
概述
夏普比率用于衡量一项投资在承担单位风险时,所能获得的超额回报。它帮助投资者判断,为了获取更高的收益而承担额外的风险是否“值得”。
夏普比率的定义与意义

上一节我们介绍了风险的概念,本节中我们来看看如何量化风险与收益的关系。

夏普比率描述的是:对于每承担一单位的风险,我们能获得多少超额收益。指标越高,意味着在承担相同风险的情况下,获得的收益越高。反之,则意味着风险与收益的匹配效率较低。

在投资选股时,如果仅从夏普比率这一指标出发,我们应选择比率更高的股票或投资组合,因为这代表其风险调整后的收益更优。
夏普比率的计算公式
理解了夏普比率的意义后,我们来看看如何计算它。
计算夏普比率需要比较两个收益率:一是你投资组合的实际收益率,二是市场的“无风险收益率”。无风险收益率通常指国债等几乎无违约风险的资产的收益率。
夏普比率的公式如下:
夏普比率 = (投资组合收益率 - 无风险收益率) / 投资组合收益率的标准差
这个公式的核心思想是:用投资组合超越无风险收益的部分(超额收益),除以组合自身的波动性(风险)。结果数值越大,说明该投资组合的“性价比”越高。
计算示例与代码实现
接下来,我们通过一个模拟的计算过程,来看看夏普比率是如何应用的。

假设我们分析了几只股票,并计算了它们的日收益率。在计算前,我们需要对数据中的缺失值进行处理,例如用前一天的收益率进行填充。
以下是计算夏普比率的关键步骤:
- 计算日收益率:获取投资组合每日的价格变动百分比。
- 设定无风险收益率:例如,年化无风险收益率为5%。
- 计算超额收益:用日收益率减去日化的无风险收益率(年化5%除以250个交易日)。
- 计算夏普比率:计算超额收益的均值,除以其标准差,再乘以根号下交易天数(以进行年化)。
用Python代码可以近似表示如下:
import numpy as np
# 假设 daily_returns 是一个包含每日收益率的数组
# 假设 annual_risk_free_rate 是年化无风险收益率,例如 0.05
daily_risk_free = annual_risk_free_rate / 250
excess_returns = daily_returns - daily_risk_free

# 计算年化夏普比率
sharpe_ratio = np.mean(excess_returns) / np.std(excess_returns) * np.sqrt(250)

通过计算,我们得到了各只股票的夏普比率。结果显示,中兴通讯(ZTE)的夏普比率最高。这意味着,在我们分析的标的中,承担每单位风险,投资中兴通讯能获得最高的超额回报。其他一些比率为负的股票,意味着其收益甚至未能覆盖无风险收益,不是理想的选择。

总结

本节课中我们一起学习了夏普比率。我们了解到,夏普比率是一个用于衡量投资“风险调整后收益”的核心指标。它通过 (组合收益 - 无风险收益) / 组合风险 的公式,帮助我们判断一项投资是否在有效地利用风险来创造收益。在选择投资标的时,更高的夏普比率通常是更优的选择。
019:阿尔法与贝塔概述
在本节课中,我们将要学习量化投资策略评估中的两个核心概念:阿尔法(α)和贝塔(β)。理解这两个指标对于区分策略收益的来源至关重要。
收益的构成
投资策略所获得的收益,可以分解为两个部分。
一部分收益与整体市场环境相关。当市场大环境向好时,大部分投资都能获得收益。这部分收益可以理解为“随大流”赚到的钱。
另一部分收益则与市场整体波动关系不大,它主要来源于投资者独特的策略、敏锐的观察力或精准的操作手法。这部分是超越市场平均水平的收益。
核心概念:贝塔(β)与阿尔法(α)

上一节我们介绍了收益的两个来源,本节中我们来看看如何用指标来衡量它们。阿尔法和贝塔就是分别衡量这两部分的指标。
- 贝塔(β):衡量策略收益与市场收益的关联程度,即策略对大盘波动的敏感性。它代表了“市场收益”的部分。β值越高,说明策略的波动与市场越同步。
- 阿尔法(α):衡量策略超越市场基准的超额收益。它代表了与市场波动无关的、由策略自身能力带来的收益。α值越高,说明策略的独立盈利能力越强。
它们的关系可以通过一个简单的线性模型来理解:

策略收益 = α + β × 市场收益 + 误差
在这个模型中,我们的目标就是求解出 α 和 β 这两个系数。
图解阿尔法与贝塔
为了更直观地理解,我们可以观察策略收益曲线图。图中通常包含三条线:
- 策略收益线:代表你的投资策略实际获得的收益走势。
- 基准收益线(如沪深300指数):代表市场大环境的收益走势。
- 超额收益线:由 策略收益 减去 基准收益 得到,它直观地展示了策略独立于市场所创造的额外价值。
在这个框架下:
- 市场收益 对应基准收益线的变化,其影响由 β 系数刻画。
- 超额收益 对应超额收益线的变化,其水平由 α 系数衡量。
我们的核心目标
市场的大环境(β部分)是个人投资者难以控制和预测的。因此,量化策略研究的核心关注点在于如何获取稳定且可持续的超额收益,即追求更高的阿尔法(α)。我们的目标是构建能够独立于市场涨跌、持续创造额外回报的策略。
总结

本节课中我们一起学习了阿尔法(α)和贝塔(β)的概念。贝塔衡量了策略收益受市场影响的程度,而阿尔法则代表了策略自身能力所带来的、超越市场的超额收益。在量化投资中,我们更致力于寻找和提升策略的阿尔法。后续在因子策略分析等课程中,我们将深入探讨如何具体计算和优化这些指标。目前,大家只需理解这两个核心概念的基本含义即可。
020:量化交易概述

在本节课中,我们将要学习量化交易的基本概念,了解它如何运作,以及入门需要掌握哪些核心技能。


什么是量化交易?
上一节我们介绍了课程主题,本节中我们来看看量化交易的本质。
传统炒股由人手动操作。交易者需要长时间盯盘,分析各种指标。但这种方法存在两个主要问题:第一,人的主观判断可能导致决策不准确;第二,人的精力、时间和计算能力有限,无法同时分析大量股票或跨越很长的历史周期。
量化交易的核心目标同样是赚钱,但方法不同。它并非由人类直接决定如何买卖,而是将策略设计交给计算机。具体来说,就是让计算机基于历史数据,寻找最优的买卖时机,以实现在特定时间段内(例如两年)收益最大化。
量化交易的本质是:基于历史数据进行分析,设计交易策略,并通过回测验证策略的有效性。回测是指在历史数据上模拟运行策略,评估其盈利能力。这本质上是一个数据挖掘任务,即从历史数据中挖掘出能带来盈利的交易模式。
简而言之,量化交易是利用数据和智能算法(如机器学习、统计学方法)来最大化投资收益的一种方法。
量化交易需要哪些核心技能?
了解了量化交易是什么之后,我们来看看实践它需要掌握哪些技能。
量化交易是一个交叉学科领域。虽然涉及金融知识,但重点并非深入掌握所有金融理论,而是理解基本概念即可。真正的核心技能集中在数据处理和算法应用上。

以下是量化交易实践中至关重要的几项技能:
- 机器学习算法:用于基于历史数据预测未来价格走势或市场行为。例如,可以使用回归模型预测股价,或使用分类模型判断买卖信号。
- 统计方法:用于分析数据中的指标,计算关键统计量(如均值、标准差),并评估策略的稳定性和风险。
- 编程能力(以Python为例):这是实现想法、处理数据和执行回测的必备工具。Python拥有丰富的库(如
pandas,numpy,scikit-learn,backtrader)来支持量化交易的各个环节。 - 数据处理与挖掘:这是量化交易的核心。所有工作都围绕如何获取、清洗、分析和从数据中提取有效信息展开。
在量化交易中,没有绝对的“必备工具”,任何能帮助你更有效地分析数据、提升策略收益的工具都是好工具。你可以将量化交易理解为数据挖掘在金融投资领域的一个热门应用。
总结

本节课中我们一起学习了量化交易的基础知识。我们了解到,量化交易是利用计算机和算法,基于历史数据自动生成并验证交易策略,以追求稳定收益的投资方法。其核心在于数据挖掘,所需的关键技能包括机器学习、统计学和编程,而非深奥的金融理论。
021:量化交易所需技能分析 📊

在本节课中,我们将要学习成为一名量化交易从业者所需的核心技能。量化交易是一个融合了金融、数学、统计学和计算机科学的交叉领域,理解这些技能有助于我们构建有效的交易策略。
核心技能概览
上一节我们介绍了量化交易的基本概念,本节中我们来看看支撑量化交易实践的具体技能。这些技能是构建和实现交易策略的基础。
以下是量化交易所需的核心技能列表:
-
机器学习算法与特征工程
机器学习算法是量化交易的核心工具,包括回归、分类、聚类等常规方法。但更重要的是特征工程,即如何处理数据并从海量数据中提取最有价值的信息。在量化交易中,我们面对的数据非常庞大且复杂,例如股票数据不仅包括开盘价、收盘价,还包括公司财务数据、市场宏观数据等。特征工程的难点在于如何设计算法,将这些多层面的数据有效融合,以选择对最终结果最有价值的信息。数据决定了模型的上限,算法只是逼近这个上限的手段。 -
统计学与数学方法
量化交易大量依赖统计学和数学方法。无论是算法还是交易策略,本质上都是将数学公式应用于数据。因此,扎实的数学基础是必不可少的,需要掌握概率论、线性代数、微积分、时间序列分析等知识点。 -
平台与框架的使用
量化交易需要借助特定的平台或框架进行策略回测和验证。课程后续将选择一个API简单、可视化清晰的平台进行教学。在这些平台上,你可以编写Python代码,模拟在特定历史时期(如10年到20年)执行某个策略,并观察其每日表现和最终收益结果。平台和框架是工具,关键在于熟练使用,而非死记硬背。


- 策略算法
量化交易涉及多种策略算法。课程将重点讲解最常用和最经典的算法原理,以及如何在Python中实现。我们的课程重点在于通过Python进行实践,将策略落地为具体的案例,而不是教授如何炒股。

实践重点:股票 vs. 期货
既然提到了实践,我们需要明确课程的重点方向。量化交易可以应用于股票、期货等多种金融产品。

- 股票:课程将更侧重于股票相关的量化交易。因为股票数据丰富,包含公司财务、市场行情等多种结构化指标,非常适合进行数据挖掘和分析。
- 期货:期货交易与市场即时供需关系更紧密,主观判断和经验成分可能更重。课程中会以期货为例进行简单介绍,但不作为重点。
量化交易的本质:数据挖掘

量化交易的核心流程可以概括为数据挖掘。具体步骤如下:
- 数据处理:获取原始金融数据并进行清洗、整理。
- 策略设计:基于处理后的数据,设计交易逻辑和规则。
- 回测验证:将策略应用于历史数据,测试其表现。回测 是评估策略有效性的关键步骤。
- 指导决策:根据回测结果,对未来的实际交易决策提供依据。
因此,量化交易本质上就是将数据挖掘算法应用于金融数据。其目的不仅仅是预测股票涨跌,更是为了在给定约束(如本金、风险)下,实现收益最大化,例如从股票池中选择单位风险收益最高的股票。



学习建议与总结
对于初学者,无需深究量化交易的复杂历史或长篇理论。关键在于理解其基本概念、核心技能以及实践方法。



本节课中我们一起学习了量化交易所需的四大核心技能:机器学习与特征工程、统计学与数学、平台框架使用以及策略算法。我们明确了课程将以股票数据挖掘为重点,并理解了量化交易本质上是数据挖掘在金融领域的应用。接下来,我们将开始学习具体的工具和实践方法。
022:Ricequant平台基础使用教程
在本节课中,我们将学习如何使用Ricequant量化交易平台。我们将了解平台的基本结构、核心函数的作用以及如何配置策略参数,为后续编写自己的量化策略打下基础。

平台界面与策略模板
首先,我们演示量化平台的使用方法。点击“量化平台”,然后选择“免费使用”。进入后,平台会提供一些预先写好的简单策略示例,方便用户熟悉策略的编写方式。

点开名为“第一个入门策略”的示例,界面类似于Jupyter Notebook,可以在此编写Python代码。但平台对代码结构有特定要求。
核心函数解析
所有操作都必须围绕三个核心函数展开。这三个函数是平台要求必须使用的,并非由用户随意指定。下面我们逐一解释它们的作用。
初始化函数:init
第一个函数是 init,意为初始化。它类似于Python类中的构造函数。在Python中,实例化一个对象时,构造函数会首先被调用。init 函数的作用与之相同:在执行任何其他操作之前,它会被最先调用一次。
该函数有一个重要参数 context。观察三个函数,会发现它们都包含 context 这个参数。它的作用是充当一个全局对象,用于在不同函数间传递数据和状态。
例如,在 init 函数中,我们可以选定股票代码或获取初始数据,并将其存入 context 对象。随后,在预处理或策略逻辑函数中,我们可以通过传入的 context 参数访问这些预先存储的数据。这实现了算法策略中不同步骤间的数据传递。
代码示例:
def init(context):
# 初始化操作,例如选定股票
context.s1 = ‘000001.XSHE‘ # 将股票代码存入context
在其他函数中,可以通过 context.s1 来访问这个股票代码。
每日预处理函数:before_trading
初始化函数仅执行一次。而接下来的两个函数会被反复执行多次。以选股为例,在 init 中选定股票后,策略需要每天对这些股票的数据进行操作,例如判断收盘价、决定买卖。
before_trading 函数在每天交易开始前被执行。你可以将其理解为数据预处理阶段。如果需要在交易前对当日的数据进行任何处理或计算,可以写在这个函数中。注意,这个预处理操作也是每天都会进行的。
策略逻辑函数:handle_bar
handle_bar 函数是策略的核心,包含了实际的交易逻辑和判断。基于预处理后的数据,在此函数中编写具体的算法来决定买入、卖出或其他操作。它同样是每天(或每分钟)执行一次。
简单总结一下这三个函数:
init:用于初始化,只执行一次。before_trading:用于每日交易前的数据预处理,每天执行。handle_bar:用于实现每日具体的交易策略逻辑,每天执行。
策略参数配置
既然提到函数会每日执行,那么“每日”是如何定义的呢?这需要通过界面右侧的参数区域进行配置。这些参数不是运行结果,而是需要用户自行设定的回测条件。
要进行历史回测,验证策略在过往数据中的表现,必须指定以下参数:
以下是需要配置的核心参数列表:
- 起止日期:定义回测的时间范围,包括开始日期和结束日期。
- 初始资金:策略开始运行时拥有的资金量,例如
100000代表10万元。回测的收益率等指标将基于此初始资金进行计算。 - 频率:选择策略执行的频率,有“每日”和“每分钟”两种选项。“每日”表示
handle_bar函数每天执行一次;“每分钟”则表示每分钟执行一次。本教程示例中,我们选择“每日”。
平台扩展性与数据分析
平台的功能并不局限于其自带的API。在代码中,你可以自由导入常用的Python数据分析库。
代码示例:
import pandas as pd
import numpy as np
平台提供的API接口返回的数据格式(如DataFrame或ndarray)与Pandas、NumPy兼容。因此,你可以将Ricequant的API与Pandas、NumPy乃至Scikit-learn等机器学习库结合使用,进行复杂的数据分析和模型构建。重点在于如何将这些工具与平台API有机结合。
本节课中,我们一起学习了Ricequant量化平台的基本框架。我们了解了三个核心函数(init, before_trading, handle_bar)的分工与调用时机,掌握了如何配置回测的关键参数(时间、资金、频率),并认识到平台支持与主流Python数据分析库协同工作。这些是开始编写和回测量化策略的基础。


023:策略任务分析

在本节课中,我们将学习如何使用交易平台构建一个简单的量化交易策略。我们将通过一个具体案例——从沪深300指数中动态选择并持有表现最佳的10只股票——来熟悉平台的核心API和工作流程。
策略概述与目标
上一节我们介绍了策略的基本框架,本节中我们来看看如何应用它。我们的目标是构建一个策略,该策略始终持有沪深300指数中根据特定财务指标(例如盈利能力)排名前10的股票。策略需要每日更新持仓,卖出不再属于前十的股票,并买入新进入前十的股票。
策略实现步骤分解
以下是实现该策略的三个核心步骤,分别对应策略模板中的三个主要函数。
1. 初始化阶段
在策略的初始化函数中,我们需要完成一些一次性设置工作。
- 设定股票池:将策略的股票池设定为沪深300指数的所有成分股。
- 其他初始化:可以在此设置策略参数,如初始资金、手续费率等。
2. 盘前处理阶段
在每日交易开始前(before_trading函数),我们需要进行数据准备和计算。
- 获取财务数据:获取股票池中所有股票的特定财务指标(例如,每股收益EPS)。
- 排序与筛选:根据该指标对股票进行降序排序,并选取排名前10的股票代码,作为当日的目标持仓列表。
3. 盘中交易逻辑
在策略的主函数(handle_bar函数)中,我们执行实际的交易逻辑。
- 获取当前持仓:查询当前账户已经持有的所有股票。
- 对比与调仓:
- 将当前持仓与盘前计算出的“目标持仓列表”进行对比。
- 卖出那些存在于当前持仓但不在目标列表中的股票。
- 买入那些存在于目标列表但尚未持有的股票,并使资金在目标股票间平均分配。
核心代码逻辑示意
以下是上述三个步骤的关键代码逻辑示意(以伪代码形式呈现):
# 1. 初始化函数
def initialize(context):
# 设置股票池为沪深300成分股
context.stock_pool = get_index_stocks('000300.SH')
context.top_n = 10 # 设定要持有的股票数量
# 2. 盘前处理函数
def before_trading(context):
# 获取股票池中所有股票的财务数据(例如:每股收益)
fundamental_data = get_fundamentals('eps', context.stock_pool)
# 根据财务数据排序并选取前N名
sorted_stocks = fundamental_data.sort_values(ascending=False)
context.target_stocks = sorted_stocks.index[:context.top_n].tolist()
# 3. 盘中交易函数
def handle_bar(context, bar_dict):
current_holdings = list(context.portfolio.positions.keys()) # 当前持仓
target_stocks = context.target_stocks # 今日目标持仓
# 卖出逻辑:当前有但目标中没有的股票
stocks_to_sell = [s for s in current_holdings if s not in target_stocks]
for stock in stocks_to_sell:
order_target_value(stock, 0) # 卖出全部
# 买入逻辑:目标中有但当前没有的股票
cash_per_stock = context.portfolio.cash / len(target_stocks)
stocks_to_buy = [s for s in target_stocks if s not in current_holdings]
for stock in stocks_to_buy:
order_value(stock, cash_per_stock) # 买入等额资金
总结

本节课中我们一起学习了如何从零开始构建一个简单的选股策略。我们明确了策略的目标(持续持有最优的10只股票),并将其分解为初始化、盘前数据准备和盘中交易执行三个清晰的步骤。通过这个案例,我们熟悉了策略的基本框架和“获取数据-处理逻辑-执行交易”的核心工作流。在接下来的课程中,我们将深入每个步骤,编写具体的代码并回测策略效果。
024:股票池筛选

概述
在本节课中,我们将学习如何在一个量化交易策略中,根据财务指标对股票池进行筛选和排序。我们将以沪深300指数成分股为初始股票池,通过查询其营业收入数据,筛选出排名靠前的股票,为后续的交易决策做准备。

策略初始化与股票池设定
上一节我们介绍了策略的基本框架,本节中我们来看看如何设定初始股票池。

在策略的构造函数中,我们需要定义我们关注的股票范围。这里我们选择沪深300指数的成分股作为我们的初始股票池。你可以使用指数名称或代码来指定。


def initialize(context):
# 设定初始股票池为沪深300指数成分股
context.stock_pool = index_stocks('沪深300')


打印信息的代码可以移除,以保持代码简洁。设定好股票池后,接下来我们将进入数据预处理阶段。
数据查询与预处理
在交易开始前,我们通常需要对股票池进行预处理,例如获取最新的财务数据。这可以通过量化平台提供的API来完成。
平台提供了详细的帮助文档,所有可用的数据指标和查询方法都在其中。量化交易在某种程度上就是数据挖掘,涉及大量指标。本次示例我们仅使用营业收入这一个指标。
以下是查询财务数据的基本步骤:
- 构建查询(Query):指定要查询的数据字段。
- 设置过滤(Filter):限定查询范围,例如只查询股票池内的股票。
- 排序(Order By):对查询结果按特定字段排序。
- 限制数量(Limit):只取排名前N的结果。
我们将查询逻辑写在 before_trading 函数中,这样它会在每个交易日开始前执行。
def before_trading(context):
# 构建查询,获取营业收入数据
query = query(
fundamentals.eod_derivative_indicator.operating_revenue
).filter(
# 过滤条件:股票代码在初始股票池中
fundamentals.stockcode.in_(context.stock_pool)
).order_by(
# 按营业收入降序排列(从高到低)
fundamentals.eod_derivative_indicator.operating_revenue.desc()
).limit(
# 只取前10名
10
)
# 执行查询,将结果(DataFrame格式)存入上下文
context.filtered_stocks = get_fundamentals(query)
# 打印查看结果
print(context.filtered_stocks)


在最初的代码中,我们可能遇到查询结果为空的问题。这通常是因为在过滤条件中,我们错误地在股票列表中寻找指数代码。需要确保我们使用的是获取指数成分股的函数 index_stocks(‘沪深300’),而不是指数代码本身。修正后,即可正常获取到按营业收入降序排列的前10只股票。

总结
本节课中我们一起学习了量化策略中股票池筛选的基本流程。我们首先在初始化阶段设定了沪深300作为初始股票池,然后在每日开盘前,使用 get_fundamentals 接口查询池内股票的营业收入,并通过过滤、排序和限制操作,最终筛选出每日营收排名前十的股票。这个筛选出的股票列表将为下一节课构建具体的交易信号奠定基础。
025:策略效果演示与指标分析 📊

在本节课中,我们将学习如何对已编写的交易策略进行效果演示和关键指标分析。我们将通过调整数据结构、实现买卖逻辑,并最终解读回测报告中的各项指标,来评估策略的优劣。

数据处理与结构转换

上一节我们介绍了如何在 before_trading 函数中筛选股票。本节中我们来看看如何将筛选结果转换为更易处理的格式。
我们最初的想法是横向排列每个股票名称,即第一个股票、第二个股票、第三个股票等。但通常大家更喜欢纵向排列数据,即第一个股票及其财务营收数据,第二个股票及其财务营收数据等。
因此,我们需要对 DataFrame 进行转置操作。转置后,数据将变为纵向排列,每一行代表一只股票及其对应的指标值。

以下是实现转置的代码:

# 对筛选结果进行转置
transposed_df = original_df.T
print(transposed_df)

转置完成后,我们得到了一个索引为股票名称、数据为实际指标的 DataFrame。此时,我们只需获取索引(即股票名称列表)即可。


接下来,我们将这个股票列表存储到 context 对象中,供后续交易函数使用。


# 获取股票名称列表并存入 context
context.stock_list = transposed_df.index.tolist()

这样,在交易开始前,我们就完成了股票的筛选和准备工作。




实现交易逻辑


在 handle_bar 函数中,我们将根据筛选出的股票列表执行实际的买卖操作。首先,我们需要判断当前账户的持仓情况。
我们可以通过 context.portfolio.positions 来获取当前持仓信息。这是一个字典,包含了所有持仓的股票及其信息。
以下是判断和交易逻辑的步骤:
- 判断持仓状态:检查当前持仓字典是否为空。如果为空,说明是首次运行,需要执行买入操作。
- 执行调仓操作:如果持仓不为空,则需要将当前持仓与最新股票池进行比较,卖出不在新股票池中的股票,买入新股票池中尚未持有的股票。

以下是具体的代码实现:

def handle_bar(context, bar_dict):
# 获取当前持仓
current_positions = context.portfolio.positions
# 获取今日待持有的股票列表
target_stocks = context.stock_list
# 如果当前无持仓,则执行初始买入
if not current_positions:
for stock in target_stocks:
# 平均分配资金买入
order_target_percent(stock, 1.0 / len(target_stocks))
else:
# 卖出已持有但不在目标列表中的股票
for stock in current_positions.keys():
if stock not in target_stocks:
order_target_percent(stock, 0) # 卖出全部
# 买入目标列表中的股票(平均分配)
for stock in target_stocks:
order_target_percent(stock, 1.0 / len(target_stocks))
在上述代码中,order_target_percent(stock, percent) 函数用于调整股票 stock 的仓位至总资产的一定 percent 比例。设置为0代表清仓,设置为正数代表买入或调整至该比例。

策略回测与指标分析
运行策略后,我们会得到一份回测报告。报告包含了多个关键指标,用于评估策略的表现。
以下是几个核心指标的解释:
- 累计收益率:策略在整个回测期间的总收益。对比策略收益与基准(如沪深300指数)收益,可以看出策略是否跑赢大盘。
- 年化收益率:将累计收益率折算为每年的平均收益率,便于不同周期策略的比较。
- 最大回撤:策略净值从最高点到最低点的最大跌幅。最大回撤 = (峰值 - 谷值) / 峰值。这个指标衡量了策略可能面临的最大亏损风险,数值越小越好。
- 夏普比率:衡量策略承担单位风险所获得的超额回报。夏普比率 = (策略年化收益率 - 无风险利率) / 策略收益波动率。通常为正数且越大越好,负数表示收益不如无风险资产。


在回测结果中,我们主要关注:
- 策略的收益曲线是否跑赢基准指数。
- 最大回撤是否在可接受范围内。
- 夏普比率是否为正且具有吸引力。
初次回测结果可能不理想(例如出现亏损),这可能与所选回测时间段的市场行情有关。可以通过调整回测周期、优化选股条件或调整买卖逻辑来改进策略。

本节课中我们一起学习了如何将股票数据转换为合适的格式,实现了基于动态股票池的调仓交易逻辑,并学会了解读回测报告中的关键绩效指标,如累计收益、年化收益、最大回撤和夏普比率。这些是评估和优化一个量化策略的基础。
026:定时器功能与作用
在本节课中,我们将学习如何在量化交易策略中使用定时器功能。定时器允许我们自定义策略的执行频率,例如从每日执行改为每月执行,从而优化交易逻辑。
上一节我们介绍了策略的基本结构和每日执行的回测流程。本节中我们来看看如何使用定时器来改变策略的执行周期。
交易详情回顾
在之前的策略中,我们设定的回测时间为2016年1月4日至2016年10月4日。策略在before_trading函数中每日执行选股和调仓操作。
因此,回测结果会生成每一天的交易记录。例如,在1月4日,策略会平均买入选出的十只股票,每只股票分配约1万元资金(总资金10万元)。这是因为在第一天,持仓为空,所以执行买入操作。
以下是交易详情中包含的信息:
- 股票名称:例如中国石化。
- 操作:买入或卖出。
- 成交量:交易数量。
- 成交价:交易价格。
- 费用:包括印花税、佣金等交易成本。
从第二天开始,策略会根据新的选股结果进行买卖操作,以维持目标持仓,并充分利用可用资金。所有详细的交易记录、每日持仓、盈亏情况和账户市值变化都可以在回测结果中查看和分析。

在回测结果中,策略收益曲线与基准收益曲线(如上证指数)的对比至关重要。超额收益的计算公式为:
超额收益 = 策略收益率 - 基准收益率
它衡量了策略相对于市场基准的盈利能力。

引入定时器功能
之前的策略每日都执行选股和调仓。但在实际交易中,我们可能希望降低调仓频率,例如每十天或每月调整一次持仓。这时,就可以使用定时器功能。
定时器允许我们按照自定义的时间间隔(如每日、每周、每月)来执行特定的函数。
以下是使用定时器的基本步骤:
- 在策略的初始化函数
__init__中定义定时器。 - 指定定时器的执行频率(如每月运行)和执行时点(如每月第一个交易日)。
- 将原本在
before_trading中每日执行的选股逻辑,移动到一个自定义函数中,并由定时器触发。
代码示例:将每日调仓改为每月调仓
假设我们想将选股逻辑改为每月第一个交易日执行。
首先,我们注释掉 before_trading 函数中的每日选股代码。然后,在 __init__ 函数中设置一个每月运行的定时器。
def __init__(self):
# ... 其他初始化代码 ...
# 定义一个每月执行的函数
def monthly_filter(context):
# 这里是每月执行的选股逻辑
# 1. 查询股票数据
q = query(fundamentals.eod_derivative_indicator.pe_ratio,
fundamentals.eod_derivative_indicator.pb_ratio
).filter(
fundamentals.eod_derivative_indicator.pe_ratio > 0,
fundamentals.eod_derivative_indicator.pb_ratio > 0
).order_by(
fundamentals.eod_derivative_indicator.pe_ratio.asc()
).limit(10)
fund = get_fundamentals(q)
# 2. 获取股票列表
context.stock_list = fund.columns.values
# 3. 调仓逻辑(卖出不在列表的,买入在列表的)
# ... (此处省略具体调仓代码)
# 设置定时器:每月第一个交易日执行 monthly_filter 函数
run_monthly(monthly_filter, 1)
通过上述修改,策略将从每日调仓变为每月初调仓。回测结果会因此发生显著变化,可能更好,也可能更差,这需要在实际数据上进行测试和验证。
实践与总结
本节课中我们一起学习了量化策略中定时器的功能与使用方法。
我们了解到,通过 run_monthly、run_weekly 等API,可以灵活控制策略中关键逻辑(如选股)的执行频率。调整执行频率是优化策略、减少不必要交易和交易成本的重要手段。
需要强调的是,策略的效果高度依赖于参数(如调仓周期)和回测时间段。不同的市场环境下,同一策略的表现可能差异很大。因此,最好的学习方法是勤阅官方API文档,理解每个函数的作用,并通过大量回测实验来验证和优化自己的策略思路。

最终,量化交易的核心是将投资逻辑转化为严谨的代码,并利用历史数据进行系统性验证。定时器是实现复杂、周期性交易逻辑的重要工具之一。
027:百分位去极值方法
在本节课中,我们将学习如何对因子数据进行预处理。预处理是量化分析中至关重要的一步,它可以帮助我们清洗数据、消除异常值的影响,并使不同尺度的数据具有可比性。我们将按照“三步走”的策略,依次介绍去极值、标准化和中性化这三种核心处理方法。
什么是因子?
在开始之前,我们需要明确“因子”的概念。在量化投资中,因子是指可能影响股票收益的指标或标准。例如,当您选择股票时,可能会依据某些标准进行筛选,这些标准就是因子。
常见的因子包括:
- 市净率:较低的市净率可能意味着股票被低估,未来上涨空间较大。
- 营收增长率:较高的营收增长率通常表明公司经营状况良好。
简单来说,任何可能对最终投资结果产生影响的指标,都可以被视为我们今天要讨论的因子。
因子数据处理流程

当我们获得因子数据后,不能直接用于建模。就像在数据挖掘任务中一样,我们需要先对数据进行预处理。假设我们的目标是预测收益 Y,而我们有多个因子 X1, X2, X3, X4... 作为输入。预处理的目的就是优化这些输入数据,以便后续更有效地挖掘因子与收益之间的关系。
本节课结束后,我们将能理解如何在多因子策略中,通过处理数据来更好地选择股票,以期获得更高的收益。
以下是因子数据预处理的三个核心步骤:
- 去极值:处理数据中的异常值或离群点。
- 标准化:将不同取值范围的数据调整到同一尺度。
- 中性化:消除因子数据中某些特定因素的影响(如行业、市值),这在因子策略中尤为重要。
前两个步骤在机器学习和数据挖掘中很常见,而“中性化”则是量化因子分析中的特色步骤。接下来,我们将按照这个顺序,详细讲解每一步的具体做法。

第一步:去极值处理

去极值是数据处理的第一步,目的是处理那些远离数据主体分布的异常值。直接删除异常值是一种方法,但可能会损失信息。更常见的做法是设定一个合理的边界,将超出边界的极值“拉回”到边界上,而不是丢弃。

例如,假设我们设定的上界是5,而某个数据点的值是10。我们不删除它,而是将其值修改为5。这样既控制了极值的影响,又保留了该数据点的存在。


有多种方法可以确定这个边界,本节我们重点介绍百分位去极值法。

理解分位数

在介绍百分位法之前,需要理解分位数的概念。大家可能熟悉中位数,它是将数据从小到大排列后,处于正中间位置的值。与均值相比,中位数对极值不敏感,更能代表数据的“一般水平”。
分位数是中位数的推广。除了中位数(50%分位数),我们常用的还有:
- 下四分位数:25%分位数,记作 Q1。
- 上四分位数:75%分位数,记作 Q3。

这三个分位数可以将数据大致分为四个部分。
百分位去极值法原理
百分位去极值法的思想很简单:我们直接通过数据的分布比例来设定边界。
- 例如,我们可以将小于1%分位数和大于99%分位数的数据视为极值。
- 然后,将所有小于1%分位数的值都设为1%分位数的值,将所有大于99%分位数的值都设为99%分位数的值。
这种方法不依赖于数据的绝对数值,而是依赖于数据在整个样本中的相对位置,因此对数据分布没有严格要求,实现起来也较为直观。
以下是使用Python代码实现百分位去极值的一个示例框架:

import numpy as np
import pandas as pd
def winsorize_percentile(series, lower_percentile=0.01, upper_percentile=0.99):
"""
使用百分位法对序列进行去极值处理。
参数:
series: 待处理的数据序列(Pandas Series)。
lower_percentile: 下界百分位,默认1%。
upper_percentile: 上界百分位,默认99%。
返回:
处理后的数据序列。
"""
# 计算上下边界的值
lower_bound = series.quantile(lower_percentile)
upper_bound = series.quantile(upper_percentile)
# 将小于下界的值设为下界,大于上界的值设为上界
series_winsorized = series.clip(lower=lower_bound, upper=upper_bound)
return series_winsorized
# 示例:假设我们有一个因子数据序列 ‘factor_data‘
# processed_data = winsorize_percentile(factor_data)
在这段代码中,我们首先计算了序列在指定百分位(如1%和99%)处的值,作为边界。然后使用 clip 函数,将所有超出边界的数据“修剪”到边界值上。
本节总结
本节课中,我们一起学习了因子数据预处理的第一步:去极值。我们首先了解了因子的基本概念,然后介绍了去极值的必要性——不是为了删除数据,而是为了控制异常值的影响。最后,我们重点讲解了百分位去极值法的原理与实现。该方法通过数据自身的分布比例确定边界,简单有效,是量化分析中常用的去极值手段。

下一节,我们将继续学习第二步:标准化,看看如何让不同尺度的因子数据站在同一起跑线上。
028:基于百分位去极值实例

在本节课中,我们将学习如何使用分位数(如Q1和Q3)来识别和处理数据集中的极端值。我们将编写一个函数,将所有低于下分位数的值设置为该分位数,将所有高于上分位数的值设置为该分位数,从而将数据规范到一个指定的范围内。

函数定义与参数说明


首先,我们需要定义一个函数来执行基于百分位的去极值操作。这个函数需要接收三个参数:一个Pandas Series数据序列,以及我们希望保留的最小和最大百分位值。

以下是函数的基本框架:
def winsorize_by_percentile(series, low_percentile, high_percentile):
# 函数主体将在这里实现

数据排序与分位数计算

上一节我们介绍了函数的基本结构,本节中我们来看看具体的实现步骤。第一步是对数据进行排序,因为只有排序后的数据,我们才能准确地找到指定百分位对应的数值。
以下是实现步骤:
- 对输入的Series数据进行排序。
- 使用Pandas的
quantile函数计算指定百分位(如下四分位数Q1和上四分位数Q3)对应的具体数值。
def winsorize_by_percentile(series, low_percentile, high_percentile):
# 1. 对数据进行排序
sorted_series = series.sort_values()
# 2. 计算指定百分位的值
quantiles = sorted_series.quantile([low_percentile, high_percentile])
low_bound = quantiles.iloc[0] # 下边界值,如Q1
high_bound = quantiles.iloc[1] # 上边界值,如Q3
应用边界值进行数据裁剪
计算出边界值后,下一步是将原始数据中超出边界的值进行替换。我们将使用NumPy的clip函数,它可以方便地将序列中的值限制在指定的最小值和最大值之间。
以下是数据裁剪的实现:
import numpy as np
def winsorize_by_percentile(series, low_percentile, high_percentile):
sorted_series = series.sort_values()
quantiles = sorted_series.quantile([low_percentile, high_percentile])
low_bound = quantiles.iloc[0]
high_bound = quantiles.iloc[1]
# 3. 使用clip函数将数据限制在[low_bound, high_bound]范围内
winsorized_series = np.clip(series, low_bound, high_bound)
return winsorized_series
np.clip(series, a_min, a_max)函数会将series中所有小于a_min的值设置为a_min,所有大于a_max的值设置为a_max。

函数调用与结果可视化

函数编写完成后,我们可以将其应用于具体的数据列,并可视化处理前后的效果,以直观地观察去极值操作的影响。

以下是调用函数和绘制对比图的示例代码:
# 假设`data`是一个DataFrame,`column_name`是我们要处理的列名
column_to_process = 'D' # 示例列名
low_p = 0.25 # 下分位数,如25%
high_p = 0.75 # 上分位数,如75%
# 调用函数处理数据
processed_data = winsorize_by_percentile(data[column_to_process], low_p, high_p)


# 可视化原始数据与处理后的数据
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 6))
# 绘制处理后的数据(放在底层,用蓝色表示)
plt.plot(processed_data.index, processed_data.values, label='Winsorized Data', alpha=0.7)
# 绘制原始数据(放在上层,用橙色表示)
plt.plot(data[column_to_process].index, data[column_to_process].values, label='Original Data', alpha=0.7)
plt.legend()
plt.title('Data Before and After Winsorization')
plt.show()
通过对比图,可以清晰地看到,原始数据中超出边界的极端值(图中橙色线的两端)被“拉回”到了边界值(蓝色线的两端)。

实际应用中的参数选择


在实际的因子分析或数据处理中,我们通常不会使用像0.25和0.75这样宽泛的分位数,因为这会将过多数据视为异常值。更常见的做法是使用如0.025和0.975这样的分位数,只处理分布两端各2.5%的极端值。本示例为了更明显地展示效果,选择了较大的范围。


你可以根据实际数据的分布特点,灵活调整low_percentile和high_percentile参数。


总结


本节课中我们一起学习了基于百分位进行去极值(缩尾处理)的方法。我们首先定义了一个函数,通过排序数据和计算分位数来确定数据的上下边界,然后利用np.clip函数将超出边界的数据替换为边界值。最后,我们通过可视化对比了处理前后的数据差异,并讨论了实际应用中分位数的选择策略。这种方法能有效减少极端值对整体数据分析的影响。
029:MAD法去极值教程


在本节课中,我们将学习第二种数据去极值方法——MAD法。我们将了解其核心概念、计算步骤,并通过代码演示其具体实现。
概述
上一节我们介绍了百分位法去极值,本节中我们来看看另一种稳健的方法:MAD法。MAD是“Median Absolute Deviation”(中位数绝对偏差)的缩写。该方法通过计算数据的中位数和绝对偏差来定义数据的正常范围,从而识别并处理异常值。其核心思想是利用数据自身的中位数特性来抵抗极端值的影响。
MAD法核心概念与计算步骤

MAD法的核心在于计算一个名为“中位数绝对偏差”的值,并用它来设定数据的上下边界。


核心公式如下:
- 计算原始数据序列的中位数:
median = median(data) - 计算每个数据点与中位数的绝对偏差:
abs_deviation = |data - median| - 计算这些绝对偏差的中位数,即MAD值:
MAD = median(abs_deviation) - 设定正常值范围的下界和上界:
- 下界:
lower_bound = median - N * MAD - 上界:
upper_bound = median + N * MAD
其中,N是一个常数,通常取值为 1.4826。
- 下界:
- 将超出
[lower_bound, upper_bound]范围的数据视为极值,并进行处理(如截断或替换)。
代码实现


以下是使用Python和Pandas库实现MAD去极值函数的示例代码。


import pandas as pd
import numpy as np


def mad_filter(series, n=1.4826):
"""
使用MAD法对序列进行去极值处理。
参数:
series (pd.Series): 待处理的数据序列。
n (float): 用于计算边界的倍数,默认为1.4826。
返回:
pd.Series: 处理后的数据序列。
"""
# 1. 计算原始序列的中位数
median = series.quantile(0.5)
# 2. 计算绝对偏差序列
abs_deviation = (series - median).abs()
# 3. 计算绝对偏差的中位数 (MAD值)
mad = abs_deviation.quantile(0.5)
# 4. 计算上下边界
upper_bound = median + n * mad
lower_bound = median - n * mad
# 5. 对超出边界的数据进行截断处理
filtered_series = series.clip(lower=lower_bound, upper=upper_bound)
return filtered_series
# 示例:使用函数处理数据
# 假设 `data` 是一个Pandas Series
# filtered_data = mad_filter(data, n=1.4826)
方法对比与应用


以下是MAD法与百分位法的主要特点对比:

- 稳健性:MAD法对极端值不敏感,因为其基于中位数计算,比基于均值和标准差的方法更稳健。
- 计算:步骤清晰,涉及两次中位数计算。
- 参数:主要参数是倍数
N,通常使用标准值1.4826(目的是使MAD在正态分布下与标准差估计量一致)。 - 适用场景:适用于数据中存在显著异常值,且希望使用对异常值不敏感的统计量进行去极值的场景。


总结


本节课中我们一起学习了MAD去极值法。我们首先理解了MAD(中位数绝对偏差)的概念,然后逐步拆解了其计算过程:先求原始数据中位数,再求绝对偏差的中位数,最后利用一个常数倍数确定数据的正常范围。通过代码演示,我们看到了如何将这一过程转化为具体的程序。与百分位法相比,MAD法基于中位数,具有更强的稳健性,是处理包含异常值数据的有效工具之一。
030:3-Sigma方法实例 📊
在本节课中,我们将要学习一种基于统计学原理的数据去极值方法——3-Sigma方法。我们将理解其核心思想,并通过代码实现来掌握如何应用此方法处理数据中的异常值。
概述
上一节我们介绍了去极值的概念,本节中我们来看看一种基于正态分布假设的经典方法:3-Sigma方法。它的原理是利用数据的均值和标准差来定义一个“正常”数据的范围,并将超出此范围的值视为极值进行处理。
3-Sigma方法原理 📈
3-Sigma方法基于一个核心假设:数据服从或近似服从正态分布(也称为高斯分布)。
如果大家理解正态分布比较麻烦,可以将其想象为一个“正常”的分布。例如,你去银行贷款,银行多借或少借你几块钱是正常情况;但如果多借或少借你十万八万,这就显得不正常了。在数据中,大多数“正常”的数据点会聚集在均值附近,而极值则出现在远离均值的位置。
我们来看下面这张正态分布图。图中,越靠近中心(均值)的区域,阴影面积越大,代表事件发生的概率越高。越向两侧延伸,概率则越低。极值,即离群点,通常就落在这些概率很低的偏远区域。

所谓“3-Sigma”,就是指以均值为中心,向左右各扩展3倍标准差(σ)所构成的区间。标准差是衡量数据波动大小的指标。

从图中可以看到,区间范围越大(例如±3σ),数据点落在这个区间外的可能性就越小。根据统计学知识,在标准正态分布中:
- 均值 ± 1σ 的区间包含了约 68.3% 的数据。
- 均值 ± 2σ 的区间包含了约 95.4% 的数据。
- 均值 ± 3σ 的区间包含了约 99.7% 的数据。
因此,3-Sigma方法的思路是:假设数据经过变换后服从正态分布,那么我们可以认为落在 均值 ± 3倍标准差 区间内的数据(约99.7%)是“正常”的,而落在该区间外的数据(约0.3%)则被视为“极值”并进行规范处理。

代码实现 💻
理解了原理后,我们来看看如何用代码实现3-Sigma方法。其核心是计算数据的均值(mean)和标准差(std),然后根据指定的倍数N来确定上下限。
以下是实现步骤:
首先,我们需要计算选定数据列的均值和标准差。

# 计算均值和标准差
mean = data['column_name'].mean()
std = data['column_name'].std()

接着,根据均值和标准差计算数据的上下限。上限为 均值 + N * 标准差,下限为 均值 - N * 标准差。
# 设定倍数N,例如N=3代表3-Sigma
N = 3
# 计算上限和下限
upper_limit = mean + N * std
lower_limit = mean - N * std



最后,对原始数据进行规范:将所有大于上限的值设置为上限值,将所有小于下限的值设置为下限值。这样,极值就被“拉回”到正常范围内,而不是被删除。
# 进行去极值处理
data['column_name_processed'] = data['column_name'].clip(lower=lower_limit, upper=upper_limit)


将以上步骤封装成一个函数,便于调用。

def three_sigma(data_series, n=3):
"""
使用3-Sigma方法去极值
:param data_series: 输入的数据序列(Pandas Series)
:param n: 标准差的倍数,默认为3
:return: 处理后的数据序列
"""
mean = data_series.mean()
std = data_series.std()
upper_limit = mean + n * std
lower_limit = mean - n * std
return data_series.clip(lower=lower_limit, upper=upper_limit)



方法应用与对比 🔍

我们可以应用这个函数,并可视化处理效果。为了更清晰地展示,我们先将倍数N设为1(即68.3%的区间)。

# 应用函数,N=1
processed_data = three_sigma(original_data, n=1)

# 可以绘制处理前后的数据分布图进行对比
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].hist(original_data, bins=50, color='skyblue')
axes[0].set_title('原始数据分布')
axes[1].hist(processed_data, bins=50, color='lightcoral')
axes[1].set_title('3-Sigma (N=1) 处理后分布')
plt.show()


执行代码后,可以看到处理后的数据分布。图中,绿色基线通常保持不变,而蓝色的阈值线(上下限)会根据不同的N值和数据本身发生变化。



在实际任务中,上下限的具体数值会因数据不同而略有差异。例如,在这个任务中上限可能在60左右,在另一个任务中可能不到50。



选择哪种去极值方法(如分位数法、MAD法、3-Sigma法)以及设定何种参数(如N的取值),取决于实际的数据特性和分析需求。它们都是数据预处理中常用的不同策略,大家可以多进行实验来选择最合适的一种。


总结

本节课中我们一起学习了3-Sigma去极值方法。我们来回顾一下核心要点:

- 核心思想:该方法基于数据服从正态分布的假设,认为距离均值超过3倍标准差的数据点属于小概率事件,应视为极值。
- 关键步骤:计算数据的均值(μ)和标准差(σ),然后根据公式 上限 = μ + Nσ 和 下限 = μ - Nσ 确定正常值范围。
- 处理方式:去极值不是删除数据,而是通过“截断”将超出上下限的值规范到边界上,使所有数据落在指定范围内。
- 方法定位:这是数据预处理“三步走”(去极值、标准化、缺失值处理)中第一步“去极值”的常用方法之一。
主要就是这三种去极值方法(分位数、MAD、3-Sigma)我们都介绍完了。掌握它们,能帮助我们在数据分析前有效地平滑数据,减少异常值对模型的干扰。


031:标准化处理方法 📊

在本节课中,我们将要学习数据预处理中一个非常重要的步骤——标准化。标准化能够调整数据,使其在不同维度上具有可比性,是许多机器学习和数据分析任务的基础。

标准化的目的与原理
上一节我们介绍了数据预处理的重要性,本节中我们来看看标准化的具体目的。
标准化的核心出发点是为了解决不同数据指标(或特征)取值范围不同的问题。例如,在一个数据集中,特征X1的取值范围可能很大(如0到100),而特征X2的取值范围可能很小(如0到1)。这种数值尺度上的差异,可能会影响后续分析(如距离计算、模型训练)的结果,让模型错误地认为数值大的特征更重要。
因此,标准化的目的是:使得不同维度的指标,其取值范围尽可能相同,以便用统一的尺度去观察和分析它们。
标准化的公式分解
理解了目的之后,我们来看标准化是如何通过数学公式实现的。标准化的通用公式如下:
z = (x - μ) / σ
这个公式可以分解为两个连续的步骤:
- 去均值
- 除标准差
步骤一:去均值
公式中的 (x - μ) 部分就是去均值操作。μ 代表该维度所有数据的平均值。
目的:将数据整体平移,使得数据在该维度上以零为中心对称。原本不以零为中心分布的数据,经过这一步后,其均值变为0。
步骤二:除标准差
公式中的 / σ 部分就是除标准差操作。σ 代表该维度所有数据的标准差。
目的:消除不同维度间取值范围(方差)的差异。标准差衡量了数据的离散程度。对于取值范围大、数据分散的维度,其标准差也大;对于取值范围小、数据集中的维度,其标准差也小。
通过除以各自的标准差,可以将所有维度的数据“缩放”到相似的尺度上。原本取值范围大的数据除以一个较大的数,会变小;原本取值范围小的数据除以一个较小的数,变化相对不大。最终结果是各维度数据的方差都近似为1。
简单来说,标准化后,数据会满足:均值为0,标准差为1。

标准化的代码实现
理论清晰后,我们动手实现标准化函数。以下是使用Python和Pandas库的一个简单实现:
def standardize(data_series):
"""
对输入的Pandas Series进行标准化处理。
参数:
data_series: 待处理的数据序列。
返回:
标准化后的数据序列。
"""
# 计算均值和标准差
mean_val = data_series.mean()
std_val = data_series.std()
# 应用标准化公式: (x - mean) / std
standardized_data = (data_series - mean_val) / std_val
return standardized_data

代码说明:
data_series.mean()计算均值μ。data_series.std()计算标准差σ。(data_series - mean_val) / std_val对应标准化的数学公式。

使用现成工具库

在实际项目中,我们通常使用成熟的机器学习库来完成标准化,这样更高效、更稳定。以下是使用 scikit-learn 库的实现方法:

from sklearn.preprocessing import StandardScaler
import pandas as pd

# 假设 df 是你的DataFrame
scaler = StandardScaler()
# 拟合数据并转换
standardized_df = scaler.fit_transform(df)
# 将结果转换回DataFrame(可选)
standardized_df = pd.DataFrame(standardized_df, columns=df.columns)
使用 scikit-learn 的 StandardScaler 只需几行代码,并且它能方便地处理整个数据集,同时保存拟合的参数(均值和标准差),便于对后续新数据做相同的转换。
效果演示

让我们通过一个简单的例子观察标准化的效果。假设原始数据如下(取值范围差异很大):

| 特征A | 特征B |
|---|---|
| 30 | 200 |
| 20 | 150 |
| 100 | 50 |

使用我们的 standardize 函数或 StandardScaler 处理后,数据可能变为:

| 特征A | 特征B |
|---|---|
| -0.27 | 1.09 |
| -0.63 | 0.36 |
| 0.90 | -1.45 |

可以看到,处理后的数据,每个特征(列)的数值都被调整到一个相对较小的、以零为中心的范围内,不同特征之间的尺度变得可比。

总结

本节课中我们一起学习了数据标准化处理。
- 目的:消除不同特征因取值范围不同带来的偏差,使所有特征处于同一量纲。
- 原理:通过
z = (x - μ) / σ公式,先去均值使数据中心为0,再除标准差使数据尺度一致。 - 实现:可以手动根据公式计算,也可以直接调用
scikit-learn等库的StandardScaler工具。
标准化是数据预处理的常规且关键的第一步,能显著提升许多机器学习模型的性能和稳定性。
032:中性化处理方法通俗解释
在本节课中,我们将要学习量化交易中的一个重要概念——中性化。我们将通过一个简单的例子来理解它的目的和意义,并了解其基本计算方法。
概述:什么是中性化?
上一节我们介绍了因子分析的基本概念,本节中我们来看看如何“提纯”因子。中性化的核心目的是提纯。它旨在从原始因子中,剥离掉那些普遍存在、对区分个股无益的共性影响,从而提取出该因子独特、有价值的信息部分。
一个生动的例子
为了更好地理解,我们先来看一个例子。假设我们设计了一个选股策略,其中使用了四个不同的因子(例如A、B、C、D)来筛选股票。
无论我们如何调整这四个因子的组合或权重,最终选出的股票池总是高度相似。这是为什么呢?
原因可能在于,这四个因子虽然名称不同,但其内部绝大部分的“成分”是相同的。例如:
- 因子A(如市净率)绝大部分受市值影响。
- 因子B、C、D也绝大部分受市值影响。
这样一来,无论使用哪个因子,最终起决定性作用的几乎都是“市值”这个共性因素。我们无法通过这四个因子看到股票之间除市值外的其他差异,策略也就失去了多样性和独特性。
中性化要做的,就是从因子A中剔除“市值”这个共性部分,保留其独有的、与市值无关的信息。对因子B、C、D也进行同样的操作。这个过程就像“提纯”,目的是获得每个因子最纯净、最具鉴别力的部分。
量化交易中的中性化
在量化交易中,我们常使用多个指标(因子)对股票池进行筛选。但在使用这些因子时,经常会受到一些共性因素(如市值、行业)的强烈影响,导致选股结果带有我们不希望的倾向性,例如选出的股票总是集中在某些大盘股上。
如果不进行中性化处理,选股结果可能会过于集中,无法充分体现因子本身想揭示的个股差异。
以本节课将用到的市净率(PB) 因子为例:
- 公式:
市净率(PB) = 每股股价 / 每股净资产 - 其中:
每股净资产 = (公司总资产 - 公司总负债) / 总股数 - 投资意义:通常认为市净率较低的股票,其投资价值可能更高,风险相对更低。

市净率与市值通常有较高的相关性。如果不做处理,基于市净率的选股可能会被市值主导。
中性化的计算方法
上一节我们通过例子理解了中性化的目的,本节中我们来看看其典型的实现方法。以下是一种常用的中性化处理步骤:
-
建立回归模型:将被处理的因子(如市净率)作为因变量(Y),将需要剔除的共性因素(如市值)作为自变量(X),建立线性回归模型。
- 公式:
Y = α + β * X + ε - 其中:
Y:原始因子值(如市净率)X:中性化目标(如市值,通常取对数)α:截距项β:回归系数ε:残差项(即我们需要的“纯因子”)
- 公式:
-
提取残差:通过回归模型计算得到的残差项
ε,即为中性化后的因子值。- 含义:残差
ε代表了原始因子中无法被共性因素(市值)解释的部分,即剥离市值影响后的“纯净”因子。
- 含义:残差
-
标准化(可选):为了便于不同因子之间的比较和组合,通常会对中性化后的因子值进行标准化处理,使其均值为0,标准差为1。
总结
本节课中我们一起学习了因子中性化的概念与方法。
- 核心目的:提纯,即剔除因子中的共性干扰(如市值、行业影响),提取其独特、有效的选股信息。
- 核心方法:通过线性回归将原始因子对中性化目标(如市值)进行回归,并取残差作为中性化后的新因子值。
- 最终作用:使选股策略更依赖于因子本身的特性,减少无关共性因素的干扰,增强策略的多样性和有效性。

接下来,我们将在量化平台中调用具体的因子数据,实际演示这一计算过程。
033:因子中性化(提纯)教程 📊

在本节课中,我们将学习量化分析中的一个核心概念——因子中性化(或称因子提纯)。我们将理解其原理,并通过一个具体的例子(市净率因子剔除市值影响)来掌握其实现步骤。
概述
因子中性化的目标是从一个原始因子中,剔除我们不希望其包含的、来自其他变量(如市值、行业)的影响,从而得到更“纯净”的因子信号。例如,我们希望研究市净率(PB)因子对股票收益的预测能力,但已知市净率本身与市值高度相关。为了单独考察市净率的影响,就需要从市净率因子中剔除掉市值所能解释的那部分信息。
核心原理:回归与残差
上一节我们介绍了因子中性化的目标,本节中我们来看看其背后的数学原理。核心思想是利用线性回归来分解因子。
我们以市净率(Y)和市值(X)为例。我们认为市净率因子中,有一部分信息可以被市值解释。我们可以建立如下线性回归方程来量化这种关系:
Y = W * X + B + ε
其中:
- Y 是原始因子值(如市净率),即真实值。
- X 是我们希望剔除的影响变量(如市值)。
- W 和 B 是通过回归分析得到的系数(斜率和截距)。
W * X + B构成了市净率中能被市值所解释的部分,我们称之为预测值。- ε 是回归的残差,即真实值与预测值之间的差异:
ε = Y - (W * X + B)。
这个残差 ε 就是我们要的“提纯”后的因子。它代表了原始市净率因子中,无法被市值解释的剩余部分,即我们想要的“纯净”的市净率信息。

操作步骤
理解了原理后,以下是执行因子中性化的具体步骤:
第一步:建立回归方程。将需要提纯的因子(如市净率)作为因变量Y,将希望剔除的影响因子(如市值)作为自变量X,进行线性回归,求解出系数W和B。
第二步:计算残差(中性化因子)。用原始因子值Y减去回归预测值 (W * X + B),得到的残差序列即为中性化后的新因子。

# 伪代码示例
neutralized_factor = original_factor - (W * market_cap + B)

在量化策略中的应用流程

接下来,我们看看如何将因子中性化整合到一个完整的量化策略流程中。通常,一个基于因子的选股策略会包含以下环节:

以下是构建策略时典型的数据处理与选股步骤:
- 获取原始数据:获取所需的因子数据(如市净率PB)和可能的需要剔除的变量数据(如市值)。
- 数据预处理:
- 股票池过滤:剔除停牌股、ST股、上市时间过短(如小于半年)的股票。
- 因子处理:依次对因子进行去极值、标准化和中性化处理。
- 生成交易信号:基于处理后的“纯净”因子生成选股信号。例如,买入市净率因子值小于0.2的股票。
- 执行策略:根据信号构建投资组合,进行回测和评估。

总结


本节课中我们一起学习了因子中性化的核心概念。其本质是通过线性回归,将原始因子分解为“可由其他变量解释的部分”和“独特的残差部分”。我们通过公式 ε = Y - (W * X + B) 来计算中性化后的因子。这个过程在量化研究中至关重要,能帮助分析师更准确地评估单个因子的真实预测能力,避免错误地将其他变量的影响归因于目标因子。在实际策略构建中,中性化是因子数据处理流水线中的关键一环。
034:因子选股策略实现
在本节课中,我们将学习如何构建一个基于因子的选股策略。我们将从获取股票数据开始,对因子进行预处理(包括去极值、标准化和中性化),然后基于处理后的因子设计选股规则,并实现一个定期调仓的策略框架。

初始化与数据准备

上一节我们介绍了因子选股的基本概念,本节中我们来看看如何用代码实现策略的初始化。
首先,我们需要导入必要的Python工具包。
import numpy as np
import pandas as pd
from statsmodels import regression
接下来,我们定义策略的初始化函数。这里需要设置一个定时器,用于定期执行我们的选股和调仓逻辑。通常,按月调仓是一个比较合理的频率。

def initialize(context):
# 设置按月调仓的定时器
schedule_function(rebalance,
date_rule=date_rules.month_start(),
time_rule=time_rules.market_open())

在rebalance函数中,我们将执行核心的选股与调仓操作。首先,我们需要获取股票池数据。
以下是获取所有股票代码的方法:
def rebalance(context, data):
# 获取所有股票代码
all_stocks = get_all_securities(types=[‘STK’])
# 后续将对 all_stocks 进行筛选,构建最终的投资组合

因子预处理与选股逻辑

在获取了基础股票池之后,我们需要对选股因子进行处理,并应用选股规则。

因子预处理通常包括三个步骤:去极值、标准化和中性化。我们将依次完成这些操作。
以下是因子预处理的核心步骤列表:

- 去极值:使用
winsorize方法处理因子值的极端异常值。 - 标准化:使用
standardize方法将因子值转化为均值为0、标准差为1的标准分布。 - 中性化:通过回归分析,剔除因子中与市值、行业等风格相关的部分,保留其独立选股能力。

完成因子预处理后,我们将根据处理后的因子值对股票进行排序,选择排名靠前的股票作为当期买入的标的。

策略回测与调仓执行

在确定了当期的选股名单后,我们需要执行具体的买卖操作,完成调仓。

调仓逻辑是:卖出当前持仓中不在新选股名单中的股票,并买入新名单中我们尚未持有的股票。

以下是执行调仓的关键步骤列表:
- 计算目标权重:为选中的股票分配资金权重,例如等权重分配。
- 生成交易订单:根据目标权重与当前权重的差异,计算需要交易的数量。
- 执行订单:调用
order_target_percent函数执行买卖操作。

这样,我们就完成了一个完整的月度调仓周期。策略会每月初自动运行此流程,实现因子的动态选股。


本节课中我们一起学习了如何构建一个完整的因子选股策略。我们从初始化环境和获取数据开始,详细讲解了因子的预处理流程(去极值、标准化、中性化),并实现了基于处理后的因子进行选股以及定期调仓的逻辑。这个框架是量化策略开发的基础,你可以在此基础上尝试不同的因子或调仓频率,以优化策略表现。
035:过滤筛选因子指标数据 📊
在本节课中,我们将学习如何对股票池进行筛选,并获取我们所需的财务指标数据。这是构建量化交易策略中非常关键的一步,可以帮助我们剔除不符合条件的股票,并聚焦于核心数据。


上一节我们介绍了量化策略的基本框架,本节中我们来看看如何具体实现数据的预处理。

过滤不想要的股票 🚫



首先,我们需要从股票池中过滤掉那些我们不希望纳入分析的股票。这通常包括停牌股、ST/*ST股以及刚上市的新股。


以下是实现过滤功能的三个核心函数:

def filter_suspended_stocks(stock_list):
"""
过滤停牌股票。
遍历股票列表,使用API判断股票是否全年停牌,保留未停牌的股票。
"""
filtered_list = []
for stock in stock_list:
if not is_suspended(stock): # 假设 is_suspended 是判断停牌的API
filtered_list.append(stock)
return filtered_list



def filter_st_stocks(stock_list):
"""
过滤ST/*ST股票。
遍历股票列表,使用API判断股票是否为ST/*ST股,保留非ST/*ST的股票。
"""
filtered_list = []
for stock in stock_list:
if not is_st(stock): # 假设 is_st 是判断ST/*ST的API
filtered_list.append(stock)
return filtered_list



def filter_new_stocks(stock_list, min_days=180):
"""
过滤上市时间过短的新股。
遍历股票列表,判断股票上市天数是否大于指定值(如180天),保留符合条件的股票。
"""
filtered_list = []
for stock in stock_list:
if days_from_listed(stock) > min_days: # 假设 days_from_listed 是获取上市天数的API
filtered_list.append(stock)
return filtered_list
在定义了这些过滤函数后,我们可以按顺序对初始股票池进行层层筛选。



# 假设 initial_stocks 是初始的股票代码列表
filtered_stocks = filter_suspended_stocks(initial_stocks)
filtered_stocks = filter_st_stocks(filtered_stocks)
filtered_stocks = filter_new_stocks(filtered_stocks, min_days=180)


经过以上步骤,我们就得到了一个经过初步清洗的、相对“干净”的股票池。

查询所需的财务指标 🔍

在获得目标股票池后,下一步是获取这些股票的关键财务指标数据,例如市净率(PB)和市值(Market Cap)。



以下是使用查询API获取指标数据的代码:

# 导入必要的库,假设 get_fundamentals 是查询财务数据的函数
from some_api import get_fundamentals


# 构建查询语句,获取市净率(pb_ratio)和市值(market_cap)
query = get_fundamentals(
query(
fundamentals.eod_derivative_indicator.pb_ratio, # 市净率
fundamentals.eod_derivative_indicator.market_cap # 市值
).filter(
fundamentals.stockcode.in_(filtered_stocks) # 只查询我们过滤后的股票
)
)


查询返回的数据通常是横向排列的(每行是一个指标,每列是一只股票)。为了便于后续分析,我们通常需要将其转置,使每行代表一只股票,每列代表一个指标,并处理可能存在的空值。
# 转置数据框,使行对应股票,列对应指标
transposed_data = query.T


# 删除包含空值的行(简单处理)
cleaned_data = transposed_data.dropna()

这样,我们就得到了一个结构清晰、包含目标股票关键指标的数据集 cleaned_data,可以用于后续的因子计算和策略构建。


本节课中我们一起学习了量化数据预处理的两个核心步骤:股票过滤与指标查询。我们通过编写函数过滤了停牌股、ST股和新股,然后使用查询API获取了股票的市净率和市值指标,并对数据进行了转置和清洗。这些步骤为后续的因子分析和策略回测奠定了坚实的数据基础。
036:因子数据预处理 📊



在本节课中,我们将学习因子数据预处理的核心三步:去极值、标准化和中性化。这些步骤对于清洗和准备金融数据至关重要,能帮助我们获得更稳定、更可靠的因子值,为后续的模型分析打下基础。

去极值处理 🎯


上一节我们介绍了如何查询因子数据,本节中我们来看看如何对数据进行预处理。第一步是去极值,目的是消除数据中异常值的影响,防止它们对后续分析造成过大干扰。

我们采用“三西格玛”方法进行去极值处理。该方法基于数据的均值和标准差,将超出特定范围的值进行截断。


以下是去极值函数 filter_three_sigma 的实现步骤:
- 计算均值与标准差:首先计算输入序列的均值(
mean)和标准差(std)。 - 确定上下限:上限为
均值 + 3 * 标准差,下限为均值 - 3 * 标准差。 - 截断处理:使用
np.clip函数将序列中超出上下限的值替换为边界值。

import numpy as np

def filter_three_sigma(series, n=3):
"""
使用三西格玛法去除极值。
:param series: 输入的数据序列(pandas Series 或 numpy array)
:param n: 西格玛倍数,默认为3
:return: 处理后的数据序列
"""
mean = series.mean()
std = series.std()
upper_limit = mean + n * std
lower_limit = mean - n * std
# 将超出范围的值截断到边界
return np.clip(series, lower_limit, upper_limit)


标准化处理 📏

完成去极值后,我们需要进行标准化。标准化的目的是将不同量纲或量级的指标转换为统一的标准分数,使其具有可比性。


标准化操作通常指“Z-Score标准化”,即减去均值后除以标准差。


以下是标准化函数 standardize 的实现:

- 计算均值与标准差:与去极值步骤相同,需要计算序列的均值(
mean)和标准差(std)。 - 应用公式:对序列中的每个值应用公式
(值 - 均值) / 标准差。

def standardize(series):
"""
对数据进行Z-Score标准化。
:param series: 输入的数据序列
:return: 标准化后的数据序列
"""
mean = series.mean()
std = series.std()
return (series - mean) / std

中性化处理 ⚖️
最后一步是中性化处理。在金融多因子模型中,中性化的目的是消除因子值与某些已知风险暴露(如市值)之间的相关性,从而提取出因子“纯粹”的选股能力。


我们通过线性回归来实现中性化。以消除市值影响为例,将因子值作为因变量Y,市值作为自变量X进行回归,然后取回归的残差作为中性化后的因子值。

以下是中性化函数 neutralize 的实现步骤:


- 准备数据:确定因变量Y(待处理的因子序列)和自变量X(需要被中性化的指标,如市值)。
- 建立回归模型:使用
statsmodels库的普通最小二乘法(OLS)进行线性回归。 - 提取残差:回归模型的残差即为去除了X影响后的“纯净”因子值。

import statsmodels.api as sm


def neutralize(factor_series, exposure_series):
"""
对因子进行中性化处理,消除指定暴露(如市值)的影响。
:param factor_series: 待中性化的因子序列(Y)
:param exposure_series: 需要被中性化的暴露序列,如市值(X)
:return: 中性化后的因子序列(残差)
"""
# 为X添加常数项(截距)
X = sm.add_constant(exposure_series.astype(float))
Y = factor_series.astype(float)
# 建立OLS模型并拟合
model = sm.OLS(Y, X)
results = model.fit()
# 返回残差,即中性化后的因子值
return results.resid
核心概念解释:残差(Residual)是实际观测值(Y)与回归模型预测值之间的差值。它代表了无法被自变量(X)解释的部分,在这里即为我们需要的、剥离了市值影响的因子。
总结 ✨

本节课中我们一起学习了因子数据预处理的三个核心步骤。

- 去极值:使用三西格玛法消除异常值,增强数据稳定性。
- 标准化:通过Z-Score方法统一数据量纲,使不同因子具有可比性。
- 中性化:借助线性回归剔除因子与特定风险暴露(如市值)的相关性,提取因子的独立信息。

这三步操作是构建稳健量化模型的基础,能有效提升因子信号的质量。在实际应用中,可以按顺序调用这三个函数来完成完整的因子预处理流程。
037:股票池筛选



概述
在本节课中,我们将学习如何基于处理后的因子数据,构建并筛选出符合特定条件的股票池。我们将完成数据预处理后的最后一步,即根据因子值筛选股票,并准备进行后续的调仓操作。

上一节我们介绍了因子的中性化处理,本节中我们来看看如何利用处理好的因子来筛选股票。
执行预处理操作
现在,我们已经完成了中性化操作的代码编写。接下来,我们将在此处依次执行之前写好的几个预处理步骤。

首先,我们需要对因子数据进行去极值处理。以下是具体操作:


- 调用
three_sigma函数,传入我们的因子数据factor。 - 此步骤旨在消除因子数据中的异常值。


完成去极值后,我们还需要进行标准化处理。操作如下:


- 将上一步的结果传入
standard_scaler函数。 - 此步骤将数据转换为均值为0、标准差为1的标准分布。

标准化操作完成后,最后一步是进行行业市值中性化。操作如下:

- 将标准化后的结果传入
neutralization函数。 - 此步骤用于消除行业和市值对因子暴露的影响。
至此,我们完成了所有必需的因子数据预处理操作,得到了可用于选股的“干净”因子数据。
基于因子筛选股票
接下来,我们将基于处理后的因子数据对股票池进行筛选。


我们需要指定一个筛选条件。例如,在我们的策略中,通常认为市净率因子值较小的股票未来可能表现更好。因此,我们可以选择因子值最小的前20%的股票。

具体操作是计算因子值的20%分位数,作为筛选阈值。
threshold = factor.quantile(0.2)

现在,我们要根据这个阈值来选股。判断逻辑是筛选出因子值小于等于该阈值的所有股票。
selected_stocks = factor[factor <= threshold].index

这样,我们就得到了一个包含目标股票的列表 selected_stocks。我们将这个列表存入上下文变量 context 中,以便后续交易函数调用。
context.stock_list = selected_stocks
确定调仓标的:卖出不在池中的股票
在执行买卖操作前,我们需要明确哪些现有持仓需要卖出,哪些新股票需要买入。



首先,我们要卖出那些不在最新股票池中的现有持仓。以下是确定待卖出股票列表的方法:
- 获取当前账户的所有持仓股票列表。
- 计算当前持仓股票列表与最新股票池
context.stock_list的差集。 - 这个差集即为需要卖出的股票列表,因为它们存在于旧持仓中,但不在新选出的股票池里。
代码逻辑如下:
# 获取当前持仓
current_holdings = context.portfolio.positions
# 计算需要卖出的股票(在持仓中但不在新股票池里)
stocks_to_sell = current_holdings.difference(context.stock_list)

总结
本节课中我们一起学习了股票池筛选的完整流程。我们首先回顾并执行了因子的预处理步骤,包括去极值、标准化和中性化。然后,我们基于处理后的因子值设定了筛选条件,选出了符合策略逻辑的股票构成新的股票池。最后,我们通过计算持仓与新股票池的差集,明确了需要卖出的股票列表,为下一步执行具体的交易订单做好了准备。
038:策略效果评估分析 📊

在本节课中,我们将学习如何对上一节构建的因子选股策略进行回测,并分析其效果。我们将运行策略代码,调试过程中遇到的问题,并最终解读回测结果报告。
策略执行与调试 🔧



上一节我们完成了策略的核心逻辑编写。本节中,我们来看看如何运行策略并进行必要的调试。



首先,我们尝试运行编写好的策略代码。由于是初次编写,代码中可能存在一些拼写错误或逻辑遗漏,需要通过运行报错信息来定位和修复。



以下是运行策略后遇到的主要问题及解决方法:



- 问题一:中性化函数参数缺失
在调用中性化函数时,缺少了必要的参数。我们需要将待处理的指标列名作为参数传入。# 修正前 neutralized_data = neutralize_factor(data) # 修正后 neutralized_data = neutralize_factor(data, ‘target_factor_name‘)



- 问题二:回归模型未训练
在中性化操作中,我们使用了线性回归模型,但只进行了初始化,没有调用fit方法进行训练。# 修正前 model = sm.OLS(y, X) # 修正后 model = sm.OLS(y, X).fit()


- 问题三:变量名拼写错误
在筛选股票时,使用了错误的方法名quantile。# 修正前 threshold = series.quantlg(0.3) # 修正后 threshold = series.quantile(0.3)


- 问题四:列表变量名错误
在调仓逻辑中,引用了一个未定义的列表变量名。# 修正前 for stock in to_delete: # 修正后 (假设正确的列表变量名是 to_delete_list) for stock in to_delete_list:



逐一修正上述问题后,策略代码成功通过编译并开始执行回测。




回测过程观察 👀


策略设置了每月调仓的定时器。在回测执行过程中,控制台会打印出每次调仓的日志信息,这有助于我们确认定时器是否按预期工作。


回测执行需要一些时间,因为策略需要逐日计算并每月执行调仓操作。在等待过程中,可以观察进度条和初步的收益曲线。
回测结果分析 📈
回测完成后,我们得到了策略的性能报告。以下是核心结果的解读:


- 收益对比:将策略收益与基准收益(如沪深300指数)进行对比。在本例的回测年份中,基准收益为负,而我们的策略收益虽然不高,但相对基准有所改善,甚至在某些时段转为正值。
- 关键指标:
- 年化收益率:策略在回测期间获得的年均收益率。
- 夏普比率:衡量策略风险调整后收益的指标。本例中该值较低,说明收益相对于波动(风险)的性价比不高。
- 最大回撤:策略资产从峰值到谷底的最大下跌幅度。本例中约为9%,属于可接受范围。
- 分析结论:基于市值和市净率因子进行筛选的简单策略,在本回测周期内表现略优于市场基准,但绝对收益和风险调整后收益并不突出。这验证了因子选股的基本逻辑,也说明单一或少数因子的效果有限。



策略逻辑回顾与总结 🧠



本节课中我们一起学习了如何运行和评估一个量化交易策略。




我们首先执行了策略代码,并通过调试解决了参数缺失、模型未训练、拼写错误等常见问题。随后,我们观察了回测过程,并最终分析了回测结果报告。




整个策略的流程可以总结为以下几步:
- 初始化:在
__init__中设置定时器(如每月调仓)。 - 股票池过滤:剔除不符合基本条件的股票(如ST股、停牌股)。
- 因子计算与处理:查询所需因子(如市值、市净率),并进行标准化、中性化等预处理。
- 因子筛选:根据因子逻辑(如认为市净率越低越好)筛选出目标股票池。
- 调仓执行:
- 卖出当前持有但不在新股票池中的股票(设置持仓比例为0)。
- 买入新股票池中的所有股票,并平均分配资金(例如,每只股票持仓比例为
1 / 股票数量)。



通过本次实践,我们完成了一个完整的因子选股策略从构建、回测到评估的闭环。需要注意的是,回测结果受所选时间段、参数设置影响很大,且历史表现不代表未来。开发者可以在此基础上,尝试引入更多因子、优化筛选条件或调整调仓频率,以进一步探索和改进策略。
039:因子分析概述
在本节课中,我们将要学习量化分析中的一个核心环节——因子分析。我们将探讨如何从海量的股票指标(因子)中,筛选出对收益率有显著影响的优质因子,并介绍两种基础的评估方法。
因子分析的目标
上一节我们介绍了因子分析的基本概念,本节中我们来看看其具体目标。在股票分析中,我们可以获取大量指标,例如基本面信息(记为A)和技术指标。这些因子可以划分为多个大类,每个大类下又可细分为众多具体指标。
假设我们手中有300个不同的因子。当进行策略回测或设计时,我们需要判断哪些因子是有效的,哪些是无效的。换句话说,我们需要根据每个因子对最终收益的影响程度,为它们打分并排序。例如,影响力大的因子可得98分,影响力小的可能只得10分或0分。我们的目标就是完成这个排序工作,找出“好”的因子。
评估因子的基本方法
那么,如何判断一个因子是否“好”呢?最直接的方法是观察它与收益率的关系。首先,我们需要明确收益率的定义。
收益率计算如下:例如计算日收益率,它等于(当日收盘价 - 上一交易日收盘价) / 上一交易日收盘价。我们的目标是实现每日盈利的累积。
因子和收益率都是随时间变化的。例如,我们连续取一年的数据(约250个交易日),每天都会有一个因子值(记为F)和一个收益率值(记为R)。我们需要分析这两个序列走势之间的关系:是线性相关、非线性相关还是不相关?如果是相关,是正相关还是负相关?相关程度有多强?
因子分析主要包含两项任务:第一是计算因子的IC值进行分析,第二是进行因子的收益率分析。本节我们重点介绍第一项:IC分析。
什么是IC分析
IC是“信息系数”的缩写,它是一个衡量相关性的指标。具体来说,IC值是通过计算因子与收益率之间的斯皮尔曼秩相关系数得到的。
其计算公式的核心是相关系数计算,在代码中通常实现为:
# 伪代码示例:计算斯皮尔曼相关系数
ic_value = spearmanr(factor_series, return_series)[0]
IC值的取值范围在-1到1之间。越接近1,表示因子与收益率正相关性越强;越接近-1,表示负相关性越强;越接近0,则表示两者几乎没有线性关系。
这种针对单一因子与收益率关系的分析,也称为单因子分析。由于我们有300个因子,而收益率序列相对固定,因此我们需要循环遍历每一个因子,分别计算其与收益率之间的斯皮尔曼相关系数。计算出的结果就是该因子的IC值。
IC分析的结果解读

计算出IC值后,我们需要对其大小和走势进行分析。通常我们会用图表来展示结果。
例如,左图可能展示了每一天IC值的变化情况。右图则可能包含两条线:蓝色折线代表IC值每日的走势,绿色均线则代表IC值在某个滚动窗口(例如10天)内的平均值,这有助于观察IC值的长期趋势。
从图中我们可以看到,IC值会在正负区间内波动。我们的目标是寻找那些绝对值较大的IC值。IC值绝对值越大,表明该因子与收益率的关系越强,越值得深入挖掘。反之,对于那些IC值接近0或与预期方向相反的因子,我们可以考虑不予采用。

本节课中我们一起学习了因子分析的基本目标,并重点介绍了IC分析的概念。我们了解到,IC值是通过计算因子与收益率的斯皮尔曼相关系数得到的,用于衡量单个因子的预测能力。通过分析IC值的大小和趋势,我们可以初步筛选出对策略可能有用的因子。
040:Alphalens工具包介绍
在本节课中,我们将要学习一个名为Alphalens的强大工具包。这个工具包专门用于因子分析,能够帮助我们自动化地计算关键指标并生成分析图表,从而极大地简化量化研究的工作流程。
工具包简介与获取
上一节我们介绍了因子分析的基本概念,本节中我们来看看能帮助我们高效完成这项工作的工具。
Alphalens是一个专门用于因子分析的Python工具包。它封装了因子分析中常见的计算(如IC值)和可视化操作,用户无需从零开始编写代码。
以下是关于Alphalens的基本信息:
- GitHub链接:可以在其GitHub页面找到源代码和基础信息。
- 官方文档:提供了详细的使用说明和API参考。
安装与学习资源
了解了Alphalens是什么之后,我们来看看如何获取它以及如何学习使用它。
安装Alphalens非常简单,只需在命令行中使用pip命令即可完成:
pip install alphalens
如果你在本地环境使用,可以执行上述命令进行安装。不过,在本课程后续的实践中,我们将使用一个已经预装了该工具包的在线平台,因此你无需在本地手动安装。
对于学习资源,建议优先参考官方提供的示例(Examples)和教程文档。这些材料详细介绍了工具包的基本使用方法。本课程的内容也主要提炼自这些官方示例,旨在帮助你快速掌握核心功能。
在线研究环境配置

介绍完工具本身,接下来我们需要一个能够运行代码的环境。由于因子分析需要获取特定的金融数据,在个人电脑上操作可能比较麻烦。
我们将使用平台提供的“投资研究”环境来编写和运行代码。这个环境类似于Jupyter Notebook,但运行在平台的服务器上,可以直接调用平台的数据接口。

操作步骤如下:
- 进入策略页面。
- 在左侧菜单中找到并点击“投资研究”。
- 新建一个Python 3笔记文件。
- 将其重命名为“因子分析”或其他你喜欢的名称。
这个环境就是我们后续编写分析代码的地方。课程中会将完整的代码提供给大家,你可以直接上传或复制到该环境中运行,也可以跟随视频一步步编写。
核心功能预览
在配置好环境后,我们简要预览一下Alphalens的核心功能,为后续实战做准备。

Alphalens的核心功能是自动化完成因子分析中的两大任务:
- 指标计算:例如计算信息系数(IC)、因子收益率等关键统计指标。
- 图表生成:自动绘制因子分组收益、IC序列、分位数组合累计收益等分析图表。

通过调用Alphalens提供的简洁API,我们可以用很少的代码完成这些复杂分析,将精力更多地集中在因子逻辑的构建上。
总结

本节课中我们一起学习了Alphalens工具包。我们了解了它是一个用于因子分析的强大工具,能够简化计算和画图流程;知道了如何安装它以及在哪里可以找到学习资料;并且熟悉了我们将要使用的在线代码编写环境——“投资研究”。在接下来的课程中,我们将在这个环境中,实际运用Alphalens来对我们的因子进行深入分析。
041:获取因子指标数据
在本节课中,我们将学习如何使用量化分析工具包,获取指定时间段内所有股票的特定因子指标数据。我们将从导入工具包开始,逐步完成日期选择、股票池构建、数据查询等步骤。
导入工具包
首先,我们需要导入必要的工具包。utils模块用于数据处理,plot模块用于绘图,其余模块用于后续的分析任务。
from alphalearn.utils import utils
from alphalearn.utils import plot
# 其他用于分析的模块
选择日期数据
上一节我们介绍了工具包的导入,本节中我们来看看如何获取分析所需的时间段数据。与回测框架不同,当前模块没有每日自动执行的handle_bar函数。因此,我们需要手动获取多天的数据。
第一步是选择一个日期范围。我们需要获取从起始日期到结束日期之间的所有交易日数据。
以下是获取交易日期的函数:
def get_trading_date(start_date, end_date):
"""
获取指定时间段内的所有交易日。
参数:
start_date (str): 起始日期,格式如'2019-01-01'
end_date (str): 结束日期,格式如'2020-01-01'
返回:
trading_dates: 交易日列表
"""
trading_dates = ... # 调用API获取日期列表的代码
return trading_dates
我们指定时间段为2019年1月1日至2020年1月1日,并检查获取的日期列表是否正确。
trading_dates = get_trading_date('2019-01-01', '2020-01-01')
构建股票池
获取日期后,下一步是确定我们要分析哪些股票。在本例中,我们选择分析A股市场的所有股票。

以下是构建股票池的代码:
# 获取所有A股股票代码
stock_pool = get_all_securities(['stock'], 'A')

变量stock_pool现在包含了我们当前要分析的所有股票。
查询因子指标数据
有了日期和股票池,现在我们可以查询具体的因子指标数据。我们将以P指标为例进行查询。

以下是构建查询语句的步骤。查询基于fundamentals数据,我们需要指定要查询的指标和过滤条件。


# 初始化查询对象
q = query(fundamentals.income_statement.P)
# 设置过滤条件,仅查询股票池中的股票
q = q.filter(fundamentals.income_statement.code.in_(stock_pool))
这段代码定义了我们的查询语句q,它表示:查询股票池中所有股票的P指标。
执行查询并获取数据


定义好查询语句后,我们需要执行它来获取数据。查询时需要传入具体的日期。
以下是执行查询并获取某一天数据的代码:
# 假设我们查询交易日列表中的第一天
current_date = trading_dates[0]
# 执行查询
factor_data = get_fundamentals(q, current_date)
执行后,factor_data变量中存储了指定日期所有股票的P指标数据。数据是一个三维结构,但通常只有最后一个维度包含有效数据。我们可以查看前几条数据以确认结果。
# 查看前5条数据,通常关注最后一个维度的数据
print(factor_data.iloc[0, 0, :5])
输出显示了股票代码及其对应的指标值。注意,因为查询时只传入了某一天(例如2019年1月2日),所以结果数据中不包含日期列,它代表的是特定日期的截面数据。
扩展到整个时间段
目前我们完成了获取指定单日数据的方法。然而,我们的目标是分析一整段时间的数据。
因此,接下来我们需要写一个循环,遍历trading_dates列表中的每一个日期,重复执行上述查询操作,并将每天的数据整合起来,最终形成一个包含多日、多股票的面板数据集。这将是我们进行因子有效性分析的基础。

本节课中我们一起学习了如何手动获取量化分析所需的因子数据,包括选择日期范围、构建股票池、编写查询语句以及执行查询。关键在于理解数据获取的流程:先确定时间和标的范围,再定义具体的指标查询。掌握这些步骤后,你就可以为后续的因子分析准备所需的数据集了。
042:获取给定区间全部数据
在本节课中,我们将学习如何从一个时间序列数据中,循环获取指定日期区间内每一天的数据,并将这些数据整理成一个结构化的表格。我们将使用循环遍历日期,提取每日数据,并最终将所有数据拼接成一个完整的 DataFrame。


核心思路与准备工作
上一节我们介绍了如何获取单日数据。本节中我们来看看如何获取一个连续时间段内的所有数据。

核心思路是:遍历日期序列中的每一天,分别获取当天的数据,然后将所有日期的数据汇总到一个表格中。

首先,我们需要明确数据源。假设我们有一个日期序列,它代表了我们想要查询的所有交易日。
# 假设 date_series 是一个包含多个日期的序列
# 例如:['2019-01-02', '2019-01-03', ...]
我们的目标是,对于这个序列中的每一个日期,都执行一次数据查询操作。
遍历日期并获取每日数据
以下是实现遍历和获取数据的关键步骤。
我们需要使用一个 for 循环来遍历日期序列。循环的次数就是序列的长度,即总天数。
for i in range(len(date_series)):
# i 代表当前是第几天
current_date = date_series[i]
# 在这里获取第 i 天的数据
在循环体内,我们需要获取第 i 天的实际数据。根据之前的知识,获取数据的函数通常会返回一个多维数组。

# 假设 get_data_by_index(i) 函数能获取第 i 天的数据
daily_data = get_data_by_index(i)
对于返回的数据,我们通常只关心其中一部分。例如,可能是一个三维数组,我们只需要第三个维度的全部数据。
# 提取有用的部分数据,例如第三个维度的所有值
useful_data = daily_data[0, 2, :] # 这是一个示例索引,具体根据数据结构调整

这样,我们就得到了单日的有用数据 useful_data。

构建每日数据表格
仅仅获取数据还不够,我们需要将数据组织起来。我们希望最终的表格包含三列信息:日期、股票名称和指标值。
因此,在每次循环中,我们都需要创建一个临时的 DataFrame 来存放当天的数据。
首先,导入必要的库并创建一个初始的空总表。
import pandas as pd
# 创建一个空的DataFrame作为总表,用于后续拼接
total_df = pd.DataFrame()
然后,在循环内部,为每一天的数据创建一个小表格:
for i in range(len(date_series)):
# ... 获取 useful_data ...
# 创建当天的DataFrame
daily_df = pd.DataFrame(useful_data) # 将数据数组转换为DataFrame
# 为这列数据命名,例如‘factor_value’
daily_df.columns = ['factor_value']
# 添加一列‘date’,记录当前日期
daily_df['date'] = date_series[i]
# 注意:这里还缺少‘stock_name’列,需要根据实际情况从数据中提取或添加

现在,daily_df 就是一个包含两列(‘factor_value’和‘date’)的每日数据表。

拼接所有数据到总表
每次循环都会生成一个 daily_df。我们需要把所有小的 daily_df 都合并到一开始创建的 total_df 中。
这可以通过 pandas 的 concat 函数实现。
在循环内部,每次生成 daily_df 后,就执行一次拼接操作:

# ... 创建 daily_df ...
# 将当天的数据拼接到总表中
total_df = pd.concat([total_df, daily_df], ignore_index=True)

关键点:必须将 concat 的结果重新赋值给 total_df,否则总表的内容不会更新。
这个过程可能会比较慢,特别是当时间区间较长(例如一年)时,因为需要进行多次网络请求或数据计算。
检查最终结果
循环结束后,total_df 就是我们需要的完整数据表。
我们可以查看表格的前几行,以确认数据格式是否正确:

print(total_df.head())

输出应该包含我们指定的列,例如 date 和 factor_value,每一行对应特定日期和股票的数据。

总结
本节课中我们一起学习了如何获取一个时间区间内的全部数据。

- 核心方法是使用
for循环遍历日期序列。 - 在循环内,获取单日数据并提取有用部分。
- 将每日数据构造成一个包含日期和数值的
DataFrame。 - 使用
pd.concat()函数将所有每日的DataFrame逐行拼接成一个总表。 - 最后,检查总表的结构和内容是否符合预期。

这个过程虽然步骤较多,但逻辑清晰,是处理时间序列数据的常用方法。通过这个练习,你掌握了循环获取和整合批量数据的基本技能。
043:数据格式转换
概述
在本节课中,我们将学习如何将数据处理成特定工具包(如 alphalens)所要求的格式。核心任务包括设置多级索引、进行数据格式转换,以及执行必要的预处理步骤(如去除异常值和标准化)。
数据格式要求分析
上一节我们完成了数据的初步处理,但数据格式与目标工具包的要求不符。
目标工具包要求的数据格式是多维结构。例如,有五只股票(A, B, C, D, E)在多个交易日(如2019-01-01, 2019-01-02)的指标数据。其理想结构是:第一层索引为日期(date),第二层索引为股票代码,对应的值是指标数值。

我们当前的数据格式并非如此,因此需要进行转换。

设置多级索引
要将数据转换为目标格式,首先需要重新设置索引。

以下是设置多级索引的核心步骤:
- 将
date列设置为第一层索引。 - 将股票代码(即指标数据的列名)设置为第二层索引。
- 最终的数据值应为单一的指标数值。
对应的关键代码如下:
# 假设 df 是当前的DataFrame,其中包含‘date’列和多列股票指标数据
# 首先,将‘date’列设置为索引
df.set_index('date', inplace=True)

# 然后,将DataFrame从“宽格式”转换为“长格式”,并为股票代码创建第二层索引
# stack() 方法可以实现此功能
formatted_data = df.stack()
执行上述代码后,formatted_data 将成为一个具有多级索引(日期, 股票代码)的Series,其值即为对应的指标数据,这与目标格式要求一致。


数据预处理
数据格式转换完成后,通常还需要进行预处理以保证后续分析的稳健性。以下是两个关键步骤:


1. 去除异常值
去除异常值(去极值)的目的是防止极端值对整体分析产生过大影响。常用方法是使用分位数进行截断。
def winsorize(series):
# 定义上下限,例如使用1%和99%分位数
lower_bound = series.quantile(0.01)
upper_bound = series.quantile(0.99)
# 将超出范围的值截断到边界
return series.clip(lower=lower_bound, upper=upper_bound)
2. 数据标准化
标准化(Z-Score标准化)可以将数据转换为均值为0、标准差为1的分布,便于不同指标间的比较。

def standardize(series):
mean = series.mean()
std = series.std()
return (series - mean) / std

在实际操作中,我们需要对每个交易日的数据分别应用去除异常值和标准化操作,以保证处理是基于横截面数据进行的。


总结
本节课中我们一起学习了为满足特定分析工具包要求而进行的数据格式转换。
- 我们理解了目标格式是多级索引结构(日期, 股票代码)。
- 我们使用
set_index和stack方法成功将数据转换成了所需的格式。 - 最后,我们介绍了数据预处理的两个重要步骤:去除异常值和标准化,并强调了需按日进行横截面处理。

完成这些步骤后,数据就准备好了,可以输入给如 alphalens 这样的工具包进行进一步的因子分析和绩效评估。
044:IC指标值计算
在本节课中,我们将学习如何计算IC(信息系数)指标值。IC值是衡量选股因子预测能力的关键指标,它通过计算因子值与股票未来收益率之间的相关性来评估因子的有效性。我们将从获取数据开始,逐步完成数据格式转换,并最终计算出IC值。
获取收盘价数据
上一节我们介绍了IC值的基本概念,本节中我们来看看如何获取计算所需的基础数据。计算IC值需要两个核心数据:因子值和股票的实际收益率。而计算收益率的基础是股票的每日收盘价。
以下是获取收盘价数据的步骤:
- 使用
get_price函数获取指定股票池在特定时间段内的价格数据。 - 从返回的多维价格数据中,提取出我们需要的“收盘价”这一列,并将其转换为二维的
DataFrame格式。 - 为
DataFrame设置清晰的索引和列名,以便后续处理。
对应的核心代码如下:
# 获取股票池的收盘价数据
price_data = get_price(
stock_pool, # 股票代码列表
start_date='2019-01-01',
end_date='2020-01-01',
fields=['close'] # 指定获取收盘价
)
# 整理数据格式
close_price = price_data['close'] # 提取收盘价序列
close_price.index.name = 'date' # 设置索引名称为‘日期’
close_price.columns.name = 'code' # 设置列名称为‘股票代码’
执行以上代码后,我们将得到一个结构清晰的收盘价数据表,其中行索引为日期,列索引为股票代码,表格内的值为对应的每日收盘价。
数据格式转换
在获得了原始的因子数据和收盘价数据后,我们需要将它们转换为量化分析库所要求的统一格式,以便进行后续的相关性计算。
这个转换过程通常由一个工具函数完成。该函数接收两个主要参数:处理好的因子数据和处理好的价格数据。它会将数据重新组织,并计算出不同持有期(例如1天、5天、10天)的收益率,同时自动根据因子值的大小将股票分成若干组。
以下是转换后数据包含的关键信息:
- date: 交易日期。
- asset: 股票代码。
- 1D/5D/10D: 股票在未来1天、5天、10天的收益率,计算公式为
(未来收盘价 - 当前收盘价) / 当前收盘价。 - factor: 因子当日的具体数值。
- factor_quantile: 因子分组。系统默认将因子值从小到大排序,并等分为5组(例如,0-20%为第1组,20%-40%为第2组,以此类推)。该列的数字表示该股票当日的因子值落在了哪个组,数字越小代表因子值越小。
我们主要关注 1D(一期收益率)和 factor 这两列,它们将用于计算斯皮尔曼秩相关系数。
计算IC值
现在,我们已经拥有了格式规整的、包含因子值和未来收益率的数据。本节中,我们将使用这些数据来计算最终的IC值。
计算IC值本质上是计算因子值与未来收益率之间的秩相关系数。我们可以直接调用量化分析库中现成的性能评估函数来完成此计算。
该函数接收我们上一步转换好的完整数据作为输入,并返回计算好的IC值序列。IC值是一个时间序列,展示了因子在不同日期的预测能力。

from quant_analysis.performance import factor_information_coefficient
# 计算IC值
ic_series = factor_information_coefficient(formatted_data)

计算完成后,ic_series 就是一个 DataFrame,其中包含一列名为 IC 的值,这就是我们需要的IC指标值序列。我们可以通过 ic_series.head() 来查看前几天的IC值。
总结

本节课中我们一起学习了IC指标值的完整计算流程。我们首先获取了基础的股票收盘价数据,然后通过专门的函数将原始因子数据和价格数据转换为包含未来收益率和因子分组的分析格式。最后,我们调用库函数计算了因子值与未来一期收益率之间的秩相关系数,即IC值。这个过程是量化因子研究与评价的基础步骤。
045:工具包绘图展示 📊
在本节课中,我们将学习如何使用 alphalens 工具包中的绘图功能,将计算得到的因子IC值(信息系数)进行可视化展示,以便更直观地分析因子的表现和稳定性。
上一节我们介绍了如何计算因子的IC值,本节中我们来看看如何将这些数值结果转化为图表。
绘制IC时间序列图
计算得到的IC值每日都在变化,仅从数值结果观察趋势不够直观。我们可以使用 alphalens 的绘图工具将其可视化。

以下是绘制IC时间序列图的核心代码:
# 导入绘图工具
from alphalens import plotting

# 绘制IC时间序列图
plotting.plot_ic_ts(ic_data)
执行上述代码后,系统会生成一张图表。默认情况下,图表包含两条线:
- 蓝色线:代表每日的实际IC值走势,波动范围通常较大。
- 绿色线:代表以一个月为周期计算的IC移动平均值,用于观察长期趋势。
此外,图表还会标注出整个分析周期内IC值的均值(Mean) 和标准差(Std) 等统计信息。
解读图表信息
我们主要观察绿色移动平均线。通过该线,我们可以评估因子与收益率的相关性是否具有持续性和趋势性。
理想情况下,我们希望IC值越大越好,并且移动平均线能呈现出稳定或向上的趋势。如果移动平均线长期在零值附近徘徊且波动平缓,则可能意味着该因子与收益率的相关性较弱,预测能力有限。
理解信息比率(IR)
在图表中,我们还会看到一个名为 信息比率(Information Ratio, IR) 的指标。其计算公式为:
IR = IC均值 / IC标准差
这个指标用于衡量因子IC值的稳定性。
- IR值越大,说明IC值的标准差相对均值较小,即因子的表现越稳定。
- IR值越小,则说明因子的表现波动较大。

信息比率是一个辅助指标,帮助我们判断因子收益的稳健程度。

探索其他分析图表
alphalens 工具包提供了丰富的绘图函数,用于多角度分析因子。除了时间序列图,你还可以生成以下图表进行深入分析:
以下是部分可用的分析图表类型:
- IC直方图(Histogram):查看IC值的分布情况。
- QQ图(Q-Q Plot):检验IC值是否服从正态分布。
- 不同调仓周期的IC分析:例如5日、10日周期的IC表现汇总。
- 分位数收益图:展示按因子值分组后,各组的平均收益情况。

这些函数在工具的API文档中均有列出。在实际进行深入的因子策略分析时,这些可视化工具能提供全面的展示。


本节课中我们一起学习了如何使用 alphalens 绘制因子IC分析图。我们掌握了如何解读IC时间序列图及其移动平均线,了解了信息比率(IR)的含义,并认识到工具包内还有其他多种图表可用于全面的因子评估。可视化分析能帮助我们更直观地判断一个因子的有效性和稳定性。
046:因子收益率简介 📈



在本节课中,我们将要学习因子收益率的概念、计算方法及其在因子分析中的应用。因子收益率是评估因子有效性的另一个重要指标,它不同于之前介绍的IC值,而是直接量化因子对收益的贡献程度。
上一节我们介绍了IC值,它衡量的是因子与未来收益率之间的相关性。本节中我们来看看因子收益率,它直接给出了因子对收益的贡献权重。
因子收益率的定义

因子收益率是一个具体的数值,它描述了因子对资产收益率的贡献权重。我们可以通过一个回归模型来理解它。
假设我们有一个回归方程,其中目标变量是资产的收益率(Y),自变量是因子(X)。模型可以表示为:

收益率 = 因子 × 因子收益率 + 偏置项

在这个公式中,我们框起来的权重参数 W 就是因子收益率。它不是一个关系度量,而是一个具体的值。对于每一个因子,我们都可以计算其对应的因子收益率。

因子收益率的作用

因子收益率主要有两个作用。


首先,它可以帮助我们从可视化角度分析因子在何种取值区间内能带来更高的收益。以下是分析步骤:
- 将因子的取值区间进行划分(例如,分为5组)。
- 计算每个因子区间对应的平均收益。
- 通过图表观察,识别出能带来较高平均收益的因子区间。


其次,计算出的因子收益率本身可以作为一个筛选指标。当我们面对数百个因子需要筛选时,因子收益率的高低可以作为参考依据。当然,在实际筛选中,我们通常会综合参考多个指标,例如IC值的均值、标准差以及由此计算出的信息比率等,以全面评估因子的稳定性和预测能力。


代码实现与结果解读


在代码中,我们可以借助 alphalens 工具包方便地计算和可视化因子收益率。其API调用较为简单,可以直接得到分析图表。

执行代码后,通常会得到以下分析结果:
- 因子分组收益图:展示不同因子分组(如5组)在一段时间内的平均收益表现。
- 收益分布对比:展示不同分组收益的分布情况。
- 具体数值:通过
.mean()方法可以计算出该因子在一段时间内的平均因子收益率。这个数值可以用于不同因子之间的横向比较。
alphalens 是一个功能强大的因子分析工具包,它提供了许多便捷的函数,能够帮助我们进行因子数据处理、分析和可视化。本节课我们仅介绍了其基本用法,未来在进行更深入的因子分析时,可以考虑更多地利用这个工具包。

总结



本节课中我们一起学习了因子收益率。我们明确了因子收益率是一个表示因子对收益贡献权重的具体数值,可以通过回归模型求得。我们了解了它的两个主要作用:一是通过可视化识别有效因子区间,二是作为因子筛选的量化指标。最后,我们介绍了如何使用 alphalens 工具包来便捷地计算和展示因子收益率分析结果。
047:因子打分法选股策略概述
在本节课中,我们将要学习一种在量化选股中常用的策略——因子打分法。我们将了解其核心思想、实施前提,并梳理出清晰的实现步骤。
策略核心思想
上一节我们介绍了因子分析的基本概念,本节中我们来看看如何综合运用多个因子进行选股。
因子打分选股的核心思想,是模仿学生考试总分的排名逻辑。我们不再孤立地看待单个因子对股票好坏的影响,而是将多个因子视为不同的“科目”,为每只股票在每个“科目”(因子)上打分,最后计算一个“总分”。根据总分对所有股票进行排名,即可选出综合表现最优的股票组合。
具体而言,假设我们有300只股票(例如沪深300成分股),以及A、B、C、D四个选股因子。我们的目标是每月从中选出排名前10的股票进行调仓。实现方法是:为每只股票在A、B、C、D四个因子上分别计算一个分数,然后加总得到该股票的综合得分。最后,根据综合得分对300只股票进行排序,选取前十名。
核心公式可以表示为:
股票综合得分 = 因子A得分 + 因子B得分 + 因子C得分 + 因子D得分
策略实施前提
在开始为因子打分之前,我们必须明确一个关键的先验知识:每个因子与股票未来收益率的相关性是正向还是负向。

这决定了我们的打分规则。例如:
- 正向因子:因子值越大,预期收益越高。如每股收益、净资产收益率。对于这类因子,数值越大,我们应给予的分数越高。
- 负向因子:因子值越小,预期收益越高。如市盈率、市净率(通常认为越低越好)。对于这类因子,数值越小,我们应给予的分数越高。
这个判断依据可以来源于:
- 金融领域的经典理论或券商的研究报告。
- 我们自己通过历史数据计算因子IC值等指标进行验证。
在后续的实际编程中,我们会预先设定好所选因子的方向性,作为策略的已知条件。
策略步骤总结
以下是因子打分法选股的基本步骤框架:
- 确定因子池与方向:选择用于评价股票的多个因子(如估值、成长、动量等),并明确每个因子与收益的相关方向(正向或负向)。
- 数据准备:获取股票池(如沪深300)所有股票在调仓日的各个因子值。
- 因子标准化:由于不同因子的量纲和范围不同,需要将它们处理到同一尺度上,常用的方法是
z-score标准化或排名归一化。 - 因子打分:根据因子的方向,对标准化后的因子值进行打分。例如,对正向因子,数值越大分数越高;对负向因子,则数值越小分数越高。
- 计算综合得分:将每只股票在所有因子上的得分相加(或加权平均),得到该股票的综合得分。
- 排序与选股:根据综合得分对所有股票进行降序排序,选取排名靠前的N只股票作为当期投资组合。

本节课中我们一起学习了因子打分选股策略的基本逻辑。我们了解到,该策略通过综合多个因子的信息,为股票进行综合评价和排名,从而做出选股决策。实施该策略的关键在于明确每个因子的影响方向,并依此建立合理的打分体系。在接下来的课程中,我们将进入实战环节,在量化平台上一步步实现这个策略。
048:整体任务流程梳理


在本节课中,我们将学习如何为股票因子进行打分,并梳理一个完整的量化选股策略流程。我们将使用一种简单直观的“打分法”,通过多个指标的综合评分来筛选出表现优异的股票。
打分法原理介绍
上一节我们介绍了如何获取和预处理股票因子数据。本节中,我们来看看如何为这些因子打分,从而对股票进行排序和筛选。
打分法的核心思想是:为每个股票在每一个因子上的表现赋予一个分数,然后将所有因子的分数加总,得到该股票的总分。最后,根据总分对所有股票进行排名,选择排名最高的股票。
以下是打分的基本步骤:
- 确定因子方向:首先明确每个因子是“越大越好”还是“越小越好”。例如,盈利能力指标通常越大越好,而负债率指标则越小越好。
- 划分区间并赋值:将每个因子的数值范围划分为若干个区间。对于“越大越好”的因子,数值落入高值区间的股票获得高分;对于“越小越好”的因子,数值落入低值区间的股票获得高分。
- 计算单因子得分:根据每只股票在每个因子上的具体数值,确定其落入的区间,并得到对应的分数。
- 汇总总分:将一只股票在所有因子上的得分相加,得到其综合总分。
- 排序与筛选:根据总分对所有股票进行排序,选择总分最高的若干只股票作为投资组合。
打分法实例演示
为了让大家更清晰地理解,我们通过一个具体的例子来演示打分过程。
假设我们有沪深300指数中的股票,并选取了四个因子:A、B、C、D。其中:
- A因子:越大越好
- B因子:越大越好
- C因子:越小越好
- D因子:越小越好
我们已获得两只股票的归一化因子数据(数值在0到1之间):
| 股票ID | A因子 | B因子 | C因子 | D因子 |
|---|---|---|---|---|
| id1 | 0.71 | 0.28 | 0.39 | 0.11 |
| id2 | 0.45 | 0.17 | 0.81 | 0.02 |
接下来,我们为每个因子的数值划分区间并设定分数。为了处理“越大越好”和“越小越好”两种不同方向的因子,我们设计两套评分标准。
以下是评分区间与分值对照表:
-
对于“越大越好”的因子(如A、B):
- 数值在 [1.0, 0.8) 区间:得 5分
- 数值在 [0.8, 0.6) 区间:得 4分
- 数值在 [0.6, 0.4) 区间:得 3分
- 数值在 [0.4, 0.2) 区间:得 2分
- 数值在 [0.2, 0.0] 区间:得 1分
-
对于“越小越好”的因子(如C、D):
- 数值在 [0.0, 0.2) 区间:得 5分
- 数值在 [0.2, 0.4) 区间:得 4分
- 数值在 [0.4, 0.6) 区间:得 3分
- 数值在 [0.6, 0.8) 区间:得 2分
- 数值在 [0.8, 1.0] 区间:得 1分
现在,我们来为 id1 和 id2 打分。
为 id1 打分:
- A因子 (0.71):属于“越大越好”,落入 [0.6, 0.8) 区间,得 4分。
- B因子 (0.28):属于“越大越好”,落入 [0.2, 0.4) 区间,得 2分。
- C因子 (0.39):属于“越小越好”,落入 [0.2, 0.4) 区间,得 4分。
- D因子 (0.11):属于“越小越好”,落入 [0.0, 0.2) 区间,得 5分。
- 总分:4 + 2 + 4 + 5 = 15分。
为 id2 打分:
- A因子 (0.45):属于“越大越好”,落入 [0.4, 0.6) 区间,得 3分。
- B因子 (0.17):属于“越大越好”,落入 [0.0, 0.2) 区间,得 1分。
- C因子 (0.81):属于“越小越好”,落入 [0.8, 1.0] 区间,得 1分。
- D因子 (0.02):属于“越小越好”,落入 [0.0, 0.2) 区间,得 5分。
- 总分:3 + 1 + 1 + 5 = 10分。
通过比较,id1(15分)的总分高于 id2(10分)。如果我们只选择一只股票,则会选择 id1。将此方法应用于沪深300的所有股票,计算出总分并排序,即可选出排名前十的股票。
补充说明:除了划分区间的方法,也可以直接根据因子值在全体股票中的排名来赋分。例如,对于“越大越好”的因子,排名第1的股票得300分,排名第300的股票得1分。方法多样,核心目标是得到一个可排序的总分。
整体策略流程梳理

理解了打分法之后,我们来看一个完整的月度调仓策略流程。这个流程将指导我们后续的代码实现。

以下是策略的核心步骤:
- 确定股票池:首先,我们需要确定在哪些股票中进行选择。本例中,我们的股票池是 沪深300指数成分股。这限定了我们的选股范围。
- 设置调仓周期:策略需要定期运行以调整持仓。这里我们设定为 每月调仓一次。我们需要设置一个定时器,在每月特定的时间点触发调仓函数。
- 实现调仓函数:这是策略的核心部分,我们将其命名为
rebalance。在此函数中,我们将执行以下操作:- 数据获取与预处理:获取当前时刻股票池中所有股票的因子数据(A, B, C, D)。我们已知A、B因子越大越好,C、D因子越小越好。
- 因子打分:使用上文介绍的“打分法”,为每只股票的每个因子计算得分。
- 计算总分:将每只股票在所有因子上的得分相加,得到综合总分。
- 生成交易清单:根据总分对所有股票进行降序排序,选取排名前10的股票,作为本次调仓的目标持仓。
这个流程结构清晰,从限定范围、定时触发,到核心的数据处理和决策,形成了一个完整的闭环。接下来,我们就可以尝试用代码实现这个策略,并回测其历史表现。
课程总结

本节课中我们一起学习了量化选股中的“打分法”及其应用。
- 我们首先介绍了打分法的基本原理:通过为不同方向的因子设定评分标准,汇总得到股票的综合评分。
- 接着,我们通过一个详细的实例,演示了如何为具体股票数据计算因子得分和总分。
- 最后,我们梳理了一个完整的月度调仓策略流程,从确定股票池、设置调仓频率到实现核心的选股逻辑。

打分法是一种直观、易于理解的多因子综合评估方法,非常适合初学者入门。在接下来的实践中,我们将运用这个方法构建一个简单的策略,并观察其效果。
049:策略初始化与数据读取 📊
在本节课中,我们将学习如何为一个因子打分选股策略进行初始化,并完成核心数据的读取工作。我们将从定义策略的触发条件开始,逐步构建数据查询逻辑,为后续的因子计算与股票排名打下基础。

上一节我们介绍了策略的基本框架,本节中我们来看看如何具体实现策略的初始化和数据获取。

策略初始化
首先,我们需要在新的策略文件中定义触发方法,并指定选股的范围。
def initialize(context):
# 指定选股的大池子为沪深300指数成分股
context.hs300 = index_components('000300.XSHG')
# 初始化一个变量,用于存储后续计算出的股票排名
context.rank_list = None
# 设置按月调仓的定时器
run_monthly(rebalance, 1)
在 initialize 函数中,我们完成了三件事:
- 定义了选股范围
context.hs300,即沪深300指数的成分股。 - 初始化了
context.rank_list变量,用于后续存储评分排名结果。 - 设置了按月运行的调仓函数
rebalance。


定义调仓函数框架

接下来,我们定义调仓函数 rebalance 的框架。这个函数将在每月定时触发,其核心任务是获取评分并执行调仓。

def rebalance(context):
# 调用自定义函数获取评分后的股票列表
stock_list = get_stocks(context)
# 此处预留位置,后续将添加调仓逻辑
# context.rank_list = stock_list
# ... (调仓操作)

rebalance 函数目前主要调用一个名为 get_stocks 的自定义函数来获取待选股票列表。实际的调仓买卖逻辑我们将在后续课程中添加。

核心数据获取函数


以下是 get_stocks 函数的具体实现步骤,它负责查询财务数据并进行初步处理。
步骤一:构建数据查询语句

我们首先构建查询语句(Query),指定需要获取的财务指标。本例中,我们选取了六个常见指标,并将其分为“越高越好”和“越低越好”两类。
def get_stocks(context):
# 构建查询“越高越好”类指标的语句
q_up = query(
fundamentals.eod_derivative_indicator.eps, # 每股收益 (EPS)
fundamentals.financial_indicator.roe, # 净资产收益率 (ROE)
fundamentals.financial_indicator.roa # 总资产收益率 (ROA)
)
# 构建查询“越低越好”类指标的语句
q_down = query(
fundamentals.financial_indicator.debt_to_asset_ratio, # 资产负债率
fundamentals.eod_derivative_indicator.pb_ratio, # 市净率 (PB)
fundamentals.eod_derivative_indicator.market_cap # 总市值
)


步骤二:添加筛选条件并获取数据
在查询语句中,我们需要添加筛选条件,确保只获取属于沪深300成分股的股票数据。然后,调用 get_fundamentals 函数获取数据。

# 为两个查询添加股票池过滤条件
q_up = q_up.filter(fundamentals.stockcode.in_(context.hs300))
q_down = q_down.filter(fundamentals.stockcode.in_(context.hs300))
# 执行查询,获取DataFrame格式的数据
df_up = get_fundamentals(q_up)
df_down = get_fundamentals(q_down)


步骤三:转换数据格式
获取到的 DataFrame 默认索引是日期,列是股票代码。为了方便后续按股票进行因子计算,我们将其转置,使股票代码成为索引。
# 转置DataFrame,使行索引为股票代码,列为财务指标
df_up = df_up.T
df_down = df_down.T
# 此处可以打印数据形状进行验证
# logger.info(f"‘越高越好‘指标数据形状:{df_up.shape}") # 预期为 (300, 3)
# logger.info(f"‘越低越好‘指标数据形状:{df_down.shape}") # 预期为 (300, 3)
return df_up, df_down
经过转置后,我们得到了两个 DataFrame:
df_up: 包含每股收益、净资产收益率、总资产收益率,数值越高代表股票基本面越好。df_down: 包含资产负债率、市净率、总市值,数值越低代表股票基本面越好(或估值越低)。

本节课中我们一起学习了量化策略初始化的关键步骤。我们首先在 initialize 中设置了策略的股票池和调仓频率,然后构建了 rebalance 函数的框架。最后,我们重点实现了 get_stocks 函数,学会了如何:
- 使用
query对象构建复杂的财务数据查询。 - 使用
filter方法对股票范围进行筛选。 - 使用
get_fundamentals函数获取数据。 - 对获取的数据进行转置,使其格式更适合后续的因子计算。


至此,我们已经成功为因子打分策略准备好了所需的基础数据。在下一节课中,我们将基于这些数据,学习如何对各个因子进行标准化处理和综合评分。
050:因子打分与排序



在本节课中,我们将学习如何对筛选出的因子进行排序和打分。我们将遍历包含“越高越好”和“越低越好”两类因子的数据框,为每个因子下的股票进行排名,并根据排名赋予相应的分数,为后续的综合评分做准备。
上一节我们完成了因子的初步筛选,得到了两个数据框。本节中我们来看看如何对这些因子进行排序和打分。
遍历因子与排序
首先,我们需要遍历数据框中的每一个因子。我们的数据框结构是300行(股票)× 3列(因子)。遍历的目标是每一个因子列。
以下是遍历“越高越好”因子数据框并进行排序的步骤:
- 获取当前数据框的所有列名,这些列名就是因子名称。
- 遍历每一个因子列。
- 对当前因子列下的股票数据进行排序。对于“越高越好”的因子,我们按降序排列(数值大的排在前面)。
- 使用
inplace=True参数将排序结果直接应用到原始数据框中,避免额外的赋值操作。

对应的核心代码如下:
# 假设 up_df 是“越高越好”因子的数据框
for factor in up_df.columns:
# 按当前因子列降序排序
up_df.sort_values(by=factor, ascending=False, inplace=True)
为排序结果打分


排序完成后,我们需要为排名赋予分数。为了简化,我们直接根据排名顺序打分:第一名得最高分,最后一名得最低分。由于共有300只股票,最高分设为300分,最低分为1分。


我们将使用NumPy的 linspace 函数来生成一个等差分数序列。这个函数非常方便,可以指定起始值、终止值和元素个数。


以下是 numpy.linspace 函数的基本用法:
import numpy as np
# 生成从1到300的300个等差数字
scores = np.linspace(start=1, stop=300, num=300)

对于“越高越好”的因子,排名第一(因子值最大)的股票应得最高分(300分)。因此,我们使用 np.linspace(300, 1, 300) 来生成一个从300递减到1的分数序列,并将其赋值给排序后的因子列。


对于“越低越好”的因子,排名第一(因子值最小)的股票也应得最高分。因此,我们使用 np.linspace(300, 1, 300) 来生成分数序列(逻辑与“越高越好”因子处理后的情况一致)。

以下是打分的核心代码:
import numpy as np
import pandas as pd

# 为“越高越好”因子打分
for factor in up_df.columns:
up_df.sort_values(by=factor, ascending=False, inplace=True)
# 生成分数并赋值:第一名300分,最后一名1分
up_df[factor] = np.linspace(300, 1, len(up_df))

# 为“越低越好”因子打分
for factor in down_df.columns:
down_df.sort_values(by=factor, ascending=True, inplace=True) # 升序排列,最小值排第一
# 生成分数并赋值:第一名300分,最后一名1分
down_df[factor] = np.linspace(300, 1, len(down_df))
合并数据框
完成所有因子的打分后,我们得到了两个新的数据框(up_df 和 down_df),它们的结构仍然是300行×3列,但单元格内的值已由原始因子值替换为对应的排名分数。
为了计算每只股票在所有因子上的综合得分,我们需要将这两个数据框合并。可以使用Pandas的 concat 函数按列方向进行合并。
合并代码如下:
# 将两个数据框按列合并
combined_df = pd.concat([up_df, down_df], axis=1)
合并后,combined_df 将成为一个300行×6列的数据框,包含了所有因子对每只股票的评分,为下一步计算总分奠定了基础。

本节课中我们一起学习了因子打分与排序的全过程。我们首先遍历因子并对股票排序,然后利用 np.linspace 函数根据排名生成分数,最后将不同类别的因子评分表合并。这为我们后续进行多因子综合评分做好了数据准备。
051:完成选股方法
概述
在本节课中,我们将要学习如何将多个股票因子评分表进行合并,并计算综合得分以完成最终的选股排序。核心操作包括数据拼接、总分计算与排序,最终输出排名前十的股票列表。
数据拼接操作
上一节我们介绍了如何为单个因子数据打分,本节中我们来看看如何将多个因子的评分表合并成一张大表。
拼接操作(Concatenation)的目的是将一个DataFrame与另一个DataFrame连接起来。我们使用Pandas的 concat 函数来实现。
以下是拼接操作的代码:
df_combined = df_down.concat([df_down, df_second], axis=1)
这段代码将第二个DataFrame(df_second)拼接到第一个DataFrame(df_down)的右侧。拼接完成后,我们得到了一个包含所有因子评分的新DataFrame,可以将其命名为 df_rank。
计算总分并排序
现在我们已经有了包含所有因子评分的大表,但表中还没有综合排名信息。接下来,我们需要计算每只股票的总分并进行排序。
首先,在DataFrame中新建一个名为“得分值”的列,并暂时用零填充。
df_rank['得分值'] = np.zeros((300, 1))
这里使用NumPy创建了一个300行1列的零矩阵,对应样本数据的数量。
然后,我们需要计算总分。假设我们有六个指标,总分就是将这六个指标的分数相加。
total_score = df_rank[['指标1', '指标2', '指标3', '指标4', '指标5', '指标6']].sum(axis=1)
df_rank['得分值'] = total_score
代码对横轴(axis=1)进行求和,得到了每只股票的综合得分。
接下来,我们需要根据这个总分进行排序。我们希望找到分数最高的股票,因此采用降序排列。
df_sorted = df_rank.sort_values(by='得分值', ascending=False)
排序完成后,df_sorted 就是按总分从高到低排列的DataFrame。
提取前十名股票
排序之后,我们通常只关心排名最靠前的股票。因此,最后一步是从排序结果中提取出前十名的股票代码。

我们不需要所有列,只需要股票代码(通常作为索引)和得分。以下是提取前十名股票列表的代码:
top_10_stocks = df_sorted['得分值'].head(10).index.tolist()
这段代码首先选取“得分值”列,然后使用 .head(10) 获取前十个得分最高的条目,接着通过 .index 获取对应的股票代码索引,最后用 .tolist() 将其转换为Python列表。



总结
本节课中我们一起学习了量化选股策略中完成选股的关键步骤。

- 数据拼接:将多个因子的评分DataFrame合并为一张综合表。
- 总分计算与排序:对合并后的因子分数进行求和,得到每只股票的总分,并按总分进行降序排序。
- 结果提取:从排序结果中提取出排名前十的股票代码列表。

通过 get_stocks 函数执行以上流程,我们就能获得基于当前日期和给定因子评分排名前十的股票,从而完成一次完整的选股操作。
052:完成策略交易展示结果 📈

在本节课中,我们将继续完成一个量化交易策略的核心函数。我们将学习如何根据新的股票排名,计算需要买入和卖出的股票,并执行相应的交易操作。整个过程将涉及持仓管理、集合运算和订单执行。

计算当前持仓 🧾
上一节我们介绍了如何获取新的股票排名。本节中,我们来看看如何获取当前的投资组合持仓。
首先,我们需要一个函数来获取当前账户中持有的所有股票。以下是该函数的实现步骤:
def get_holdings(context):
# 初始化一个空列表,用于存放当前持有的股票代码
holdings = []
# 遍历投资组合中的所有持仓
for position in context.portfolio.positions:
# 获取该股票的持仓数量
quantity = context.portfolio.positions[position].quantity
# 判断持仓数量是否大于0
if quantity > 0:
# 如果持有,则将股票代码添加到列表中
holdings.append(position)
# 返回当前持有的股票列表
return holdings
计算交易清单 📝
获取了当前持仓和新的股票排名后,我们需要计算具体的交易指令,即需要买入和卖出的股票列表。
以下是计算交易清单的逻辑:
- 计算买入清单 (
to_buy):新的股票排名 (stocks) 减去当前持仓 (holdings)。这代表了排名靠前但我们尚未持有的股票。to_buy = set(stocks) - set(holdings)
- 计算卖出清单 (
to_sell):当前持仓 (holdings) 减去新的股票排名 (stocks)。这代表了我们已经持有但已不在新排名中的股票。to_sell = set(holdings) - set(stocks)
执行交易订单 💹
计算出交易清单后,下一步就是执行实际的买入和卖出操作。通常的策略是先卖出再买入。
以下是执行交易的代码框架:

# 首先,执行卖出操作
for stock in to_sell:
# order_target_percent 函数将指定股票的持仓比例调整为目标值
# 这里设置为0,代表清仓该股票
order_target_percent(stock, 0)
# 然后,执行买入操作
# 检查是否有股票需要买入
if len(to_buy) == 0:
return # 如果没有,则直接返回


# 计算可用于投资的现金总额
total_cash = context.portfolio.cash
# 计算平均每只计划买入的股票应分配的资金
average_cash = total_cash / len(to_buy)

for stock in to_buy:
# 使用 order_target_value 函数,以目标价值的方式买入股票
# 此处按平均资金分配买入
order_target_value(stock, average_cash)


策略流程整合与调试 🔧


现在,我们将以上所有步骤整合到每月调仓的主函数中,并处理可能出现的错误。

整个策略的月度调仓流程如下:

- 获取新的股票排名 (
stocks)。 - 获取当前持仓 (
holdings)。 - 计算需要买入 (
to_buy) 和卖出 (to_sell) 的股票列表。 - 先执行卖出操作。
- 再执行买入操作。



在代码整合过程中,需要注意细节,例如确保使用英文标点、正确处理集合类型数据等。如果遇到报错,应逐步检查:
- 标点符号是否正确(避免中文逗号)。
- 变量类型是否匹配(如对列表使用集合运算前先转换为
set)。 - 函数调用参数是否完整。

总结 🎯

本节课中我们一起学习了如何完成一个量化交易策略的调仓部分。我们掌握了三个关键环节:
- 获取持仓:编写函数遍历投资组合,筛选出当前持有的股票。
- 生成交易信号:通过集合运算,对比新排名与旧持仓,得到明确的买入和卖出清单。
- 执行交易:按照先卖后买的顺序,调用平台订单函数,将交易信号转化为实际的操作。

通过本课的学习,你已经能够构建一个完整的、可自动执行调仓的逻辑闭环。下一步可以在此基础上回测策略,分析其历史表现。
053:策略总结与分析 📊


在本节课中,我们将对一个基于财务指标的打分选股策略进行回测分析。我们将通过调整不同的回测时间段和股票选择范围,来观察策略在不同市场环境下的表现,并总结其有效性与局限性。
策略回测与初步结果

上一节我们介绍了打分策略的构建方法,本节中我们来看看该策略在不同时间段下的具体表现。首先,我们执行了策略回测,代码运行没有报错,表明策略逻辑和数据处理基本正确。



初步观察回测结果,基准收益为负值,而我们的策略收益虽然不高,但成功稳住了净值。策略的最大回撤约为17%,表现尚可。策略相对于基准产生了约67%的超额收益,这在较短的测试时间段内是一个不错的结果。

延长回测时间分析

为了更全面地评估策略,我们需要在更长的时间周期内进行测试。短期回测(如几个月)容易受到市场偶然波动的影响,通常3到5年的回测期更为常见。


我们将回测起始日期调整为2016年1月4日,结束日期为2018年1月4日,进行为期两年的测试。



在这个时间段(2016-2018年)的回测结果显示,策略收益与基准收益相差不大,没有表现出明显的优势。

调整回测时间段

为了探究策略在不同市场阶段的表现,我们进一步调整回测区间。将时间段改为2018年1月4日至2020年1月4日。




以下是该时间段内的回测结果分析:

- 策略逻辑本身没有问题,所有预设指标均已计算并用于选股。
- 基准收益约为0.xx%,而策略回测收益约为2.xx%,效果提升仍不显著。
- 这可能与2018年股市整体处于熊市环境有关,市场系统性风险影响了大部分策略的表现。


优化测试与对比分析

考虑到市场环境影响,我们选择一个理论上市场环境可能更好的时间段进行测试:2016年1月4日至2017年1月4日。



在这个时间段内,策略的回测收益明显高于基准收益,取得了更好的效果。这初步验证了在非极端熊市环境下,该打分策略具备一定的选股能力。

为了进一步验证打分指标的有效性,我们进行了一项对比实验。之前策略是选择综合打分排名前十的股票。现在我们调整策略,改为选择排名最后(最差) 的股票组合。


以下是调整后的回测结果:

- 在2016-2017年的测试中,原策略(选前十)收益约为10.xx%。
- 当选择打分最差的股票时,策略收益比基准收益(约-10%)更差。
- 这个对比实验反向证明了我们的打分指标是有效的:得分高的股票组合表现优于得分低的股票组合。

策略细节与总结

本节课中我们一起学习了如何对一个量化策略进行多角度的回测分析。我们查看了策略的交易详情,确认了其按月调仓的运作频率。我们也分析了持仓和账户信息的变动,例如初始100万本金在投资最差股票组合后最终变为77万。

最后,我们将策略恢复为默认的“选择前十名股票”的模式。该模式基于研报结论,筛选特定指标表现优异的股票,是一种直接且常用的量化选股方法。

以下是关于本策略的总结:

- 策略核心:通过财务指标综合打分,定期(如每月)选择得分最高的股票构建投资组合。
- 代码表示:
selected_stocks = df_scores.nlargest(10, ‘综合得分’) - 有效性:回测表明,该策略在多数情况下能产生超越基准的超额收益,且通过正反对比验证了打分指标的有效性。
- 局限性:策略表现受整体市场环境影响较大,在熊市期间可能失效或表现平庸。这是所有选股策略面临的共同挑战。

打分法因其逻辑直观、实现简单,在量化投资中应用广泛。大家可以通过调整打分指标、权重和回测参数,进一步优化和探索属于自己的策略。
054:聚类分析实例 📊


在本节课中,我们将学习如何在Python中使用聚类分析方法来构建一个简单的量化交易策略。我们将使用K-Means算法对股票数据进行聚类,并根据聚类结果来模拟交易决策。

第一步:导入工具包

首先,我们需要导入所有必要的Python库。这些工具包与之前课程中使用的保持一致。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

第二步:导入并准备数据
上一节我们导入了工具包,本节中我们来看看如何准备数据。我们将导入数据集,并选择用于建模的两个特征指标。

以下是数据准备步骤:
- 导入数据集。
- 选择“前一天指标”和“前两天指标”作为建模的特征。


# 假设数据已加载到变量 `data` 中
features = data[['feature_1', 'feature_2']] # 替换为实际的列名

第三步:建立K-Means聚类模型
有了特征数据之后,我们就可以开始建模了。这次的任务与之前的回归分析不同,我们将使用聚类方法。
在scikit-learn库中,我们选择KMeans模块。K-Means算法的核心思想是将数据点划分为K个簇,使得每个点都属于离它最近的簇中心(质心)所在的簇。其目标是最小化所有数据点到其所属簇中心的距离平方和。

公式:J = Σ Σ ||x_i - μ_j||^2,其中x_i是数据点,μ_j是第j个簇的中心。

以下是建立模型的步骤:
- 从
sklearn.cluster导入KMeans。 - 实例化模型,并指定簇的数量(
n_clusters)。在本例中,我们希望将数据分为“涨”和“跌”两类,因此K=2。 - 使用
.fit()方法在特征数据上训练模型。

# 实例化K-Means模型,设定簇数为2
model = KMeans(n_clusters=2, random_state=42)
# 在特征数据上训练模型
model.fit(features)
模型训练完成后,会显示一些参数,如初始化方法、最大迭代次数和模型重复运行次数。这些参数的默认值通常足够使用,如需详细了解可以查阅官方文档。
第四步:预测并处理聚类结果
模型建立后,我们需要用它来预测每个数据点属于哪个簇。

以下是预测与结果处理的步骤:
- 使用
.predict()方法获取每个样本的簇标签(0或1)。 - 将预测结果添加到原始数据框中。
- 为了方便理解,我们可以将标签0和1映射为更直观的“-1”(跌)和“1”(涨)。

# 预测每个数据点所属的簇
cluster_labels = model.predict(features)
# 将预测结果添加到数据中
data['cluster'] = cluster_labels
# 将簇标签映射为交易信号:假设0对应跌(-1),1对应涨(1)
data['signal'] = data['cluster'].map({0: -1, 1: 1})


第五步:可视化聚类结果


为了直观地查看聚类效果,我们可以将数据点用散点图表示,并用不同颜色区分不同的簇。

以下是绘制散点图的代码:
- X轴和Y轴分别代表我们选定的两个特征。
- 点的颜色(
c参数)根据其所属的簇(data[‘signal’])来决定。

plt.figure(figsize=(8, 6))
plt.scatter(features.iloc[:, 0], features.iloc[:, 1], c=data['signal'], cmap='coolwarm', alpha=0.6)
plt.xlabel('Feature 1 (e.g., Previous Day Return)')
plt.ylabel('Feature 2 (e.g., Two Days Ago Return)')
plt.title('K-Means Clustering Results')
plt.colorbar(label='Cluster / Signal')
plt.show()
从图中可以观察两个簇的分布情况。例如,可能一个簇集中在两个特征都为负值的区域(代表连续下跌趋势),另一个簇则分布在其他区域。

第六步:评估策略效果


现在,我们来评估基于聚类结果的简单交易策略是否有效。基本思路是:当预测信号为“涨”(1)时持有或买入,为“跌”(-1)时卖出或做空(本例简化处理为乘以收益率)。

以下是计算策略收益的步骤:
- 计算每日的原始收益率(
returns)。 - 根据聚类信号(
signal)计算策略收益率:strategy_returns = signal * returns。 - 计算策略的累计收益,并与买入持有的基准收益进行对比。


# 假设‘returns’是股票的实际日收益率列
data['strategy_returns'] = data['signal'] * data['returns']
# 计算累计收益
data['cumulative_returns'] = (1 + data['returns']).cumprod()
data['cumulative_strategy_returns'] = (1 + data['strategy_returns']).cumprod()


# 打印最终收益对比
print(f"基准累计收益: {data['cumulative_returns'].iloc[-1]:.4f}")
print(f"聚类策略累计收益: {data['cumulative_strategy_returns'].iloc[-1]:.4f}")

注意:由于K-Means聚类具有随机性(质心初始化的随机性),每次运行的结果可能略有不同。因此,在实际应用中需要多次运行或设置random_state以确保结果可复现。此外,聚类分析是一种无监督学习方法,缺乏明确的标签来进行严格的模型评估,其生成的交易信号的可靠性需要谨慎判断。


总结

本节课中我们一起学习了如何在量化分析中应用K-Means聚类。
- 我们回顾了从导入库、准备数据到建立聚类模型的完整流程。
- 我们使用
KMeans算法将数据点分为两类,并将其解读为交易信号。 - 我们通过可视化直观展示了聚类结果,并计算了基于此的简单策略收益。


需要强调的是,聚类分析在量化交易中通常不作为核心策略方法,因为它缺乏可解释性和稳定的预测能力。本节课的目的在于让大家熟悉聚类在Python中的实现过程,理解其基本思想。在实际复杂的市场环境中,需要结合更多监督学习方法和严谨的回测框架来开发稳健的策略。
055:统计分析所需数据准备 📊
在本节课中,我们将学习如何为一种简单的统计分析策略准备数据。我们将基于股票价格的历史数据,统计“前两天”和“前一天”的涨跌情况,并观察“当天”实际涨跌的概率分布。我们的目标是使用Python和Pandas库,将原始数据处理成可用于统计分析的格式。
数据准备思路 💡

上一节我们介绍了特征工程的概念。本节中,我们来看看如何为统计分析准备数据。核心思路是:我们想统计在“前两天”和“前一天”出现特定涨跌组合(例如,连续两天都跌)时,“当天”出现上涨、下跌或不变的概率。这需要我们将连续的价格变化数据,转换为代表“涨”或“跌”的类别标签。


以下是实现此目标的关键步骤:
- 数据转换:将代表价格变化的数值列(如“L1_Close”表示前一天收盘价变化)转换为二元的“涨/跌”标签(例如,用1表示上涨,0表示下跌或不变)。
- 数据整合:将转换后的“前两天”和“前一天”标签,与实际“当天”的涨跌方向(
direction列)组合在一起。 - 分组统计:使用分组操作,统计所有可能的“前两天+前一天+当天”组合出现的次数。
第一步:创建涨跌标签函数 ⚙️



首先,我们需要一个函数来将价格变化数据转换为涨跌标签。我们将创建一个名为 create_bins 的函数。

def create_bins(data, bins=[0]):
global col_names
col_names = []
for col in data.columns:
if col.startswith('L'):
name = f"{col}_B"
data[name] = np.digitize(data[col], bins=bins)
col_names.append(name)

代码解释:
np.digitize(data[col], bins=[0]):这是一个将数据分箱(离散化)的函数。bins=[0]表示以0为分界点。如果data[col]的值大于0,则返回1(表示“涨”);如果小于或等于0,则返回0(表示“跌”或“平”)。- 我们遍历所有以
‘L’开头的列(即我们之前创建的特征列,如L1_Close,L2_Close),为它们创建对应的标签列,新列名在原列名后加‘_B’。 col_names列表用于记录所有新创建的标签列的名称,方便后续使用。
执行这个函数:
create_bins(data)

现在,查看一下处理后的数据:
print(data[['L1_Close_B', 'L2_Close_B']].head())
输出将显示两列由0和1组成的数据,1代表对应周期价格上涨,0代表价格未上涨(下跌或持平)。

第二步:整合目标数据 🎯


我们的原始数据中已经存在一列 direction,它直接记录了“当天”实际的涨跌情况(通常1为涨,-1为跌,0为平)。现在,我们需要将“前两天的标签”、“前一天的标签”和“当天的实际方向”放在一起,以便进行分组统计。

我们已有的数据列是:
L2_Close_B:代表“前两天”的涨跌标签。L1_Close_B:代表“前一天”的涨跌标签。direction:代表“当天”的实际涨跌方向。

第三步:执行分组统计 📈


上一节我们准备好了标签数据,本节中我们来看看如何进行分组统计。我们将使用Pandas的 groupby 方法,按照“前两天标签”、“前一天标签”和“当天实际方向”进行分组,并计算每个分组的大小(即出现的次数)。


以下是执行分组统计的代码:
grouped = data[['L2_Close_B', 'L1_Close_B', 'direction']].groupby(['L2_Close_B', 'L1_Close_B', 'direction'])
result = grouped.size()
print(result)



代码解释:
data[[‘L2_Close_B‘, ‘L1_Close_B‘, ‘direction‘]]:从数据中选取我们需要的三列。.groupby([‘L2_Close_B‘, ‘L1_Close_B‘, ‘direction‘]):按照这三列进行分组。这意味着所有在这三列上值完全相同的行会被分到同一个组里。.size():计算每个分组中的行数,即该特定情况在历史中出现的次数。

执行后,result 是一个Series对象,其索引是一个多层索引(L2_Close_B, L1_Close_B, direction),值是对应的出现次数。例如,索引 (0, 1, 1) 表示“前两天跌(0),前一天涨(1),当天实际涨(1)”这种情况在历史上发生了多少次。

总结 🏁

本节课中我们一起学习了为统计分析策略准备数据的过程。我们首先定义了一个函数,将价格变化特征转换为二元的“涨/跌”标签。然后,我们整合了“前两天”、“前一天”的标签以及“当天”的实际方向数据。最后,我们使用 groupby 方法对历史数据进行了分组统计,得到了在各种前期市场情况下,后续涨跌分布的实际计数。这些统计结果是构建简单概率交易模型的基础。在接下来的课程中,我们可以基于这些统计结果来计算条件概率,并形成交易信号。
056:统计效果展示 📊

在本节课中,我们将学习如何对股票数据进行统计分析,并基于统计结果构建一个简单的交易策略。我们将使用 groupby 操作来统计不同特征组合下的结果分布,并根据出现频率最高的结果来预测未来走势。
数据准备与变换
上一节我们介绍了如何获取和处理数据,本节中我们来看看如何为统计分析准备数据。当前数据中,特征(如前一日涨跌、前两日涨跌)和结果(当日涨跌)是组合在一起的。我们需要对结果列进行变换,以便进行分组统计。

具体操作是使用 unstack 方法,将结果数据转换为更清晰的格式。对于缺失值,我们将其填充为0,确保所有可能性都被展示出来。
# 对结果列进行 unstack 操作并填充缺失值
result_unstacked = data['result_column'].unstack(fill_value=0)
执行此操作后,我们得到一个清晰的表格,其中行和列代表不同的特征组合,单元格内的值代表在该特征组合下,结果为“涨”或“跌”的次数。这使我们能够直观地看到不同特征对结果的影响。
执行分组统计
以下是进行分组统计的核心步骤:
- 选择特征列:我们关注“前一日涨跌”和“前两日涨跌”这两列作为特征。
- 计算特征组合:我们检查这两个特征是否都等于1(即“涨”)。
- 分组与统计:使用
groupby方法,按照特征组合对数据进行分组,并统计每个组内“涨”和“跌”的结果数量。
# 定义特征组合条件:前一日和前两日都上涨
condition = (data[‘feature1’] == 1) & (data[‘feature2’] == 1)
# 根据条件和其他特征组合进行分组统计
grouped_stats = data.groupby([condition, data[‘feature1’], data[‘feature2’]])[‘direction’].value_counts().unstack(fill_value=0)
通过观察统计结果,我们可以发现一种模式:当“前一日涨”且“前两日涨”时,第三日“跌”的可能性似乎更大;而在其他特征组合下,“涨”的可能性更高。
构建统计策略
基于上述统计发现,我们可以构建一个简单的预测规则。这个规则纯粹基于我们观察到的频率。
import numpy as np
# 构建预测列:如果前两日都涨,则预测今日跌(-1),否则预测涨(1)
data[‘prediction’] = np.where((data[‘feature1’] == 1) & (data[‘feature2’] == 1), -1, 1)
这个策略的逻辑是:在大多数情况下跟随“涨”的趋势,但在连续上涨两天后,转而预测回调(跌)。
策略效果评估
策略构建完成后,我们需要评估其有效性。我们将预测结果与实际走势进行比较。
# 计算预测正确的天数
correct_predictions = (data[‘prediction’] == data[‘direction’]).value_counts()
print(correct_predictions)
在我们的示例中,预测正确的天数略多于错误的天数,表明这个简单的统计策略比随机猜测或单纯跟随大盘略有优势。
最后,我们计算按照此策略进行交易的理论累计收益,并与单纯跟随大盘走势的收益进行对比。
# 计算策略收益:预测信号 * 实际收益率
strategy_returns = data[‘prediction’] * data[‘actual_returns’]
cumulative_strategy_return = (1 + strategy_returns).cumprod()
# 计算大盘收益
cumulative_market_return = (1 + data[‘actual_returns’]).cumprod()
# 比较最终收益
final_return_comparison = {
“统计策略”: cumulative_strategy_return.iloc[-1],
“大盘走势”: cumulative_market_return.iloc[-1]
}
print(final_return_comparison)
结果显示,基于此统计策略的亏损小于单纯跟随大盘的亏损,虽然两者最终都未能盈利,但统计方法在一定程度上减少了损失。
总结
本节课中我们一起学习了如何利用 groupby 进行数据统计分析,并基于统计频率构建了一个简单的量化交易策略。我们经历了数据变换、分组统计、策略构建和效果评估的全过程。
需要强调的是,这种基于简单频率统计的策略并不十分可靠,它受限于历史数据的特定模式和偶然性。本节课的主要目的是解释此类策略的基本思路、实现方法以及如何评估其表现。在实际应用中,需要更复杂的模型和严谨的回测来验证策略的有效性。

浙公网安备 33010602011771号