English Русский Español Deutsch 日本語 Português
preview
MetaTrader 5 和 R 进行算法交易新手指南

MetaTrader 5 和 R 进行算法交易新手指南

MetaTrader 5交易 | 14 八月 2024, 10:31
400 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana



概述

MetaTrader 是全球公认的交易平台领域的巅峰之作。该软件以其工业级的质量而闻名,免费提供,使广大用户都能使用。因此,MetaTrader 社区逐年稳步增长。该社区现在比历史上任何时候都更加多元化,由来自不同文化背景、拥有不同编程语言能力的个人组成。值得注意的是,除 MQL 5(平台官方语言)外,Python 也是 MetaTrader 平台唯一完全支持的编程语言。

对于从 R 语言过渡而来的社区成员,无论其背景是学术界还是科学计算领域,MetaQuotes 社区都张开双臂欢迎您。尽管 Python 取得了进步,而且 MetaTrader 终端还独家集成了 Python 作为唯一一种完全支持的语言,但精通 R 语言的个人不必认为他们的编程技能已经过时。本文对任何暗示过时的概念都提出了挑战,它说明了只要运用创造力和一点小聪明,使用 R 和 MetaTrader 5 构建一个全面的算法 EA 交易仍然是完全可行的。

根据作者的经验,必须注意的是,本文所讨论的软件包在 MetaTrader 5 终端中单独使用时,会表现出不完美的交互。每个软件包都有其独特的局限性。然而,当统一使用时,这些软件包有效地弥补了彼此的缺点,共同形成了一个强大的框架,有利于使用 R 和 MetaTrader 开发交易算法。

请注意:

1.操作系统考虑因素:

本演示在运行 Windows 11 操作系统 Build 22621.1848 的 Windows 机器上进行。这些步骤尚未在其他操作系统上进行过测试。

2.R 版本兼容性:

本演示使用的是 R 4.3.2 版本。参与者必须使用相同版本的 R 语言,因为本演示中的某些软件包不支持早期版本的 R 语言。

3.RStudio 集成:

此演示与 RStudio 集成,RStudio 是一个为编写 R 代码而设计的复杂集成开发环境。建议参与者在整个演示过程中利用 RStudio 获得最佳编码体验。


设置环境

首先,让我们来设置环境。

首先,请确保您的电脑已经安装了 R 4.3.2 版本。如果您不确定或没有安装 R,我们将一起完成这些步骤。

要检查 R 是否已安装,请在桌面上查找 R 图标。如果没有,可以搜索 "R" - 如果已经安装,应该会出现 R 终端。如果你需要安装 R 或使用的是旧版本,可以从 Comprehensive R Archive Network 的官方 R 代码库中获取安装包。该链接将始终带您访问 R 的最新版本,目前是 4.3.2 版。下载设置文件后,只需按照弹出的说明操作,首先选择语言。 

现在您已经安装了 R,让我们确认一下您正在运行的版本。打开 R 终端,每次启动新会话时,你都会看到问候语顶部显示的版本。如果需要更详细的信息,可以在终端中使用 "version" 命令。

检查您的 R 版本

图 1:检查您的 R 版本

现在,让我们来设置 RStudio。如果您没有安装 RStudio,可以从以下链接下载:RStudio Desktop - Posit。只需按照屏幕上出现的提示操作即可,类似于我们之前讨论过的 R 安装过程。

安装完成后,让我们来验证 R studio 安装指向的 R 版本。

首先打开 R Studio。

然后选择 "工具" 和 "全局选项"。

全局选项

图 2:检查 RStudio 中运行的 R 版本

在那里,你会看到正在运行的 R 版本。

如果您的计算机上安装了两个或更多版本的 R,包括我们一起安装的 4.3.2 版本和您之前已经安装的版本,请点击 "更改"。

选项菜单

图 3:检查 RStudio 中运行的 R 版本

从中选择 "选择特定的 R 版本",然后选择 4.3.2 版,点击 "确定" 并重启 RStudio 使更改生效。 

选择您的 R 版本

图 4:选择不同版本的 R

重新启动 RStudio 后,我们需要安装一些依赖项。 

#Algorithmic Trading With RStudio And MetaTrader 5
#Gamuchirai Zororo Ndawana
#This script will help you get your environment setup and test that everything is working fine
#Sunday 17 December 2023

