MT5是为程序员准备的,不是为交易者准备的 - 页 17

 
Andrey F. Zelinsky:

在学习C语言之前,程序员经历了机器命令、汇编、Fortran、PL/1、Pascal和许多其他东西,所有这些都没有教科书,没有适当的解释,正如Volchansky所说,文件是用歪歪扭扭的字母打印出来的,这样的日子早就过去了。

这是一个复杂的多功能应用系统的时代--带有嵌入式应用编程语言--这种语言的任务是使应用终端对应用用户开放,用户无需深挖编程,就能在短时间内快速学习和使用这种终端语言。


我同意!正确的说法是。交易终端 应促进交易。

 
ILNUR777:
你是根据谁来判断的?作为公司的形象代言人,你滑向拖泥带水是不好的。你已经写过,即使是那些有MT4经验的人也有困难。而且他们也不需要。

你看到的是扯皮吗?

我已经写过很多次,有很多机会可以简单轻松地为你的策略编写一个测试EA。

我甚至给了一个帮助的链接。但如果我们开始询问有关帮助的问题,我们会发现我们对这种知识感兴趣。但它一直在说一切是多么糟糕。

这就是为什么我说--也许我们现在这一代人--18岁,父母-媳妇要买车,21岁--买房。但要学习--不,为什么?

 
Mickey Moose:

汇编程序有多复杂? 我需要它的功能

为什么?写在μl5中。这很简单,也很直接。
 
Artyom Trishkin:

因为仅在10年前,初学者在这个论坛上问的问题就大不相同--更复杂、更有趣。

顺便说一下,今天MQL4中的问题比MQL5中的信息量大得多。原因很明显,这不是MQL-版本的时代。

 
Alexey Viktorov:

不,我不知道。你是什么意思?

阿列克谢,真的,我现在不能给你一个例子。但你喜欢实验--运行它,看看它在不同的输入数据下会返回什么。

只是我现在很难记住并寻找它。

好吧,fxsaber给出了一个例子(我为自己修改了一下),比较一下代码。

//+------------------------------------------------------------------+
//| Возвращает смещение бара таймфрейма по времени                   |
//+------------------------------------------------------------------+
int CTimes::BarShift(const string symbol_name,const ENUM_TIMEFRAMES timeframe,const datetime time) const
  {
   int res=WRONG_VALUE;
   datetime last_bar;
   if(::SeriesInfoInteger(symbol_name,timeframe,SERIES_LASTBAR_DATE,last_bar)){
      if(time>last_bar) res=0;
      else{
         const int shift=::Bars(symbol_name,timeframe,time,last_bar);
         if(shift>0) res=shift-1;
         }
      }
   return(res);
  }
//+------------------------------------------------------------------+

这里你可以看到在什么条件下Bars()会返回一个犯规。

 
fxsaber:

初学者写的第一件事是交易脚本。第二个是一个简单的指标。第三种 - 一个简单的专家顾问。

即使是MQL4的第一阶段也比MQL5快。

一个交易脚本?为什么不是专家顾问,甚至是标准库 的替代品?

在我看来,编写交易脚本是一项明确的中级任务,绝不适合初学者。

初学者应首先限于最简单的线上输出。然后--一个简单的指标。然后才是--转到请求指标和交易功能。

 
Mickey Moose:

汇编程序有多复杂? 我需要它的功能。

什么功能?

汇编程序过于具体,不能说 "我需要它的功能"。

 

关于SB在论坛上向新手推销什么的问题,以及为什么即使是远离懒惰的人也真的很难受的问题,这是一个新鲜的例子。今天我得到了一个关于MQL5的非常有建设性的问题,没有任何抱怨。

这是一个关于交易、自动交易系统和交易策略测试的论坛。

虫子,虫子,问题

damirqa, 2018.01.09 12:14

你好!我从https://www.mql5.com/ru/articles/100 开始学习MQL5。我启动了代码,收到了错误4756。我想我应该从简单的东西开始(警报/打印......)。其中一个最重要的功能是OrderSend。我开始在论坛/文档中搜索如何使用OrderSend。我发现这篇文章https://www.mql5.com/ru/docs/constants/tradingconstants/enum_trade_request_actions,并 找到了开设买入头寸的代码。我得到了错误4756和检索码10030。我理解10030--它是OrderSend属性,但我没有理解这个属性应该如何使用(我看了别人的代码),它主要用于什么。然后我打开https://www.mql5.com/ru/docs/trading/ordersend, 复制了代码,启动了它,一切都很好,它工作了。
但我仍然不明白为什么会出现错误4756,以及如何摆脱它,还有10030。

看了一下之间的代码

