English Русский Español Deutsch 日本語 Português
preview
多层感知器和反向传播算法(第 3 部分):与策略测试器集成 - 概述(I)

多层感知器和反向传播算法(第 3 部分):与策略测试器集成 - 概述(I)

MetaTrader 5测试者 | 4 七月 2023, 09:51
941 0
Jonathan Pereira
Jonathan Pereira

概述

在之前的文章中,我们讨论了按照客户端-服务器连接的简化方式来构建和使用机器学习模型。 然而,这些模型仅适用于生产环境,因为测试器不支持执行网络功能。 因此,在开始这项研究时,我在 Python 脚环境中测试了模型。 还不错,但这意味着在决定是否买入或卖出特定资产,或以 Python 实现技术指标时,需要模型(或多个模型)具有判定力。 由于自定义或封闭源代码,后者并不总能适用。 当用自定义指标作为过滤器的策略,或测试具有止盈和止损、尾随停止或盈亏平衡的策略时,缺乏策略测试器参与尤为重要。 构建自己的测试器,即使是使用更易于访问的语言,如 Python,亦是相当严峻的一个挑战。


概览

我需要一些适配回归和分类模型的东西,要求它们可在 Python 中轻松使用,如此我决定构建一个消息传递系统。 系统将在交换消息时同步工作。 Python 将代表服务器端,而 MQL5 则代表客户端。



组织开发

在尝试找到一种集成系统的途径时,我首先想到了使用 REST API,它在构建和管理方面相当简单。 然而,在查看了 WebRequest 函数的文档后,我意识到此选项不适用,因为文档明确指出它不能在此场景下使用。  

"在策略测试器里 WebRequest() 不能运行"。

我发现这种对网络功能的限制相当令人沮丧,我只好继续探索其它共享信息的方法。 我考虑过使用命名管道以二进制文件形式发送消息,但于我情况而言,它仅用于实验,当时没有必要。 不过,我已经搁置了这个想法以备将来更新。

在我的深入研究中,我遇到了一些消息,给了我一个新的解决方案:

"许多开发人员面临同样的问题 — 如何在不调用不安全的 DLL 的情况下进入交易终端沙箱。

最简单、最安全的方法之一是使用标准命名管道,按照正常文件操作来工作。 它们允许您在进程间组织客户端-服务器程序之间的通信。“

“MetaTrader 5 交易平台的保护系统不允许 MQL5 程序在其沙箱之外运行,从而保护交易者在使用不受信任的 EA 交易时免受威胁。 而使用命名管道,您可以轻松创建与第三方软件的集成,并从外部管理 EA。”


思考更深入,我意识到我可以利用 CSV 文件来交换消息。 这是因为在 Python 中处理 CSV 数据不会有问题,并且处理文件的标准 MQL5 类(CFile、CFileTxt、等等)允许写入所有类型和数组的数据,但它们不包括将消息标头写入 CSV 文件的选项。 但这个限制很容易解决。

因此,我决定在 MQL5 端开发解决方案之前,先开发一个能够共享文件的架构。 进程间通信(IPC)是一组允许在进程之间传输信息的机制。

在研究了如何实现所需的控件后,我设计了要部署的体系结构。 虽然这个过程可能看起来不必要,甚至荒谬,但它对开发非常重要,因为它给出了将要做什么,以及需要首先完成哪些活动的思路。

使用 Figma,我的设计,稍后会用作文档和参考的内容。




为了更好地理解前一个主题的上下文,我将针对所要建立的消息流进行解释,以便创建稳定和安全的通信。 这个思路最初不涉及一些技术问题,以便令架构更容易理解。

每当服务器(Python)初始化时,它都会等待发送初始化消息,即 "1 - Waiting for initialization" 流。 消息交换过程仅在智能系统加载到图表后开始。 MetaTrader 的任务是向 Python 发送一条消息,告知它在哪台主机、端口和环境上运行。


以下宏负责生成初始化消息标头。

#define HEADER_FILE_INIT {"host","port","typerun"}
#define LINES_FILE_INT(HOST, PORT, TYPE) {{string(HOST), string(PORT), string(TYPE)}}


当我谈论环境时,我指的是 EA 运行的所在,无论是策略测试器,亦或真实账户。 如此,我们将使用 “Test” 标签作为测试环境,将 “Live” 用于实时环境。

您可以在下面看到 EA 接收 “Host” 和 “Port” 参数。

sinput group   "General Configuration"
sinput string            InpHost           = "127.0.0.1";
sinput int               InpPort           = 8081;


