trading-ai-lstm $ python3 -m venv venvtrading-ai-lstm $ source venv/.bin/activate(venv)交易-ai-lstm $
创建一个“requirements.txt”文件熊猫numpyscikit-learnscipymatplotlib张量流eodhdpython-dotenv
确保您在虚拟环境中升级了PIP并安装依赖项(venv) trading-ai-lstm $ pip install --upgrade pip(venv) trading-ai-lstm $ python3 -m pip install -r requirements.txt
我们已经将EODHD API的API密钥包含在“.env”文件中API_TOKEN=<YOUR_API_KEY_GOES_HERE>
那应该都准备好了如果您正在使用VSCode,并希望使用与我们相同的“.vscode/settings.json”文件,它就在这里{"python.formatting.provider": "无","python.formatting.blackArgs": ["--line-length", "160"],"python.linting.flake8Args":["--max-line-length=160","--ignore=E203,E266,E501,W503,F403,F401,C901"],"python.analysis.diagnosticSeverityOverrides": {"reportUnusedImport": "信息","reportMissingImports": "无"},"[python]": {"editor.defaultFormatter": "ms-python.black-formatter"}}
这是这个项目的GitHub存储库,以防您需要指导构建代码第一步是您需要导入必要的库导入osos.environ["TF_CPP_MIN_LOG_LEVEL"] = "1"进口泡菜将熊猫导入为pd导入numpy为np从dotenv导入load_dotenv从sklearn.metrics导入mean_squared_error,mean_absolute_error从 tensorflow.keras.models import Sequential从tensorflow.keras.layers导入LSTM,密集,退出从 tensorflow.keras.models 导入 load_model从sklearn.preprocessing导入MinMaxScaler将matplotlib.pyplot导入为plt从eodhd导入APIClient
默认情况下,TensorFlow通常会生成大量警告和调试详细信息我们更喜欢更干净、更有序的输出,因此我们抑制这些通知这是在导入“os”模块后使用os.environ的特定行来实现的训练机器学习和人工智能模型的过程需要大量的微调,主要通过所谓的超参数进行管理这个主题错综复杂,掌握它有点像一种艺术形式最佳超参数的选择受到各种因素的影响根据我们通过EODHD API获得的每日标准普尔500数据,我们启动了一些广为认可的设置鼓励您修改这些以增强结果目前,建议将序列长度保持在20# 可配置的超参数seq_length = 20batch_size = 64lstm_units = 50纪元=100
下一步包括从我们的“.env”文件检索您的EODHD API的API_TOKEN#从.env文件加载环境变量load_dotenv()# 检索API密钥API_TOKEN = os.getenv("API_TOKEN")如果API_TOKEN不是None: print(f"API密钥加载:{API_TOKEN[:4]}")其他: raise LookupError(“加载API密钥失败”)
确保您拥有有效的EODHD API的API_TOKEN,以成功访问数据我们已经建立了几个可重用的功能,并将在下面进一步使用它们时详细说明它们的功能评论包含在这些功能中,以澄清其操作def get_ohlc_data(use_cache: bool = False) -> pd.DataFrame:ohlcv_file = "data/ohlcv.csv" 如果使用缓存: 如果os.path.exists(ohlcv_file): 返回pd.read_csv(ohlcv_file,index_col=None) 其他:api = APIClient(API_TOKEN)df = api.get_historical_data(symbol="HSPX.LSE",interval="d",iso8601_start="2010-05-17",iso8601_end="2023-10-04",)df.to_csv(ohlcv_file,index=False) 返回df 其他:api = APIClient(API_TOKEN) 返回api.get_historical_data(symbol="HSPX.LSE",interval="d",iso8601_start="2010-05-17",iso8601_end="2023-10-04",)def create_sequences(数据,seq_length):x,y = [],[] 对于i在范围内(len(数据)-seq_length):x.append(数据[i:i + seq_length])y.append(data[i + seq_length, 3]) # 预测目标“关闭”是第4列(索引3) 返回np.array(x),np.array(y)def get_features(df: pd.DataFrame = None, feature_columns: list = ["open", "high", "low", "close", "volume"]) -> list: 返回df[feature_columns].valuesdef get_target(df: pd.DataFrame = None, target_column: str = "close") -> 列表: 返回df[target_column].valuesdef get_scaler(use_cache: bool = True) -> MinMaxScaler:scaler_file = "data/scaler.pkl" 如果使用缓存: 如果os.path.exists(scaler_file): # 加载缩放器 用open(scaler_file,“rb”)作为f: 返回pickle.load(f) 其他:缩放器 = MinMaxScaler(feature_range=(0,1)) 用open(scaler_file,“wb”)作为f:pickle.dump(缩放器,f) 返回缩放器 其他: 返回MinMaxScaler(feature_range=(0,1))def scale_features(scaler:MinMaxScaler = None,功能:列表=[]): 返回scaler.fit_transform(功能)def get_lstm_model(use_cache: bool = False) -> 顺序:model_file = "data/lstm_model.h5" 如果使用缓存: 如果os.path.exists(model_file): # 加载模型 返回load_model(model_file) 其他: # 训练LSTM模型并保存它模型=顺序()model.add(LSTM(units=lstm_units, activation='tanh', input_shape=(seq_length, 5)))model.add(退出(0.2))model.add(密集(单位=1))model.compile(optimizer="adam", loss="mean_squared_error")model.fit(x_train, y_train, epochs=epochs, batch_size=batch_size, validation_data=(x_test, y_test)) #将整个模型保存到HDF5文件中model.save(model_file) 返回模型 其他: # 训练LSTM模型模型=顺序()model.add(LSTM(units=lstm_units, activation='tanh', input_shape=(seq_length, 5)))model.add(退出(0.2))model.add(密集(单位=1))model.compile(optimizer="adam", loss="mean_squared_error")model.fit(x_train, y_train, epochs=epochs, batch_size=batch_size, validation_data=(x_test, y_test)) 返回模型def get_predicted_x_test_prices(x_test:np.ndarray = 无):predicted = model.predict(x_test) #创建一个零填充矩阵来帮助逆变换zero_filled_matrix = np.zeros((predicted.shape[0],5)) #用预测值替换zero_filled_matrix的“关闭”列zero_filled_matrix[:, 3] = np.squeeze(预测) # 执行逆变换 返回scaler.inverse_transform(zero_filled_matrix)[:, 3]def plot_x_test_actual_vs_predicted(actual_close_prices: list = [], predicted_x_test_close_prices = []) -> None: #绘制实际和预测的收盘价plt.figure(figsize=(14,7))plt.plot(actual_close_prices, label="Actual Close Prices", color="blue")plt.plot(predicted_x_test_close_prices, label="Predicted Close Prices", color="red")plt.title(“实际与预测收盘价”)plt.xlabel(“时间”)plt.ylabel(“价格”)plt.legend()plt.show()def predict_next_close(df: pd.DataFrame = None, scaler: MinMaxScaler = None) -> float: # 获取最后X天的数据并进行扩展last_x_days = df.iloc[-seq_length:][["open", "high", "low", "close", "volume"]].valueslast_x_days_scaled = scaler.transform(last_x_days) #将此数据重塑为单个序列并进行预测last_x_days_scaled = np.reshape(last_x_days_scaled, (1, seq_length, 5)) # 预测未来收盘价future_close_price = model.predict(last_x_days_scaled) #为逆变换创建一个零填充矩阵zero_filled_matrix = np.zeros((1,5)) #将预测值放在“关闭”列中(索引3)zero_filled_matrix[0, 3] = np.squeeze(future_close_price) #执行逆变换,以获得原始规模的未来价格 返回scaler.inverse_transform(zero_filled_matrix)[0, 3]def evaluate_model(x_test: list = []) -> 无: #评估模型y_pred = model.predict(x_test)mse = mean_squared_error(y_test,y_pred)mae = mean_absolute_error(y_test,y_pred)rmse = np.sqrt(mse) print(f"均方误差:{mse}") print(f"平均绝对错误:{mae}") print(f"根均方误差:{rmse}")
我们想强调的一个方面是在各种函数中包含一个“use_cache”变量该策略旨在减少对EODHD API的不必要的API调用,并避免使用相同的每日数据对模型进行冗余再培训激活“use_cache”变量允许将数据保存到“data/”目录中的文件中如果数据不存在,它将被生成;如果已经存在,它将被加载当脚本多次执行时,这种方法会显著提高效率要在每次运行时获取新数据,只需在调用函数时停用“use_cache”选项,或清除“data/”目录中的文件,即可实现相同的结果我们现在进入代码的核心......if __name__ == "__main__": #检索3369天的标准普尔500数据df = get_ohlc_data(use_cache=True) 打印(df)
最初,我们从EODHD API获取我们的OHLCV数据,并将其存入名为“df”的Pandas DataFrame中OHLCV表示开盘、高、低、收盘和成交量,这是交易蜡烛数据的标准属性如前所述,启用缓存可以简化流程或者,我们还有助于在屏幕上显示这些数据我们将一次性涵盖以下代码块......功能= get_features(df)目标 = get_target(df)缩放器=get_scaler(use_cache=True)scaled_features = scale_features(缩放器,功能)x,y = create_sequences(scaled_features,seq_length)train_size = int(0.8 len(x)) # 创建80/20%的火车/测试拆分x_train, x_test = x[:train_size], x[train_size:]y_train, y_test = y[:train_size], y[train_size:] # 重新塑造输入以适应lstm层x_train = np.reshape(x_train, (x_train.shape[0], seq_length, 5)) # 5 功能x_test = np.reshape(x_test, (x_test.shape[0], seq_length, 5)) # 5 功能
“功能”包括我们将用于预测目标的输入列表,即“关闭”“目标”包括目标值的列表,例如“关闭”“缩放器”代表一种用于使数字正常化的方法,使它们具有可比性例如,我们的数据集可能以接近值784开始,以3538结束最后一行中较高的数字本质上并不意味着对预测目的具有更大的意义标准化确保了可比性“scaled_features”是这个缩放过程的结果,我们将用它来训练我们的人工智能模型“x_train”和“x_test”分别表示我们将用于训练和测试人工智能模型的数据集,80/20拆分是一种常见做法这意味着我们80%的交易数据被分配用于培训,20%被保留用于测试模型“x”表示这些是功能或输入“y_train”和“y_test”函数类似,但只包含目标值,例如“关闭”最后,必须对数据进行重塑,以适应LSTM层的要求我们开发了一个功能,要么重新训练模型,要么加载以前训练的模型model = get_lstm_model(use_cache=True)
显示的图像提供了训练序列的一瞥您将观察到,最初,“损失”和“val_loss”指标可能不一致然而,随着培训的进行,这些数字预计将趋同,表明取得了进展损失:这是在训练数据集上计算的均方误差(MSE)它反映了每个培训时代的预测标签和真实标签之间的“成本”或“错误”目标是通过连续的时代来减少这个数字Val_loss:这个在验证数据集上确定的均方误差,衡量模型在训练期间没有遇到的数据上的性能它是模型推广到新的、看不见的数据的能力的指标如果您想查看测试集上预测收盘价的列表,您可以使用此代码predicted_x_test_close_prices = get_predicted_x_test_prices(x_test) print("Predicted 收盘价:", predicted_x_test_close_prices)
就其本身而言,数据可能不是特别有启发性或直观的可视化然而,通过将实际收盘价与预测的收盘价(请记住,这占整个数据集的20%),我们得到了更清晰的画面,如下所示 #绘制测试数据的实际和预测收盘价plot_x_test_actual_vs_predicted(df["close"].tail(len(predicted_x_test_close_prices)).values, predicted_x_test_close_prices)
结果表明,该模型在测试阶段预测收盘价方面表现值得称赞现在,转到最可能预期的方面:我们能确定明天的预测收盘价吗? # 预测下一个收盘价predicted_next_close = predict_next_close(df,缩放器) print("Predicted next 收盘价:", predicted_next_close)预测下一个收盘价:3536.906685638428
这是教育目的的基本例子,标志着只是一个开始从这里开始,您可以考虑合并额外的训练数据,调整超参数,或将模型应用于各种市场和时间间隔如果你想评估模型,你可以包括这个 #评估模型evaluate_model(x_test)
在我们的场景中,这是...均方误差:0.00021641664334765608平均绝对误差:0.01157513692221611根均方误差:0.014711106122506767
来自scikit-learn的度量模块的`mean_squared_error`和`mean_absolute_error`函数分别用于计算均方误差(MSE)和均值绝对误差(MAE)根均方误差(RMSE)是通过取MSE的平方根得出的这些指标提供了对模型准确性的数值评估,而图形表示有助于将预测值与实际数字进行视觉比较这些指标提供了对模型性能的定量评估,而情节有助于在视觉上比较预测值和实际数字(图片来源网络,侵删)
0 评论