English Русский Español Deutsch 日本語 Português
preview
数据科学和机器学习(第 26 部分):时间序列预测的终极之战 — LSTM 对比 GRU 神经网络

数据科学和机器学习(第 26 部分):时间序列预测的终极之战 — LSTM 对比 GRU 神经网络

MetaTrader 5EA交易 | 28 三月 2025, 14:51
511 0
Omega J Msigwa
Omega J Msigwa

内容


什么是长-短期记忆(LSTM)神经网络?

长-短期记忆(LSTM)是一种专为序列任务设计的递归神经网络,擅长捕获和利用数据中的长期依赖关系。与本系列上一篇文章(必读)中讨论的普通递归神经网络(简单 RNN)不同。其无法捕获数据中的长期依赖关系。

引入 LSTM 是为了修复简单 RNN 中普遍存在的短期记忆。


简单递归神经网络的问题

简单递归神经网络(RNN)的设计是在处理序列数据时,利用它们的内部隐藏状态(记忆)来捕获序列中先前输入的有关信息。尽管它们的概念很简单,并且在针对顺序数据进行建模方面取得了初步成功,然它们也存在若干局限性。

一个明显的问题就是梯度迷失问题。在反向传播期间,梯度用于更新网络的权重。在简单的 RNN 中,这些梯度会随着时间的倒退传播而呈指数级衰减,尤其是对于长序列。结果导致网络学习长期依赖关系失能,因梯度变得太小,故权重更新失效,这令简单的 RNN 难以捕获跨越多个时间步骤的形态。

另一个挑战是梯度膨胀问题,它与梯度迷失问题相反。在这种情况下,梯度在反向传播期间呈指数成长。这可能会导致数值不稳定,并令训练过程颇具挑战性。尽管比梯度迷失少见,但梯度膨胀会导致网络权重的大范围更新,其效力会导致学习过程失败。

简单的 RNN 也很难训练,因为它们容易受到梯度迷失和膨胀问题的影响,这会令训练过程效率低下,且缓慢。训练简单 RNN 会让计算成本更高,并且可能需要仔细调整超参数。

甚而,简单 RNN 无法处理数据中复杂的时间依赖关系。由于它们的记忆容量有限,它们往往难以理解并捕获复杂的顺序形态。

对于涉及理解数据中长期依赖关系的任务,简单 RNN 也许在捕获必要的上下文时失效,从而导致性能欠佳。


长-短期记忆(LSTM)网络背后的数学

为了搞明白 LSTM 背后的细节,我们首先看看 LSTM 单元。

lstm 单元概括

01:遗忘门

由方程式给出。

遗忘门方程

sigmoid 函数 取以前的隐藏状态 ,和当前输入 作为输入。输出 是一个介于 0 和 1 之间的数值,表示在每个组件 中应保留多少前一个单元状态。

 - 遗忘门的权重。

 - 遗忘门的乖离。

遗忘门判定来自前一个单元状态中的哪些信息应结转。它为单元状态 中的每个数字输出一个介于 0 和 1 之间的数字,其中 0 表示 彻底遗忘1 表示 完全保留


02:输入门

由公式给出。

sigmoid 函数 判定哪个数值要更新。该门控制新数据输入到记忆单元之中。

 - 权重输入门。

 - 乖离输入门。

该门决定新输入中的哪些值 用来更新单元状态。它监管流入单元的新信息。


03:候选记忆单元

由方程式给出。


tanh 函数生成可存储在单元状态中的潜在新信息。

 - 候选记忆单元的权重。

 - 候选记忆单元的乖离。

该组件生成可添加到单元状态的新候选值。它调用 tanh 激活函数来确保值介于 -1 和 1 之间。


04:单元状态更新 

由方程式给出。

将前一个单元状态 乘以 (遗忘门输出),来舍弃不重要的信息。然后,(输入门的输出)乘以 (候选单元状态),并将结果相加,形成新的单元状态

通过把旧单元状态和候选值相组合,来更新单元状态。遗忘门输出控制先前的单元状态贡献,输入门的输出控制新候选值的贡献。


05:输出门

由方程式给出。

sigmoid 函数判定单元状态的哪些部分要输出。该门控制来自记忆单元的信息输出。

 - 输出层的权重

 - 输出层的乖离

该门判定当前单元状态的最终输出。它基于输入 和之前的隐藏状态 ,决定单元状态的哪些部分应当输出。


