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-versionsの時代ではありません。

 
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:

初心者が最初に書くのは、トレーディングスクリプトです。2つ目は、シンプルな指標です。3つ目は、シンプルなExpert Advisorです。

MQL4の最初のステージでさえ、MQL5より高速です。

トレーディングスクリプト?Expert AdvisorやStandard Libraryの 代わりになるものはないのでしょうか?

私の考えでは、トレーディングスクリプトを書くことは、明らかに中級者向けの作業であり、決して初心者には向いていません。

初心者は、まず一番簡単なライン出力に限ります。続いて-シンプルなインジケーターです。そして初めて、インジケーターや取引機能のリクエストに移ります。

 
Mickey Moose:

アセンブラはどの程度複雑なのか、機能を教えてほしい。

どんな機能?

アセンブラは、「その関数が必要」というには特殊すぎるのです。

 

SBがフォーラムで初心者に何を売っているのか、なぜlazybonesから遠い人でも本当に苦労しているのか、というテーマで新鮮な例を挙げています。今日、MQL5について、愚痴をこぼすことなく、非常に建設的な質問をいただきました。

トレーディング、自動売買システム、トレーディング戦略のテストに関するフォーラムです。

バグ、バグ、質問

ダミルカ さん 2018.01.09 12:14

こんにちは!https://www.mql5.com/ru/articles/100 からMQL5の勉強を始めました。コードを起動したら、エラー4756が発生しました。まずは簡単なもの(Alert/Print...)から始めてみようと思ったのです。最も重要な機能のひとつがOrderSendです。OrderSendの使い方について、フォーラム/ドキュメントを検索し始めました。この記事https://www.mql5.com/ru/docs/constants/tradingconstants/enum_trade_request_actions、 買いポジションを開くためのコードが見つかりました。エラー4756とretcode10030が出ました。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)は見えません。指をさして説明してください。


司会者回答

トレーディング、自動売買システム、ストラテジーテスターに関するフォーラム

エラー、バグ、質問

ウラジミール・カルプトフ さん 2018.01.09 12:20


CTradeのトレードクラスを使用することで、間違いをできるだけ少なくすることができます。

買いを開くための取引注文を送信する例。


おそらく、整理するのに役立ったのでしょう。しかし、実はこのルーキーには、とても深刻な問題があるのです。

つまり、指定されたトピックのリンクを読みに行く必要があるのです。SBでは、これを素晴らしい場を通じて解決しています。つまり、最もシンプルなトレーディングスクリプト(初心者が最初に使うプログラム)を書くのは、ほとんど簡単なことなのです。

 
George Merts:

トレードスクリプト ?Expert Advisorや、Standard Libraryの 代わりになるものはないのでしょうか?

私の考えでは、トレードスクリプトを書くのは明確な中級者向けの作業であり、初心者には決して向いていません。

初心者は、まず一番簡単なライン出力に限ります。続いて-シンプルなインジケーターです。そして初めて、インジケーターや取引機能のリクエストに移ります。

早速、多通貨MTF Expert Advisorを使い始めました。そして、新人のブランチでもいい感じでした。そして、あまり多くの質問をしなかったのは、どこかにヘルプと脳みそがあるから・・・。

 

あ、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);         
};