Projekt des Beraters - Seite 3

 
Alexey Volchanskiy:

Und dann müssen Sie nachschlagen, worauf sich die vier Klammern am unteren Rand beziehen.

Übrigens macht es mich sehr nervös, wenn die Verschachtelung mehr als zwei Ebenen umfasst. Ich versuche, es nie so zu schreiben und den Code über Funktionen zu verteilen.

Und selbst wenn es zwei Verschachtelungsebenen gibt - achten Sie darauf, nach jeder schließenden Klammer einen Kommentar zu schreiben, der angibt, welcher Block verschüttet wird (z. B. ein doppelter Schleifenkopf).

Wie für Stil, hier ist mein Code für die Auswahleiner Geschichte Position für MT5 (von einem bestimmten Magier, Symbol, mit einem bestimmten Datum Bereich):

int CMT5TradeHistory::Select(ulong ulMagic,ECurrencySymbol csSymbol,datetime dtFrom = MIN_DATETIME,datetime dtTill = NEVER_EXPIRES)
{
   ASSERT(dtFrom <= dtTill);

   // Очистим список ядер позиции
   m_aoPosCores.Clear();
   
   // Запросим историю ордеров и сделок
   if(HistorySelect(dtFrom,dtTill)!=true)
      return(WRONG_VALUE);
   
   // Соберем тикеты исторических позиций
   // Просмотрим все сделки выхода, и выпишем оттуда тикеты позиций.
   int iHistoryDealsTotal=HistoryDealsTotal();

   CArrayLong   alHistoryPosIDs;
   int iI = WRONG_VALUE;
   ulong ulCurTicket = 0;
   long lCurPosID = 0;
   long lCurMagic = 0;
   long lCurEntry = 0;
   string strCurSymbol;
   
   for(iI=0;iI<iHistoryDealsTotal; ++iI)
      {
      ulCurTicket = HistoryDealGetTicket(iI);
      
      if(ulCurTicket == 0)
         return(WRONG_VALUE);
      
      // Получим направление сделки   
      if(HistoryDealGetInteger(ulCurTicket,DEAL_ENTRY,lCurEntry)!=true)
         {
         TRACE_INTEGER("Не удалось получить направление сделки ! Тикет: ",ulCurTicket);
         continue;
         };
      
      // Проверим направление сделки
      if(lCurEntry != DEAL_ENTRY_OUT)
         continue;
      
      // Получим магик сделки
      if(HistoryDealGetInteger(ulCurTicket,DEAL_MAGIC,lCurMagic)!=true)
         {
         TRACE_INTEGER("Не удалось получить магик сделки ! Тикет: ",ulCurTicket);
         continue;
         };
         
      // Проверим магик
      if(ulMagic != NULL && lCurMagic != ulMagic)
         {
         //TRACE_INTEGER("Сделка не подходит ! Имеет неверный магик ! Magic сделки: ",lCurMagic);
         //TRACE_INTEGER("Требуемый Magic : ",ulMagic);
         continue;
         };
      
      // Получим символ сделки
      if(HistoryDealGetString(ulCurTicket,DEAL_SYMBOL,strCurSymbol)!=true)
         {
         TRACE_INTEGER("Не удалось получить символ ордера ! Тикет: ",ulCurTicket);
         continue;
         };
      
      // Проверим символ
      if(csSymbol != CS_UNKNOWN)
         if(csSymbol == CS_CURRENT)
            {
            if(_Symbol2CurrencyEnum(strCurSymbol) != _Symbol2CurrencyEnum(Symbol()))
               {
               //TRACE2("Symbol выбираемой позиции: ",_Enum2CurrencySymbol(csSymbol));
               //TRACE2("Выбранный ордер имеет неверный символ: ",strCurSymbol);
               continue;
               };
            }
         else 
            {
            if(_Symbol2CurrencyEnum(strCurSymbol) != csSymbol)
               {
               //TRACE2("Symbol выбираемой позиции: ",_Enum2CurrencySymbol(csSymbol));
               //TRACE2("Выбранный ордер имеет неверный символ ! Символ ордера: ",poiBuffer.GetSymbolString());
               continue;
               };
            };

      // Получим ID позиции
      if(HistoryDealGetInteger(ulCurTicket,DEAL_POSITION_ID,lCurPosID)!=true)
         {
         TRACE_INTEGER("Не удалось получить ID позиции ! Тикет: ",ulCurTicket);
         continue;
         };
         
      // Проверим ID позиции
      if(lCurPosID <= NULL)
         continue;        
         
      if(alHistoryPosIDs.Add(lCurPosID)!=true)
         return(WRONG_VALUE);
      };  // цикл перебора всех сделок
   
   // Здесь ID всех позиций собраны в массиве alHistoryPosIDs, необходимо убрать повторения (в позиции может быть много ордеров)
   CArrayLong alUnicalHistoryPosIDs;   
   
   if(_DeleteDoubles(GetPointer(alHistoryPosIDs),GetPointer(alUnicalHistoryPosIDs))!=true)
      return(WRONG_VALUE);

   TRACE_INTEGER("Уникальных ID позиций в истории: ",alUnicalHistoryPosIDs.Total());

   // Здесь массив alUnicalHistoryPosIDs заполнен уникальными ID позиций в истории.
   // Заполним ядра позиции
   CMT5HistoryPositionInfoCore* phpiHistPosCore = NULL;
   
   for(iI=0;iI<alUnicalHistoryPosIDs.Total(); ++iI)
      {
      //TRACE_INTEGER("Выберем позицию: ",iI);
      
      // Выберем очередной тикет
      lCurPosID = alUnicalHistoryPosIDs.At(iI);

      // Позиция является нужной компонентой 
      ASSERT(phpiHistPosCore == NULL);
      
      phpiHistPosCore = new CMT5HistoryPositionInfoCore;
      
      if(phpiHistPosCore == NULL)
         {
         m_aoPosCores.Clear();
         ASSERT_DSC(false,"Не удалось создать объект CMT5HistoryPositionInfoCore по new");
         return(WRONG_VALUE);
         };
      
      ASSERT_MYPOINTER(phpiHistPosCore);

      if(phpiHistPosCore.SelectByID(lCurPosID)!=true)
         {
         TRACE("Не удалось создать выбрать позицию ! Возможно, позиция открыта, и еще не полностью в истории.");
         TRACE_INTEGER("ID невыбранной позиции: ",lCurPosID);
         delete phpiHistPosCore;
         phpiHistPosCore = NULL;
         continue;
         };
      
      ASSERT(phpiHistPosCore.GetTPCOpenTime() > MIN_DATETIME && phpiHistPosCore.GetTPCOpenTime() < phpiHistPosCore.GetTPCCloseTime() && phpiHistPosCore.GetTPCCloseTime() < NEVER_EXPIRES);
      
      // Найдена и выбрана еще одна компонента позиции
      if(m_aoPosCores.Add(phpiHistPosCore) == false)
         {
         delete phpiHistPosCore;
         m_aoPosCores.Clear();
         ASSERT_DSC(false,"Не удалось добавить новый объект в список ядер позиции");
         return(WRONG_VALUE);
         };
      
      phpiHistPosCore = NULL;   
      }; // цикл перебора уникальных PosID

   // TRACE_INTEGER("Ядер в выбранной позиции: ",m_aoPosCores.Total());     
   
   return(m_aoPosCores.Total());
};

