从头开始自学MQL5语言 - 页 52

注意在位置循环中按符号和按向导过滤。如果没有过滤器,但你在所有符号上拖曳所有未结头寸,这就不好了。

因此,乍看之下,一切似乎都很好。

Совершение сделок - Торговые операции - Справка по MetaTrader 5
Совершение сделок - Торговые операции - Справка по MetaTrader 5
  • www.metatrader5.com
Торговая деятельность в платформе связана с формированием и отсылкой рыночных и отложенных ордеров для исполнения брокером, а также с управлением текущими позициями путем их модификации или закрытия. Платформа позволяет удобно просматривать торговую историю на счете, настраивать оповещения о событиях на рынке и многое другое. Открытие позиций...
Andrei Novichkov:

注意在位置循环中按符号和按向导过滤。如果没有过滤器,但你在所有符号上拖曳所有未结头寸,这就不好了。

所以,一切乍看之下似乎都很好。

非常感谢你,安德烈!我明白关于魔术的一切,因为一个符号可能会有几个位置,但另一个问题出现了。如果没有明确指出当前的符号,专家顾问是否会一次性浏览所有符号的开仓?而这尽管是为某一货币对设置的,例如,欧元兑美元?说实话,我不太理解这一点。

问候,弗拉基米尔。

MrBrooklin:

非常感谢你,安德烈!我明白关于魔术的一切,因为在一个符号上可以建立几个头寸,但我有另一个问题。如果没有明确指出当前的符号,EA是否会一次性查看所有符号的未结头寸?而这尽管是为某一货币对设置的,例如,欧元兑美元?说实话,我不太理解这一点。

真诚的,弗拉基米尔。


是的,它被设置在所有符号的所有开放位置。
始终如一地对所有开放的职位。
这里是在教科书中进行的简单拖放。

https://book.mql4.com/ru/build/trading
MrBrooklin:

因此,根据我阅读的文献,我写了一个简短的算法,用于创建一个 带有尾随止损功能的专家顾问。

  1. 让我们创建一个专家顾问来自动追踪 止损水平 损失 ,对已经开仓的头寸指定止盈和 止损水平 。
  2. 在专家顾问中,创建一个有两个参数的输入参数块:设置 "尾随水平 "和设置 "尾随步骤"。
  3. 当有新的报价进来时,用OnTick( )函数处理它们。只有当当前符号出现新的刻度线时,拖曳才起作用。
  4. 让我们创建并运行一个循环来搜索所有的位置。
  5. 如果我们突然发现没有空仓,我们就会返回到循环。
  6. 我们刷新了报价。
  7. 如果有一个空缺职位,我们就继续。
  8. 我们定义未结头寸的类型: 买入 或卖出。
  9. 如果有一个未平仓的 买入头寸 ,我们定义当前价格相对于未平仓头寸 的位置。
  10. 如果目前的价格高于开仓的价格,我们要检查它在什么水平上上涨。
  11. 如果当前价格已经达到输入参数中定义的 "追踪水平",我们将 止损 损失 ,移动 到没有损失的水平,相当于 买入 头寸的开盘价 。否则我们什么都不做。
  12. 如果当前价格超过追踪止损水平,其数值 等于追踪止损水平,则止损 , 从 买入 头寸的开盘价水平 ,其数值等于追踪止损水平,以此类推,直到价格达到为该头寸指定的止盈水平。
  13. 如果价格转向并达到已经移动的 止损 水平 ,则关闭头寸 。
  14. 如果 是卖出,我们定义当前价格相对于未平仓头寸的位置 。
  15. 如果目前的价格低于开仓的价格,我们就检查它在什么水平上下跌。
  16. 如果当前价格已经达到输入参数中指定的尾随水平,我们将 止损 损失 到没有损失的水平,等于卖出 头寸的开盘价 。否则我们什么都不做。
  17. 如果当前价格已经超过了追踪止损水平,其数值 等于追踪止损水平,则止损 , 从开仓 卖出 水平 ,其数值等于追踪止损水平,以此类推,直到价格达到 为该仓位指定的 止盈水平。
  18. 如果价格转向并达到 止损 的水平 ,则平仓 。

请回顾一下这个算法,给我一些提示,告诉我有哪些地方被遗漏了。

真诚的,弗拉基米尔。

理论还不错,现在让我们关注一下实践。它能起作用吗?

Aliaksandr Hryshyn:

理论上很好,现在是实践。你能做到吗?

我试试。但你要明白,这需要完全不同的知识水平,而我还没有这个能力。

