English Русский Español Deutsch 日本語 Português
为 MetaTrader 打造的高级 EA 构造器 - botbrains

为 MetaTrader 打造的高级 EA 构造器 - botbrains

MetaTrader 5交易 | 7 二月 2022, 09:33
2 642 0
Evgenii Shcherbakov
Evgenii Shcherbakov

概述

通常来讲,交易策略可以简化为一种可自动执行的特定算法。 交易机器人的交易速度是人类的数千倍,但并非每位交易员都精通编程。

botbrains.app 是一款无代码开发交易机器人的平台。 在 BotBrains 编辑器中创建交易机器人不需要编写任何程序,只需将所需的模块拖规划图上,设置它们的参数,并在它们之间建立连接即可。

botbrains.app 有文档(docs.botbrains.app),详细讲述了编辑器的所有概念。 本文讲述编辑器的界面、主要功能,以及“移动平均线交叉”交易策略的实现。


编辑器功能

在 BotBrains 里,您不仅可以开发基本的交易机器人(例如基于移动平均线交叉的交易机器人),还可以实现更复杂的交易算法(例如基于品种间差价交易的交易机器人)。 BotBrains 允许您把一个交易机器人链接到您的 telegram 机器人上 — 利用特殊的模块,您可以发送交易结果的消息,甚至可以按照自定义大小发送图表截图。 您可以轻松创建按钮,并为其分配操作,在机器人运行时添加输入字段来控制变量值,并添加各种其它模块来构建机器人界面。 

下表罗列出了 botbrains 编辑器中可用的模块类别。 本文末尾给出了可用模块的完整列表。
类别
说明
Events
事件。当某些事情发生时,这些模块会被激活。 例如,“机器人工作开始”事件模块在机器人启动后激活。
Condition
条件。您可以使用 “if” 模块执行各种检查。
Loop
循环。循环有很多应用。 例如,您可以循环遍历所有可用的品种,并识别在上一个交易时段中走势最大的品种。
Indicators
指标。指标是技术分析的关键手段之一。 BotBrains 提供了一系列的指标:从简单的交易量,到 Ichimoku 云
Chart analysis
图表分析。获取特定图表柱线的信息,获取特定时间段的最高/最低价格。 在图表上绘制水平线和垂直线。
DOM analysis
DOM 分析。获取有关市场报价深度及其点差的信息。
Transactions
业务。下达和取消限价/停损订单。 下达市价订单,平仓。
Variables
变量。更改变量值。 例如,您可在一个变量中为您的机器人存储交易量手数。
Sounds
声音。当某些事情发生时,使用声音模块来播放特定的声音。 例如,当满足多头开仓的所有条件时,您可以播放“买入信号”声音。
Getting information
获取信息。获取有关交易账户、交易时段、限价/停损订单、历史限价/停损订单、品种、时间和历史成交、等等的信息。
Enumerations
枚举。品种、限价/停损订单、历史限价/停损订单、历史成交、等等的枚举。
Telegram
向您的 telegram 端发送消息和图表截图。
Interface
界面。构建交易机器人的完整界面。 独立的界面元素可经由修改模块来修改。 可以将动作绑定到界面按钮。 “输入字段”界面模块可用于在机器人运行时修改变量值。 所有这些模块组合,即可创建动态用户界面。
Predefined constants
预定义常量。基本上,预定义的常量都是用于比较。 例如,持仓方向有 3 个预定义常量:买入、卖出和无方向。 您可以获取当前持仓的方向,并将其与预定义的常量之一进行比较。
Debug
调试。利用“调试”模块,可以将调试信息输出到终端日志。 例如,利用此模块可以检查变量或常量是否包含正确数值。
Other blocks
其它模块。终端关闭,停止机器人工作,机器人工作暂停。 记录到文件,在终端中通知,在图表上注释。
Math operators
数学运算符。加法、减法、乘法、除法、除法余数、平方根、指数运算。 比较:小于,大于,小于或等于,大于或等于。
Logic operators
逻辑运算符。构建条件表达式的逻辑运算符:AND(与)、OR(或)、NOT(非)。
Teleports
进入和离开 teleport 的模块。 有时,您需要快速便捷地切换到规划图的另一部分执行 — 这可以通过 teleports 传送,在几秒钟内完成。
Type conversion blocks
类型转换模块。默认情况下,所有用户数据都作为一个数字存储。 利用类型转换模块,可以明确指定数据的表示格式。 有 4 种类型:整数、十进制小数、字符串、日期和时间。
变量或常量选择
变量或常量选择
数值输入
数值输入

BotBrains 编辑器界面

BotBrains 编辑器有三种模式:Logic(逻辑)Interface(界面)Code(代码)

"逻辑" 模式:

在“逻辑”模式下,您可以构建机器人的逻辑规划图。 在这种模式下,您可以使用大多数模块:从 “if” 模块和业务模块,到发送消息到 telegram 的模块,以及制作机器人日志的模块。 在该模式下,您正在开发交易机器人的逻辑。 使用简单的模块,您可以指定自己的机器人应该做什么 — 何时买入、何时卖出、何时向 telegram 发送消息、何时停止交易,诸如此类。 botbrains 编辑器里总共有 140 多个模块,利用它们您几乎可以实现任何交易策略。

逻辑模式

界面模式

顾名思义,在“界面”模式下,您以为一个交易机器人构建界面。 只需几分钟,您就可以为自己的交易机器人构建一个完整的界面。 每种界面元素都有一套完整的设置。 使用“修改界面元素”模块块,可以动态修改界面元素的属性。

界面模式

代码模式:

在这种模式下,您可以看到为您的交易机器人生成的代码。 若要生成交易机器人的代码,只需按 ~ 键,或单击右侧工具栏中相应的按钮。

代码模式

启动一个交易机器人

