14、用回归算法进行股票价格预测

用回归算法进行股票价格预测

1. 股票市场与股票价格概述

股票代表着对公司的所有权。例如,若一家公司共有 1000 股流通股,某投资者持有 50 股,那么该投资者就拥有公司 5% 的资产和收益权益。股票可通过证券交易所和相关组织在股东及其他方之间进行交易,像纽约证券交易所、纳斯达克、伦敦证券交易所集团、上海证券交易所和香港证券交易所等都是主要的证券交易所。

股票价格的波动本质上是由供求关系决定的。在任何时刻,供给是公众投资者手中持有的股票数量,需求则是投资者想要购买的股票数量,股价会上下波动以达到并维持平衡。一般来说,投资者都希望低买高卖,但判断股票涨跌极具挑战性。目前主要有两种研究方向来探究价格变化因素甚至预测未来股价:
- 基本面分析 :关注影响公司价值和业务的潜在因素,从宏观角度涵盖整体经济和行业状况,从微观角度涉及公司财务状况、管理水平和竞争对手等。
- 技术分析 :通过对过去交易活动(包括价格走势、成交量和市场数据)进行统计研究来预测未来价格走势。如今,利用机器学习技术预测股价是技术分析中的一个重要课题,许多量化交易公司已开始运用机器学习来推动自动化和算法交易。

2. 什么是回归

回归是机器学习中有监督学习的另一种主要形式。给定包含观测值及其相关连续输出的训练数据集,回归的目标是探索观测值(也称为特征)与目标之间的关系,并根据未知样本的输入特征输出一个连续值。

回归与分类的主要区别在于,回归的输出是连续的,而分类的输出是离散的。这导致了这两种有监督学习方法的应用领域不同。分类主要用于确定期望的成员资格或特征,如判断邮件是否为垃圾邮件、新闻主题分类以及广告是否被点击等;回归则主要涉及估计结果或预测响应。

机器学习回归的示例包括:
- 根据位置、平方英尺数以及卧室和浴室数量预测房价。
- 根据系统进程和内存信息估计电力消耗。
- 零售库存预测。
- 当然还有股票价格预测。

3. 用回归算法预测股票价格

理论上,我们可以运用回归技术来预测特定股票的价格。但要确保所选股票适合用于学习并非易事,其价格应遵循一定的可学习模式,且不受意外事件或不规则情况的影响。因此,我们将聚焦于最受欢迎的股票指数之一,以更好地阐述和推广价格回归方法。

股票指数是衡量整体股票市场一部分价值的统计指标,它包含多只股票,这些股票具有足够的多样性以代表整个市场的一个部分,指数的价格通常是所选股票价格的加权平均值。

