고문 프로젝트 - 페이지 3

 
Alexey Volchanskiy :

그런 다음 하단에 있는 4개의 괄호가 무엇을 의미하는지 눈으로 샅샅이 살펴보세요.

그건 그렇고, 나는 중첩이 2 단계 이상일 때 매우 긴장합니다. 나는 절대 이런 식으로 쓰지 않으려고 하고, 코드를 함수 위에 퍼뜨립니다.

그리고 두 수준의 중첩이 있는 경우에도(항상 각 닫는 대괄호 뒤에) 어떤 블록이 묻어 있는지에 대한 설명을 작성합니다(예: 순환 제목을 복제함).

스타일과 관련하여 다음은 MT5의 역사적 위치 를 선택하는 코드입니다(지정된 날짜 범위의 지정된 매직, 기호로).

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

동시에 히스토리 클래스 자체는 추상 인터페이스 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 ;
};

필요한 이력을 선택하면 해당 구성 요소(MT5의 경우 위치 또는 MT4의 경우 주문)를 다시 계산하고 추상 인터페이스 형태의 구성 요소에 대한 인터페이스를 얻을 수 있습니다.

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

MT4의 경우 - 이러한 인터페이스에서 상속되는 해당 히스토리 클래스가 있습니다. 따라서 크로스 플랫폼도 제공됩니다. 어드바이저는 작동 위치를 전혀 찾을 필요가 없으며, 히스토리에 대한 모든 작업은 추상 인터페이스를 통해 수행됩니다.

 
Vitaly Muzichenko :

항상 일정하고 이 스타일로 절대 변경되지 않는 함수를 작성하지 마십시오.

간결하게 쓰세요. 어쨌든 아무도 쳐다보지도 않고 줄도 절반만 씁니다.

이 함수들은 바뀌지 않는데 왜 거기에 불필요한 중괄호를 잔뜩 넣었을까? 그것들을 제거하면 모든 것이 저절로 줄어들 것입니다. 그렇지 않으면 당신의 예가 우스꽝스러워 보입니다. 당신 자신이 코드를 닦은 다음 그것을 줄이기 위해 목발을 발명합니다.
 
Alexey Navoykov :
이 함수들은 바뀌지 않는데 왜 거기에 불필요한 중괄호를 잔뜩 넣었을까? 그것들을 제거하면 모든 것이 저절로 줄어들 것입니다. 그렇지 않으면 당신의 예가 우스꽝스러워 보입니다. 당신 자신이 코드를 닦은 다음 그것을 줄이기 위해 목발을 발명합니다.

동의합니다. 3줄을 더 자르고 코드를 단축할 수 있지만 목표는 사용할 코드를 배치하는 것이 아니라 실제로는 제 것도 아닙니다. 그러나 단축하기 위해 이러한 기능 5개를 하나에 넣을 수 있습니다. 하나가 아닌 화면. 그 후에 프로그램을 읽기가 더 쉬워지고 150번 스크롤할 필요가 없습니다. 그리고 파일의 크기가 줄어듭니다.

 
George Merts :

잘했어, 나는 그것을 좋아하지만 나는 OOP를 좋아하지 않고 OOP 없이 하려고 노력한다. 스레드가 분리된 프로세서를 좋아하지 않는 것처럼(예: 4개 코어 및 8개 스레드). 커널의 스레드 분할이든 코드의 기능 가상화이든 분할 및 가상화는 성능 손실 및 구현을 위한 기계 시간 손실이라는 것이 분명해야 합니다.

비탈리 무지첸코 :

동의합니다. 3줄을 더 자르고 코드를 단축할 수 있지만 목표는 사용할 코드를 배치하는 것이 아니라 실제로는 제 것도 아닙니다. 그러나 단축하기 위해 이러한 기능 5개를 하나에 넣을 수 있습니다. 하나가 아닌 화면. 그 후에 프로그램을 읽기가 더 쉬워지고 150번 스크롤할 필요가 없습니다. 그리고 파일의 크기가 줄어듭니다.

간결함은 재능의 자매, 나는 이것이 더 나은 것 같아요.

감사합니다.
 
Vitaly Muzichenko :

작업 화면 27"

나는 게시물을 다시 읽지 않을 것이며 인용할 것입니다: " 항상 일정하고 이 스타일로 절대 변경되지 않는 함수를 작성하지 마십시오 "

플랫폼이 출시되면 한 번 작성되고 앞으로도 변경되지 않는 기능에 왜 눈을 찔러야 합니까? 로트 크기, 주문 수 , 일반 주문을 가져오는 함수에서 코드를 자주 변경/편집합니까? 그렇다면 32인치 모니터의 3개 화면에 걸쳐 확장하는 이유는 무엇입니까?

추신: 코드가 첨부되어 있으며, 코드 베이스에서 발췌했습니다.