当您按下键盘上的某个键之后,会在几秒钟内为您的交易机器人生成所有代码。 为了运行由 BotBrains 制造的机器人,您需要确保安装了所有必要的依赖项:

  1. botbrains_lib.ex5 函数库
  2. 包含 botbrains_lib.mqh 文件
  3. 及 botbrains_constants.mqh 包含文件。
  4. 声音文件
如果没有安装上述依赖项,BotBrains 制造的机器人将无法启动,或无法正常工作。

您可以从此处下载所有必要的依赖项。 依赖项的安装已在相关文档的文章里详细讨论过了。

移动平均线交叉示例

我们来看看 botbrains 编辑器如何实现“移动平均线交汇”交易策略。 通过单击链接,您可直接在编辑器中打开完整的规划图。

打开编辑器,并创建 5 个常量:

  1. slow_ma_period - 慢速移动平均周期(常值:60)
  2. fast_ma_period - 快速移动平均周期(常值:30)
  3. symbol - 交易品种的代码(常值:MGCV21 或您交易终端上可用的任何其它品种)
  4. admin_id - telegram 的用户 id,我们的机器人将向该用户发送含有交易信息和图表截图的消息(常值:您的 telegram 用户 id)
  5. lot - 交易手数
常量

为了检测两条移动平均线交叉的事实,我们需要创建 4 个变量:

  1. ma_slow - 慢速移动平均线的当前值
  2. ma_fast - 快速移动平均线的当前值
  3. ma_slow_prev - 前一根柱线上的慢速移动平均值
  4. ma_fast_prev - 前一根柱线上的快速移动平均值


这样,我们就可以通过比较这些变量的值来检测快、慢速移动平均线的交叉。 请注意,我们没有为变量设置初始值 — 一旦收到品种的新即时报价,这些变量的值就会更新。

我们在机器人启动时打印“机器人启动”消息。为了实现这一点,只需将两个模块拖放到机器人的逻辑规划图上:“robot start” 事件模块和 “Journal message” 模块。 然后经由连接器把这些模块勾连:

机器人启动消息

在 “Journal message” 模块的设置中,指定所要显示的消息:

日志消息模块设置

当收到品种的新即时报价时,我们的机器人应该更新变量值。 为了做到这一点,我们将 “New Tick” 事件模块和 4 个 “Set variable complex value” 模块拖到机器人的逻辑规划图上。 将 “Moving average” 模块拖放到每个 “Set variable complex value” 模块的主体中,并在每个模块中选择相应的变量。 然后在这些模块之间建立连接:

变量在新的即时报价上更新

使用 “Moving average” 模块,我们可以得到 “Moving average” 指标的值。 该块有 6 个参数:

  1. Symbol(品种)
  2. Timeframe(时间帧)
  3. Period(周期)
  4. Smoothing method(平滑方法)
  5. Applied price(所应用价格)
  6. Shift(偏移)

使用 “shift” 参数,我们可以得到 “moving average” 指标在指定柱线上的值。 例如,shift 值为 0 表示最后一根可用柱线上的值(无偏移),而值 1 表示上一根柱线上的值。 通过这种途径,我们可以得到当前和上一根柱线上的慢速和快速移动平均值 — 只是 “shfit” 和 “period” 参数值的设置不同。

我们为第一个 “MA” 模块指定参数:


请注意,选取相应常量作为 “symbol” 和 “period” 参数的值。 您也可以使用变量作为参数值。

有关使用变量和常量的更多信息,请参阅相关的文档文章

如此,每次收到品种的新即时报价时,“ma_slow” 变量的值都会更新。 

我们对 “ma_fast” 变量执行同样的操作:

在这个阶段,我们更新移动平均线的当前值,但为了判断交叉的事实,我们还需要知道上一根柱线上移动平均线的值。 当前柱线的偏移量为 0,上一根柱线的偏移量为 1,依此类推。 因此,在 “Set variable complex value” 的最后两个模块中,我们将以类似的方式执行所有操作,但这次我们应该将 "Moving Average" 模块的 "shift" 参数设置为 1,以便 “ma_slow_prev” 和 “ma_fast_prev” 变量包含上一根柱线的移动平均值。

针对 “ma_fast_prev” 变量执行同样的操作。

我们要确保为变量设置正确的值。 将 “Timer” 模块拖放到规划图上。 该模块只有一个参数 — “Interval (sec)”,间隔(秒)。 默认情况下,此参数设置为 1,因此默认情况下该模块每秒执行一次。 然后把 4 个“Print debug info(打印调试信息)”模块拖放到规划图中 — 该模块仅仅将指定的消息打印到终端日志之中。 使用这个模块,我们可以看到变量包含哪些值。 在每个 “Print debug info” 中,按以下顺序放置 3 个模块:

  1. Value input(数值输入) - 这个块是一个简单的输入字段,我们可以在其中输入任何文本。 在我们的案例中,我们应该输入变量名,以便了解哪些变量引用哪些值。
  2. "+" operator(运算符) - 使用这个模块我们可以合并两个字符串。
  3. Variable select(变量选择)- 使用这个模块,我们可以得到指定变量的值。

在这个阶段,我们的规划图应该是这样的:

在 BotBrains 中,所有用户值都存储为数字。 在这种情况下,我们需要明确指定 “value input” 模块包含文本,从而输出文本自身,而非其数字表示。

为此,只需在每个 “value input” 模块上放置 “Regular string(常规字符串)”类型转换模块:

在此节点,我们就能够编译机器人代码了,甚至运行它 — 所指定变量的值会被每秒打印到终端日志中。 不过,编译过程中将出现一条警告:“从 ‘number(数字)’ 到 ‘string(字符串)’ 的隐式转换”:

实际上,在我们的规划图中,我们试图将字符串值(“value input” 模块转换为字符串),并与数值(包含移动平均数数值的变量)相加。 为了修复这个问题,我们简单地将所有变量转换为字符串。 我们可以通过两种方式做到这一点:

  1. 使用 “Regular string” 类型转换模块
  2. 使用“Fraction(分数)”类型转换模块 - 此模块与 “Regular string” 模块的不同之处在于,它能够限制小数点之后的位数