#Installing Dependencies
install.packages("stringi")             #This package is a dependency for devtools
install.packages("devtools")            #We will use this package to install R packages hosted on github
install.packages("xts")                 #We will use this package to plot data fetched from the MetaTrader 5 Terminal
install.packages("quantmod")            #We will use this package to plot data fetched from the MetaTrader 5 Terminal
install.packages("ttr")                 #We will use this package to calculate technical indicator values 
install.packages("reticulate")          #We will use this package to call Python libraries in R 
devtools::install_github("Kinzel/mt5R") #We will use this open source package to interact with the MetaTrader 5 Terminal

让我们先导入第一个库,即 "reticulate"。这个库使我们能够在R中执行Python代码,我们将利用 reticulate 在虚拟环境中安装 MT5 Python 库。安装 MT5 库后,我们就可以使用 reticulate 作为 RStudio 会话和 Python 虚拟环境之间的桥梁。通过这种中介连接,我们可以向 MetaTrader 5 终端发送指令,从而方便交易的执行。

我们首先加载 reticulate 库。

#Libraries
#Importing reticulate
library(reticulate)
接下来,我们将使用 reticulate 库中的 `virtualenv_create()` 函数创建一个虚拟环境。该函数有一个字符串参数,代表虚拟环境的名称。在程序设计中,虚拟环境提供了一种为单个项目构建独立和自足空间的方法。采用虚拟环境的根本原因在于有效管理依赖关系、缓解冲突和维护项目的可重复性。当多个项目或软件包共享共同的依赖项,但需要相同依赖项的不同版本时,这一点就变得尤为重要。 
#Create a virtual environment
virtualenv_create("rstudio-metatrader5")

虚拟环境一旦建立,下一步就是激活和使用它。这是通过使用 reticulate 库中的 `use_virtualenv()` 函数实现的。激活虚拟环境可确保后续 Python 操作在该环境的隔离上下文中执行,使我们能够管理项目的特定依赖关系和配置。 

#Use the virtual environemnt 
use_virtualenv("rstudio-metatrader5")

接下来,让我们使用 reticulate 库中的 `py_install()` 函数安装 MetaTrader 5 Python 库。我们向函数提供我们打算安装的库的名称,在本例中就是 "MetaTrader5"。

#Install metatrader5 python library
py_install("MetaTrader5")

安装库后,接下来的步骤是导入 MetaTrader5 库,并将其存储在名为 `MT5` 的变量中。这是通过使用 reticulate 库中的 `import()`函数实现的。变量 `MT5` 将作为我们在后续步骤中与 MetaTrader 5 库交互的接口。

#Import MetaTrader 5
MT5 <- import("MetaTrader5")

在继续操作前,请确保 MetaTrader 5 终端当前不在运行。如果正在运行,请将其关闭。

现在,让我们直接从 RStudio 会话中启动 MetaTrader 5 终端。我们可以通过调用 MetaTrader5 Python 库中的 "initialize" 函数来实现这一目标。如果初始化成功,函数将返回 TRUE,表明 MetaTrader 5 终端已成功启动。

#Initialize the MetaTrader 5 Terminal
MT5$initialize()

[1] TRUE

虽然我们有能力访问账户信息、终端详情和交易品种详情,但我们还是遇到了第一个限制:无法以编程方式登录到不同的账户。除非用户手动更改,否则终端初始化时激活的账户将成为唯一可访问的账户。虽然使用 MetaTrader 5 库创建一个 Python 脚本登录到不同的账户,并使用 reticulate 从 R 中执行该脚本是可行的,但本文假定读者仅具备 R 编程知识以及对 MQL5 编程的基本了解。


下一个限制是无法使用 reticulate 获取历史价格信息。这种限制可能是由于 reticulate 在 R 和 Python 之间传递对象时,会在幕后自动转换数据类型。因此,在从终端请求历史价格数据时,处理返回的对象似乎遇到了困难。在这里,我们利用第二个软件包来弥补 reticulate 的不足。

#Get account information
account_info <- MT5$account_info()

#Print account information
account_info$company
account_info$balance
account_info$name

#Get terminal information
terminal_info <- MT5$terminal_info()

terminal_info$build
terminal_info$company
terminal_info$name