06:隐藏状态更新

由方程式给出。


将输出门 与更新的单元状态 的 tanh 函数值相乘,来获得新的隐藏状态

隐藏状态基于单元状态和输出门的决策进行更新。它用作当前时间步骤的输出,和下一个时间步骤的输入


什么是门控递归单元(GRU)神经网络?

门控递归单元(GRU)是递归神经网络(RNN)的一个种类,在某些情况下,它比长-短期记忆(LSTM)更具优势。GRU 所用记忆更少,速度比 LSTM 更快,不过,当用于具有较长序列的数据集时,LSTM 更准确。

引入 LSTM 和 GRU 是为了缓解简单递归神经网络中普遍存在的短期记忆。两者都通过在单元中的门来开启长期记忆。

尽管 LSTM 和 GRU 在许多方面与简单 RNN 工作相似,但它们解决了简单递归神经网络所遭受的梯度迷失问题。


门控递归单元(GRU)网络背后的数学

下图概括了 GRU 单元在剖析时的模样。

GRU 单元概括

01: 更新门

由公式给出。

该门判定应保留多少先前的隐藏状态 ,以及应取多少候选隐藏状态 来更新隐藏状态。

更新门控制应把多少先前的隐藏状态 结转到下一个时间步骤。它有效地决定了保留旧信息、以及合并新信息之间的平衡。


02:重置门

由公式给出。


该门中的 sigmoid 函数 判定创建候选激活之前,先前隐藏状态的哪些部分应重置,并与当前输入结合。

重置门判定在计算新的候选激活之前,应遗忘多少先前的隐藏状态。它允许模型舍弃来自过去的不相关信息。


03:候选激活

由公式给出。

候选激活是依据当前输入 ,和重置隐藏状态 计算的。

该组件为隐藏状态生成新的潜在值,这些值可基于更新门的决策进行组合。


04:隐藏状态更新

由公式给出。

更新门输出 控制使用多少候选隐藏状态 来形成新的隐藏状态

隐藏状态通过把前一个隐藏状态,与候选隐藏状态组合来更新。更新门 控制组合,确保在合并新信息的同时保留过去的相关信息。


为 LSTM 和 GRU 网络构建父类

由于 LSTM 和 GRU 在许多方面的工作方式相似,并且它们采用相同的参数,因此最好为所需的模型构建、编译、优化、检查特征重要性、以及保存函数提供一个基(父)类。该类将由后续的 LSTM 和 GRU 子类继承。

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, GRU, Dense, Input, Dropout
from keras.callbacks import EarlyStopping
from keras.optimizers import Adam
import tf2onnx
import optuna
import shap
from sklearn.metrics import accuracy_score


class RNNClassifier():
    def __init__(self, time_step, x_train, y_train, x_test, y_test):
        self.model = None
        self.time_step = time_step
        self.x_train = x_train
        self.y_train = y_train
        self.x_test = x_test
        self.y_test = y_test
    
    # a crucial function that all the subclasses must implement 
    
    def build_compile_and_train(self, params, verbose=0):
        raise NotImplementedError("Subclasses should implement this method")
    
    # a function for saving the RNN model to onnx & the Standard scaler parameters
    
    def save_onnx_model(self, onnx_file_name):
    
    # optuna objective function to oprtimize
    def optimize_objective(self, trial):
    
    # optimize for 50 trials by default
    def optimize(self, n_trials=50):

    
    def _rnn_predict(self, data):

    
    def check_feature_importance(self, feature_names):


使用 Optuna 优化 LSTM 和 GRU

前述,神经网络对超参数非常敏感。若未经正确调整、且无最优参数,神经网络可能会失效。

Python

def optimize_objective(self, trial):
    params = {
        "neurons": trial.suggest_int('neurons', 10, 100),
        "n_hidden_layers": trial.suggest_int('n_hidden_layers', 1, 5),
        "dropout_rate": trial.suggest_float('dropout_rate', 0.1, 0.5),
        "learning_rate": trial.suggest_float('learning_rate', 1e-5, 1e-2, log=True),
        "hidden_activation_function": trial.suggest_categorical('hidden_activation_function', ['relu', 'tanh', 'sigmoid']),
        "loss_function": trial.suggest_categorical('loss_function', ['categorical_crossentropy', 'binary_crossentropy', 'mean_squared_error', 'mean_absolute_error'])
    }

    val_accuracy = self.build_compile_and_train(params, verbose=0) # we build a model with different parameters and train it, just to return a validation accuracy value

    return val_accuracy