我们采用第二种方法。 为此,将 “Fraction(分数)”类型转换模块拖放在每个 “variable select” 模块上:

在每个 “Fraction” 模块的设置中,为唯一参数 “Decimal places(小数位)” 指定 2:

相关的文档文章详细介绍了如何使用类型。

打开终端,在品种图表上添加 2 个 “Moving Average” 指标,参数与我们的机器人中采用的参数相同,从而我们就可以将变量值与图表上的指标值进行比较:

然后使用 “~” 键或右侧工具箱中相应的按钮来生成机器人代码。

在终端中运行我们的机器人,并将终端日志中的输出值与图表上的指标实际值进行比较:

所有数值都匹配,如此我们可以继续我们的工作。

余下唯一要做的一件事是在收到新的即时报价时比较变量值,从而检测移动平均线交叉,并在满足所有交易条件的情况下进行交易。

首先,我们来判定进行交易的条件。 对于多头开仓需要满足 3 个条件:

  1. 快速移动平均线的当前值大于慢速移动平均线的当前值。
  2. 前一根柱线上的快速移动平均值小于前一根柱线上的慢速移动平均值。
  3. 当前持仓的方向不是“多头”

请注意,如果我们省略最后一个条件,我们的机器人可能会在短时间内执行很多笔交易,因为第一个和第二个条件可能在一秒钟内反复满足若干次。 由于第三个条件的结果,我们的机器人不会在现有的多头持仓上再开立新的多头持仓。

请注意,在 BotBrains 编辑器中,您可以指定最大和猜测的成交频率。 如果达到最大成交频率,机器人将立即把所有持仓平仓,并删除所有已下订单,且机器人还将通过 telegram 通知您,和/或直接在终端中发出通知,具体取决于机器人的安全参数设置。 如果达到猜测成交 频率,机器人并不会把任何持仓平仓,或删除任何订单;取而代之的是,它只会通过 telegram 和/或直接在终端通知您。

必须满足以下条件才能入常多空:

  1. 快速移动平均线的当前值低于慢速移动平均线的当前值
  2. 前一根柱线上的快速移动平均值大于前一根柱线上的慢速移动平均值。
  3. 当前持仓的方向不是“空头”

我们构建多头开仓的条件:

“Position info(持仓信息)” 模块有两个参数:

  1. Symbol - 有关您想要获取持仓信息的品种
  2. Block value - 目标持仓参数。 可选项:持仓量、持仓开仓时间、开仓价格、持仓当前盈利和持仓方向

我们将使用 “symbol” 常量作为 “symbol” 参数的值。 选择“持仓方向”作为第二个参数的值。

通过这种方式,“持仓信息”模块的值将等于依据指定品种的当前持仓方向。

在这个模块之后有三个模块:“Not(不等)”、“Equals(相等)” 和 “Position direction(持仓方向)”。 “持仓方向”模块包含所有可能的方向:“买入方向”、“卖出方向” 和 “无方向”。 我们检查当前持仓方向是否不是“ 买入”。

然后在规划图中添加一个 “Market order(市价订单)”模块,并将该模块与 “if” 模块的 “Yes” 连接器链接:

“Market order(市价订单)” 模块有三个参数:

  1. Symbol - 将为该品种下达市价订单
  2. Direction - 订单方向 (做多/做空)
  3. Volume - 订单交易量


请注意,对于多余大多数参数,我们用常量作为其数值。 这让我们可以轻松地配置机器人 — 我们仅需简单地在一个地方修改常量的值,新值就会自动用于所有引用该常量的地方。

如果在 BotBrains 编辑器中制作的交易机器人有常量,它们将列在生成代码的最顶端:

/********** <ROBOT CONSTANTS> **********/

const double __slow_ma_period = user_value("60");
const double __fast_ma_period = user_value("30");
const double __symbol         = user_value("MGCG22");
const double __admin_id       = user_value("744875082");
const double __lot            = user_value("1");

/********** </ROBOT CONSTANTS> **********/

您可以把常量想象成机器人设置。 例如,如果您决定交易不同的品种,只需更改 “symbol” 常量的值即可。 或者,如果您不想直接使用代码,可以直接在 BotBrains 编辑器中更改常量的值,并重新生成代码。 

因此,在这一点上,当快速移动平均线从底部到顶部穿过慢速移动平均线时,交易机器人将开立一笔多头仓位。 我们在机器人开立多头仓位时发出通知:

  1. 在终端日志中显示它
  2. 创建终端警报
  3. 将交易执行记录添加到机器人的日志文件中
  4. 播放 "Buy Signal" 声音
  5. 发送消息至 telegram
  6. 发送 图表 截屏至 telegram
请注意,如果我们在进行交易后立即播放 “Buy Signal” 声音,很可能不会播放我们的声音,因为 MetaTrader 会在每次进行业务时自动播放它自己的声音。 因此,我们不需要立即播放声音,而是等待业务完成后延迟一会儿,例如 1 秒。 

似乎实现所有 6 个步骤需要很长时间;但事实上,所有这些都有特殊的模块。 执行所有 6 个步骤实际上只需要 20-30 秒钟。 我们以前所做的一切也可以在几分钟内完成 — 比手工编写代码快很多倍。

为了令 telegram 模块正常工作,您需要在 @BotFather bot 中注册您的 bot,获取 bot 的令牌,并在 BotBrains 编辑器中的 bot 设置里指定它。 您还需要知道自己的 Telegram ID,您可以从 @getmyid_bot 获取其值。 所有这些都已在相关文档文章中详细描述。 用于操控 telegram 的模块仅限 pro 用户使用。

与此类似,我们实现开立空头仓位的逻辑。 为此,仅需选择与开立多头仓位相关的所有模块,复制(CTRL+C),粘贴(CTRL+V),并进行必要的更改。