카운터 질문)) MyFunc.mqh 파일에 그런 기능이 있는데 압축할 이유가 조금도 없습니다. 디스크에 10-20Kb를 저장해야 하는 이유는 무엇입니까? 그리고 솔직히 이 코드 스타일은 기분이 나쁩니다))

 
Alexey Volchanskiy :

카운터 질문)) MyFunc.mqh 파일에 그런 기능이 있는데 압축할 이유가 조금도 없습니다. 디스크에 10-20Kb를 저장해야 하는 이유는 무엇입니까? 그리고 솔직히 이 코드 스타일은 기분이 나쁩니다))

플러그인 파일도 사용하는데 편리합니다. 특히 순서대로 쓰다보면 많은 기능이 똑같고 똑같은거 쓰는게 좀 어리둥절해서 어드바이저에서 파일과 기능을 연결해 놓았습니다.
저에게 코드는 이해하기 쉽고, 짧고, 빠르게 작동해야 하며, 어떤 조건에서도 오류 없이 작동해야 합니다.


감사합니다.

 
Alexey Volchanskiy :

카운터 질문)) MyFunc.mqh 파일에 그런 기능이 있는데 압축할 이유가 조금도 없습니다. 디스크에 10-20Kb를 저장해야 하는 이유는 무엇입니까? 그리고 솔직히 이 코드 스타일은 기분이 나쁩니다))

그 사람은 "나는 모든 것을 나에게 가지고 간다"는 원칙을 가지고 있다고 썼습니다. 고문의 전체 코드는 하나의 파일에 채워질 것입니다. 따라서 그는 이러한 모든 기능을 복사하여 각 고문에 붙여넣습니다.
따라서 개수: 1000 Expert Advisors x 10kb = 10Mb - 이미 절약에 대해 생각할 이유가 있음))
 
Alexey Volchanskiy :

카운터 질문)) MyFunc.mqh 파일에 그런 기능이 있는데 압축할 이유가 조금도 없습니다. 디스크에 10-20Kb를 저장해야 하는 이유는 무엇입니까? 그리고 솔직히 이 코드 스타일 은 기분이 나쁩니다 ))

저도 그렇지만 저는 오래전에 한 번도 보지 않은 곳에서 코드를 압축해야 한다는 결론에 도달했습니다.

포함된 사용자 정의 코드를 분산시키면 또 다른 골칫거리가 생깁니다 . 파일을 다른 터미널로 드래그 하거나 공유하려면 하나의 파일이 아니라 여러 파일을 드래그해야 합니다. 물론 내포물을 모든 단말로 옮길 수도 있지만, 하나의 단말에서 수정하거나 추가했다면 모두 새 단말로 교체해야 한다.

Expert Advisors와 지표는 너무 작아서 프로그램의 본체에서 무언가를 옮기는 것은 의미가 없습니다. 또는 오히려 작지 않고 단일 파일입니다. 이것은 클래스와 포함 없이는 할 수 없는 10,000페이지의 사이트가 아닙니다. 게다가, 이제 구조가 있고 100% 효율적인 간결한 코드를 작성하기에 충분합니다.

 
George Merts :

그건 그렇고, 나는 중첩이 2 단계 이상일 때 매우 긴장합니다. 나는 절대 이런 식으로 쓰지 않으려고 하고, 코드를 함수 위에 퍼뜨립니다.

그리고 두 수준의 중첩이 있는 경우에도(항상 각 닫는 대괄호 뒤에) 어떤 블록이 묻어 있는지에 대한 설명을 작성합니다(예: 순환 제목을 복제함).

스타일과 관련하여 다음은 MT5의 역사적 위치 를 선택하는 코드입니다(지정된 날짜 범위의 지정된 매직, 기호로).

동시에 히스토리 클래스 자체는 추상 인터페이스 CTradeHistoryI의 상속자입니다.

필요한 이력을 선택하면 해당 구성 요소(MT5의 경우 위치 또는 MT4의 경우 주문)를 다시 계산하고 추상 인터페이스 형태의 구성 요소에 대한 인터페이스를 얻을 수 있습니다.

MT4의 경우 - 이러한 인터페이스에서 상속되는 해당 히스토리 클래스가 있습니다. 따라서 크로스 플랫폼도 제공됩니다. 어드바이저는 작동 위치를 전혀 찾을 필요가 없으며, 히스토리에 대한 모든 작업은 추상 인터페이스를 통해 수행됩니다.


맛있어 보이는데 TRACE_***와 ASSERT도 볼 수 있나요?

 
Vitaly Muzichenko :

파일을 다른 터미널로 드래그 하거나 공유하려면 한 파일이 아닌 여러 파일을 드래그해야 합니다. 물론 내포물을 모든 단말로 옮길 수 있지만, 하나의 단말에서 수정하거나 추가했다면 모두 새 단말로 교체해야 한다.

MQL 폴더에 대한 심볼릭 링크 또는 접합 링크를 사용하는 것이 좋습니다. 모든 터미널은 하나의 폴더에서 찾습니다.