# optimize for 50 trials by default
def optimize(self, n_trials=50):
    study = optuna.create_study(direction='maximize') # we want to find the model with the highest validation accuracy value
    study.optimize(self.optimize_objective, n_trials=n_trials) 

    return study.best_params # returns the parameters that produced the best performing model 

该方法 optimize_objective 遵照 Optuna 框架定义超参数优化的意向函数。它指导优化过程去发现可最大程度提高模型性能的最佳超参数集。

Optimize 方法使用 Optuna,重复调用 optimize_objective 方法来执行超参数优化。


使用 SHAP 检查特征重要性

衡量特征对模型预测的影响程度,对于数据科学家来说很重要,它不仅可以帮助我们了解需要改进的关键领域,还可以更加清楚我们对模型特定数据集的理解。

def check_feature_importance(self, feature_names):

    # Sample a subset of training data for SHAP explainer
    sampled_idx = np.random.choice(len(self.x_train), size=100, replace=False)
    explainer = shap.KernelExplainer(self._rnn_predict, self.x_train[sampled_idx].reshape(100, -1))

    # Get SHAP values for the test set
    shap_values = explainer.shap_values(self.x_test[:100].reshape(100, -1), nsamples=100)

    # Update feature names for SHAP
    feature_names = [f'{feature}_t{t}' for t in range(self.time_step) for feature in feature_names]

    # Plot the SHAP values
    shap.summary_plot(shap_values, self.x_test[:100].reshape(100, -1), feature_names=feature_names, max_display=len(feature_names), show=False)

    # Adjust layout and set figure size
    plt.subplots_adjust(left=0.12, bottom=0.1, right=0.9, top=0.9)  
    plt.gcf().set_size_inches(7.5, 14) 
    plt.tight_layout()

    # Get the class name of the current instance
    class_name = self.__class__.__name__

    # Create the file name using the class name
    file_name = f"{class_name.lower()}_feature_importance.png"

    plt.savefig(file_name)
    plt.show()


将 LSTM 和 GRU 分类器保存为 ONNX 模型格式

最后,在我们构建模型之后,我们必须将它们保存为与 MQL5 兼容的 ONNX 格式

def save_onnx_model(self, onnx_file_name):
    # Convert the Keras model to ONNX
    spec = (tf.TensorSpec((None, self.time_step, self.x_train.shape[2]), tf.float16, name="input"),)
    self.model.output_names = ['outputs']

    onnx_model, _ = tf2onnx.convert.from_keras(self.model, input_signature=spec, opset=13)

    # Save the ONNX model to a file
    with open(onnx_file_name, "wb") as f:
        f.write(onnx_model.SerializeToString())
    # Save the mean and scale parameters to binary files
    scaler.mean_.tofile(f"{onnx_file_name.replace('.onnx','')}.standard_scaler_mean.bin")
    scaler.scale_.tofile(f"{onnx_file_name.replace('.onnx','')}.standard_scaler_scale.bin")



LSTM 和 GRU 神经网络子类

递归神经网络在许多方面的工作方式相似,即使实现时使用 Keras 也要遵循类似的方式和参数。它们的主要区别在于模型的类型,其它一切都保持不变。

LSTM 分类器

Python

class LSTMClassifier(RNNClassifier):
    def build_compile_and_train(self, params, verbose=0):
        self.model = Sequential()
        self.model.add(Input(shape=(self.time_step, self.x_train.shape[2]))) 
        self.model.add(LSTM(units=params["neurons"], activation='relu', kernel_initializer='he_uniform')) # input layer

        for layer in range(params["n_hidden_layers"]): # dynamically adjusting the number of hidden layers
            self.model.add(Dense(units=params["neurons"], activation=params["hidden_activation_function"], kernel_initializer='he_uniform'))
            self.model.add(Dropout(params["dropout_rate"]))

        self.model.add(Dense(units=len(classes_in_y), activation='softmax', name='output_layer', kernel_initializer='he_uniform')) # the output layer

        # Compile the model
        adam_optimizer = Adam(learning_rate=params["learning_rate"])
        self.model.compile(optimizer=adam_optimizer, loss=params["loss_function"], metrics=['accuracy'])
        
        if verbose != 0:
            self.model.summary()

        early_stopping = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)

        history = self.model.fit(self.x_train, self.y_train, epochs=100, batch_size=32,
                                 validation_data=(self.x_test, self.y_test),
                                 callbacks=[early_stopping], verbose=verbose)

        val_loss, val_accuracy = self.model.evaluate(self.x_test, self.y_test, verbose=verbose)
        return val_accuracy