[1]"Deriv.com Limited"

[1]868.51

[1]"Gamuchirai Ndawana"

[1]4094

[1]"MetaQuotes Software Corp."

[1]"MetaTrader 5"

#Requesting price data
price_data <- MT5$copy_rates_from_pos("Boom 1000 Index",MT5$TIMEFRAME_M1,0,10)

Error in py_call_impl(callable, call_args$unnamed, call_args$named) :

SystemError: <built-in function copy_rates_from_pos> returned a result with an exception set

Run `reticulate::py_last_error()` for details.

我们将使用 MT5R 来解决这些问题。不过,首先让我们了解一下 MT5R 在内部是如何运行的。

MT5R 软件包在 RStudio 和 MetaTrader 5 终端之间建立 WebSocket 连接,创建全双工通信通道。在全双工系统中,数据可以同时发送和接收。为使该通道有效,MetaTrader 5 终端需要监听一个特定端口,我们将使用该端口传输指令。此外,它还必须与我们的 RStudio 会话在同一端口上通信。幸运的是,MT5R 包含一个用 MQL 5 编写的 EA 交易,它可以监听我们的命令。该 EA 是开放源码的,可在必要时灵活地加入其他功能。 此外,如果您需要源代码,可以访问这里下载。请注意,我们随文附上了此 EA 交易的定制版本,我们的定制版本包括自动设置追踪止损和获利的附加功能。

MT5R

图 5:MT5R 示意图

下载 EA 交易后,需要将其与其他 EA 交易放在相同的文件中。只需打开 MetaTrader 5 终端,按 "文件",然后再按 "打开数据文件夹"。

打开数据文件夹

图 6:查找数据文件夹

然后转到".\MQL5\experts\",并将 EA 交易放入这个 Experts 文件夹中。完成后,打开要交易的交易品种,然后将 MT5R EA 交易放在图表上。计算机可能会提示您是否应授予权限,允许 MetaTrader 5 在您的计算机上使用网络操作,请授予权限。完成后,我们就可以返回 RStudio,继续构建我们的交易算法。 

打开 RStudio 并导入 MT5R、xts 和 quantmod 库。

#Import the MT5R Library
library(mt5R)
#Import xts to help us plot
library(xts)
#Import quantmod to help us plot
library(quantmod)

然后,我们使用 MT5R 软件包中的 Ping() 函数检查与终端的连接是否建立。如果函数能够与智能交易系统通信,则返回 TRUE。

#Check if our connection is established
MT5.Ping()
#Global variables
MARKET_SYMBOL <- "Volatility 75 Index"

[1] TRUE

MT5R 没有解决我们之前讨论过的登录问题,但解决了请求价格数据的问题。

要从 MetaTrader 5 终端请求价格数据,我们需要调用 MT5R 库中的 GetSymbol 函数。该函数需要输入交易品种名称,然后是以分钟为单位的时间框架,因此日数据将是 1440,最后是行数。数据返回时,最旧的数据在顶部,当前价格在底部。 

请注意,我们将 xts 参数设置为 true。这将把数据帧转换为 R 时间序列对象,并在幕后自动格式化绘图中的日期。它还能让我们轻松地将技术指标值添加到数据帧中。

#Request historical price data
price_data <- MT5.GetSymbol(MARKET_SYMBOL, iTF = 1, iRows=5000,xts = TRUE)
我们可以使用 quantmod 中名为 lineChart() 的函数轻松绘制价格数据图

#Plotting a line chart
lineChart(price_data, name = MARKET_SYMBOL)


折线图

图 7:在 RStudio 中绘制价格数据图

我们还可以使用 quantmod 中的 addIndicator 函数在图表中添加技术指标,例如,我们将在图表中添加 60 周期数的相对强弱指数(RSI)。 

#We can also add technical indicators to the plot
addRSI(n=60)

添加技术指标

图 8:在绘图中添加技术指标


处理您的数据

当我们添加 RSI 和 Aroon 指标时,我们只是将它们添加到绘图中,要将技术指标值添加到我们的数据帧中,我们必须调用 quantmod 软件包中的指标,然后将计算所需的相应列传递给它。本例中,我们将在数据框架中添加 20 周期数的简单移动平均线(SMA)、20 周期数的相对强弱指标(RSI)和 20 周期数的平均真实范围指标(ATR)。

