Proyecto del asesor - página 3

 
Alexey Volchanskiy:

Y luego hay que buscar a qué se refieren esos cuatro paréntesis de la parte inferior.

Por cierto, me pone muy nervioso cuando el anidamiento es de más de dos niveles. Intento no escribirlo nunca así, repartiendo el código en funciones.

E incluso cuando hay dos niveles de anidación - asegúrese de escribir comentarios después de cada corchete de cierre - que bloque entierra (por ejemplo, la cabecera del bucle duplicado).

En cuanto al estilo, aquí está mi código para seleccionaruna posición de la historia para MT5 (por un mago especificado, símbolo, con un rango de fechas 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());
};

La propia clase historia es descendiente de la interfaz abstracta 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;
};

Seleccionando el historial requerido - puede recalcular sus componentes (posiciones para MT5 u órdenes para MT4), y obtener una interfaz para cualquier componente como una interfaz abstracta:

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 MT4 - hay clases de historia correspondientes también heredadas de estas interfaces - así, al mismo tiempo, se proporciona la multiplicidad de plataformas - un EA no necesita averiguar donde trabaja, todo el trabajo con la historia se hace a través de las interfaces abstractas.

 
Vitaly Muzichenko:

No escriba funciones que sean siempre constantes y nunca cambien en este estilo

Escríbalos de forma concisa, de todas formas nadie los mira y ocupan la mitad de líneas

Dado que estas funciones nunca cambian, ¿por qué poner un montón de corchetes innecesarios allí? Quítalos y todo se reducirá por sí mismo. Porque tu ejemplo parece absurdo: tú mismo has desdibujado el código y luego inventas muletas para reducirlo.
 
Alexey Navoykov:
Dado que estas funciones no cambian, ¿por qué has puesto un montón de corchetes innecesarios? Quítalos y todo se comprimirá. Porque tu ejemplo parece absurdo: tú mismo has desdibujado el código y luego te inventas muletas para reducirlo.

Estoy de acuerdo, se pueden recortar 3 líneas más, y acortar el código, pero el propósito no era poner el código al uso, de hecho ni siquiera es mío, sino acortar, y esas funciones se pueden poner cinco en una pantalla, no una. Después, los programas son más fáciles de leer y no hay que desplazarse 150 veces. Y el peso del archivo disminuye.

 
George Merts:

Buen trabajo, me gusta, pero no me gusta la POO e intento prescindir de ella. No me gustan los procesadores con división de hilos (por ejemplo, 4 núcleos y 8 hilos). Hay que tener claro que la división y cualquier virtualización es una pérdida de rendimiento y de tiempo de máquina para su implementación, ya sea la división de hilos en el kernel o la virtualización de funciones en el código.

Vitaly Muzichenko:

Estoy de acuerdo, se pueden recortar 3 líneas más, y acortar el código, pero el propósito no era poner el código a usar, ni siquiera es mío de hecho, sino acortarlo, y se pueden poner cinco funciones de este tipo en una pantalla, no una. Después, los programas son más fáciles de leer y no hay que desplazarse 150 veces. Y el peso del archivo se reduce.

La brevedad es la hermana del talento, creo que suena mejor.

Sinceramente.
 
Vitaly Muzichenko:

Pantalla de trabajo de 27".

No voy a releerlo, me limitaré a citarlo: "No escribas funciones que sean siempre constantes y nunca cambien en ese estilo"

¿Por qué elegir los ojos sobre una función que se escribe una vez cuando se lanza la plataforma y que nunca cambiará en el futuro? ¿Sueles cambiar/editar el código en las funciones para obtener el tamaño del lote, el número de órdenes y lo típico? Entonces, ¿por qué extenderlo a través de 3 pantallas de un monitor de 32"?

P.D. El código adjunto está forjado en kodobase.


Contrapregunta ))) Tengo tales funciones están en el archivo MyFunc.mqh, no veo el más mínimo sentido en la compresión de la misma. ¿Por qué, para ahorrar 10-20 KB en el disco? Y francamente hablando, tal codestream me enferma ))

 
Alexey Volchanskiy:

Contrapregunta ))) Tengo dichas funciones en el archivo MyFunc.mqh, no le veo el menor sentido a comprimirlo. ¿Por qué, para ahorrar 10-20 KB en el disco? Para ser honesto, este codestream me pone enfermo )).

Yo también uso archivos de inclusión, es conveniente. Especialmente cuando escribes un script personalizado, hay un montón de funciones idénticas y es una tontería escribir una y la misma cosa, simplemente incluye el archivo y la función está en tu EA.
En cuanto a mí, el código debe ser claro, corto, rápido de trabajar y debe funcionar en todas las condiciones sin errores.


Sinceramente.

 
Alexey Volchanskiy:

Contrapregunta ))) Tengo dichas funciones en el archivo MyFunc.mqh, no le veo el menor sentido a comprimirlo. ¿Por qué, para ahorrar 10-20 KB en el disco? Y francamente hablando, tal codestino me pone enfermo ))

El programador ha escrito que el principio "lo llevo todo yo"; todo el código del Asesor Experto está metido en un solo archivo. En consecuencia, copia todas estas funciones en cada EA.
Así que cuenta: 1000 EAs x 10 Kb = 10 Mb - ya tienes que pensar en economizar ))
 
Alexey Volchanskiy:

Contrapregunta ))) Tengo dichas funciones en el archivo MyFunc.mqh, no le veo el menor sentido a comprimirlo. ¿Por qué, para ahorrar 10-20 KB en el disco? Y francamente hablando, tal codestream me enferma ))

Yo también, pero hace tiempo llegué a la conclusión de que el código debe ser compacto en lugares donde nunca se mire, donde nunca se corrija ni se corregirá.

La dispersión del código de usuario con todas estas ranuras es un dolor de cabeza adicional, porque tendrá que arrastrar y soltar los archivos en diferentes terminales o compartirlos. Por supuesto, puedes transferir los includniks a todos los terminales, pero si cambias o añades algo en un terminal, entonces todos ellos deben ser reemplazados por uno nuevo.

Los Asesores Expertos y los indicadores son tan pequeños que no tiene sentido alejarlos del cuerpo del programa. Para ser más correctos, no son pequeños, son de un solo archivo, no es como un sitio con 10 000 páginas donde no se puede prescindir de clases e inludes. Además, ahora hay estructuras, y son suficientes para escribir un código compacto y 100% viable.

 
George Merts:

Por cierto, me pone muy nervioso cuando el anidamiento es de más de dos niveles. Intento no escribirlo nunca así, repartiendo el código en funciones.

E incluso cuando hay dos niveles de anidación - asegúrese de escribir los comentarios después de cada paréntesis de cierre, que el bloque que entierra (por ejemplo, la cabecera del bucle duplicado).

En cuanto al estilo, aquí está mi código para seleccionar laposición de la historia para MT5 (por magik especificado, símbolo, con rango de fechas especificado):

La propia clase historia es descendiente de la interfaz abstracta CTradeHistoryI:

Seleccionando el historial requerido - puede recalcular sus componentes (posiciones para MT5 u órdenes para MT4), y obtener una interfaz para cualquier componente como una interfaz abstracta:

Para MT4 existen las correspondientes clases de historial que también heredan de estas interfaces - así al mismo tiempo se proporciona la multiplicidad de plataformas - el Asesor Experto no necesita averiguar donde trabaja, todo el trabajo con el historial se realiza a través de interfaces abstractas.


Se ve bien, ¿podemos ver también TRACE_*** y ASSERT?

 
Vitaly Muzichenko:

Para arrastrar y soltar un archivo a otro terminal, o para compartirlo, es necesario arrastrar no sólo un archivo, sino varios. Por supuesto, puede transferir las inluencias a todos los terminales, pero si cambia o añade algo en un terminal, deberá sustituirlo por uno nuevo en todos los terminales.

Recomiendo utilizar enlaces simbólicos o enlaces de unión para la carpeta MQL. Todos los terminales buscarán en una carpeta.