Die History-Klasse selbst ist ein Abkömmling der abstrakten Schnittstelle CTradeHistoryI:

class CTradeHistoryI: public CMyObject
{
public:
   void CTradeHistoryI() {    SetMyObjectType(MOT_TRADE_HISTORY_I); };
   virtual void ~CTradeHistoryI() {};
   
   // Выбор существующей истории. 
   // Указывается магик и символ, по которому выбираются исторические ордера, а также промежуток времени, в котором необходимо искать их.
   // Если ulMagic = 0 - выбираются все позиции по всем магикам.
   // Если ECurrencySymbol = CS_UNKNOWN - выбираются все позиции по всем символам
   // Если ECurrencySymbol = CS_CURRENT - запрашивается функция Symbol(), и выбираются все позиции по этому символу
   // Возвращает число компонент позиции внутри истории (может быть нулевым если ничего не найдено) или WRONG_VALUE в случае ошибок
   // NOTE !!! 
   // При выборе - отложенные ордера не учитываются.
   virtual int Select(ulong ulMagic = 0,ECurrencySymbol csSymbol = CS_CURRENT,datetime dtFrom = MIN_DATETIME,datetime dtTill = NEVER_EXPIRES) = 0;

   virtual uint GetTotalComponents() const = 0;  // Получение общего числа компонент
   virtual CHistoryPosComponentI* GetComponent(uint uiComponentIdx) const = 0;
   