void OnTick(){
      //--- объявление и инициализация запроса и результата
      MqlTradeRequest request={0};
      MqlTradeResult  result={0};
      //--- параметры запроса
      request.action   =TRADE_ACTION_DEAL;                     // тип торговой операции
      request.symbol   =Symbol();                              // символ
      request.volume   =0.1;                                   // объем в 0.1 лот
      request.type     =ORDER_TYPE_BUY;                        // тип ордера
      request.price    =SymbolInfoDouble(Symbol(),SYMBOL_ASK); // цена для открытия
      request.deviation=5;                                     // допустимое отклонение от цены
      request.magic    =EXPERT_MAGIC;                          // MagicNumber ордера
      //--- отправка запроса
      if(!OrderSend(request,result))
         PrintFormat("OrderSend error %d",GetLastError());     // если отправить запрос не удалось, вывести код ошибки
         Alert(GetLastError());
      //--- информация об операции
      PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
   }

和这个。

uint SendRandomPendingOrder(long const magic_number) 
  { 
//--- готовим запрос 
   MqlTradeRequest request={0}; 
   request.action=TRADE_ACTION_PENDING;         // установка отложенного ордера 
   request.magic=magic_number;                  // ORDER_MAGIC 
   request.symbol=_Symbol;                      // инструмент 
   request.volume=0.1;                          // объем в 0.1 лот 
   request.sl=0;                                // Stop Loss не указан 
   request.tp=0;                                // Take Profit не указан    
//--- сформируем тип ордера 
   request.type=GetRandomType();                // тип ордера 
//---сформируем цену для отложенного ордера 
   request.price=GetRandomPrice(request.type);  // цена для открытия 
//--- отправим торговый приказ 
   MqlTradeResult result={0}; 
   OrderSend(request,result); 
//--- выведем в лог ответ сервера   
   Print(__FUNCTION__,":",result.comment); 
   if(result.retcode==10016) Print(result.bid,result.ask,result.price); 
//--- вернем код ответа торгового сервера 
   return result.retcode; 
  } 

在我看来它们几乎是一样的,我没有看到这些错误出现的地方(4756和10030)。请指证和解释


主持人回答

关于交易、自动交易系统和策略测试器的论坛

错误, 漏洞, 问题

Vladimir Karputov, 2018.01.09 12:20


使用 CTrade 交易类 - 这样可以保证你尽可能少犯错误。

发送交易指令打开买入的例子。


可能有助于解决这个问题。但事实上,这个新秀有一个非常严重的问题。

也就是说,你必须去阅读指定主题的链接。在SB,这一点通过一个奇妙的地方得到了解决。也就是说,在编写最简单的交易脚本(任何初学者的第一个程序)时,几乎是轻而易举的。

 
George Merts:

交易脚本 ??为什么不是专家顾问,甚至是标准库 的替代品?

在我看来,写交易脚本是一项明确的中级任务,绝不适合初学者。

初学者应首先限于最简单的线上输出。然后--一个简单的指标。然后才是--转到请求指标和交易功能。

我马上就开始使用多货币MTF专家顾问。而且我在新手支部感觉很好。而且我没有问太多的问题--我在某处有帮助,有脑子......。

 

哦,顺便说一下,关于iBarsShift

这是我的对应函数(CTSTime是一个时间序列类),我觉得对新手来说有点复杂,即使有注释。所以它真的缺乏SB。

// Функция ищет бар, внутри которого находится указанный момент.
// Если такой бар найден - возвращается true и ссылка rIdx устанавливает нужное значение 
// Если такой бар не найден - возвращается false и ссылка rIdx устанавливает значение на ближайший индекс,
// время которого меньше, требуемого.
// При недостаточности буффера функция возвращает false, ссылка устанавливается на отрицательное значение.   
// NOTE !!! ненаступивший момент может лежать внутри нулевого бара, в этом случае вернется true,
// даже если фактически данный момент еще не наступил. false вернется только если данный момент лежит позже
// нулевого бара. 
// Если искомый момент находится раньше самого раннего бара, возвращается false и  rIdx = INT_MAX

bool CTSTime::FindIdxOf(datetime dtMomentToFind,int& rIdx)
{
   ASSERT(dtMomentToFind > MIN_DATETIME && dtMomentToFind < NEVER_EXPIRES);

   // Пока найденный индекс - невалиден.
   rIdx = WRONG_VALUE;

   // Если данных нет - возвращаем отрицательный результат поиска.
   if(GetTSSize() == 0)
      return(false);

   // Найдем продолжительность текущего бара
   int iSecondsInBar = PeriodSeconds(m_etTimeframe);
   
   ASSERT(iSecondsInBar >= SECS_IN_MINUTE);

   datetime dtMomentOfZeroBar = GetTime(0);
   datetime dtMomentOfLastBar = GetTime(GetTSSize()-1);
   
   // Искомый момент лежит внутри или позже, чем нулевой бар ?  
   if(dtMomentToFind >= dtMomentOfZeroBar)   
      {
      rIdx = 0;
      
      // Искомый момент лежит на открытии минус первого бара или позже ?  
      if(dtMomentToFind >= dtMomentOfZeroBar + iSecondsInBar) 
         return (false);   // Искомый момент лежит после нулевого бара. 
      
      // Искомый момент лежит внутри нулевого бара.
      return(true);               
      };
      
   // Здесь ясно, что искомый момент был ранее нулевого бара. 
   
   // Проверим, может быть искомый момент лежит раньше последнего бара ? 
   if(dtMomentToFind < dtMomentOfLastBar)
      {
      // Увы, такого раннего момента в таймсерии нет. 
      // Возвращаем самый большой индекс бара, какой можем возвратить
      // (Потому, что бара со временем меньше требуемого - в таймсерии нет).
      rIdx = INT_MAX;
      return(false);
      };

   // Здесь ясно, что искомый момент был позже начала самого раннего бара, но раньше начала самого нового бара.

   ASSERT(GetTSSize()  > 1);  // Проверим, размер буффера должен быть минимум два бара. (Иначе условие не выполняется, что искомый момент был позже начала самого раннего бара, но раньше начала самого нового бара, не выполняется)

   // Ищем примерно, где был данный момент.
   ASSERT(dtMomentOfZeroBar > dtMomentToFind);
   ASSERT(dtMomentOfZeroBar > dtMomentOfLastBar);
   
   ulong ulSecFromSearchToLatest = dtMomentOfZeroBar - dtMomentToFind;   
   ulong ulSecFromEarlestToLatest = dtMomentOfZeroBar - dtMomentOfLastBar;
   
   ASSERT(ulSecFromEarlestToLatest > ulSecFromSearchToLatest);
    
   double dResIdx = (double)ulSecFromSearchToLatest*(double)(GetTSSize()-1)/(double)ulSecFromEarlestToLatest;
   
   ASSERT(dResIdx <INT_MAX && dResIdx >= 0);
   
   int iResIdx = (int)MathRound(dResIdx);
   
   ASSERT(iResIdx >= 0 && iResIdx<(int)GetTSSize());   // По идее, мы должны уложиться в этот диапазон.
   
   // Поскольку мы исследуем минимум два бара (текущий и следующий),
   // Текущий бар не должен быть нулевым.
   // Поскольку случай с буффером в один бар у нас был отсеян раньше, мы не должны получить ошибку.
   
   if(iResIdx == 0)  
      iResIdx = 1;

   // Получен приблизительный индекс (uiResIdx).
   
   // Уточняем. Берем два бара, текущий и следующий.
   datetime dtResMoment;
   datetime dtNextMoment;
   
   
   dtResMoment = GetTime(iResIdx);
   dtNextMoment = GetTime(iResIdx-1);
   
   int iShift = 0;
   bool bUp = false;
   bool bDown = false;
   
   do
      {
      if(dtResMoment > dtMomentToFind)       // Если искомый момент раньше начала первого бара
         {
         iShift = 1;                         // Возьмем на один бар раньше.
         bUp = true;                         // Запомним направление
         }
      else                                   // Иначе - Искомый момент равен или позже начала первого бара. 
         {
         if(dtNextMoment <= dtMomentToFind)  // Если искомый момент больше или равен началу второго бара
            {
            iShift = -1;                     // Возьмем на один бар позже
            bDown = true;                    // Запомним направление
            }
         else                          // Иначе - искомый момент равен или позже начала перого бара и раньше начала  второго бара   
            {         
            iShift = 0;                // То есть, можно выходить из цикла коррекции.
            }
         }  

      iResIdx += iShift;               // Смещаемся

      if(iResIdx > (int)GetTSSize() || iResIdx <= 0) // Проверим на допустимый диапазон
         {
         ASSERT(false);    // В серии недостаточно данных (мал буффер)
         rIdx = INT_MAX;
         return(false);
         };
                  
      dtResMoment = GetTime(iResIdx);  // Запрашиваем новые данные 
      dtNextMoment = GetTime(iResIdx-1);
            
      if(bUp == true && bDown == true) // Контроль направления коррекции
         {
         ASSERT(false);                // Сменилось направление коррекции !!!
         iShift = 0;
         }           
      }
   while(iShift != 0);
   
   // В этой точке искомый момент равен или позже начала перого бара и раньше начала  второго бара.
   // Проверим, может быть, искомый момент позже конца первого бара, но раньше начала второго бара 
   // (между барами разрыв, и искомый момент находится именно там)
   
   // Ясно, что ссылка должна показывать на первый бар в любом случае 
   
   rIdx = iResIdx;
   
   if(dtMomentToFind >= dtResMoment+iSecondsInBar)
      // Действительно, искомый момент - внутри разрыва между барами.
      return(false);
   
   return(true);         
};