在初始化期间,服务器收集环境、主机和端口项目参数,并存储它们以供以后读取。
static EtypeRun typerun= (MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE))?TEST:LIVE;
if(!monitor.OnInit(typerun, InpHost, InpPort))
   return(INIT_FAILED);
bool CMonitor::OnInit(EtypeRun type_run, string host, int port)
  {
   ...
   File.SetCommon(true);
   File.Open("TransferML/init.csv", FILE_WRITE|FILE_SHARE_READ|FILE_ANSI);
   string header[3]   = HEADER_FILE_INIT;
   string lines[1][3] = LINES_FILE_INT(host,port,type_run);

   if((File.WriteHeader(header)<1&File.WriteLine(lines)<1&!Strategy.Config(m_params))!=0)
      res=false;

   File.Close();

...
  }

在上面的代码中,我们读取了 “init” 文件,并传送环境和主机数据。 步骤 "1- Startup Initialization" 和 "2- Send Initialization" 在此处执行。

下面是一段 Python 代码,它将接收初始化,处理数据,设置所要使用的环境,并向客户端确认初始化。 于此,执行步骤 "2- Collect data input","3 -Startup process data input","4 - Set Env" 和 "5 - Confirm initialization"。

host, port, typerun = file.check_init_param(PATH_COMMON.format(INIT_ARCHIVE))
file.save_file_csv(PATH_COMMON.format(INIT_OK_ARCHIVE))


完成所有这些步骤后,MetaTrader 应该等待接收服务器启动确认。 这一个步骤是 "3 - Waiting for confirmation"。

bool CMonitor::OnInit(EtypeRun type_run, string host, int port)
  {
   ...
   while(!File.IsExist("TransferML/init_checked.csv", FILE_COMMON))
     {
      //waiting for startup
      Comment("waiting for startup");
     }
...

  }


完成此步骤后,Python 会选择要使用的线程。 如果是生产环境线程,我们将通过套接字进行连接,利用上一篇文章中完成的部分工作。 如果这是一个测试环境线程,那么我们将使用 CSV 文件进行消息传递。 服务器端等待来自客户端的指令,客户端发送 “START”、“STOP” 和 “BREAK” 等命令(名称是随机选择的),来启动发送某个模型生成的数值过程。


优点和缺点

我们的通信清晰高效,因为每个阶段数据的标准化确保了系统的稳定性。 此外,在 Python 端使用 pandas 库,以及在 MQL5 端使用矩阵和向量,两者均简化了数据解析。



消息交换是问题的核心,因此我选择以 CSV 格式标准化数据发送和接收。 为了简化此任务,我开发了一个类来抽象创建字符串和标头的工作。 它在环境间数据交换时被当作基础。 接下来是带有主要方法和属性的类标头。

class CFileCSV : public CFile
  {
private:
   template<typename T>
   string            ToString(const int, const T &[][]);
   template<typename T>
   string            ToString(const T &[]);
   short             m_delimiter;

public:
                     CFileCSV(void);
                    ~CFileCSV(void);
   //--- methods for working with files
   int               Open(const string,const int, const short);
   template<typename T>
   uint              WriteHeader(const T &values[]);
   template<typename T>
   uint              WriteLine(const T &values[][]);
   string            Read(void);
  };  

如您所见,写入行和标头的方法接受动态向量和矩阵,如此就允许您在运行时构建文件,而无需使用主代码中的 “StringAdd()” 或 “StringConcatenate()” 函数连接文本。 这项工作由 “ToString” 函数完成,该函数接收向量或矩阵,并将其转换为 CSV 文格式。


例如:

假想我们有一个模型,它接收最后 4 根烛条的数值,我们参考需要传输的信息是这样的:

data;close;val_ma

10202022;10.55;10.49

10212022;10.95;11.09

10222022;11.55;11.29

10232022;11.15;11.29


此示例描绘的是存储在全局变量中的静态数据的用法。 然而,在实际系统中,将根据每个策略的需求收集这些数据,正如展示集成架构的图例所示。 重要的是要强调策略是系统的主要元素,因为它才能决定模型正常工作所需的信息。 例如,如果我们需要添加有关价格或指标的信息,这将是选项之一。 不过,请记住,修改正在发送或接收的数据格式,需要相应的代码支持。 尽管这个问题很容易解决,但规划系统开发非常重要。 如前所述,这只是一个概念验证(POC)示例,如果它看起来很有前景,将来可能会得以改进。

若要手动创建上面的示例,我们需要一个包含三个数值的数组来表示标头,以及一个包含数据的数组 [4][3]。 如您所见,编写和读取此 CSV 文件很简单。

#include "FileCSV.mqh"

#define PATH(path) "Test/"+path+".csv"

string H[3] = { "data", "close", "val_ma" };
string L[4][3]  = {{"10202022", "10.55", "10.49"},{"10212022", "10.95", "11.09"},{"10222022", "11.55", "11.29"},{"10232022", "11.15", "11.29"}};

CFileCSV              File;
ulong start=0,time=0;
void OnStart()
  {
   start=0;
   time=0;
   start=GetTickCount();
   for(int i=0; i<100; i++)
     {
      File.Open(PATH("init"), FILE_WRITE|FILE_SHARE_READ|FILE_ANSI);
      ResetLastError();
      if((File.WriteHeader(H)<1&File.WriteLine(L)<1)!=0)
         Print("Error : ", GetLastError());
      File.Close();

      while(!File.IsExist(PATH("init_checked")))
        {
         //waiting for startup
         Comment("waiting for startup");
        }
      File.Delete(PATH("init"));
      File.Delete(PATH("init_checked"));
     }
   time=GetTickCount()-start;
   Print("Time send 100 archives with transfer message [ms]: ",time);
  }


这种方法的缺点是数据要写入磁盘,这可能会影响平均处理速度。 不过,如果比之用套接字系统的处理速度,则其性能似乎是合理的。


实现测试发送:

我们将发送 100 个包含 3 个数据列和 4 个数据行的文件,然后测量数据传输速度。

from Services import File

PATH_COMMON     = r'C:\Users\letha\AppData\Roaming\MetaQuotes\Terminal\B8C209507DCA35B09B2C3483BD67B706\MQL5\Files\Test\{}.csv'
INIT_ARCHIVE    = 'init'
INIT_OK_ARCHIVE = 'init_checked'

if __name__ == "__main__":
    file = File()
    file.delete_file(PATH_COMMON.format(INIT_ARCHIVE))
    file.delete_file(PATH_COMMON.format(INIT_OK_ARCHIVE))
  
    while True:
        
        receive = file.check_open_file(PATH_COMMON.format(INIT_ARCHIVE))
        file.delete_file(PATH_COMMON.format(INIT_ARCHIVE))
        file.save_file_csv(PATH_COMMON.format(INIT_OK_ARCHIVE))
void OnStart()
  {

   start=0;
   time=0;

   start=GetTickCount();

   for(int i=0; i<100; i++)
     {
      File.Open(PATH("init"), FILE_WRITE|FILE_SHARE_READ|FILE_ANSI);

      ResetLastError();
      if((File.WriteHeader(H)<1&File.WriteLine(L)<1)!=0)
         Print("Error : ", GetLastError());
      File.Close();

      while(!File.IsExist(PATH("init_checked")))
        {
         //waiting for startup
         Comment("waiting for startup");
        }
        
      File.Delete(PATH("init"));
      File.Delete(PATH("init_checked"));

     }
     
   time=GetTickCount()-start;
   Print("Time send 100 archives with transfer message [ms]: ",time);

  }

此为结果:

testeCSV (EURUSD,M1) Time to send 100 files, transfer message [ms]: 5578

这个系统不出意外,但它有其价值,因为我们会向服务器发送少量数据。 每根新烛条开盘时会发送一次数据,如此我们无需担心。 但如果您要为流式报价、订单簿数据、或其它任何内容创建一个系统,则不建议使用这样的体系结构。 将来该系统有可能会演变成更精细的东西。

此外,该过程仅限于一个模型/策略,但可以对其进行改进,从而在未来提供更好的可扩展性。


使用线性回归:

什么是线性回归?

线性回归是一种广泛应用于金融分析的统计技术,用于预测股票、债券和货币等金融资产的行为。 这种技术允许金融分析师辨别不同变量之间的关系,从而“预测”资产的未来表现。

若要对金融资产应用线性回归,首先我们需要收集相关的历史数据。 这包括有关资产收盘价、交易量、利润和其它相关财经变量的信息。 这些数据可以从证券交易所或金融网站等来源获取。

一旦收集到数据之后,有必要选择我们将在分析中使用的因变量和自变量。 因变量是需要预测的变量,而自变量是解释因变量行为的变量。 例如,如果目标是预测股票的价格,则因变量是股票的价格,而自变量可以是交易量、利润、等等。

然后,需要应用统计技术来查找回归线的方程,该方程表示自变量和因变量之间的关系。 该方程即可用于预测资产的未来行为。

应用线性回归技术后,评估所做预测的品质非常重要。 为此,我们可以将预测结果与实际历史数据进行比较。 如果预测精度较低,则可能需要对方法或所选的自变量进行调整。