   // Расширенный интерфейс
   virtual void Sort(ESortTPCMode stmMode = STM_BY_OPEN_TIME_A) = 0;
   
   
   // Функция ищет внутри истории компоненту с указанным тикетом. 
   // В случае, если ее нет - возвращается false.
   // Если компонента найдена - возвращается true, и uiComponentIdx устанавливается на индекс компоненты внутри позиции.
   virtual bool FindComponentByTicket(long lTicket,uint &uiComponentIdx) const = 0;
};

Durch Auswahl der gewünschten Historie können Sie deren Komponenten (Positionen für MT5 oder Aufträge für MT4) neu berechnen und eine Schnittstelle zu jeder Komponente als abstrakte Schnittstelle erhalten:

class CTradePosComponentI: public CMyObject
{
public:
   void CTradePosComponentI() {    SetMyObjectType(MOT_TRADEPOS_COMPONENT_I); };
   virtual void ~CTradePosComponentI() {};
   
   // Основной интерфейс
   virtual long               GetTPCTicket()       const = 0;
   virtual long               GetTPCMagic()        const = 0;
   virtual ECurrencySymbol    GetTPCSymbol()       const = 0;
   virtual ENUM_POSITION_TYPE GetTPCType()         const = 0;
   virtual datetime           GetTPCOpenTime()     const = 0;
   virtual double             GetTPCVolume()       const = 0;
   virtual double             GetTPCOpenPrice()    const = 0;
   virtual double             GetTPCStopLoss()     const = 0;
   virtual double             GetTPCTakeProfit()   const = 0;
   virtual string             GetTPCCommentary()   const = 0;
   
   virtual bool               IsTPCInUnloss() const { if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE) return(false); if(GetTPCType() == POSITION_TYPE_BUY) { if(GetTPCStopLoss() >= GetTPCOpenPrice()) return(true); } else { if(GetTPCStopLoss() <= GetTPCOpenPrice())return(true); }; return (false); };
   virtual double             GetTPDistance() const { if(GetTPCTakeProfit() == 0 || GetTPCTakeProfit() == EMPTY_VALUE) return(EMPTY_VALUE); if(GetTPCType() == POSITION_TYPE_BUY) return(GetTPCTakeProfit() - GetTPCOpenPrice()); return(GetTPCOpenPrice() - GetTPCTakeProfit());  };
   virtual double             GetSLDistance() const { if(GetTPCStopLoss() == 0 || GetTPCStopLoss() == EMPTY_VALUE) return(EMPTY_VALUE); if(GetTPCType() == POSITION_TYPE_BUY) return(GetTPCOpenPrice()- GetTPCStopLoss()); return(GetTPCStopLoss() - GetTPCOpenPrice());  };
};

class CHistoryPosComponentI: public CTradePosComponentI
{
public:
   void CHistoryPosComponentI() {    SetMyObjectType(MOT_HISTORYPOS_COMPONENT_I); };
   virtual void ~CHistoryPosComponentI() {};

   virtual datetime           GetTPCCloseTime()    const = 0;
   virtual double             GetTPCClosePrice()   const = 0;
   virtual double             GetTPCProfit()       const = 0;  // Возвращает профит по исторической позиции
   
   virtual bool               IsProfitClosePrice() const = 0;   // Возвращает true, если цена зарытия отличается от цены открытия в прибыльную сторону   
   
   // Возвращает профит исторической позиции для случая, когда бы ход цены (в сторону профита) был бы равен dPriceMove, а лот был бы единичным.
   // Функция используется для расчета лота для такой же позиции с нужным ходом цены 
   // Рекомендуется отнимать от цены двойной спред.
   virtual double             CalculateOneLotProfit(double dPriceMove) const = 0;  
  
};

Für MT4 gibt es entsprechende History-Klassen, die ebenfalls von diesen Interfaces geerbt werden - damit ist gleichzeitig die Cross-Plattformalität gegeben - ein EA muss nicht herausfinden, wo er arbeitet, die gesamte Arbeit mit der History wird über die abstrakten Interfaces erledigt.

 
Vitaly Muzichenko:

