Projeto do assessor - página 3

 
Alexey Volchanskiy:

E então você tem que olhar para cima o que esses quatro colchetes na parte inferior se referem.

A propósito, fico muito nervoso quando o ninho é mais de dois níveis. Tento nunca escrevê-lo dessa maneira, espalhando o código sobre as funções.

E mesmo quando há dois níveis de encaixe - não deixe de escrever comentários após cada colchete de fechamento - que bloqueiam o encaixe (digamos, cabeçalho de laço duplicado).

Quanto ao estilo, aqui está meu código para selecionaruma posição histórica para MT5 (por um mágico especificado, símbolo, com um intervalo de datas especificado):

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

A própria classe de história é descendente da interface abstrata 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;
};

Ao selecionar o histórico necessário - você pode recalcular seus componentes (posições para MT5 ou pedidos para MT4), e obter uma interface para qualquer componente como uma interface abstrata:

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

Para o MT4 - há classes de história correspondentes também herdadas dessas interfaces - assim, ao mesmo tempo, a cross-platformidade é fornecida - uma EA não precisa descobrir onde trabalha, todo o trabalho com a história é feito através das interfaces abstratas.

 
Vitaly Muzichenko:

Não escreva funções que são sempre constantes e nunca mudam neste estilo

Escreva-os concisamente, ninguém nunca olha para eles de qualquer maneira, e eles pegam metade das linhas

Uma vez que estas funções nunca mudam, por que você coloca um monte de parênteses desnecessários? Remova-os e tudo encolherá por si só. Porque o seu exemplo parece absurdo: você mesmo desfocou o código e depois inventou muletas para reduzi-lo.
 
Alexey Navoykov:
Como estas funções não mudam, por que você colocou um monte de parênteses desnecessários? Removê-los, e tudo será comprimido. Porque o seu exemplo parece absurdo: você mesmo desfocou o código e depois inventou muletas para reduzi-lo.

Concordo, você pode cortar mais 3 linhas e encurtar o código, mas o objetivo não era colocar o código para usar, na verdade ele não é nem meu, mas para encurtar, e tais funções podem ser colocadas cinco em uma tela, não uma. Depois disso, os programas são mais fáceis de ler e você não precisa rolar 150 vezes. E o peso do arquivo diminui.

 
George Merts:

Bom trabalho, eu gosto, mas não gosto do OOP e tento fazer sem ele. Não gosto de processadores com divisão de thread (por exemplo, 4 núcleos e 8 threads). Deve ficar claro que a divisão e qualquer virtualização é uma perda de performance e perda de tempo de máquina para sua implementação, seja ela divisão de thread no kernel ou virtualização de funções no código.

Vitaly Muzichenko:

Concordo, você pode cortar mais 3 linhas, e encurtar o código, mas o objetivo não era colocar o código para usar, ele nem é meu de fato, mas encurtá-lo, e você pode colocar cinco dessas funções em uma tela, não uma. Depois disso, os programas são mais fáceis de ler e você não precisa rolar 150 vezes. E o peso do arquivo é reduzido.

Brevity é a irmã do talento, acho que soa melhor.

Sinceramente.
 
Vitaly Muzichenko:

Tela de trabalho de 27".

Não vou relê-la, vou apenas citá-la:"Não escreva funções que são sempre constantes e nunca mudam nesse estilo"

Por que escolher seus olhos sobre uma função que é escrita uma vez quando a plataforma é liberada e nunca mudará no futuro? Você costuma mudar/editar o código nas funções para obter tamanho de lote, número de pedidos e típico? Então por que esticá-lo através de 3 telas de um monitor de 32"?

P.S. O código anexo é forjado a partir do kodobase.


Pergunta contrária )))) Tenho tais funções no arquivo MyFunc.mqh, não vejo o menor sentido em comprimi-lo. Por que, para economizar 10-20 KB em disco? E francamente falando, tal codestream me deixa doente ))

 
Alexey Volchanskiy:

Pergunta contrária )))) Eu tenho tais funções no arquivo MyFunc.mqh, não vejo o menor sentido em comprimir isto. Por que, para economizar 10-20 KB em disco? Para ser honesto, este codestream me deixa doente )).

Eu também uso incluir arquivos, é conveniente. Especialmente quando você escreve um roteiro personalizado, muitas funções são as mesmas e é bobagem escrever uma e a mesma coisa, basta incluir o arquivo e a função está em seu EA.
Quanto a mim, o código deve ser claro, curto, rápido para trabalhar e deve funcionar em todas as condições sem erros.


Sinceramente.

 
Alexey Volchanskiy:

Pergunta contrária )))) Eu tenho tais funções no arquivo MyFunc.mqh, não vejo o menor sentido em comprimir isto. Por que, para economizar 10-20 KB em disco? E francamente falando, tal codestream me deixa doente ))

O programador escreveu que o princípio "eu mesmo carrego tudo"; todo o código do Expert Advisor está amontoado em um único arquivo. Assim, ele copia todas essas funções em cada EA.
Então, conte: 1000 EAs x 10 Kb = 10 Mb - você já tem que pensar em economizar ))
 
Alexey Volchanskiy:

Pergunta contrária )))) Eu tenho tais funções no arquivo MyFunc.mqh, não vejo o menor sentido em comprimir isto. Por que, para economizar 10-20 KB em disco? E francamente falando, tal codestream me deixa doente ))

Eu também, mas há muito tempo cheguei à conclusão de que o código deve ser compacto em lugares onde nunca se olha para ele, onde ele nunca é corrigido e nunca será corrigido.

Espalhar o código do usuário com todos os inlúdios é uma dor de cabeça adicional, quando você precisa arrastar e soltar um arquivo em outro terminal, ou compartilhá-lo, você precisará arrastar e soltar vários arquivos. É claro que você pode transferir os incluídos para todos os terminais, mas se você mudar ou acrescentar algo em um terminal, então todos eles devem ser substituídos por um novo.

Os Conselheiros Especialistas e os indicadores são tão pequenos que não adianta afastá-los do corpo do programa. Para ser mais correto, eles não são pequenos, são um único arquivo, não é como um site com 10 000 páginas onde não se pode prescindir de aulas e inlúdios. Além disso, agora existem estruturas, e elas são suficientes para escrever um código compacto e 100% funcional.

 
George Merts:

A propósito, fico muito nervoso quando o ninho é mais de dois níveis. Tento nunca escrevê-lo dessa maneira, espalhando o código sobre as funções.

E mesmo quando há dois níveis de aninhamento - não deixe de escrever comentários após cada parêntese de fechamento, que bloqueiam o enterramento (digamos, cabeçalho de laço duplicado).

Quanto ao estilo, aqui está meu código para selecionar aposição do histórico para MT5 (por magik especificado, símbolo, com intervalo de datas especificado):

A própria classe de história é descendente da interface abstrata CTradeHistoryI:

Ao selecionar o histórico necessário - você pode recalcular seus componentes (posições para MT5 ou pedidos para MT4), e obter uma interface para qualquer componente como uma interface abstrata:

Para o MT4 existem classes de história correspondentes que também herdam destas interfaces - assim, ao mesmo tempo em que a plataforma cruzada é fornecida - o Expert Advisor não precisa descobrir onde trabalha, todo o trabalho com a história é feito através de interfaces abstratas.


Parece bom, podemos também olhar para TRACE_**** e ASSERT?

 
Vitaly Muzichenko:

Para arrastar e soltar um arquivo para outro terminal, ou para compartilhá-lo, você precisa arrastar não apenas um arquivo, mas vários. Você pode, é claro, transferir os nódulos para todos os terminais, mas se você mudar ou acrescentar algo em um terminal, então você precisa substituí-lo por um novo em todos os terminais.

Recomendo o uso de links simbólicos ou links de junção para a pasta de MQL. Todos os terminais procurarão em uma pasta.
Razão: