0


手把手教会你用 AI 和 Python 进行股票交易预测(完整代码干货)

作者:老余捞鱼

原创不易,转载请标明出处及原作者。

**写在前面的话:
**本文手把手教会大家使用 Python 和 AI 进行股票交易预测。首先介绍了不同的预测方法,特别是 LSTM 处理序列预测的能力。然后提供了概念验证步骤,包括安装、创建项目等,还展示代码建立,如导入库、用函数训练测试模型,最后还评估了模型的性能。

** 我们探寻了多种预测股价的方式,像 Facebook 的 Prophet 等预测工具、SARIMA 模型等统计手段、多项式回归等机器学习策略,还有基于人工智能的循环神经网络(RNN)。在众多人工智能模型与技术里,我们发现长短时记忆(LSTM)模型能带来最理想的结果。**

** **** **LSTM 模型是递归神经网络架构的一种变形,擅长处理序列预测难题。它与传统的前馈神经网络不同,具有类似记忆的结构,能在大量序列中保留上下文数据。这一特性使其非常适合时间序列预测、自然语言处理以及其他依赖序列数据的任务。它通过缓解消失和梯度爆炸问题,解决了标准 RNN 的基本缺陷,从而提升了模型识别数据集内长期依赖关系的能力。因此,LSTM 已成为需要长时间深入理解数据的复杂任务的首选。

为了验证其有效性,我们开发了一个概念验证。

一、准备工作

  1. 你需要在你的计算机中(或选择使用 VSCode 会更加方便)安装最新版本的 Python 和 PIP。
  2. 创建一个带有 "main.py "文件的 Python 项目。
  3. 在项目中添加 “data”目录。
  4. 设置并激活虚拟环境。
  1. trading-ai-lstm $ python3 -m venv venv
  2. trading-ai-lstm $ source venv/.bin/activate
  3. (venv) trading-ai-lstm $

**​​​​​​​ **创建一个 "requirements.txt "文件。

  1. pandas
  2. numpy
  3. scikit-learn
  4. scipy
  5. matplotlib
  6. tensorflow
  7. eodhd
  8. python-dotenv

**​​​​​​​ **确保已在虚拟环境中升级 PIP 并安装依赖项。

  1. (venv) trading-ai-lstm $ pip install --upgrade pip
  2. (venv) trading-ai-lstm $ python3 -m pip install -r requirements.txt

**​​​​​​​ **需要在".env "文件中加入了 EODHD API 的 API 密钥。

  1. API_TOKEN=<YOUR_API_KEY_GOES_HERE>

**​​​​​​​ **一切就绪。如果你正在使用 VSCode ,并希望使用与我们相同的".vscode/settings.json "文件,请点击 Fork 本项目 GitHub 仓库,以备不时之需。

  1. {
  2. "python.formatting.provider": "none",
  3. "python.formatting.blackArgs": ["--line-length", "160"],
  4. "python.linting.flake8Args": [
  5. "--max-line-length=160",
  6. "--ignore=E203,E266,E501,W503,F403,F401,C901"
  7. ],
  8. "python.analysis.diagnosticSeverityOverrides": {
  9. "reportUnusedImport": "information",
  10. "reportMissingImports": "none"
  11. },
  12. "[python]": {
  13. "editor.defaultFormatter": "ms-python.black-formatter"
  14. }
  15. }

二、代码构建

**​​​​​​​ **第一步是导入必要的库。

  1. import os
  2. os.environ["TF_CPP_MIN_LOG_LEVEL"] = "1"
  3. import pickle
  4. import pandas as pd
  5. import numpy as np
  6. from dotenv import load_dotenv
  7. from sklearn.metrics import mean_squared_error, mean_absolute_error
  8. from tensorflow.keras.models import Sequential
  9. from tensorflow.keras.layers import LSTM, Dense, Dropout
  10. from tensorflow.keras.models import load_model
  11. from sklearn.preprocessing import MinMaxScaler
  12. import matplotlib.pyplot as plt
  13. from eodhd import APIClient

**​​​​​​​ **TensorFlow 往往会自动生成诸多警告与调试信息。而我们更倾向于简洁明了的输出,故而对这些通知进行了控制。这可以在导入“os”模块后,借助 os.environ 来达成。

​​​​​​​ 机器学习和人工智能模型的训练过程需要大量的微调,主要是通过所谓的超参数(hyperparameters)进行管理。这个问题错综复杂,掌握它需要不断学习和耐心,最佳超参数的选择受到各种因素的影响。根据我们通过 EODHD API 获取的标准普尔 500 指数每日数据,我们首先使用了一些广为认可的设置。我们鼓励您修改这些设置以提高结果。目前,建议将序列长度保持在 20。

  1. # Configurable hyperparameters
  2. seq_length = 20
  3. batch_size = 64
  4. lstm_units = 50
  5. epochs = 100

**​​​​​​​ **下一步是从我们的".env "文件中获取 EODHD API’s 的 API_TOKEN。

  1. # Load environment variables from the .env file
  2. load_dotenv()
  3. # Retrieve the API key
  4. API_TOKEN = os.getenv("API_TOKEN")
  5. if API_TOKEN is not None:
  6. print(f"API key loaded: {API_TOKEN[:4]}********")
  7. else:
  8. raise LookupError("Failed to load API key.")

**​​​​​​​ **需要确保拥有有效的 EODHD API 的 API_TOKEN 才能成功访问数据。