道琼斯工业平均指数(DJIA)是全球历史最悠久、最受关注的指数之一,它由美国 30 家最重要的股票组成,如微软、苹果、通用电气和华特迪士尼公司等,约占整个美国市场价值的四分之一。我们可以在雅虎财经(https://finance.yahoo.com/quote/%5EDJI/history?p=%5EDJI)查看其每日价格和表现。

在每个交易日,股票价格实时变化,以下五个值是关键的交易指标:
|指标|含义|
| ---- | ---- |
|开盘价|给定交易日的起始价格|
|收盘价|当日的最终价格|
|最高价|当日股票交易的最高价格|
|最低价|当日股票交易的最低价格|
|成交量|市场收盘前当日交易的股票总数量|

除了 DJIA,其他主要指数还包括:
- 标准普尔 500 指数(S&P 500) :由美国 500 只最常交易的股票组成,代表了整个美国市场价值的 80%(https://finance.yahoo.com/quote/%5EGSPC/history?p=%5EGSPC)。
- 纳斯达克综合指数 :包含在纳斯达克交易的所有股票(https://finance.yahoo.com/quote/%5EIXIC/history?p=%5EIXIC)。
- 罗素 2000 指数 :由美国 3000 家最大的公开交易公司中的最后 2000 家组成(https://finance.yahoo.com/quote/%5ERUT/history?p=%5ERUT)。
- 伦敦富时 100 指数 :由在伦敦证券交易所上市的市值最高的 100 家公司组成。

我们将重点关注 DJIA,并利用其历史价格和表现来预测未来价格。接下来,我们将探讨如何开发价格预测模型(特别是回归模型)以及可以用作指标/特征的因素。

4. 特征工程

在使用机器学习算法时,通常首先要考虑的问题是有哪些可用特征或预测变量。用于预测 DJIA 未来收盘价的驱动因素显然包括历史和当前的开盘价以及历史表现(最高价、最低价和成交量)。需要注意的是,不能包含当前或当日的表现(最高价、最低价和成交量),因为我们无法提前预知当日股票交易的最高和最低价格以及市场收盘前的总成交量。

仅使用这四个指标来预测收盘价可能效果不佳,还可能导致欠拟合。因此,我们需要想办法添加更多特征以增强预测能力。在机器学习中,特征工程是基于现有特征创建特定领域特征以提高机器学习算法性能的过程。这需要足够的领域知识,并且可能非常困难和耗时。实际上,解决机器学习问题所使用的特征通常并非直接可用,需要专门设计和构建,例如垃圾邮件检测和新闻分类中的词频或 tf - idf 特征。所以,特征工程在机器学习中至关重要,也是解决实际问题时通常需要投入最多精力的部分。

在做出投资决策时,投资者通常会关注一段时间内的历史价格,而不仅仅是前一天的价格。因此,在股票价格预测中,我们可以计算过去一周(五天)、过去一个月和过去一年的平均收盘价作为三个新特征,还可以自定义时间窗口大小,如过去一个季度、过去六个月等。除了这三个平均价格特征,我们还可以通过计算三个不同时间框架内每对平均价格之间的比率来生成与价格趋势相关的新特征,例如过去一周平均价格与过去一年平均价格的比率。

除了价格,成交量也是投资者分析的另一个重要因素。同样,我们可以通过计算多个不同时间框架内的平均成交量以及每对平均值之间的比率来生成基于成交量的新特征。

除了时间窗口内的历史平均值,投资者还非常关注股票的波动性。波动性描述了给定股票或指数价格随时间的变化程度,从统计学角度来看,它基本上是收盘价的标准差。我们可以通过计算特定时间框架内收盘价的标准差以及交易成交量的标准差来轻松生成新的特征集。同样,每对标准差之间的比率也可以纳入我们的特征库。

最后,回报率是投资者密切关注的一个重要财务指标。回报率是指股票或指数在特定时期内收盘价的涨跌百分比,例如我们经常听到的日回报率和年回报率,其计算公式如下:
[Return = \frac{P_i - P_{i - 1}}{P_{i - 1}}]
其中 (P_i) 是第 (i) 天的价格,(P_{i - 1}) 是前一天的价格。周回报率和月回报率的计算方法类似。基于日回报率,我们可以计算特定天数的移动平均值,例如,给定过去一周的日回报率 (r_1, r_2, r_3, r_4, r_5),该周的移动平均值计算如下:
[Moving\ Average = \frac{r_1 + r_2 + r_3 + r_4 + r_5}{5}]

综上所述,通过特征工程技术,我们可以生成以下预测变量:
- 平均价格相关
- 过去五天的平均收盘价
- 过去一个月的平均收盘价
- 过去一年的平均收盘价
- 过去一周平均价格与过去一个月平均价格的比率
- 过去一周平均价格与过去一年平均价格的比率
- 过去一个月平均价格与过去一年平均价格的比率
- 平均成交量相关
- 过去五天的平均成交量
- 过去一个月的平均成交量
- 过去一年的平均成交量
- 过去一周平均成交量与过去一个月平均成交量的比率
- 过去一周平均成交量与过去一年平均成交量的比率
- 过去一个月平均成交量与过去一年平均成交量的比率
- 标准差相关
- 过去五天收盘价的标准差
- 过去一个月收盘价的标准差
- 过去一年收盘价的标准差
- 过去一周收盘价标准差与过去一个月收盘价标准差的比率
- 过去一周收盘价标准差与过去一年收盘价标准差的比率
- 过去一个月收盘价标准差与过去一年收盘价标准差的比率
- 过去五天成交量的标准差
- 过去一个月成交量的标准差
- 过去一年成交量的标准差
- 过去一周成交量标准差与过去一个月成交量标准差的比率
- 过去一周成交量标准差与过去一年成交量标准差的比率
- 过去一个月成交量标准差与过去一年成交量标准差的比率
- 回报率相关
- 前一天的日回报率
- 过去一周的周回报率
- 过去一个月的月回报率
- 过去一年的年回报率
- 过去一周日回报率的移动平均值
- 过去一个月日回报率的移动平均值
- 过去一年日回报率的移动平均值

最终,我们总共可以生成 31 组特征,再加上六个原始特征:
- 开盘价
- 前一天的开盘价
- 前一天的收盘价
- 前一天的最高价
- 前一天的最低价
- 前一天的成交量

4. 数据获取和特征生成

为了便于参考,我们在此实现生成特征的代码,而不是在后续章节中进行。首先,我们要获取项目所需的数据集。

在整个项目中,我们通过 Quandl Python API(https://www.quandl.com/tools/python)获取股票指数价格和表现数据。Quandl(www.quandl.com)提供一些免费的金融、经济和股票市场数据。Python 包是免费的,可以通过命令行 pip install quandl 在终端或 shell 中下载和安装,导入方式如下:

import quandl

我们可以使用 get 方法,通过股票/指数符号(也称为代码)以及指定的开始和结束日期来加载特定股票的价格和表现,例如:

mydata = quandl.get("YAHOO/INDEX_DJI", start_date="2005-12-01", end_date="2005-12-05")
print(mydata)

输出结果是一个 pandas 数据框对象, Date 列是索引列,其余列是相应的金融变量。pandas(http://pandas.pydata.org/)是一个强大的 Python 包,旨在简化对关系型或表格型数据的分析,可以通过命令行 pip install pandas 进行安装。

在开始特征生成之前,还有一点需要注意:建议注册一个免费的 Quandl 账户,并在数据查询中包含自己的认证令牌(该令牌可以在账户中找到),否则每天的数据调用次数不能超过 50 次。将这些整合到一个从 Quandl 获取数据的函数中:

authtoken = 'XXX'
def get_data_quandl(symbol, start_date, end_date):
    data = quandl.get(symbol, start_date=start_date, end_date=end_date, authtoken=authtoken)
    return data

接下来,我们实现特征生成函数:

import pandas as pd

def generate_features(df):
    """ Generate features for a stock/index based on
        historical price and performance
    Args:
        df (dataframe with columns "Open", "Close", "High",
                "Low", "Volume", "Adjusted Close")
    Returns:
        dataframe, data set with new features
    """
    df_new = pd.DataFrame()
    # 6 original features
    df_new['open'] = df['Open']
    df_new['open_1'] = df['Open'].shift(1)
    # Shift index by 1, in order to take the value of previous
    # day. For example, [1, 3, 4, 2] -> [N/A, 1, 3, 4]
    df_new['close_1'] = df['Close'].shift(1)
    df_new['high_1'] = df['High'].shift(1)
    df_new['low_1'] = df['Low'].shift(1)
    df_new['volume_1'] = df['Volume'].shift(1)
    # 31 original features
    # average price
    df_new['avg_price_5'] = pd.rolling_mean(df['Close'], window=5).shift(1)
    # rolling_mean calculates the moving average given a
    # window. For example, [1, 2, 1, 4, 3, 2, 1, 4]
    # -> [N/A, N/A, N/A, N/A, 2.2, 2.4, 2.2, 2.8]
    df_new['avg_price_30'] = pd.rolling_mean(df['Close'], window=21).shift(1)
    df_new['avg_price_365'] = pd.rolling_mean(df['Close'], window=252).shift(1)
    df_new['ratio_avg_price_5_30'] = df_new['avg_price_5'] / df_new['avg_price_30']
    df_new['ratio_avg_price_5_365'] = df_new['avg_price_5'] / df_new['avg_price_365']
    df_new['ratio_avg_price_30_365'] = df_new['avg_price_30'] / df_new['avg_price_365']
    # average volume
    df_new['avg_volume_5'] = pd.rolling_mean(df['Volume'], window=5).shift(1)
    df_new['avg_volume_30'] = pd.rolling_mean(df['Volume'], window=21).shift(1)
    df_new['avg_volume_365'] = pd.rolling_mean(df['Volume'], window=252).shift(1)
    df_new['ratio_avg_volume_5_30'] = df_new['avg_volume_5'] / df_new['avg_volume_30']
    df_new['ratio_avg_volume_5_365'] = df_new['avg_volume_5'] / df_new['avg_volume_365']
    df_new['ratio_avg_volume_30_365'] = df_new['avg_volume_30'] / df_new['avg_volume_365']
    # standard deviation of prices
    df_new['std_price_5'] = pd.rolling_std(df['Close'], window=5).shift(1)
    # rolling_mean calculates the moving standard deviation
    # given a window
    df_new['std_price_30'] = pd.rolling_std(df['Close'], window=21).shift(1)
    df_new['std_price_365'] = pd.rolling_std(df['Close'], window=252).shift(1)
    df_new['ratio_std_price_5_30'] = df_new['std_price_5'] / df_new['std_price_30']
    df_new['ratio_std_price_5_365'] = df_new['std_price_5'] / df_new['std_price_365']
    df_new['ratio_std_price_30_365'] = df_new['std_price_30'] / df_new['std_price_365']
    # standard deviation of volumes
    df_new['std_volume_5'] = pd.rolling_std(df['Volume'], window=5).shift(1)
    df_new['std_volume_30'] = pd.rolling_std(df['Volume'], window=21).shift(1)
    df_new['std_volume_365'] = pd.rolling_std(df['Volume'], window=252).shift(1)
    df_new['ratio_std_volume_5_30'] = df_new['std_volume_5'] / df_new['std_volume_30']
    df_new['ratio_std_volume_5_365'] = df_new['std_volume_5'] / df_new['std_volume_365']
    df_new['ratio_std_volume_30_365'] = df_new['std_volume_30'] / df_new['std_volume_365']
    # return 
    df_new['return_1'] = ((df['Close'] - df['Close'].shift(1)) / df['Close'].shift(1)).shift(1)
    df_new['return_5'] = ((df['Close'] - df['Close'].shift(5)) / df['Close'].shift(5)).shift(1)
    df_new['return_30'] = ((df['Close'] - df['Close'].shift(21)) / df['Close'].shift(21)).shift(1)
    df_new['return_365'] = ((df['Close'] - df['Close'].shift(252)) / df['Close'].shift(252)).shift(1)
    df_new['moving_avg_5'] = pd.rolling_mean(df_new['return_1'], window=5)
    df_new['moving_avg_30'] = pd.rolling_mean(df_new['return_1'], window=21)
    df_new['moving_avg_365'] = pd.rolling_mean(df_new['return_1'], window=252)
    # the target
    df_new['close'] = df['Close']
    df_new = df_new.dropna(axis=0)
    # This will drop rows with any N/A value, which is by-
    # product of moving average/std.
    return df_new

需要注意的是,这里的时间窗口大小分别为 5、21 和 252,而不是代表每周、每月和每年的 7、30、365,这是因为一年大约有 252 个交易日,一个月大约有 21 个交易日,一周有 5 个交易日。

我们可以将这种特征工程策略应用于 2001 年至 2014 年查询的 DJIA 数据:

symbol = 'YAHOO/INDEX_DJI'
start = '2001-01-01'
end = '2014-12-31'
data_raw = get_data_quandl(symbol, start, end)
data = generate_features(data_raw)

让我们看看带有新特征的数据是什么样的:

print(data.round(decimals=3).head(3))

用回归算法进行股票价格预测

5. 线性回归
5.1 什么是线性回归

线性回归是一种基本且广泛应用的回归算法。它假设特征与目标之间存在线性关系,即可以用一个线性方程来描述它们之间的关系。在股票价格预测中,我们尝试找到一个线性函数,使得根据输入的特征(如前面生成的各种价格、成交量相关特征)能够尽可能准确地预测股票的收盘价。

5.2 线性回归的原理

线性回归的目标是找到一组系数,使得预测值与实际值之间的误差最小。通常使用最小二乘法来求解这些系数。最小二乘法的原理是使预测值与实际值之间的误差平方和最小。假设我们有 (n) 个样本,每个样本有 (m) 个特征,线性回归模型可以表示为:
[y = \theta_0 + \theta_1x_1 + \theta_2x_2 + \cdots + \theta_mx_m + \epsilon]
其中 (y) 是目标值(如收盘价),(x_1, x_2, \cdots, x_m) 是特征值,(\theta_0, \theta_1, \cdots, \theta_m) 是待求解的系数,(\epsilon) 是误差项。通过最小化误差平方和 (\sum_{i = 1}^{n}(y_i - \hat{y}_i)^2)(其中 (y_i) 是实际值,(\hat{y}_i) 是预测值),可以得到最优的系数 (\theta)。

5.3 线性回归的实现

在 Python 中,可以使用 scikit - learn 库来实现线性回归。以下是一个简单的示例代码:

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import pandas as pd

# 假设 data 是前面生成的带有特征和目标值的数据
X = data.drop('close', axis = 1)
y = data['close']

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)

# 创建线性回归模型
model = LinearRegression()

# 训练模型
model.fit(X_train, y_train)

# 进行预测
y_pred = model.predict(X_test)
6. 决策树回归
6.1 什么是决策树回归

决策树回归是一种基于树结构进行决策的回归算法。它通过对特征空间进行递归划分,将其划分为多个区域,每个区域对应一个预测值。在股票价格预测中,决策树会根据不同的特征条件进行分支,最终到达一个叶子节点,该叶子节点的值就是预测的股票价格。

6.2 决策树回归的原理

决策树的构建过程是一个递归的过程。在每个节点上,选择一个最优的特征和划分点,将数据集划分为两个子集,使得划分后的子集在目标值上的纯度尽可能高(即方差尽可能小)。重复这个过程,直到满足停止条件(如叶子节点的样本数小于某个阈值)。

6.3 决策树回归的实现

同样可以使用 scikit - learn 库来实现决策树回归,示例代码如下:

from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
import pandas as pd

# 假设 data 是前面生成的带有特征和目标值的数据
X = data.drop('close', axis = 1)
y = data['close']

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)

# 创建决策树回归模型
model = DecisionTreeRegressor()

# 训练模型
model.fit(X_train, y_train)

# 进行预测
y_pred = model.predict(X_test)
7. 从决策树回归到随机森林回归

随机森林回归是由多个决策树组成的集成学习模型。它通过对数据集进行有放回抽样(bootstrap 抽样),构建多个决策树,然后将这些决策树的预测结果进行平均,得到最终的预测值。随机森林回归可以有效减少决策树的过拟合问题,提高模型的泛化能力。

scikit - learn 中实现随机森林回归的代码如下:

from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
import pandas as pd

# 假设 data 是前面生成的带有特征和目标值的数据
X = data.drop('close', axis = 1)
y = data['close']

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)

# 创建随机森林回归模型
model = RandomForestRegressor()

# 训练模型
model.fit(X_train, y_train)

# 进行预测
y_pred = model.predict(X_test)
8. 支持向量回归
8.1 什么是支持向量回归

支持向量回归是支持向量机在回归问题上的应用。它的目标是找到一个超平面,使得在一定的误差范围内,尽可能多的样本点落在这个超平面附近。在股票价格预测中,支持向量回归尝试找到一个合适的超平面,根据输入的特征预测股票价格。

8.2 支持向量回归的原理

支持向量回归通过引入一个松弛变量和一个惩罚参数 (C) 来处理误差。它的目标是在满足一定误差约束的条件下,最小化超平面的权重向量的范数。通过求解一个二次规划问题,可以得到最优的超平面。

8.3 支持向量回归的实现

scikit - learn 中实现支持向量回归的代码如下:

from sklearn.svm import SVR
from sklearn.model_selection import train_test_split
import pandas as pd

# 假设 data 是前面生成的带有特征和目标值的数据
X = data.drop('close', axis = 1)
y = data['close']

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)

# 创建支持向量回归模型
model = SVR()

# 训练模型
model.fit(X_train, y_train)

# 进行预测
y_pred = model.predict(X_test)
9. 回归性能评估

为了评估回归模型的性能,我们可以使用以下几种常见的指标:
|指标|含义|计算公式|
| ---- | ---- | ---- |
|均方误差(MSE)|预测值与实际值之间误差平方的平均值| (\frac{1}{n}\sum_{i = 1}^{n}(y_i - \hat{y} i)^2) |
|均方根误差(RMSE)|均方误差的平方根| (\sqrt{\frac{1}{n}\sum
{i = 1}^{n}(y_i - \hat{y} i)^2}) |
|平均绝对误差(MAE)|预测值与实际值之间绝对误差的平均值| (\frac{1}{n}\sum
{i = 1}^{n}\vert y_i - \hat{y} i\vert) |
|决定系数((R^2))|表示模型对数据的拟合程度,取值范围为 ([0, 1]),越接近 1 表示拟合效果越好| (1 - \frac{\sum
{i = 1}^{n}(y_i - \hat{y} i)^2}{\sum {i = 1}^{n}(y_i - \bar{y})^2}) |

以下是一个计算这些指标的示例代码:

from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import numpy as np

# 计算均方误差
mse = mean_squared_error(y_test, y_pred)

# 计算均方根误差
rmse = np.sqrt(mse)

# 计算平均绝对误差
mae = mean_absolute_error(y_test, y_pred)

# 计算决定系数
r2 = r2_score(y_test, y_pred)

print(f"MSE: {mse}, RMSE: {rmse}, MAE: {mae}, R2: {r2}")
10. 总结

通过以上步骤,我们完成了使用回归算法进行股票价格预测的整个流程。从股票市场和价格的基本概念出发,进行特征工程生成了丰富的特征,然后使用多种回归算法(线性回归、决策树回归、随机森林回归和支持向量回归)进行建模和预测,最后使用常见的评估指标对模型性能进行了评估。在实际应用中,我们可以根据评估结果选择最合适的模型,并进一步优化特征工程和模型参数,以提高股票价格预测的准确性。

整个流程可以用以下 mermaid 流程图表示:

graph LR
    A[股票市场与价格概述] --> B[特征工程]
    B --> C[数据获取与特征生成]
    C --> D[线性回归]
    C --> E[决策树回归]
    C --> F[随机森林回归]
    C --> G[支持向量回归]
    D --> H[回归性能评估]
    E --> H
    F --> H
    G --> H

通过不断地学习和实践,我们可以更好地掌握回归算法在股票价格预测中的应用,为投资决策提供更有力的支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值