问候,弗拉基米尔。

Aleksey Masterov:

是的,在所有符号上的所有开放姿势上...
在所有开放的姿势中都是如此。
这里有一个已经在教科书中的简单拖网。

https://book.mql4.com/ru/build/trading

是的,阿列克谢,我已经看过这个代码了。它是以包含文件的形式存在的。说实话,虽然我看了好几遍,但我没有发现里面有关于这个符号的东西。也许我误解了什么,或者只是搜索得不好。

真诚的,弗拉基米尔。

现在,让我们继续讨论这些功能。

正如我之前写的,功能无处不在,你必须热爱它们,你必须知道如何写它们。职能部门,是我们解决全球问题的小斗士。如果我们是军队中的将军,我们想控制什么样的战士?这里有一个粗略的清单。

  • 一个战士必须明确地执行命令。一个步兵的平均智力水平并不高。因此,最好为这类战斗人员设定明确而简单的目标:"拿下一个掩体","获得一条舌头","在一座桥上布雷"。
  • 如果任务很困难,不要寻找一个超级聪明的战士来完成它。最好是把任务分成几个子任务,采取两三个比较愚蠢但更有效的战士。让每个人在没有任何问题的情况下解决他们的子任务,或者最好让他们对整个概念和任务一无所知。那么如果有人被俘,就不会有问题,整个计划就不会被暴露。
  • 无论周围环境如何,士兵都必须遵守命令:雪、雨、巴黎和女人--如果这种环境对命令的执行没有影响,那么这些条件和外部环境就必须被忽略。
  • 碰巧的是,这些任务可能很困难。它们需要许多战士来解决。不能给每个战士分配一名将军。相反,你必须指派一个更聪明的士兵来领导几个战斗人员。这群人又与同样的人联合起来组成一个士兵连,并任命他们为高级军官。

但我们走偏了,让我们再来谈谈功能。

如果一个函数在一般情况下解决了太多的问题--按照这个比喻,它是一个非常聪明的战士,如果它出了问题,可能会毁了整个企业。如果你问这样一个函数是做什么的,答案可能很长。如果这个函数的结果突然不再正确,将很难找出其中的错误原因(因为有很多任务,很多代码,很多对子程序的调用,到底哪里出了错误,很难理解)。

如果一个函数在周一、周三和周日以及休息日根据我们的 "心情 "计算出正确的结果, 我们可以依赖这个函数吗?想象一下,比如说,OrderSend函数只在星期四开仓,如果定义了一些神奇的参数13。而这根本不是胡说八道或幻想。这种行为可以通过点击手指来安排--只要让函数依赖于外部环境中的一些参数即可。

假设函数。

double sum(double a, double b)
{
   return a+b;
}

将总是返回两个值的总和,不管外部环境如何。这意味着,即使我们在另一个脚本或专家顾问中复制这个函数,它也会在其中完美地工作。这个函数可以写一次,并在我们的许多程序中使用,只需钝化复制即可。我们将始终能够依赖它的结果,因为我们知道它的运作不依赖于任何东西。这样的函数,其结果不依赖于其环境,被称为无副作用 或纯函数。如果我们努力编写纯粹的函数,我们很快就会得到大量的函数。这意味着你可以将它们合并成一个文件,并将它们纳入你的新项目中。这就是所谓的代码重用。我们不会做两次工作。相反,我们使用已经写好的函数,我们知道,其可靠性已经被测试了不止一次。

我们现在来看看反例。

double c = 0.0;
double sum(double a, double b)
{
   return a+b+c;
}

结果似乎是一样的,因为c 总是为零。还是不一定呢?如果有人在某个地方换了C 怎么办?然后呢?如果有人也在某个地方使用外部变量c,但是为了他自己的目的,他有一个不同类型的变量c,比方说字符串,怎么办?合并这两个函数已经不可能了(编译器将不允许声明两个同名的变量)。他们共同的依赖关系也很难解决。我根本不知道该怎么做。例如,我仍然不知道有什么可靠而简单的方法可以让这些功能一起工作。

即使没有其他函数,只有一个函数读取一个外部变量,要把它复制到其他地方也不是那么容易。我们必须同时复制这个函数和它的依赖关系。但是,如果我们把这些函数复制到一个普通的文件中呢?我们在那里得到50或100个这样的功能。而它们中的每一个都与自己复制了一堆自己的因变量。我们得到了一个功能不明确的相关变量的纠结。但这一切是为了什么呢?它能解决什么问题?当你在绝大多数情况下可以不依赖它们时,为什么还要创造不必要的依赖性?