**​​​​​​​ **我们已经建立了几个可重复使用的函数,并将在下文中详细介绍它们的功能。我把这些函数进行了代码注释,以说明其操作。

  1. def get_ohlc_data(use_cache: bool = False) -> pd.DataFrame:
  2. ohlcv_file = "data/ohlcv.csv"
  3. if use_cache:
  4. if os.path.exists(ohlcv_file):
  5. return pd.read_csv(ohlcv_file, index_col=None)
  6. else:
  7. api = APIClient(API_TOKEN)
  8. df = api.get_historical_data(
  9. symbol="HSPX.LSE",
  10. interval="d",
  11. iso8601_start="2010-05-17",
  12. iso8601_end="2023-10-04",
  13. )
  14. df.to_csv(ohlcv_file, index=False)
  15. return df
  16. else:
  17. api = APIClient(API_TOKEN)
  18. return api.get_historical_data(
  19. symbol="HSPX.LSE",
  20. interval="d",
  21. iso8601_start="2010-05-17",
  22. iso8601_end="2023-10-04",
  23. )
  24. def create_sequences(data, seq_length):
  25. x, y = [], []
  26. for i in range(len(data) - seq_length):
  27. x.append(data[i : i + seq_length])
  28. y.append(data[i + seq_length, 3]) # The prediction target "close" is the 4th column (index 3)
  29. return np.array(x), np.array(y)
  30. def get_features(df: pd.DataFrame = None, feature_columns: list = ["open", "high", "low", "close", "volume"]) -> list:
  31. return df[feature_columns].values
  32. def get_target(df: pd.DataFrame = None, target_column: str = "close") -> list:
  33. return df[target_column].values
  34. def get_scaler(use_cache: bool = True) -> MinMaxScaler:
  35. scaler_file = "data/scaler.pkl"
  36. if use_cache:
  37. if os.path.exists(scaler_file):
  38. # Load the scaler
  39. with open(scaler_file, "rb") as f:
  40. return pickle.load(f)
  41. else:
  42. scaler = MinMaxScaler(feature_range=(0, 1))
  43. with open(scaler_file, "wb") as f:
  44. pickle.dump(scaler, f)
  45. return scaler
  46. else:
  47. return MinMaxScaler(feature_range=(0, 1))
  48. def scale_features(scaler: MinMaxScaler = None, features: list = []):
  49. return scaler.fit_transform(features)
  50. def get_lstm_model(use_cache: bool = False) -> Sequential:
  51. model_file = "data/lstm_model.h5"
  52. if use_cache:
  53. if os.path.exists(model_file):
  54. # Load the model
  55. return load_model(model_file)
  56. else:
  57. # Train the LSTM model and save it
  58. model = Sequential()
  59. model.add(LSTM(units=lstm_units, activation='tanh', input_shape=(seq_length, 5)))
  60. model.add(Dropout(0.2))
  61. model.add(Dense(units=1))
  62. model.compile(optimizer="adam", loss="mean_squared_error")
  63. model.fit(x_train, y_train, epochs=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
  64. # Save the entire model to a HDF5 file
  65. model.save(model_file)
  66. return model
  67. else:
  68. # Train the LSTM model
  69. model = Sequential()
  70. model.add(LSTM(units=lstm_units, activation='tanh', input_shape=(seq_length, 5)))
  71. model.add(Dropout(0.2))
  72. model.add(Dense(units=1))
  73. model.compile(optimizer="adam", loss="mean_squared_error")
  74. model.fit(x_train, y_train, epochs=epochs, batch_size=batch_size, validation_data=(x_test, y_test))
  75. return model
  76. def get_predicted_x_test_prices(x_test: np.ndarray = None):
  77. predicted = model.predict(x_test)
  78. # Create a zero-filled matrix to aid in inverse transformation
  79. zero_filled_matrix = np.zeros((predicted.shape[0], 5))
  80. # Replace the 'close' column of zero_filled_matrix with the predicted values
  81. zero_filled_matrix[:, 3] = np.squeeze(predicted)
  82. # Perform inverse transformation
  83. return scaler.inverse_transform(zero_filled_matrix)[:, 3]
  84. def plot_x_test_actual_vs_predicted(actual_close_prices: list = [], predicted_x_test_close_prices = []) -> None:
  85. # Plotting the actual and predicted close prices
  86. plt.figure(figsize=(14, 7))
  87. plt.plot(actual_close_prices, label="Actual Close Prices", color="blue")
  88. plt.plot(predicted_x_test_close_prices, label="Predicted Close Prices", color="red")
  89. plt.title("Actual vs Predicted Close Prices")
  90. plt.xlabel("Time")
  91. plt.ylabel("Price")
  92. plt.legend()
  93. plt.show()
  94. def predict_next_close(df: pd.DataFrame = None, scaler: MinMaxScaler = None) -> float:
  95. # Take the last X days of data and scale it
  96. last_x_days = df.iloc[-seq_length:][["open", "high", "low", "close", "volume"]].values
  97. last_x_days_scaled = scaler.transform(last_x_days)
  98. # Reshape this data to be a single sequence and make the prediction
  99. last_x_days_scaled = np.reshape(last_x_days_scaled, (1, seq_length, 5))
  100. # Predict the future close price
  101. future_close_price = model.predict(last_x_days_scaled)
  102. # Create a zero-filled matrix for the inverse transformation
  103. zero_filled_matrix = np.zeros((1, 5))
  104. # Put the predicted value in the 'close' column (index 3)
  105. zero_filled_matrix[0, 3] = np.squeeze(future_close_price)
  106. # Perform the inverse transformation to get the future price on the original scale
  107. return scaler.inverse_transform(zero_filled_matrix)[0, 3]
  108. def evaluate_model(x_test: list = []) -> None:
  109. # Evaluate the model
  110. y_pred = model.predict(x_test)
  111. mse = mean_squared_error(y_test, y_pred)
  112. mae = mean_absolute_error(y_test, y_pred)
  113. rmse = np.sqrt(mse)
  114. print(f"Mean Squared Error: {mse}")
  115. print(f"Mean Absolute Error: {mae}")
  116. print(f"Root Mean Squared Error: {rmse}")

**​​​​​​​ **我们需着重指出的是,在各类函数中增添了“use_cache”变量。此策略意在降低对 EODHD 应用程序接口的冗余 API 调用,防止利用相同的每日数据对模型进行重复的重新训练。激活“use_cache”变量会将数据存储至“data/”目录下的文件里。若数据不存在,则会创建;若已存在,则会加载。当多次运行脚本时,此方法能显著提升效率。若要在每次运行时获取新数据,只需在调用函数时禁用“use_cache”选项或清空“data/”目录中的文件,就能得到相同的结果。

​​​​​​​ 现在进入代码的核心部分...

  1. if __name__ == "__main__":
  2. # Retrieve 3369 days of S&P 500 data
  3. df = get_ohlc_data(use_cache=True)
  4. print(df)

**​​​​​​​ **首先,我们从 EODHD API 获取 OHLCV 数据,并将其存入名为 "df "的 Pandas DataFrame。OHLCV 表示开盘价、最高价、最低价、收盘价和成交量,是交易蜡烛图数据的标准属性。如前所述,我们启用了缓存以简化流程。我们还可以选择在屏幕上显示这些数据。

**​​​​​​​ **我们将一次性介绍以下代码块...

  1. features = get_features(df)
  2. target = get_target(df)
  3. scaler = get_scaler(use_cache=True)
  4. scaled_features = scale_features(scaler, features)
  5. x, y = create_sequences(scaled_features, seq_length)
  6. train_size = int(0.8 * len(x)) # Create a train/test split of 80/20%
  7. x_train, x_test = x[:train_size], x[train_size:]
  8. y_train, y_test = y[:train_size], y[train_size:]
  9. # Re-shape input to fit lstm layer
  10. x_train = np.reshape(x_train, (x_train.shape[0], seq_length, 5)) # 5 features
  11. x_test = np.reshape(x_test, (x_test.shape[0], seq_length, 5)) # 5 features
  • “features” 包括我们将用来预测目标(即 “close”)的一系列输入。
  • “target” 包含一个目标值列表,如 "close"。
  • “scaler”代表一种用于将数字标准化的方法,使它们具有可比性。例如,我们的数据集开始时的接近值可能是 784,最后可能是 3538。最后一行的数字越高,并不意味着预测的意义越大。归一化可确保可比性。
  • “scaled_features” 是缩放过程的结果,我们将用它来训练人工智能模型。
  • “x_train” and “x_test” 分别表示我们将用于训练和测试人工智能模型的数据集,通常的做法是 80/20 分配。这意味着 80% 的交易数据用于训练,20% 用于测试模型。x "表示这些特征或输入。
  • “y_train” and “y_test” 的功能类似,但只包含目标值,如 "close"。
  • 最后,必须对数据进行重塑,以满足 LSTM 层的要求。

**​​​​​​​ **我们开发了一种功能,既能对模型进行重新训练,又能载入之前已训练好的模型。

  1. model = get_lstm_model(use_cache=True)

​​​​​​​ 从显示的图片中可以一窥训练序列。你会发现,最初, “loss”和 “val_loss” 指标可能并不完全一致。不过,随着训练的进行,这些数据有望趋于一致,这表明训练取得了进展。

  • Loss: 这是在训练数据集上计算的均方误差(MSE)。它反映了每个训练期预测标签和真实标签之间的“cost” 或 “error” 。我们的目标是通过连续的历时来减少这一数字。
  • Val_loss: 这个均方误差是在验证数据集上确定的,用于衡量模型在训练过程中未遇到的数据上的表现。它是模型泛化到新的未见数据能力的指标。

**​​​​​​​ **查看测试集的预测收盘价列表,可以使用此代码。

  1. predicted_x_test_close_prices = get_predicted_x_test_prices(x_test)
  2. print("Predicted close prices:", predicted_x_test_close_prices)

**​​​​​​​ **单看这些数据,可能并不特别具有启发性或直观。不过,通过绘制实际收盘价与预测收盘价的对比图(请注意,这只占整个数据集的 20%),我们可以得到更清晰的图像,如下图所示。

  1. # Plot the actual and predicted close prices for the test data
  2. plot_x_test_actual_vs_predicted(df["close"].tail(len(predicted_x_test_close_prices)).values, predicted_x_test_close_prices)

**​​​​​​​ **结果表明,在测试阶段,该模型在预测收盘价方面表现出色。

**​​​​​​​ **现在,我们来看看最令人期待的方面:我们能确定明天的预测收盘价吗?

  1. # Predict the next close price
  2. predicted_next_close = predict_next_close(df, scaler)
  3. print("Predicted next close price:", predicted_next_close)
  4. Predicted next close price: 3536.906685638428

**​​​​​​​ **这是一个用于教育目的的基本示例,仅仅是一个开始。从这里开始,您可以考虑加入更多的训练数据,调整超参数,或将模型应用于不同的市场和时间区间。

**​​​​​​​ **如果您想对模型进行评估,可以将其包括在内。

  1. # Evaluate the model
  2. evaluate_model(x_test)

**​​​​​​​ **在我们的方案中的输出情况是:

  1. Mean Squared Error: 0.00021641664334765608
  2. Mean Absolute Error: 0.01157513692221611
  3. Root Mean Squared Error: 0.014711106122506767

​​​​​​​ "平均平方误差"(mean_squared_error)和 "平均绝对误差"(mean_absolute_error)函数来自 scikit-learn 的度量模块,分别用于计算平均平方误差(MSE)和平均绝对误差(MAE)。均方根误差 (RMSE) 是通过对 MSE 取平方根得出的。

**​​​​​​​ **这些指标为模型的准确性提供了数字化的评估,也为模型的性能进行了定量的分析,而图形化的展示则更有利于直观地对比预测值与实际数值,以及直观地比较预测值和实际值。

三、总结

**​​​​​​​ **在本文中我详细介绍了用 Python 和 AI 做交易预测的流程。首先是各种预测办法,像 Facebook 的 Prophet、SARIMA 模型、多项式回归,还有基于人工智能的循环神经网络(RNN),这里面我觉得 LSTM 模型最厉害。LSTM 模型是种特殊的递归神经网络,能处理序列预测问题,还解决了标准 RNN 的消失和梯度爆炸问题,适合时间序列预测和自然语言处理这些任务。

**​​​​​​​ **接下来,我给大家提供了一个概念验证的准备步骤,包括安装Python和PIP、创建项目和文件、设置虚拟环境以及创建requirements.txt文件。还包括 VSCode的设置文件示例,以及本项目的 GitHub 代码仓库。

**​​​​​​​ **而在建立代码的部分,我详细说明了如何导入必要的库和调用 EODHD API’s,并介绍了一系列可重用的函数,这些函数用于获取数据、创建序列、获取特征和目标值、缩放特征、获取LSTM模型、进行预测以及评估模型。此外,我们还讨论了如何使用缓存来减少不必要的API调用和数据重复加载。

**​​​​​​​ **最后,本文展示了如何使用这些函数来训练和测试LSTM模型,并展示了如何预测下一个交易日的收盘价。通过比较实际收盘价和预测收盘价的图表,以及计算均方误差(MSE)、均方根误差(RMSE)和均绝对误差(MAE)等指标,来评估模型的性能。简单总结起来就是下面6句话:

LSTM模型在交易预测中的效果优于其他方法,因为它能够更好地处理长期依赖问题。

使用缓存机制可以提高数据处理的效率,避免重复的API调用和模型训练。

通过可视化实际和预测的收盘价,以及计算相关的误差指标,可以直观地评估模型的预测准确性。

模型的训练和测试应该使用不同的数据集,以确保模型的泛化能力。

调整超参数和使用额外的训练数据可以进一步提高模型的性能

模型的预测结果可以作为交易决策的参考,但应谨慎使用,因为预测并不总是准确的。


本文内容仅仅是技术探讨和学习,并不构成任何投资建议。

转发请注明原作者和出处。


本文转载自: https://blog.csdn.net/weixin_70955880/article/details/142328182
版权归原作者 老余捞鱼 所有, 如有侵权,请联系我们删除。

“手把手教会你用 AI 和 Python 进行股票交易预测(完整代码干货)”的评论:

还没有评论