Schreiben Sie keine Funktionen, die immer konstant sind und sich nie ändern, in diesem Stil

Schreiben Sie sie kurz und bündig, niemand schaut sie sich an, und sie brauchen nur halb so viele Zeilen.

Da sich diese Funktionen nie ändern, warum setzen Sie dort einen Haufen unnötiger geschweifter Klammern? Entfernen Sie sie, und alles wird von selbst schrumpfen. Denn Ihr Beispiel wirkt absurd: Sie selbst haben den Code verwischt und erfinden dann Krücken, um ihn zu reduzieren.
 
Alexey Navoykov:
Da sich diese Funktionen nicht ändern, warum haben Sie dort einen Haufen unnötiger geschweifter Klammern gesetzt? Entfernen Sie sie, und alles wird komprimiert. Denn Ihr Beispiel wirkt absurd: Sie selbst haben den Code verwischt und erfinden dann Krücken, um ihn zu reduzieren.

Ich stimme zu, man kann 3 weitere Zeilen herausschneiden und den Code kürzen, aber der Zweck war nicht, den Code zu verwenden, er ist nicht einmal meiner, sondern ihn zu kürzen, und man kann fünf solcher Funktionen in einem Bildschirm unterbringen, nicht eine. Danach sind die Programme leichter zu lesen und man muss nicht 150 Mal blättern. Und das Gewicht der Datei sinkt.

 
George Merts:

Nette Arbeit, gefällt mir, aber ich mag OOP nicht und versuche, darauf zu verzichten. Ich mag keine Prozessoren mit Threadsplitting (z.B. 4 Kerne und 8 Threads). Es muss klar sein, dass Splitting und jede Virtualisierung einen Leistungsverlust und einen Verlust an Maschinenzeit für die Implementierung bedeutet, sei es Threadsplitting im Kernel oder Virtualisierung von Funktionen im Code.

Vitaly Muzichenko:

Ich stimme zu, Sie können 3 weitere Zeilen herausschneiden und den Code kürzen, aber der Zweck war nicht, den Code zu verwenden, es ist in der Tat nicht einmal meine, sondern zu verkürzen, und solche Funktionen können fünf in einem Bildschirm, nicht eine gesetzt werden. Danach sind die Programme leichter zu lesen und man muss nicht 150 Mal blättern. Und das Gewicht der Datei wird reduziert.

Kürze ist die Schwester des Talents, ich finde, das klingt besser.

Mit freundlichen Grüßen.
 
Vitaly Muzichenko:

27"-Arbeitsbildschirm

Ich werde ihn nicht noch einmal lesen, sondern nur zitieren:"Schreiben Sie keine Funktionen, die immer gleich bleiben und sich nie in diesem Stil ändern".

Warum sollte man sich über eine Funktion aufregen, die einmal geschrieben wird, wenn die Plattform freigegeben wird, und die sich in der Zukunft nie ändern wird? Ändern Sie häufig den Code in den Funktionen, um die Losgröße, die Anzahl der Aufträge und die typischen Werte zu erhalten? Warum sollte man sie dann auf 3 Bildschirme eines 32-Zoll-Monitors verteilen?

P.S. Der beigefügte Code ist eine Fälschung von kodobase.


Gegenfrage ))) Ich habe solche Funktionen in der Datei MyFunc.mqh, ich sehe nicht den geringsten Sinn darin, sie zu komprimieren. Warum, um 10-20 KB auf der Festplatte zu sparen? Und offen gesagt, macht mich ein solcher Codestream krank ))

 
Alexey Volchanskiy:

Gegenfrage ))) Ich habe solche Funktionen in der Datei MyFunc.mqh, ich sehe nicht den geringsten Sinn darin, sie zu komprimieren. Warum, um 10-20 KB auf der Festplatte zu sparen? Um ehrlich zu sein, dieser Codestream macht mich krank )).

Ich benutze auch Include-Dateien, das ist praktisch, vor allem wenn man ein eigenes Skript schreibt, es gibt viele identische Funktionen und es ist dumm, ein und dasselbe zu schreiben, man muss nur die Datei einbinden und schon ist die Funktion im EA.
Was mich betrifft, so sollte der Code klar, kurz und schnell sein und unter allen Bedingungen ohne Fehler funktionieren.


