Progetto del consigliere - pagina 3

 
Alexey Volchanskiy:

E poi devi cercare a cosa si riferiscono quelle quattro parentesi in basso.

A proposito, mi rende molto nervoso quando l'annidamento è più di due livelli. Cerco di non scriverlo mai in questo modo, distribuendo il codice sulle funzioni.

E anche quando ci sono due livelli di annidamento - assicuratevi di scrivere commenti dopo ogni parentesi di chiusura - quale blocco seppellisce (diciamo, l'intestazione di un ciclo duplicato).

Per quanto riguarda lo stile, ecco il mio codice per selezionareuna posizione storica per MT5 (da un mago specificato, simbolo, con un intervallo di date specificato):

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());
};

La classe storia stessa è una discendente dell'interfaccia astratta 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;
};

Selezionando la storia richiesta - è possibile ricalcolare i suoi componenti (posizioni per MT5 o ordini per MT4), e ottenere un'interfaccia a qualsiasi componente come interfaccia astratta:

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;  
  
};

Per MT4 - ci sono classi di storia corrispondenti anche ereditate da queste interfacce - così, allo stesso tempo, la multipiattaforma è fornita - un EA non ha bisogno di scoprire dove lavora, tutto il lavoro con la storia è fatto attraverso le interfacce astratte.

 
Vitaly Muzichenko:

Non scrivere funzioni che sono sempre costanti e non cambiano mai in questo stile

Scrivili in modo conciso, tanto nessuno li guarda mai, e ci vogliono la metà delle righe

Dato che queste funzioni non cambiano mai, perché ci mettete un mucchio di parentesi graffe inutili? Rimuoveteli e tutto si ridurrà da solo. Perché il tuo esempio sembra assurdo: tu stesso hai offuscato il codice e poi inventi delle stampelle per ridurlo.
 
Alexey Navoykov:
Dato che queste funzioni non cambiano, perché hai messo un mucchio di parentesi graffe inutili? Rimuoveteli e tutto sarà compresso. Perché il tuo esempio sembra assurdo: tu stesso hai offuscato il codice e poi inventi delle stampelle per ridurlo.

Sono d'accordo, si possono tagliare altre 3 righe, e accorciare il codice, ma lo scopo non era quello di mettere il codice in uso, infatti non è nemmeno mio, ma di accorciare, e tali funzioni possono essere messe cinque in una schermata, non una. Dopo di che i programmi sono più facili da leggere e non devi scorrere 150 volte. E il peso del file diminuisce.

 
George Merts:

Buon lavoro, mi piace, ma non mi piace l'OOP e cerco di farne a meno. Non mi piacciono i processori con threads splitting (per esempio, 4 core e 8 threads). Deve essere chiaro che lo splitting e qualsiasi virtualizzazione è una perdita di prestazioni e perdita di tempo macchina per la sua implementazione, sia che si tratti di threads splitting nel kernel o di virtualizzazione di funzioni nel codice.

Vitaly Muzichenko:

Sono d'accordo, si possono tagliare altre 3 righe, e accorciare il codice, ma lo scopo non era quello di mettere il codice in uso, infatti non è nemmeno mio, ma di accorciare, e tali funzioni possono essere messe cinque in una schermata, non una. Dopo di che i programmi sono più facili da leggere e non devi scorrere 150 volte. E il peso del file è ridotto.

La brevità è la sorella del talento, penso che suoni meglio.

Sinceramente.
 
Vitaly Muzichenko:

Schermo di lavoro da 27 pollici

Non ho intenzione di rileggerlo, mi limiterò a citare:"Non scrivere funzioni che sono sempre costanti e non cambiano mai in quello stile"

Perché puntare gli occhi su una funzione che viene scritta una volta sola quando la piattaforma viene rilasciata e non cambierà mai in futuro? Modificate spesso il codice nelle funzioni per ottenere la dimensione del lotto, il numero di ordini e il tipico? Allora perché allungarlo su 3 schermi di un monitor 32"?

P.S. Il codice allegato è falsificato da kodobase.


Contro domanda ))) Ho tali funzioni sono nel file MyFunc.mqh, non vedo il minimo senso nel comprimerlo. Perché, per risparmiare 10-20 KB sul disco? E francamente parlando, tale codestream mi fa star male ))

 
Alexey Volchanskiy:

Contro domanda ))) Ho tali funzioni nel file MyFunc.mqh, non vedo il minimo senso nel comprimerlo. Perché, per risparmiare 10-20 KB sul disco? Per essere onesti, questo codestream mi fa star male )).

Anche io uso gli include file, è comodo. Specialmente quando si scrive uno script personalizzato, ci sono un sacco di funzioni identiche ed è stupido scrivere una e la stessa cosa, basta includere il file e la funzione è nel vostro EA.
Per quanto mi riguarda, il codice dovrebbe essere chiaro, breve, veloce da lavorare e dovrebbe funzionare in tutte le condizioni senza errori.


Sinceramente.

 
Alexey Volchanskiy:

Contro domanda ))) Ho tali funzioni nel file MyFunc.mqh, non vedo il minimo senso nel comprimerlo. Perché, per risparmiare 10-20 KB sul disco? E francamente parlando, un tale codestream mi fa star male ))

Il programmatore ha scritto che il principio "porto tutto io"; l'intero codice dell'Expert Advisor è stipato in un unico file. Di conseguenza, copia tutte queste funzioni in ogni EA.
Quindi, conta: 1000 EAs x 10 Kb = 10 Mb - devi già pensare ad economizzare ))
 
Alexey Volchanskiy:

Contro domanda ))) Ho tali funzioni nel file MyFunc.mqh, non vedo il minimo senso nel comprimerlo. Perché, per risparmiare 10-20 KB sul disco? E francamente parlando, tale codestream mi fa star male ))

Anch'io, ma molto tempo fa sono arrivato alla conclusione che il codice deve essere compatto in posti dove non lo si guarda mai, dove non viene mai corretto e mai lo sarà.

La dispersione del codice utente con tutti questi slot è un ulteriore mal di testa, perché sarà necessario trascinare e rilasciare i file in diversi terminali o condividerli. Naturalmente, potete trasferire gli includenti a tutti i terminali, ma se cambiate o aggiungete qualcosa in un terminale, allora tutti devono essere sostituiti con uno nuovo.

Gli Expert Advisors e gli indicatori sono così piccoli che non ha senso distanziarli dal corpo del programma. Per essere più corretti, non sono piccoli, sono file singoli, non è come un sito con 10 000 pagine dove non si può fare a meno di classi e inludi. Inoltre, ci sono strutture ora, e sono sufficienti per scrivere codice compatto e funzionante al 100%.

 
George Merts:

A proposito, mi rende molto nervoso quando l'annidamento è più di due livelli. Cerco di non scriverlo mai in questo modo, distribuendo il codice sulle funzioni.

E anche quando ci sono due livelli di annidamento - assicuratevi di scrivere commenti dopo ogni parentesi di chiusura, quale blocco seppellisce (per esempio, l'intestazione di un ciclo duplicato).

Per quanto riguarda lo stile, ecco il mio codice per selezionare laposizione storica per MT5 (per mago specificato, simbolo, con intervallo di date specificato):

La classe storia stessa è una discendente dell'interfaccia astratta CTradeHistoryI:

Selezionando la storia richiesta - è possibile ricalcolare i suoi componenti (posizioni per MT5 o ordini per MT4), e ottenere un'interfaccia a qualsiasi componente come interfaccia astratta:

Per MT4 ci sono classi di storia corrispondenti che ereditano anche da queste interfacce - così allo stesso tempo viene fornita la multipiattaforma - l'Expert Advisor non ha bisogno di scoprire dove lavora, tutto il lavoro con la storia viene fatto attraverso interfacce astratte.


Sembra bello, possiamo anche guardare TRACE_*** e ASSERT?

 
Vitaly Muzichenko:

Per trascinare un file su un altro terminale, o per condividerlo, è necessario trascinare non solo un file, ma diversi. Potete, naturalmente, trasferire gli inludi a tutti i terminali, ma se cambiate o aggiungete qualcosa in un terminale, allora dovete sostituirlo con uno nuovo in tutti i terminali.

Raccomando di usare collegamenti simbolici o collegamenti di giunzione per la cartella MQL. Tutti i terminali cercheranno in una cartella.