GRU 分类器

Python

class GRUClassifier(RNNClassifier):
    def build_compile_and_train(self, params, verbose=0):
        self.model = Sequential()
        self.model.add(Input(shape=(self.time_step, self.x_train.shape[2]))) 
        self.model.add(GRU(units=params["neurons"], activation='relu', kernel_initializer='he_uniform')) # input layer

        for layer in range(params["n_hidden_layers"]): # dynamically adjusting the number of hidden layers
            self.model.add(Dense(units=params["neurons"], activation=params["hidden_activation_function"], kernel_initializer='he_uniform'))
            self.model.add(Dropout(params["dropout_rate"]))

        self.model.add(Dense(units=len(classes_in_y), activation='softmax', name='output_layer', kernel_initializer='he_uniform')) # the output layer

        # Compile the model
        adam_optimizer = Adam(learning_rate=params["learning_rate"])
        self.model.compile(optimizer=adam_optimizer, loss=params["loss_function"], metrics=['accuracy'])
        
        if verbose != 0:
            self.model.summary()

        early_stopping = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)

        history = self.model.fit(self.x_train, self.y_train, epochs=100, batch_size=32,
                                 validation_data=(self.x_test, self.y_test),
                                 callbacks=[early_stopping], verbose=verbose)

        val_loss, val_accuracy = self.model.evaluate(self.x_test, self.y_test, verbose=verbose)
        return val_accuracy

从子类分类器中可见,唯一的区别是模型的类型,LSTM 和 GRU 都采用类似的方式。


训练两个模型

首先,我们必须初始化两个模型类的实例。从 LSTM 模型开始。

lstm_clf = LSTMClassifier(time_step=time_step,
                          x_train= x_train_seq, 
                          y_train= y_train_encoded, 
                          x_test= x_test_seq, 
                          y_test= y_test_encoded
                         )

然后我们初始化 GRU 模型。

gru_clf = GRUClassifier(time_step=time_step,
                          x_train= x_train_seq, 
                          y_train= y_train_encoded, 
                          x_test= x_test_seq, 
                          y_test= y_test_encoded
                         )

两个模型优化选拔 20 遍之后;

best_params = lstm_clf.optimize(n_trials=20)
best_params = gru_clf.optimize(n_trials=20)

第 19 次选拔的 LSTM 分类器模型最佳。

[I 2024-07-01 11:14:40,588] Trial 19 finished with value: 0.5597269535064697 and parameters: {'neurons': 79, 'n_hidden_layers': 4, 'dropout_rate': 0.335909076638275, 'learning_rate': 3.0704319088493336e-05, 'hidden_activation_function': 'relu', 'loss_function': 'categorical_crossentropy'}. 
Best is trial 19 with value: 0.5597269535064697.

同时,在第 3 次选拔中发现的 GRU 分类器模型,依据验证数据产生了大约 55.97% 的准确率,是所有模型中最好的。

[I 2024-07-01 11:18:52,190] Trial 3 finished with value: 0.532423198223114 and parameters: {'neurons': 55, 'n_hidden_layers': 5, 'dropout_rate': 0.2729838602302831, 'learning_rate': 0.009626688728041802, 'hidden_activation_function': 'sigmoid', 'loss_function': 'mean_squared_error'}. 
Best is trial 3 with value: 0.532423198223114.

它为验证数据提供了大约 53.24% 的准确率。


检查两个模型的特征重要性

LSTM 分类器 GRU 分类器
feature_importance = lstm_clf.check_feature_importance(X.columns)

成果。

feature_importance = gru_clf.check_feature_importance(X.columns)

成果。
gru 分类器特征重要性

LSTM 分类器特征重要性看起来与我们用简单 RNN 模型获得的特征重要性有点相似。重要性最少的变量来自较远的时间步骤,而重要性最高的特征是来自较近时间步骤的变量。

这就像说,对当前柱线变化贡献最大的变量是最近收盘的柱线信息。