Mit freundlichen Grüßen.

 
Alexey Volchanskiy:

Gegenfrage ))) Ich habe solche Funktionen in der Datei MyFunc.mqh, ich sehe nicht den geringsten Sinn darin, sie zu komprimieren. Warum, um 10-20 KB auf der Festplatte zu sparen? Und ehrlich gesagt, ein solcher Codestream macht mich krank ))

Der Programmierer hat sich das Prinzip "Ich trage alles selbst" auf die Fahne geschrieben; der gesamte Code des Expert Advisors ist in eine einzige Datei gezwängt. Dementsprechend kopiert er alle diese Funktionen in jeden EA.
Also, rechnen Sie: 1000 EAs x 10 Kb = 10 Mb - da müssen Sie schon über Einsparungen nachdenken ))
 
Alexey Volchanskiy:

Gegenfrage ))) Ich habe solche Funktionen in der Datei MyFunc.mqh, ich sehe nicht den geringsten Sinn darin, sie zu komprimieren. Warum, um 10-20 KB auf der Festplatte zu sparen? Und offen gesagt, macht mich ein solcher Codestream krank ))

Ich auch, aber ich bin schon vor langer Zeit zu dem Schluss gekommen, dass der Kodex an Orten kompakt sein muss, wo man ihn nie ansieht, wo er nie korrigiert wird und nie korrigiert werden wird.

Das Verteilen von Benutzercode mit all diesen Steckplätzen bereitet zusätzliche Kopfschmerzen, da Sie die Dateien auf verschiedene Terminals ziehen oder gemeinsam nutzen müssen. Natürlich können Sie die Includniks auf alle Terminals übertragen, aber wenn Sie in einem Terminal etwas ändern oder hinzufügen, müssen alle durch ein neues Terminal ersetzt werden.

Die Expert Advisors und Indikatoren sind so klein, dass es keinen Sinn macht, sie vom Hauptteil des Programms zu trennen. Um genau zu sein, sie sind nicht klein, sie sind eine einzige Datei, es ist nicht wie bei einer Website mit 10 000 Seiten, wo man nicht ohne Klassen und Einschübe auskommt. Außerdem gibt es jetzt Strukturen, und die reichen aus, um kompakten, 100 % funktionsfähigen Code zu schreiben.

 
George Merts:

Übrigens macht es mich sehr nervös, wenn die Verschachtelung mehr als zwei Ebenen umfasst. Ich versuche, es nie so zu schreiben und den Code über Funktionen zu verteilen.

Und selbst wenn es zwei Verschachtelungsebenen gibt - achten Sie darauf, nach jeder schließenden Klammer einen Kommentar zu schreiben, der angibt, welchen Block sie verdeckt (z. B. einen doppelten Schleifenkopf).

Wie für Stil, hier ist mein Code für die Auswahleiner Geschichte Position für MT5 (von bestimmten Magier, Symbol, mit bestimmten Datum Bereich):

Die History-Klasse selbst ist ein Abkömmling der abstrakten Schnittstelle CTradeHistoryI:

Durch Auswahl der gewünschten Historie können Sie deren Komponenten (Positionen für MT5 oder Aufträge für MT4) neu berechnen und eine Schnittstelle zu jeder Komponente als abstrakte Schnittstelle erhalten:

Für MT4 gibt es entsprechende History-Klassen, die ebenfalls von diesen Interfaces erben - damit ist gleichzeitig die Cross-Plattformalität gegeben - der Expert Advisor muss nicht herausfinden, wo er arbeitet, die gesamte Arbeit mit der History wird über abstrakte Interfaces erledigt.


Sieht gut aus, können wir uns auch TRACE_*** und ASSERT ansehen?

 
Vitaly Muzichenko:

Um eine Datei auf ein anderes Terminal zu ziehen und dort abzulegen oder sie freizugeben, müssen Sie nicht nur eine Datei ziehen, sondern mehrere. Natürlich können Sie die Inludes auf alle Terminals übertragen, aber wenn Sie in einem Terminal etwas ändern oder hinzufügen, müssen Sie es in allen Terminals durch ein neues ersetzen.

Ich empfehle die Verwendung von symbolischen Links oder Junction Links für den MQL-Ordner. Alle Terminals werden in einem Ordner gesucht.