#Add moving average
price_data$SMA_20 <- SMA(price_data$Close,n = 20)
#Add RSI to the dataframe
price_data$RSI_20 <- RSI(price_data$Close,n=20)
#Add ATR to the dataframe
price_data$ATR_20 <- ATR(price_data[,c("High","Low","Close")], n=20)

完成后,我们将删除所有缺失值的行。

#Drop missing values
price_data <- na.omit(price_data)

我们将添加一个名为 "next close" 的功能,其中包含下一个收盘价。如果下一次收盘价大于收盘价,我们的目标值就是 1,否则就是 0。这是使用 R 中的 ifelse 函数实现的。

#Next close price
price_data$Next_Close <- lag(price_data$Close,-1)

#Setting up our target
price_data$Target <- ifelse( ( (price_data$Next_Close) > price_data$Close) , 1 , 0)

之后,我们就可以进行训练测试拆分了。我们将使用前 4000 行进行训练,其余的将用于测试。在处理时间序列数据时,我们避免采用随机拆分的做法,以避免数据泄漏 - 即模型无意中学习了未来的信息来预测过去。相反,我们优先考虑保持数据的自然时间顺序。在实际操作中,我们按照时间顺序选择最初的 4000 行,然后再选择其余的行。这种方法确保了我们的模型能够从过去的数据中学习,从而预测未来,坚持了时间序列分析的最佳实践。

#Train test split
train_set <- price_data[1:4000,]
train_y <- price_data[1:4000,c("Target")]

test_set <- price_data[4001:4980,]
test_y <- price_data[4001:4980,c("Target")]

现在,我们已经将数据分为训练集和测试集,下一步就是训练所选模型。在这种情况下,我们选择了二次判别分析(Quadratic Discriminant Analysis,QDA)。QDA 致力于最大限度地区分两个类别,从而促进更有效的数据分类。它通过最大限度地分离两个类别的平均值,以及最大限度地缩小每个类别内平均值的差值来实现这一目标。为了实现 QDA,我们使用了包含 QDA 模型的 MASS 库。因此,我们导入 MASS 库,在分析中访问并使用 QDA 模型。

#Fitting models
library(MASS)
#Quadratic Discriminant Analysis
#Using OHLC Data
qda  <- qda(Target ~ Open+High+Low+Close,data = train_set)
qda

调用:

qda(Target ~ Open + High + Low + Close, data = train_set)

各组的先验概率:

      0 1 

0.49925 0.50075 

组平均值:

      Open     High      Low    Close

0 365424.6 365677.8 365159.9 365420.5

1 365125.4 365384.0 364866.6 365131.4

从混淆矩阵中我们可以看出,我们的模型对市场上涨走势的预测优于下跌走势。

#Evaluating model performance
#Custom Quadratic Discriminant Analysis
qda_predictionts <- predict(qda,test_set)
qda_predictionts <- qda_predictionts$class

#Confusion matrix
table(qda_predictionts,test_y)

混淆矩阵

图 9:混淆矩阵


实现您的交易逻辑

我们已经掌握了交易算法的基本要领,最初的当务之急是建立一个名为 last_trade 的变量。这一变量具有重要意义,因为它在监测最近启动的交易方面发挥着关键作用。它的重要性在于,当我们的模型预测到可能破坏我们整体市场敞口的不利市场变动时,它有助于及时关闭仓位。请注意,我们的编码系统并不复杂,1 表示购买(BUY),0 表示出售(SELL)。

#Keeping track of the last trade
last_trade <- -1

在操作我们的算法时,必须启动一个无限循环,我们的交易逻辑就嵌套在这个循环中。这种无限循环是通过加入超时函数来实现的,从而调节了迭代的频率。为了与新烛形的生成保持一致,我们的目标是实现同步迭代周期。Sys.sleep 函数的集成可确保我们的交易行动与市场每分钟的变化节奏保持一致。

我们的第一步是获取当前的市场信息。

然后,我们将数据传递给模型,得到预测结果。

一旦我们的模型做出预测,我们就会使用安装了 reticulate 的 MT5 软件包检查是否有未平仓头寸。如果我们没有未平仓头寸,那么我们就按照市场预测的方向开仓,然后更新 last_trade 变量。

