顾问的项目 - 页 3

 
Alexey Volchanskiy:

然后你必须查一下底部的那四个括号指的是什么。

顺便说一句,当嵌套超过两层时,我就会非常紧张。 我尽量不这样写,把代码分散到各个函数上。

即使有两层嵌套--也要确保在每个闭合括号后写上注释--它埋葬了哪个块(例如,重复的循环头)。

至于风格,这是我为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());
};

历史类本身是抽象的TradeHistoryI接口的后裔。

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 - 有相应的历史类也从这些接口继承 - 因此,在同一时间,提供了跨平台 - EA不需要找到它的工作地点,所有与历史的工作是通过抽象接口完成的。

 
Vitaly Muzichenko:

不要用这种风格来写永远不变的函数,也不要写永远不变的函数

简明扼要地写,反正也没人看,而且只需要一半的行数。

既然这些函数从不改变,为什么还要在那里放一堆不必要的大括号?移除它们,一切都会自行收缩。因为你的例子看起来很荒谬:你自己模糊了代码,然后发明拐杖来减少它。
 
Alexey Navoykov:
既然这些函数没有变化,你为什么要在那里放一堆不必要的大括号?移除它们,一切都会被压缩。因为你的例子看起来很荒谬:你自己模糊了代码,然后发明拐杖来减少它。

我同意,你可以再剪掉3行,缩短代码,但目的不是为了把代码用上,事实上它根本不是我的,而是为了缩短,这样的功能可以在一个屏幕上放5个,而不是一个。此后,程序更容易阅读,你不必滚动150次。而文件的重量也会减少。

 
George Merts:

不错的工作,我喜欢,但我不喜欢OOP,并尽量不使用它。 我不喜欢有线程划分的处理器(例如,4个核心和8个线程)。 必须清楚,划分和任何虚拟化对其实现来说都是性能的损失和机器时间的损失,无论是内核中的线程划分还是代码中函数的虚拟化。

维塔利-穆齐琴科

我同意,你可以再剪掉3行,缩短代码,但目的不是为了把代码用上,事实上它甚至不是我的,而是为了缩短它,你可以在一个屏幕上放五个这样的函数,而不是一个。此后,程序更容易阅读,你不必滚动150次。而且文件的重量也减少了。

简洁是人才的姐妹,我认为听起来更好。

真诚的。
 
Vitaly Muzichenko:

27英寸工作屏幕

我不打算重读了,我只是引用。"不要写那些总是不变的函数,永远不要改变 这种风格"

为什么要为一个在平台发布时只写一次、将来永远不会改变的函数挑花眼?你是否经常修改/编辑函数中的代码以获得手数、订单数 和典型?那为什么要把它延伸到32英寸显示器的3个屏幕上呢?

P.S. 所附代码是从kodobase上伪造的。


反问))))。我的MyFunc.mqh文件中就有这样的函数,我看不出压缩它有什么意义。为什么,为了在磁盘上节省10-20KB?坦率地说,这样的代码流让我感到恶心 ))

 
Alexey Volchanskiy:

反问))))。我在MyFunc.mqh文件中有这样的函数,我看不出压缩它有丝毫意义。为什么,为了在磁盘上节省10-20KB?说实话,这个代码流让我感到恶心))。

我也用include文件,很方便。 特别是当你写一个自定义脚本时,有很多相同的函数,写一个相同的东西是很傻的,只要包含文件,函数就在你的EA中。
对我来说,代码应该是清晰的、简短的、快速工作的,并且应该在所有条件下无错误地工作。


真诚的。

 
Alexey Volchanskiy:

反问))))。我在MyFunc.mqh文件中有这样的函数,我看不出压缩它有丝毫意义。为什么,为了在磁盘上节省10-20KB?坦率地说,这样的代码流让我感到恶心 ))

程序员写道:"我自己携带一切 "的原则;专家顾问的整个代码被压缩在一个文件中。因此,他在每个EA中复制所有这些功能。
因此,计算一下:1000个EA x 10 Kb = 10 Mb - 你已经要考虑节约了 ))
 
Alexey Volchanskiy:

反问))))。我在MyFunc.mqh文件中有这样的函数,我看不出压缩它有丝毫意义。为什么,为了在磁盘上节省10-20KB?坦率地说,这样的代码流让感到恶心))

我也是,但是很久以前我就得出结论,代码必须被压缩在人们从不看它的地方,在那里它永远不会被纠正,也不会被纠正。

将用户代码散落在各处是一个额外的头痛问题,当你需要将一个文件拖 放到另一个终端,或分享它时,你将需要拖放几个文件。当然,你可以把includniks转移到所有的终端,但如果你在一个终端中改变或增加了一些东西,那么所有的终端都必须用新的终端替换。

专家顾问和指标非常小,没有必要将它们与程序主体分开。更正确地说,它们并不小,它们是单一的文件,它不像一个有10000个页面的网站,你不能没有类和嵌套。此外,现在有了结构,它们足以写出紧凑、100%可行的代码。

 
George Merts:

顺便说一句,当嵌套超过两层时,我就会非常紧张。 我尽量不这样写,把代码分散到各个函数上。

即使有两个嵌套层次--也要确保在每个闭合小括号后写上注释,它埋葬了哪个块(例如,重复的循环头)。

至于风格,这是我为MT5选择历史位置 的代码(通过指定的magik,符号,指定的日期范围)。

历史类本身是抽象的TradeHistoryI接口的后裔。

通过选择所需的历史 - 你可以重新计算其组件(MT5的头寸或MT4的订单),并获得任何组件的抽象接口。

对于MT4有相应的历史类,它们也继承自这些接口 - 因此同时提供了跨平台性 - 专家顾问不需要找到它的工作地点,所有与历史的工作都是通过抽象接口完成的。


看起来不错,我们能不能也看看TRACE_***和ASSERT?

 
Vitaly Muzichenko:

拖放一个文件 到另一个终端,或分享它,你需要拖动的不仅仅是一个文件,而是几个文件。当然,你可以把inludes转移到所有的终端,但如果你在一个终端中改变或增加了一些东西,那么你需要在所有的终端中用新的东西来替换它。

我建议对MQL-文件夹使用符号链接或结点链接。所有终端将在一个文件夹中查找。