アドバイザープロジェクト - ページ 3

 
Alexey Volchanskiy:

そして、その下にある4つの括弧が何を指しているのかを調べなければならないのです。

ちなみに、ネストが2階層以上になると、とても不安になります。 関数にコードを分散させるような書き方は絶対にしないようにしています。

また、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行削って、コードを短くすることはできますが、目的は、そのコードを使うことではなく、実は私のものでもなく、短くすることで、そのような機能は、1画面ではなく、5画面に入れることができるのです。それからは、プログラムが見やすくなり、150回もスクロールする必要がなくなりました。そして、ファイルの重量も減少します。

 
George Merts:

しかし、私はOOPが好きではなく、OOPなしでやっていこうとしています。 スレッドが分割されたプロセッサ(4コア8スレッドなど)も好きではありません。 分割やあらゆる仮想化は、カーネルでのスレッド分割やコードでの機能の仮想化など、その実装のためのパフォーマンスの損失とマシンタイムの損失であることは明らかでなければならないでしょう。

ヴィタリー・ムジチェンコ

確かに、あと3行削って、コードを短くすることはできますが、目的は、そのコードを使うことではなく、実は私のものでもなく、短くすることで、そのような機能は、1画面ではなく、5画面に入れることができるのです。それからは、プログラムが見やすくなり、150回もスクロールする必要がなくなりました。そして、ファイルの軽量化も実現しました。

brevity is the sister of talent(簡潔は才能の姉妹)、その方が響きがいいと思います。

敬具
 
Vitaly Muzichenko:

27インチ ワーキングスクリーン

読み返さずに引用します。"常に一定で、 そのスタイルで変化することのない関数を書いては いけない"

プラットフォームがリリースされた時に一度だけ書かれ、今後も変わることのない関数になぜ目を摘むのか。ロットサイズや注文数、代表的なものを取得するために、関数内のコードを変更・編集することはよくあることでしょうか?では、なぜ32インチモニター3画面に引き伸ばすのか?

追伸:添付のコードは、kodobaseから偽造したものです。


カウンタークエスチョン )))このような関数はMyFunc.mqhファイル内にあり、圧縮する意味は全くないと思います。なぜかというと、ディスクに10〜20KB保存するためです。そして、率直に言って、このようなコードストリームは私を病気にします )) 。

 
Alexey Volchanskiy:

カウンタークエスチョン )))MyFunc.mqhにそのような関数がありますが、それを圧縮する意味は少しもないと思います。なぜかというと、ディスクに10〜20KB保存するためです。正直、このコードストリームは気持ち悪いです )) 。

私もインクルードファイルを使っています、便利ですよね。 特にカスタムスクリプトを書く場合、同じ関数がたくさんあって、一つ一つ同じものを書くのは馬鹿らしい、ファイルをインクルードするだけでその関数がEAに入るのですからね。
私としては、コードは明確で短く、素早く動作し、どんな状況でもエラーなく動作するものであるべきです。


敬具

 
Alexey Volchanskiy:

カウンタークエスチョン )))MyFunc.mqhにそのような関数がありますが、それを圧縮する意味は少しもないと思います。なぜかというと、ディスクに10〜20KB保存するためです。そして、率直に言って、このようなコードストリームは私をうんざりさせます ))

プログラマーは「すべてを自分で運ぶ」という原則のもと、Expert Advisorの全コードを1つのファイルに詰め込んで書いています。そのため、各EAでこれらの機能をすべてコピーしている。
つまり、1000EA×10Kb=10Mbとカウントして、すでに節約を考える必要があるのです。)
 
Alexey Volchanskiy:

カウンタークエスチョン )))MyFunc.mqhにそのような関数がありますが、それを圧縮する意味は少しもないと思います。なぜかというと、ディスクに10〜20KB保存するためです。そして、率直に言って、このようなコードストリームは私を病気に します )) 。

私もそうですが、ずいぶん前に、コードは誰も見ない場所、修正されない場所、修正されることのない場所にコンパクトにまとめなければならないと思うようになりました。

ユーザーコードをinludesのあちこちにばらまくと、別の端末にファイルをドラッグ&ドロップ したり、共有する必要があるときに、複数のファイルをドラッグ&ドロップする必要があり、さらなる頭痛の種になります。もちろん、すべての端末にインクルードを転送することもできますが、ある端末で何かを変更したり追加したりすると、すべての端末を新しいものに入れ替えなければなりません。

Expert Advisorやインジケータは非常に小さいので、プログラム本体から距離を置く意味はない。正しくは、小さいのではなく、単一ファイルです。1万ページあるサイトのように、クラスやインルードがないとできないわけではありません。しかも、今は構造体があり、コンパクトで100%実行可能なコードを書くにはそれで十分なのです。

 
George Merts:

ちなみに、ネストが2階層以上になると、とても不安になります。 関数にコードを分散させるような書き方は絶対にしないようにしています。

また、ネストレベルが2つある場合でも、それぞれの閉じ括弧の後に、どのブロック(例えば、重複するループヘッダ)を葬り去るのか、必ずコメントを書いてください。

スタイルについては、MT5のヒストリーポジションを 選択する私のコードです(指定されたマジック、シンボル、指定された日付範囲による)。

履歴クラス自体は、抽象的な CTradeHistoryI インターフェースの子孫である。

必要な履歴を選択することで、そのコンポーネント(MT5の場合はポジション、MT4の場合は注文)を再計算し、抽象的なインターフェースとして任意のコンポーネントへのインターフェースを取得することができます。

MT4では、これらのインターフェイスを継承する対応するヒストリークラスがあり、同時にクロスプラットフォームが提供されています。


良さそうですね、TRACE_***とASSERTも見てみましょうか?

 
Vitaly Muzichenko:

ファイルを 他の端末にドラッグ&ドロップ したり、共有するためには、1つのファイルだけでなく、複数のファイルをドラッグする必要があります。もちろん、すべての端末にインルードを転送することは可能ですが、ある端末で何かを変更・追加した場合、すべての端末で新しいものに置き換える必要があります。

MQL-folderには、シンボリックリンクやジャンクションリンクを使用することをお勧めします。すべての端末が1つのフォルダに収まります。