线性回归是一种广泛应用于金融分析的统计技术,用于预测股票、债券和货币等金融资产的行为。 该技术允许金融分析师辨别不同变量之间的关系,从而预测资产的未来表现。 使用 scikit-learn 库在 Python 中实现线性回归很容易,并且可以成为预测金融资产价格的宝贵工具。 不过,要记住一个重点,线性回归是一种基本技术,可能不适用于所有类型的金融资产或特定情况。 评估预测品质,并参考其它财务分析技术始终很重要。

因此,您可以参考其它技术,例如基于人工智能的时间序列分析或预测模型,这些技术也可用于预测金融资产的行为。


Python 的实现:

import random
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.preprocessing import OneHotEncoder

random.seed(42)

encoder = OneHotEncoder()

# Create an empty dataframe
data = pd.DataFrame(columns=['ticker', 'price', 'volume', 'economic_indicator'])

# Fill the dataframe with random values
for i in range(500):
    row = {
        'ticker': "FAKE3",
        'price': round(random.uniform(100, 200), 2),
        'volume': round(random.uniform(10000, 100000), 2),
        'economic_indicator': round(random.uniform(1, 100), 2)
    }
    data = data.append(row, ignore_index=True)

print(data)

# apply one-hot encoding of the column "ticker"
onehot_encoded = encoder.fit_transform(data[['ticker']])

# add a new one-hot encoded column to the original dataframe
data['tiker_encoder'] = onehot_encoded.toarray()

# Selecting independent and dependent variables
X = data[['tiker_encoder', 'volume', 'economic_indicator']]
y = data['price']

# Creating the linear regression model
model = LinearRegression()

# Training the model on historical data
model.fit(X, y)

# Making predictions with the trained model
y_pred = model.predict(X)

# Evaluating prediction quality
r2 = r2_score(y, y_pred)
print("Determination coefficient:", r2)

# Making predictions for new data
new_data = [[1, 23228.17, 61.21]]
new_price_pred = model.predict(new_data)
print("Price prediction for new data:", new_price_pred)

这段代码利用 scikit-learn 库根据历史价格数据、交易量和一个指标生成线性回归模型。 该模型根据历史数据进行训练,可用于预测价格。 决定系数 (R²) 也作为预测品质的度量进行计算。 此外,该模型还能依据新提供的数据进行预测。

请注意,此代码是静态的,仅作为示例。 它可以轻松应用在处理实时和动态数据,并在生产环境中使用。 此外,还需要获取市场数据和经济指标来训练模型。

需要注意的重点是,这只是预测股票价格线性回归模型的基本实现,且需要根据您的特定需求调整模型和数据。 不建议在实盘账户上使用该模型。

提供的示例仅用于论述目的,不应当作完整的实现。 完整实现的详细演示将在下一篇文章中介绍


结束语

所提出的架构有效地克服了在测试模型时 Python 的限制,提供了多种测试选项,并有助于验证和评估 ML 模型的效率。 在下一篇文章中,我们将更深入地讨论 CFileCSV 类的实现,该类将在 MQL5 中作为数据传输的基础。

重点需要注意的是,CFileCSV 类的实现将作为 MQL5 和 Python 之间数据交换的基础,支持在两个平台上依据高级数据分析和建模功能,且作为基础组件,发挥此架构的优势。

本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/9875

附加的文件 |
构建自动运行的 EA(第 14 部分):自动化(VI) 构建自动运行的 EA(第 14 部分):自动化(VI)
在本文中,我们将把本系列中的所有知识付诸实践。 我们最终将建立一个 100% 自动化和功能性的系统。 但在此之前,我们仍然需要学习最后一个细节。
构建自动运行的 EA(第 13 部分):自动化(V) 构建自动运行的 EA(第 13 部分):自动化(V)
您知道什么是流程图吗? 您能用它吗? 您认为流程图适合初学者吗? 我建议我们一起继续阅读这篇新文章,学习如何使用流程图。
构建自动运行的 EA(第 15 部分):自动化(VII) 构建自动运行的 EA(第 15 部分):自动化(VII)
我们将继续讨论上一篇文章的主题,以便完成有关自动化的这一系列文章。 我们将看到所有内容如何搭配到一起,令 EA 像钟表一样运行。
种群优化算法:引力搜索算法(GSA) 种群优化算法:引力搜索算法(GSA)
GSA 是一种受无生命自然启发的种群优化算法。 万幸在算法中实现了牛顿的万有引力定律,对物理物体相互作用进行建模的高可靠性令我们能够观察到行星系统和星系团的迷人舞蹈。 在本文中,我将研究最有趣和最原始的优化算法之一。 还提供了空间物体运动的模拟器。