否则,如果我们有一个未平仓头寸,我们将检查我们的模型是否预测到针对该头寸的不利变化,如果是,我们将平仓。

最后,我们需要添加一个超时,这样我们的算法就可以每个柱检查一次我们的仓位。

while(TRUE){
  #Fetching current market data
  print("Fetching market information")
  data <- MT5.GetSymbol(MARKET_SYMBOL,iTF=1, iRows = 2)
  data <- data[1,]
  
  #Forecasting market move
  qda_forecast <- predict(qda,data)
  qda_forecast <- qda_forecast$class
  print("Model forecast: ")
  print(qda_forecast)
  #Checking if we have no open positions
  current_positions <- MT5$positions_total()
  
  #If we have no open positions, open a position following the model forecast
  if(current_positions == 0){
    print("We have no open positions. Opening a new position")
    
    #A Forecast of 1 means buy
    if(qda_forecast == 1){
      print("Opening Buy Position")
      MT5$Buy(MARKET_SYMBOL,symbol_info$volume_min)
      last_trade <- 1
    }
    #A Forecast of 0 means sell
    if (qda_forecast == 0){
      print("Opening Sell Position")
      MT5$Sell(MARKET_SYMBOL,symbol_info$volume_min)
      last_trade <- 0
    }
  }
  
  else{
      #Are we anticipating a move against our open position?
      if(last_trade != qda_forecast){
        #If we are, let's close our position
        print("Closing open position")
        MT5$Close(MARKET_SYMBOL)
        #Reset last trade
        last_trade <- -1
      }
    
    else{
      #Otherwise everything is expected to be fine
      print("The current position is aligned with the market")
      info <- MT5$account_info()
      print("Current Profit")
      print(info$profit)
    }
    
  }
  #Wait for a new candle to form 
  Sys.sleep(60)
}


把一切放在一起

图 10:R 中的二次判别分析模型 在 MetaTrader 5 中实时交易。


结论

总之,尽管在使用 R 和 MetaTrader 5 开发实时交易算法的过程中遇到了挑战,但本文表明,这项任务比最初看起来要容易得多。即使是 R 语言的初学者也能取得显著进步。互补包有效地缓解了单个包的局限性,值得注意的是,所采用的方法最大限度地减少了依赖性。总体而言,它为任何 R 用户提供了一个可行且健壮的框架。

此外,本文所附的 MT5R EA 交易定制版可自主设置止损和获利,有助于交易管理。用户可以根据需要增强其功能。祝你们平安、繁荣和交易盈利,直到我们再次相遇。

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

附加的文件 |
mt5R_v0_1_5.mq5 (151.33 KB)
用于时间序列挖掘的数据标签(第 6 部分):使用 ONNX 在 EA 中应用和测试 用于时间序列挖掘的数据标签(第 6 部分):使用 ONNX 在 EA 中应用和测试
本系列文章介绍了几种时间序列标注方法,可以创建符合大多数人工智能模型的数据,根据需要进行有针对性的数据标注可以使训练好的人工智能模型更符合预期的设计,提高我们模型的准确性,甚至帮助模型实现质的飞跃!
为 Metatrader 5 开发 MQTT 客户端:TDD 方法 - 第 5 部分 为 Metatrader 5 开发 MQTT 客户端:TDD 方法 - 第 5 部分
本文是系列文章的第五部分,介绍了我们为 MQTT 5.0 协议开发本地 MQL5 客户端的步骤。在这一部分中,我们将介绍 PUBLISH 数据包的结构、如何设置其发布标志(Publish Flag)、如何对主题名称(Topic Name)字符串进行编码,以及在需要时如何设置数据包标识符(Packet Identifier)。
频域中的滤波和特征提取 频域中的滤波和特征提取
在本文中,我们探索了在时间序列由数字滤波器在频域上进行表达的应用,如此即可提取也许对预测模型有用的独特特征。
种群优化算法:进化策略,(μ,λ)-ES 和 (μ+λ)-ES 种群优化算法:进化策略,(μ,λ)-ES 和 (μ+λ)-ES
本文研究一套称为进化策略(ES)的优化算法。它们是最早使用进化原理来寻找最优解的种群算法之一。我们将针对传统的 ES 变体实现变更,并修改算法的测试函数和测试台方法。