生成机器人的代码,编译并在终端中运行,检查我们的交易机器人的性能。

请仅在演示帐户上测试交易机器人! 默认情况下,BotBrains 编辑器中所有机器人的安全设置都禁止在真实账户上交易! 仅当您的交易机器人在一个演示账户上进行了全面测试之后,才能切换到实盘账户里进行交易!

我们为移动平均线设置随机周期,并在一分钟时间帧内运行交易机器人。 有一点很重要,就是测试我们的交易机器人的性能,检测并纠正任何潜在的错误。 经过 80 分钟的交易,交易机器人已开立了 4 笔仓位。 每笔仓位开立之后,机器人成功地在终端中发出通知,播放相应的声音,并在机器人的日志文件和终端日志中编制相应的记录。 机器人还将信息和图表截图直接发送到 telegram,没发现任何问题。 我们尚未针对所有这些编写一行代码。 这样简单的一个机器人可以在几分钟内建成。

似乎一切良好。 然而,如果您仔细观察交易机器人进行的交易,就会发现,除了第一笔之外,我们的交易机器人在每次交易时都进行了两笔成交:第一次交易是为了持仓平仓,第二次交易则开立下一笔仓位。 这很容易检测到 — 只需查看图表、查看日志文件、查看交易机器人发送给 telegram 的消息,或者只需聆听交易机器人在交易执行后播放声音警报的次数。 我们还可以分析终端中的业务历史。

在 BotBrains 编辑器中,可以在机器人安全设置中指定猜测成交和最大成交的频率。 默认情况下,最大成交频率为每 10 秒 2 笔。 如果达到最大成交频率,交易机器人会立即把所有持仓平仓,删除所有已下订单,并通过实时聊天通知您,和/或在终端中创建通知。

问题不仅在于我们的机器人最终的交易量将是所需交易量的两倍,且大多数仓位的入场价格极其糟糕,但在某个时刻,我们的机器人达到其最大成交频率,从而停止交易。 这正是本案例所发生的情况。 注意日志文件中的最后记录:


我们来修复这个故障。 创建 full_lot 变量:

当启动机器人时,把 full_lot 变量设置为 lot 常量定义的数值:

然后在业务模块中,我们应该采用 full_lot 变量,取代 lot 常量:

然后,在入场建仓之后,把 full_lot 变量的值设置为 lot 常量值的两倍。 在第一次交易之后,机器人会以双倍的交易量开始交易。 因此,它将在一次交易中先把持仓平仓,并开立一笔新持仓:

交易机器人现在会于一次交易中完成所有仓位。 我们在终端里运行机器人来检查这一点。 如果我们查看交易历史,我们会发现第一笔交易是以 1 手开仓的,而随后的交易则是以 2 手开仓的:

我们来为交易机器人创建一个界面。 为此,转到编辑器的 “Interface(界面)”模式,并使用特殊模块构建界面:

为了构建这样的界面,使用以下界面模块就足够了:

  1. Rectangle(矩形)
  2. Button(按钮)
  3. Text(文本)
应采用快速和慢速移动平均线的当前值,替代虚线。 为了实现这一点,使用 “Modify interface element(修改界面元素)” 模块 — 该模块可用于修改指定 ID 的模块的属性。

我们移动 4 个“Modify interface element” 模块至机器人的逻辑规划图当中:

为了复制模块 ID,执行以下操作

  1. 按下 CTRL
  2. 不释放 CTRL 键,双击目标模块

复制第一个虚线的 ID:

打开第一个 “Modify interface element” 模块的设置,并设置参数值。 第一件要做的事就是指定模块 ID,我们随后要修改其属性。 在我们的例子中,它是第一个虚线的 ID。 我们应选择 “Text” 作为修改类型。 指定变量 ma_fast_prev 作为新文本。 如果您需要,还可以设置新文本中的小数位数(仅当用数字作为界面文本的内容时,才应使用此设置)。


与此类似,我们设置其余 3 个 “Modify interface element” 模块的参数。 以此方式,当收到正在交易的品种的新即时报价时,将把移动平均线的相应值写入变量 ma_fastma_slowma_fast_prevma_slow_prev 当中,然后再把这些变量值写入界面的相应文本元素。

现在,我们为界面按钮分配相应的动作。 为做到这一点,我们将三个模块移动到机器人的逻辑示意图上:2 个 “Market order(市价订单)”模块和 1 个 “Close position(平仓)”模块:

然后设置这些模块:

复制第一个 “Market Order” 模块 ID,并将该 ID 指定为第一个按钮的 “Linked block ID(链接模块 ID)” 参数的值:

以此方式,当按下 “Buy” 按钮时,将调用相应的模块。 与此类似,为其它按钮指定动作。 我们来生成机器人代码,并在终端中运行它,以便检查其功能:

请注意,我们在编辑器中构建的界面已完全转换到交易终端。 界面按钮已起作用,且取代了虚线,我们可以看到移动平均线的实际值。

结果就是,交易机器人生成的代码如下所示:

//+------------------------------------------------------------------+
//|                                      moving_average_cross_en.mq5 |
//|                                                    botbrains.app |
//+------------------------------------------------------------------+

#include <botbrains_constants.mqh>
#include <botbrains_lib.mqh>

/********** <ROBOT CONSTANTS> **********/

const double __slow_ma_period = user_value("60");
const double __fast_ma_period = user_value("30");
const double __symbol         = user_value("MGCG22");
const double __admin_id       = user_value("744875082");
const double __lot            = user_value("1");

/********** </ROBOT CONSTANTS> **********/

/********** <ROBOT VARIABLES> **********/

double __ma_slow      = user_value("");
double __ma_fast      = user_value("");
double __ma_slow_prev = user_value("");
double __ma_fast_prev = user_value("");
double __full_lot     = user_value("");

/********** </ROBOT VARIABLES> **********/

