从头开始自学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时,你为什么需要通过循环。这个零调用是第二个打印的来源。