功能还有一个令人惊讶的特点。职能是自我描述的。换句话说,你不必画出一个方案,只需选择好的名字,并将一般的算法划分为函数。下面是一个例子。

void OnTick()
{
   if(SelectFirstPendingOrder(ORDER_TYPE_BUY))
       CancelSelectPendingOrder();
}

我不知道这段代码是做什么的,因为函数都没有写。但如果让我来解读的话,它可能意味着如果第一个<第一张>方向为ORDER_TYPE_BUY的挂单被成功选中,它将被取消(第一个函数选中,第二个函数取消)。由于该代码会在每一个tick运行,无论有多少挂单,每一个都会迟早被取消。这也意味着,任何试图下达待定买入订单的行为都会被抑制--订单会被立即删除。同时,卖出订单也会顺利下达。

只有两行代码和两个函数。而且该算法非同小可,更重要的是,它是可靠的。

在谈到MCL时,我们应该多提一下纯函数。由 于它是一种应用语言,如果不依靠终端提供的数据,就很难写出任何东西。毕竟,这是主要任务:与交易环境适当互动。从形式上看,任何交易环境都是可以改变的:价格、订单数量、余额变化,等等,等等。因此,任何与这种多变的交易环境互动的功能都是不明确的。因为外部贸易环境也可以被视为一些全球变量,它是不断变化的。但是当我们写OrdersTotal()时,我们并不期望这个函数总是返回相同的值。相反,我们希望它能返回自然变化的挂单数量。因此,在MQL中,我们会认为这些函数是干净和可重复使用的,即使它们调用了外部API的函数,如OrdersTotal()。这将是我们合理的放纵。

Vasiliy Sokolov:

让我们继续讨论这些功能...

非常感谢你,Vasily,感谢你不仅与我,而且与那些阅读或将阅读这个主题的新手程序员 分享无价的知识

同样非常尊重,弗拉基米尔。

我继续学习MQL5编程语言。虽然对Trailing_Stop Expert Advisor的代码编写算法没有认真的评论(我记得符号和Magic,我以后会把它添加到算法中!),但我为EA创建了输入参数,并编写了开始搜索未结头寸的循环代码。

当我运行该EA时,我看到了一个问题--在交易终端的 "专家 "选项卡中,每一个刻度都会出现两条相同的信息 "一个循环已经开始",尽管交易终端只有一个货币对欧元兑美元 的图表,而且只在上面开了一个头寸。而且这些信息的输出时间完全相同。

我一直战斗到半夜,但没能获胜。我无法理解问题出在哪里。


专家顾问的代码是用英语写的,而评论是用俄语写的,以便使这个过程更容易。在本EA中,我试图按照我之前的承诺,以编程学校一年级学生可以理解的方式描述一切。

问候,弗拉基米尔。

//+------------------------------------------------------------------+
//|                                                Trailing_Stop.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

input ushort TrailingLevel=100; //Уровень трейлинга (для включения)
input ushort TrailingStep=10;   //Шаг трейлинга (для перемещения)
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   /* Для цикла for создаем локальную переменную i и присваиваем ей значение "торговая функция PositionsTotal",
      которая возвращает нам количество открытых позиций*/
   int i=PositionsTotal();
   /* Разберемся, что такое оператор for.
      Оператор for состоит из трех Выражений и выполняемого Оператора:
      for(Выражение_1; Выражение_2; Выражение_3)
         Оператор;
      Выражение_1 описывает инициализацию цикла. За инициализацию цикла будет отвечать "Торговая функция PositionsTotal".
      Выражение_2 проверяет условия завершения цикла. Если оно истинно, то выполняется Оператор в теле цикла for.
      Все повторяется до тех пор, пока Выражение_2 не станет ложным. Если оно ложно, цикл заканчивается
      и управление передается следующему оператору.
      Выражение_З вычисляется после каждой итерации (т.е. после каждого повторения действия).
   */
   for(i; i>=0; i--) //запускаем цикл перебора открытых позиций (i) от максимума до нуля (i>=0) с шагом минус 1 (i--)
      Print("Запущен цикл");
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---

  }
//+------------------------------------------------------------------+
MrBrooklin:


i等于开放职位 的数量,因此许多周期将与印刷有关。

Print("Запущен цикл");
你需要删除"="符号,在
   for(i; i>=0; i--)
当未结头寸数量为0时,你为什么需要通过循环。这个零调用是第二个打印的来源。