int OnInit(){

  //Is autotrading allowed:
  if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)){
    MessageBox("Autotrading is not allowed, expert will be removed");
    ExpertRemove();
    return(-1);
  }

  //Is trading on live account allowed:
  if(AccountInfoInteger(ACCOUNT_TRADE_MODE) == ACCOUNT_TRADE_MODE_REAL){
    MessageBox("Expert is not allowd to trade on live account!");
    ExpertRemove();
    return(-1);
  }

  //Set robot name:
  set_robot_name("moving_average_cross_en");

  //Set license key:
  set_license_key("2L5J7K-K986ND-KMPT94-1Q");

  //Set language:
  set_lang("en");

  //Generate robot magic number:
  generate_magic();

  //Set initial trading account balance:
  set_init_account_balance();

  //Set suspicious deals frequency params:
  set_suspicous_deals_frequency(60, 3, false, true);

  //Set maximal deals frequency params:
  set_max_deals_frequency(10, 2, false, true);

  //Set the timer with an interval of 1 second:
  EventSetTimer(1);

  //Blocks executed when the robot is launched:
  block_bYi6ikfde();
  block_bYUS6GLT0();

  //Create interface elements:
  create_rectangle("b1ELCu5iq", 0, 0, CORNER_LEFT_UPPER, 15, 15, 390, 195, C'20,20,20', BORDER_FLAT, STYLE_SOLID, C'10,191,254', 2, 0, false, false, false);
  create_button("b4rh5uKlb", 0, 0, CORNER_LEFT_UPPER, 195, 165, 150, 30, "Sell", "Ubuntu Mono", 8, C'255,255,255', C'20,20,20', C'255,51,0', false, 0, false, false, false);
  create_text("b5BGSldua", 0, 0, CORNER_LEFT_UPPER, 120, 135, "-", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_text("b9EjNpibO", 0, 0, CORNER_LEFT_UPPER, 30, 30, "\"MA Cross\" trading robot", "Ubuntu Mono", 14, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_button("bBTrBQlkS", 0, 0, CORNER_LEFT_UPPER, 30, 165, 150, 30, "Buy", "Ubuntu Mono", 8, C'255,255,255', C'20,20,20', C'51,255,0', false, 0, false, false, false);
  create_text("bEncRhDIR", 0, 0, CORNER_LEFT_UPPER, 285, 75, "Current bar:", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_text("bI0vadsS2", 0, 0, CORNER_LEFT_UPPER, -195, 270, "Text", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_text("bK1cW1i6s", 0, 0, CORNER_LEFT_UPPER, 30, 135, "MA slow:", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_text("bLcIIkYqO", 0, 0, CORNER_LEFT_UPPER, 285, 135, "-", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_text("bSDWBsxbk", 0, 0, CORNER_LEFT_UPPER, 285, 105, "-", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_text("bTK8r1zb1", 0, 0, CORNER_LEFT_UPPER, 30, 105, "MA fast:", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_text("bhKcJ2pwx", 0, 0, CORNER_LEFT_UPPER, 120, 75, "Previous bar:", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_text("bj6MtjhZF", 0, 0, CORNER_LEFT_UPPER, 120, 105, "-", "Ubuntu Mono", 9, C'255,255,255', 0, ANCHOR_LEFT_UPPER, 0, false, false, false);
  create_button("bympSPhAp", 0, 0, CORNER_LEFT_UPPER, 360, 165, 30, 30, "X", "Ubuntu Mono", 8, C'255,255,255', C'20,20,20', C'255,51,0', false, 0, false, false, false);

  ChartRedraw();

  //Robot initialization was successful:
  return(INIT_SUCCEEDED);

}

void OnDeinit(const int reason){

  Comment("");
  PlaySound(NULL);

  //Remove all graphical objects from the chart on which the robot was launched:
  remove_all_objects(0);

}

void OnTimer(){

  //Timer with 1 sec. interval (bgP4YbxaQ):
  block_bTonyumSN();
  block_bO7LNEs4m();
  block_bS1JODjZj();
  block_bM4s77Wvc();

  //Every 10 sec. reset the counter of the performed deals number (max deals frequency):
  if(get_timer_tick_index() % 10 == 0){

    deals_max_frequency_counter_reset();

  }

  //Every 60 sec. reset the counter of the performed deals number (suspicious deals frequency):
  if(get_timer_tick_index() % 60 == 0){

    deals_suspicious_frequency_counter_reset();

  }

  timer_tick_index_increment();

}

void OnTick(void){

  //Blocks executed when a new tick is received on the symbol on the chart of which the robot was launched:
  block_bHxbWWtwW();
  block_bvIvs7SMe();
  block_boHVNlqnO();

}

void OnTrade(){

  //Check deals frequency:
  check_deals_frequency();

}

void OnChartEvent(
  const int      id,      // event identificator
  const long&    lparam,  // event parameter of type long
  const double&  dparam,  // event parameter of type double
  const string&  sparam   // event parameter of type string
){

  if(id == CHARTEVENT_OBJECT_CLICK){

    string object_name = sparam;

    if(ObjectGetInteger(0, object_name, OBJPROP_TYPE) == OBJ_BUTTON){

      if(object_name == "b4rh5uKlb"){

        block_bYGEhpZDM();

      }

      if(object_name == "bBTrBQlkS"){

        block_bCa4uSC92();

      }

      if(object_name == "bympSPhAp"){

        block_bXRrICVna();

      }

      Sleep(100);
      ObjectSetInteger(0, object_name, OBJPROP_STATE, false);
      ChartRedraw();

    }

  }

}

//Function of the block b0Wkfq6OD (set_complex_variable_value):
void block_b0Wkfq6OD(){

  vset(__ma_fast, ( moving_average(to_string(__symbol), PERIOD_CURRENT, (int)__fast_ma_period, MODE_SMA, PRICE_CLOSE, 0) ));

  block_b6LYVQGej();

}

//Function of the block b1VYatoxs (interface_element_modify):
void block_b1VYatoxs(){

  modify_text("b5BGSldua", DoubleToString(__ma_slow_prev, 2) );

}

//Function of the block b3UJfY74N (interface_element_modify):
void block_b3UJfY74N(){

  modify_text("bSDWBsxbk", DoubleToString(__ma_fast, 2) );

}

//Function of the block b3crj4ayK (log):
void block_b3crj4ayK(){

  log_to_file("Open short!");

  block_bzVyOb4tW();

}

//Function of the block b6LYVQGej (set_complex_variable_value):
void block_b6LYVQGej(){

  vset(__ma_slow_prev, ( moving_average(to_string(__symbol), PERIOD_CURRENT, (int)__slow_ma_period, MODE_SMA, PRICE_CLOSE, 1) ));

  block_bQMre45Bd();

}

//Function of the block bCa4uSC92 (place_market_order):
void block_bCa4uSC92(){

  place_market_order(to_string(__symbol), "BUY", __lot);

}

//Function of the block bHxbWWtwW (condition):
void block_bHxbWWtwW(){

  if (( __ma_fast > __ma_slow ) && ( __ma_fast_prev < __ma_slow_prev ) && ( get_position_info(to_string(__symbol), "POSITION_DIRECTION") != BUY_DIRECTION )){

    block_bUySCvh6M();

  }

}

//Function of the block bM4s77Wvc (print_debug_info):
void block_bM4s77Wvc(){

  print_debug_info(( to_string(to_double("ma_fast_prev = ")) + DoubleToString(__ma_fast_prev, 2) ));

}

//Function of the block bMgaVnT74 (pause):
void block_bMgaVnT74(){

  pause(1000);

  block_bT8xv0qGj();

}

//Function of the block bO7LNEs4m (print_debug_info):
void block_bO7LNEs4m(){

  print_debug_info(( to_string(to_double("ma_fast = ")) + DoubleToString(__ma_fast, 2) ));

}

//Function of the block bPlXoF1uA (set_complex_variable_value):
void block_bPlXoF1uA(){

  vset(__full_lot, ( __lot * to_double("2") ));

}

//Function of the block bQ4zsFoIh (log):
void block_bQ4zsFoIh(){

  log_to_file("Open long!");

  block_bMgaVnT74();

}

//Function of the block bQMre45Bd (set_complex_variable_value):
void block_bQMre45Bd(){

  vset(__ma_fast_prev, ( moving_average(to_string(__symbol), PERIOD_CURRENT, (int)__fast_ma_period, MODE_SMA, PRICE_CLOSE, 1) ));

  block_bRUr8MnXh();
  block_b1VYatoxs();
  block_b3UJfY74N();
  block_bofgi9HOT();

}

//Function of the block bRUr8MnXh (interface_element_modify):
void block_bRUr8MnXh(){

  modify_text("bj6MtjhZF", DoubleToString(__ma_fast_prev, 2) );

}

//Function of the block bS1JODjZj (print_debug_info):
void block_bS1JODjZj(){

  print_debug_info(( to_string(to_double("ma_slow_prev = ")) + DoubleToString(__ma_slow_prev, 2) ));

}

//Function of the block bT8xv0qGj (buy_signal_sound):
void block_bT8xv0qGj(){

  play_sound("buy_signal");

  block_bPlXoF1uA();

}

//Function of the block bTonyumSN (print_debug_info):
void block_bTonyumSN(){

  print_debug_info(( to_string(to_double("ma_slow = ")) + DoubleToString(__ma_slow, 2) ));

}

//Function of the block bTs8fZtAO (telegram_send_chart_screenshot):
void block_bTs8fZtAO(){

  telegram_send_chart_screenshot(to_string(__symbol), 2160, 720, (int)__admin_id);

}

//Function of the block bUySCvh6M (place_market_order):
void block_bUySCvh6M(){

  place_market_order(to_string(__symbol), "BUY", __full_lot);

  block_bfrp6ajWk();
  block_bjBTLJMym();
  block_boX0sSwri();
  block_bQ4zsFoIh();

}

//Function of the block bWkG0nSQa (telegram_send_message):
void block_bWkG0nSQa(){

  telegram_send_message("Open short!", (int)__admin_id);

}

//Function of the block bX2tY0y68 (terminal_print):
void block_bX2tY0y68(){

  terminal_print("Open short!");

}

//Function of the block bXRrICVna (close_position):
void block_bXRrICVna(){

  close_position(to_string(__symbol));

}

//Function of the block bYGEhpZDM (place_market_order):
void block_bYGEhpZDM(){

  place_market_order(to_string(__symbol), "SELL", __lot);

}

//Function of the block bYUS6GLT0 (set_complex_variable_value):
void block_bYUS6GLT0(){

  vset(__full_lot, ( __lot ));

}

//Function of the block bYi6ikfde (terminal_print):
void block_bYi6ikfde(){

  terminal_print("Робот запущен!");

}

//Function of the block bfrp6ajWk (terminal_print):
void block_bfrp6ajWk(){

  terminal_print("Open long!");

}

//Function of the block biLE3RJAD (sell_signal_sound):
void block_biLE3RJAD(){

  play_sound("sell_signal");

  block_bPlXoF1uA();

}

//Function of the block bjBTLJMym (telegram_send_message):
void block_bjBTLJMym(){

  telegram_send_message("Open long!", (int)__admin_id);

}

//Function of the block boHVNlqnO (set_complex_variable_value):
void block_boHVNlqnO(){

  vset(__ma_slow, ( moving_average(to_string(__symbol), PERIOD_CURRENT, (int)__slow_ma_period, MODE_SMA, PRICE_CLOSE, 0) ));

  block_b0Wkfq6OD();

}

//Function of the block boX0sSwri (telegram_send_chart_screenshot):
void block_boX0sSwri(){

  telegram_send_chart_screenshot(to_string(__symbol), 2160, 720, (int)__admin_id);

}

//Function of the block bofgi9HOT (interface_element_modify):
void block_bofgi9HOT(){

  modify_text("bLcIIkYqO", DoubleToString(__ma_slow, 2) );

}

//Function of the block bugmLdNsU (place_market_order):
void block_bugmLdNsU(){

  place_market_order(to_string(__symbol), "SELL", __full_lot);

  block_bX2tY0y68();
  block_bWkG0nSQa();
  block_bTs8fZtAO();
  block_b3crj4ayK();

}

//Function of the block bvIvs7SMe (condition):
void block_bvIvs7SMe(){

  if (( __ma_fast < __ma_slow ) && ( __ma_fast_prev > __ma_slow_prev ) && ( get_position_info(to_string(__symbol), "POSITION_DIRECTION") != SELL_DIRECTION )){

    block_bugmLdNsU();

  }

}

//Function of the block bzVyOb4tW (pause):
void block_bzVyOb4tW(){

  pause(1000);

  block_biLE3RJAD();

}

在 BotBrains 编辑器中,这样的交易机器人只需花费几分钟即可构建完成,而代码生成只需几分之一秒。

可用的模块

BotBrains 编辑器中有 140 多个可用模块。 下面是所有可用模块的完整清单。 请注意,为了充分理解如何使用编辑器,强烈建议阅读文档。

事件模块:

模块 说明
Start of robot work
一旦机器人被启动,该模块即被激活。
End of robot work
机器人关闭后,该模块即被激活。
Depth of market change
每当指定品种市场深度发生变化时,该模块即被激活。
New tick
每当机器人启动所处图表上的品种上收到新的即时报价时,该模块即被激活。
Open volume change
每次指定品种的开仓量发生变化时,该模块即被激活。
Limit order number change
每当指定品种的有效限价订单数量发生变化时,该模块即被激活。
Stop orders number change
每当指定品种的有效停损订单数量发生变化时,该模块即被激活。
Timer
每隔指定秒数,该模块被激活一次。
Key press
每当指定代码的键被按下时,该模块即被激活。

条件:

“if” 模块用于检查某些条件。
模块 说明
"If" block
该模块用于执行各种检查。 该模块有 1 个输入和 2 个输出。

Loop:

循环模块主要用于列举某些值。 例如,循环模块可用于列举有效的限价订单列表。

模块
说明
Loop "While"
只要指定的条件为真,该模块即被激活。

趋势指标:

模块
Adaptive Moving Average(自适应移动均线)
Average Directional Movement Index(平均定向走势指数)
Average Directional Movement Index by Welles Wilder(韦尔斯·怀尔德的平均定向走势指数)
Bollinger Bands(布林带)
Double Exponential Moving Average(双重指数移动平均线)
Envelopes(轨道线)
Fractal Adaptive Moving Average(分形自适应移动平均)
Ichimoku
Moving Average(移动均线)
Parabolic SAR(抛物线转向)
Standard Deviation(标准背离)
Triple Exponential Moving Average(三重指数移动平均线)
Variable Index Dynamic Average(可变指数动态平均线) 

振荡器:

模块
Average True Range(平均真实范围)
Bears Power(看跌推力)
Bulls Power(看涨推力)
Chaikin Oscillator(Chaikin 振荡器)
Commodity Channel Index(商品通道指数)
DeMarker
Force Index(强推指数)
MACD
Momentum(动量)
Moving Average of Oscillator(振荡移动平均)
RSI (Relative Strength Index - 相对强度指数)
Relative Vigor Index(相对能量指数)
Stochastic Oscillator(随机振荡器)
TRIX (Triple Exponential Moving Averages Oscillator - 三重指数移动平均线振荡器)
Larry Williams' Percent Range(拉里·威廉姆斯的百分比范围)
Accumulation / Distribution(建仓/派发)
Money Flow Index(资金流动指数)
On Balance Volume(平衡量)
Volumes(交易量)

Bill Williams(比尔·威廉姆斯):

模块
Accelerator Oscillator(加速振荡器)
Alligator(鳄吻)
Awesome Oscillator(动量振荡器)
Fractals(分形)
Gator(鳄鱼)
Market Facilitation Index(市场促进指数)

图表分析:

获取有关图表和单根柱线图的信息。 在图表上绘制垂直线和水平线。

模块 说明
Bar information
获取指定图表柱线信息
Chart information
获取指定图表信息 例如,获取可用柱线数量、第一根或最后一根可用柱线的时间。
Max price
获取指定时间段内该品种的最高价格。
Min price
获取指定时间段内符号的最低价格。
Average price
获取指定时间段内该品种的平均价格。
Draw horizontal line
在品种的图表上绘制一条水平线。
Draw vertical line
在品种的图表上绘制一条垂直线。
Remove all lines
从图表上删除所有水平线和垂直线。

图表分析:

获取有关市场深度及其具体报价的信息。

模块
说明
Quote info
获取 DOM(市场深度)指定报价的信息
Spread
获取指定品种的 DOM 的点差值(即时报价内)。

业务:

下达市价、限价和停损订单。 删除限价或停损订单。 平仓。

模块 说明
Market order
下达市价订单。
Limit order
下达一笔限价订单。
Remove limit order
删除限价订单
Remove all limit orders
删除指定品种的所有限价订单
Stop order
下达停损订单。
Remove stop order
删除一笔停损订单
Remove all stop orders
删除指定品种的所有停损订单
Close position
依据指定品种平仓。
Close all open positions
由机器人开立的所有持仓平仓。

变量:

模块
说明
Set variable simple value
变量的新值由单个输入字段指定。 也就是说,有了这个模块,您可以为变量编写一些特定内容 — 譬如一个数字或一段文本。
Set variable complex value
变量的新值由计算值判断。 例如,利用此模块,可以将当前资金值或品种的当前价格写入变量。
Variable select(变量选择)
选择变量。 例如,“variable select” 模块可在条件模块内用于检查变量的值。

声音:

模块
说明
Smooth sound
播放 "Smooth" 声音
Alarm
播放 "Alarm" 声音
Buy signal
播放 "Buy signal" 声音
Sell signal
播放 "Sell signal" 声音

信心:

获取有关账户、持仓、有效和历史限价/停损订单、历史成交的信息。 获取有关交易时段、品种规格和时间的信息。

模块
Account information(账户信息)
Position information(持仓信息)
Limit order information(限价订单信息)
All limit orders information(所有限价订单信息)
Stop order information(停损订单信息)
All stop orders information(所有停损订单信息)
History limit order information(历史限价订单信息)
History stop order information(历史停损订单信息)
History deal information(历史成交信息)
Trading session information(交易时段信息)
Symbol information(品种信息)
Time information(时间信息)

枚举:

枚举模块用于列举某些内容。 例如,您可以利用枚举模块列举品种清单或有效限价/停损订单清单。

模块 说明
Symbol name
获取品种名称。
Request active orders list
请求有效订单列表。
Active limit order ticket
获取有效限价订单票据。
Active stop order ticket
获取有效停损订单票据。
在列举有效订单清单之前,请确保使用 “Request list active orders list” 模块。 否则,您处理的就是不相关的数据。

历史:

模块 说明
Request history
请求指定时间段的历史记录。
History deal ticket
获取历史成交票据。
History limit order ticket
获取历史限价订单票据。
History stop order ticket
获取历史停损订单票据。
在列举历史订单和成交清单之前,请确保使用 “Request history” 模块。 否则,您处理的就是不相关的数据。

Telegram:

使用特殊模块,您的机器人可以直接向 telegram 发送信息和图表截图。

模块
说明
Send message
向 telegram 的指定 ID 用户发送消息。
Send chart screenshot
向 telegram 的指定 ID 用户发送图表截图。
New line
此模块用于在 Telegram 消息中创建换行符。

其它模块:

模块 说明
Journal message
将消息打印到终端日志。
Terminal alert
在终端发出通知。
Chart comment
在指定品种的图表上显示注释。
Log to file
编写日志并记录到机器人的日志文件当中。
Pause
机器人暂停指定的毫秒数。
Turn the robot off
关闭机器人。
Close terminal
关闭终端。

界面元素:

模块
说明
Rectangle(矩形)
"矩形" 界面元素。
Button(按钮)
"按钮" 界面元素。
Text(文本)
"文本" 界面元素。
数值输入
"数值输入" 界面元素。

界面元素修改:

模块
说明
Modify interface element
修改界面元素的特定属性。

界面元素信息:

模块
说明
Interface element info
该模块返回含有指定 ID 界面元素的指定属性的值。

预定义常量:

预定义某些属性可能的常量值。 例如,“direction” 模块包含可能方向的预定义常量:买入、卖出、无方向。

模块
说明
Direction
可能的仓位方向(买入、卖出、无方向)
Deal entry
可能的成交入场方向(入场、离场、逆转、以逆反仓位平仓)
Deal type
可能的成交类型(买入、卖出、余额、信用抵扣、更正、等等)

Debug:

“print debug info(打印调试信息)”有多种用途。 它的主要用途可检查变量的值,和模块的返回值。

模块 说明
Print debug info
将特定消息打印到终端日志。

数学运算符:

模块 说明
+
加法
-
减法
/ 除法
* 乘法

平方根
^ 指数幂
%
除法余数
( 左括号
) 右括号
> 大于
< 小于
>= 大于或等于
<= 小于或等于

逻辑运算符:

模块 说明
AND 逻辑"与"
OR 逻辑"或"
NOT 逻辑"非"

Teleports:

模块
Teleport IN(接收)
Teleport OUT(发送)
变量或常量选择:
模块
Variable select(变量选择)
Constant select(常量选择)

数值输入:

模块 说明
数值输入
该模块可用于在规划图中正确设置某些值。

类型转换:

默认情况下,所有数据都以数字表示。 利用类型转换模块,您可以显式指定必须由特定格式表示的值。

模块 说明
Regular string
将该值转换为常规字符串。
Date and time format string
将该值转换为日期和时间格式。
Integer
将该值转换为整数。
Fraction
将该值转换为分数。

结束语

完成这个项目花费数月的时间。 如果您对编者的工作有任何想法或建议,请发电子邮件至 support@botbrains.app。 您的意见会加以充分考虑。

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/9998

针对交易的组合数学和概率论(第四部分):伯努利(Bernoulli)逻辑 针对交易的组合数学和概率论(第四部分):伯努利(Bernoulli)逻辑
在本文中,我决定重点阐述著名的伯努利(Bernoulli)规划案,并展示如何用它来描述与交易相关的数据数组。 所有这些将被用来创建一个自适应的交易系统。 我们还将寻找一个更通用的算法,一个特例是伯努利公式,并查找能够运用它的应用。
预测市场价格的通用回归模型(二):自然、技术和社会暂态函数 预测市场价格的通用回归模型(二):自然、技术和社会暂态函数
本文是前一篇文章的逻辑延续。 它彰显一个事实,即确认第一篇文章的结论。 这些事实在该书出版后的十年内就得以显露。 它们围绕着三个检测到的描述市场价格变化形态的动态暂态函数展开。
针对交易的组合数学和概率论(第五部分):曲线分析 针对交易的组合数学和概率论(第五部分):曲线分析
在本文中,我决定进行一项研究,探讨将多重状态系统简化为双重状态系统的可能性。 本文的主要目的是分析并推导出有用的结论,这些结论也许有助于基于概率论的可伸缩交易算法的深入发展。 当然,这个话题会涉及到数学知识。 不过,根据之前文章的经验,我认为广谱信息比细节作用更大。
更好的程序员(第 07 部分):变为一名成功的自由职业开发者的注意事项 更好的程序员(第 07 部分):变为一名成功的自由职业开发者的注意事项
您想成为一名成功的 MQL5 自由开发者吗? 如果答案是肯定的,这篇文章适合您。