GRU 分类器有不同的观点,这似乎没有多大意义。这可能是因为它的模型精度较低。

 它说的是影响最大的变量是前 7 天的星期几。来自时间步骤值 6(即最新信息)的开盘价、最高价、最低价、和收盘价等特征被放置在中间,表明它们对最终预测结果的贡献比较平均。


策略测试器上的 LSTM 与 GRU 分类器

训练后不久,LSTM 和 GRU 分类器模型都保存为 ONNX 格式。

LSTM | Python

lstm_clf.build_compile_and_train(best_params, verbose=1) # best_params = best parameters obtained after optimization

lstm_clf.save_onnx_model("lstm.EURUSD.D1.onnx")

GRU | Python

gru_clf.build_compile_and_train(best_params, verbose=1)

gru_clf.save_onnx_model("gru.EURUSD.D1.onnx") # best_params = best parameters obtained after optimization

将 ONNX 模型及其定标器文件保存在 MQL5\Files 目录下之后,我们可将文件作为资源文件添加到两个智能系统当中。

LSTM GRU
#resource "\\Files\\lstm.EURUSD.D1.onnx" as uchar onnx_model[]; //lstm model in onnx format
#resource "\\Files\\lstm.EURUSD.D1.standard_scaler_mean.bin" as double standardization_mean[];
#resource "\\Files\\lstm.EURUSD.D1.standard_scaler_scale.bin" as double standardization_std[];

#include <MALE5\Recurrent Neural Networks(RNNs)\LSTM.mqh>
CLSTM lstm;

#include <MALE5\preprocessing.mqh>
StandardizationScaler *scaler; //For loading the scaling technique
#resource "\\Files\\gru.EURUSD.D1.onnx" as uchar onnx_model[]; //gru model in onnx format
#resource "\\Files\\gru.EURUSD.D1.standard_scaler_mean.bin" as double standardization_mean[];
#resource "\\Files\\gru.EURUSD.D1.standard_scaler_scale.bin" as double standardization_std[];

#include <MALE5\Recurrent Neural Networks(RNNs)\GRU.mqh>
CGRU gru;

#include <MALE5\preprocessing.mqh>
StandardizationScaler *scaler; //For loading the scaling technique

智能系统的其余代码与我们曾讨论的相同。

使用自本系列文章的第 24 部分以来一直使用的默认设置,我们从时间序列预测开始。

止损:500,止盈:700,滑点:50。

再次,由于数据是在日线时间帧上收集而来,因此在较低的时间帧上对其进行测试可能是个好主意,以遍避免在“休市错误”时出现错误,因为我们正在新柱线开盘时寻找交易信号。我们还可以将建模类型设置为“开盘价”,从而加快测试速度。

测试器设置

LSTM 智能系统结果

lstm ea 测试器报告

lstm EA 测试器报告

GRU 智能系统结果

gru EA 测试器报告

GRU 智能系统测试图形

我们可以从策略测试器结果中学到什么

尽管模型的准确度较低,只有 44.98%,但基于 LSTM 的智能系统是最有利可图的,净盈利为 138 美元,其次是基于 GRU 的智能系统,尽管总净盈利为 120 美元,但在 45.25% 的时间里盈利。

在这种情况下,LSTM 在盈利方面显然是胜者。尽管 LSTM 在技术上比其它同类 RNN 更聪明,但可能是很多因素导致的这种情况,所有递归模型都很不错,并且在某些状况下可以优于其它模型,请随意使用本文和上一篇文章中讨论的任何模型。


LSTM 和 GRU 神经网络模型之间的差异

比较并理解这些模型有助于判断每个模型相较于其它模型能提供什么。何时应当使用哪个,何时不应当。以下以表格形式列出它们的差异。

层面 LSTM  GRU

架构复杂度

  LSTM 具有更复杂的设计,搭配三个门(输入、输出、遗忘)、和一个单元状态,提供控制细节,如在每个时间步骤保留或舍弃哪些信息。  GRU 的设计更简单,仅搭配两个门(重置和更新)。这种简单的架构令它们更易于实现。

训练速度

在 LSTM 中拥有额外的门和单元状态,意味着要完成更多的过程,更多参数需要优化。在训练期间它们的速度较慢 由于门数更少、且操作更简单,故典型情况下它们的训练快于 LSTM

性能

在复杂问题中,如捕获长期依赖关系至关重要,往往 LSTM 的性能略好于竞品。 对于许多任务,GRU 通常提供与 LSTM 相当的性能。

长期依赖关系的处理

LSTM 的设计明确保留数据中的长期依赖关系,这要归功于单元状态、以及随时间控制信息流的门控机制。  而 GRU 也可能很好地处理长期依赖关系,但由于其结构更简单,它们在捕获超长期的依赖关系方面或许不如 LSTM 有效。 
内存用法 
由于结构复杂、且附加参数众多,LSTM 会消耗更多内存,这在资源制约环境中可能是一个限制。 

另一方面,GRU 更简单,参数更少,所用内存也更少。它们更适合计算资源有限的应用。 


后记

LSTM(长-短期记忆)和 GRU(门控递归单元)神经网络都是强力工具,为交易者寻求先进的时间序列预测模型。而 LSTM 提供了更复杂的架构,擅长捕获市场数据中的长期依赖关系,GRU 则提供了一种更简单、更高效的替代品,通常能以更低的计算成本匹敌 LSTM 的性能。

这些时间序列深度学习模型(LSTM 和 GRU)已在外汇交易之外的各个领域所用,例如天气预报、能耗建模、异常检测、和语音识别,且如大肆宣传般取得了巨大成功,然而,在始终变化的外汇市场中,我不能保证这样的承诺。

本文旨在仅提供针对这些模型的深入理解,以及如何将它们部署在 MQL5 中进行交易。您可随意探索并尝试本文中讨论的模型和数据集,并在讨论区分享您的结果。

此致敬意。


跟踪机器学习模型的开发,在 GitHub 存储库 上有更多本系列文章的讨论内容。


附件表

文件名
文件类型 说明/用法
GRU EA.mq5
LSTM EA.mq5
智能系统         基于 GRU 的智能系统。
基于 LSTM 的智能系统。
gru.EURUSD.D1.onnx
lstm.EURUSD.D1.onnx
ONNX 文件 ONNX 格式的 GRU 模型。
ONNX 格式的 LSTM 模型。
lstm.EURUSD.D1.standard_scaler_mean.bin 
lstm.EURUSD.D1.standard_scaler_scale.bin
二进制文件  LSTM 模型的标准化定标二进制文件
gru.EURUSD.D1.standard_scaler_mean.bin 
gru.EURUSD.D1.standard_scaler_scale.bin
二进制文件 
GRU 模型的标准化定标器二进制文件  

preprocessing.mqh
包含文件
由标准化定标器组成的函数库
lstm-gru-for-forex-trading-tutorial.ipynb
 Python 脚本/Jupyter 笔记簿  包含本文讨论的所有 python 代码 


源码 & 参考


本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15182

附加的文件 |
Attachments.zip (634.47 KB)
数据科学和机器学习(第 27 部分):MetaTrader 5 中训练卷积神经网络(CNN)交易机器人 — 值得吗? 数据科学和机器学习(第 27 部分):MetaTrader 5 中训练卷积神经网络(CNN)交易机器人 — 值得吗?
卷积神经网络(CNN)以其在检测图像和视频形态方面的出色能力而闻名,其应用涵盖众多领域。在本文中,我们探讨了 CNN 在金融市场中识别有价值形态,并为 MetaTrader 5 交易机器人生成有效交易信号的潜力。我们来发现这种深度机器学习技术如何能撬动更聪明的交易决策。
交易中的神经网络:一种复杂的轨迹预测方法(Traj-LLM) 交易中的神经网络:一种复杂的轨迹预测方法(Traj-LLM)
在本文中,我想向您介绍一种为解决自动驾驶领域问题而开发的有趣的轨迹预测方法。该方法的作者结合了各种架构解决方案的最佳元素。
重塑经典策略(第六部分):多时间框架分析 重塑经典策略(第六部分):多时间框架分析
在这一系列文章中,我们重新审视经典策略,看看是否可以利用人工智能(AI)对其进行改进。在本文中,我们将研究流行的多时间框架分析策略,以判断该策略是否可以通过人工智能得到增强。
威廉·江恩(William Gann)方法(第二部分):创建江恩宫格指标 威廉·江恩(William Gann)方法(第二部分):创建江恩宫格指标
我们将基于“江恩九宫格”创建一个指标,该指标通过时间和价格方格构建而成。我们将提供指标代码,并在平台上针对不同的时间区间,对该指标进行测试。