как создавать объекты динамически? (Некоторые вопросы ООП)

 

А вот и немного OOP.

Идея программы:

* Я рисую линию тренда на графике и называю ее "beep" - когда цена пересечет эту линию в следующий раз, я получу звуковой сигнал.

* Я рисую линию тренда и называю ее "buy" - в следующий раз, когда цена пересечет эту линию, я получу длинную позицию.

Я уже написал объект под названием "CSmartLine", который умеет подавать звуковой сигнал, покупать и продавать, закрывать и .... (пока без кофе-сервиса).

В моем советнике у меня есть три строки кода:

CSmartLinie mSmartLinie1;    // Create one object of class CSmartLine  


void OnTick()
      mSmartLinie1.CheckForAction(); // check for crossing prices

void OnChartEvent()
      if(id == CHARTEVENT_OBJECT_CLICK  ||
         id == CHARTEVENT_OBJECT_DRAG   ||
         id == CHARTEVENT_OBJECT_CHANGE ||
         id == CHARTEVENT_OBJECT_CREATE)
         if (sparam == "beep" || sparam == "buy" || sparam == "sell" || sparam == "close")
            {
            mSmartLinie1.CheckForAction(sparam);  // activation and tracking changes
            return;
            };

Пока все работает хорошо.

Теперь ... ... Я хотел бы нарисовать на графике любое количество линий умного тренда.

При этом объект меняет название линии (например, на "SmartLine_x"), чтобы показать, что линия находится под его контролем.

Каждый раз, когда советник обнаруживает новую линию, он должен создавать новый объект класса "CSmartLine".

Код может быть таким

OnChartEvent()
      if (sparam = "beep")
             mSmartLine2 = new CSmartLine;

OnTick()
      if(mSmartLine2 != Null)
             mSmartLine2.CheckForAction();

Но как .... хммм ....

Может быть, "mSmartLine" должен быть массивом указателей? Если да, то какого типа?

В руководстве приведен пример, который я не могу понять.


Когда линия тренда исчезает (потому что пользователь удалил ее с графика, например) .

Код должен быть ...

      delete mSmartLine2;
      mSmartLine2=NULL;

Поскольку объект сам распознает исчезновение линии, он должен сам себя удалять, а не просить советника удалить его.

WIllbur

 

Где можно просто использовать

ObjectCreate(0,"mSmartLinie"+IntegerToString(X),OBJ_HLINE,0,0,0);

Где простой

X++;

Увеличит целое число X так, чтобы создать

"mSmartLinie0"
"mSmartLinie1"
"mSmartLinie2"
"mSmartLinie3"

И т.д.

 
Марко прав.

Ваша проблема в том, что если объект "beep" существует, то новый объект не будет создан.
Как сказал Марко
плюс
Включайте команды в первый символ имени объекта после
распознаваемой последовательности символов

например:

A->Сигнал
B->Купить
C->продать
последовательность -> SMRTLN_

таким образом, первое появление умной линии звукового сигнала будет называться "SMRTLN_A_"+TotalBeepCount
 

В ООП существуют общие паттерны проектирования для хранения объектов и их итерации. Они называются контейнерами, множествами, коллекциями, картами, векторами (и другими похожими названиями), но, насколько известно, ни один из них не поставляется с базовым дистрибутивом MQL. Вы должны написать их сами или найти в кодовой базе.

C++ Programming/Code/Design Patterns - Wikibooks, open books for an open world
C++ Programming/Code/Design Patterns - Wikibooks, open books for an open world
  • en.wikibooks.org
A design pattern is neither a static solution, nor is it an algorithm. A pattern is a way to describe and address by name (mostly a simplistic description of its goal), a repeatable solution or approach to a common design problem, that is, a common way to solve a generic problem (how generic or complex, depends on how restricted the target goal...
 

Кажется, я понял - это и есть волшебные линии (я надеюсь):

CSmartLine *ArrayOfSmartLineS[10]; // An array of pointers to CSmartLine object


On ChartEvent

           //--- Create another object
           CSmartLine*mSmartLine = new CSmartLine();
           // Make the new object the owner of the new trend line
           mSmartLine.SetName(sparam);
           //--- Place the pointer value in an Array
           ArrayOfSmartLineS[NoLines]=mSmartLine;

В руководстве использовалась форма "CSmartLine*mSmartLine = new CSmartLine();" только при создании первого экземпляра этого класса.
В следующий раз только "mSmartLine = new CSmartLine();".
В моем тестовом фрейме это невозможно (ошибки компиляции). ? ? ?


Я написал простой тестовый фрейм с очень слабой функциональностью, но тест на динамическое создание объектов.

Каждый раз, когда советник обнаруживает линию тренда с именем "beep" - он создает объект "SmartLine", который переименовывает линию и управляет ею впредь.

//+------------------------------------------------------------------+
//| Test frame OOP                                                   |
//+------------------------------------------------------------------+
class CSmartLine
{
protected:
string   iName;
double   iRate;

public:
   void   SetName(string xName)
            {
            for(int i=0;i<99;i++)
               {
               iName = "SmartLine_"+IntegerToString(i);
               if(ObjectFind(0,iName) < 0) break; // find unused name
               }
            ObjectSetString(0,xName,OBJPROP_NAME,0,iName); // rename trend line
            // --- Get rate
            iRate = ObjectGetDouble(0,iName,OBJPROP_PRICE,0);
            // signal identification of the line
               Sleep(300); PlaySound("ok.wav");
               ObjectSetInteger(0,iName,OBJPROP_WIDTH,4); ChartRedraw();
               Sleep(300);
               ObjectSetInteger(0,iName,OBJPROP_WIDTH,1); ChartRedraw();
            //
            };
           
   string GetName(void) {return(iName);}
  
   void   checkForChange(string xName)
            {
            if(xName != iName) return;
            // Check whether the line has been moved
            // get the new position
                        // --- Get rate
            iRate = ObjectGetDouble(0,iName,OBJPROP_PRICE,0);
            MessageBox("New rate: "+iName+" = "+DoubleToString(iRate,5));
            };
   void   checkForAction(double iAsk)
            {
            if(MathAbs(100 * (iRate - iAsk)/iAsk) < 0.005)
              {
              MessageBox("it's hit me "+iName+
              "\n myRate "+DoubleToString(iRate,5)+
              "\n actAsk "+DoubleToString(iAsk, 5)+
              "\n actDiff "+DoubleToString(100 * (iRate - iAsk)/iAsk,5) );
              }
            // Calculation whether the price hits the line
            // action: beep, buy, sell, close
            };         

};

//################# E N D - - - C S m a r t L i n e ##########################

//################# B E G I N of E A program ##################################

//--- Declare an array of object pointers of type CSmartLine

      CSmartLine *ArrayOfSmartLineS[10]; // An array of pointers to CSmartLine object

int   NoLines=0;

//----------------------------------------------------------------------------
void OnInit(void)
{
// --- do I need this?
   for(int i=0;i<10;i++)
     ArrayOfSmartLineS[i]=NULL;     

//--- delete all old trend lines
    ObjectsDeleteAll(0,"SmartLine",-1);

}
//+--------------------------------------------------------------------------
void OnChartEvent(const int id,        
                  const long& lparam,  
                  const double& dparam,
                  const string& sparam) 
{
      if(id == CHARTEVENT_OBJECT_CLICK  ||
         id == CHARTEVENT_OBJECT_DRAG   ||
         id == CHARTEVENT_OBJECT_CHANGE ||
         id == CHARTEVENT_OBJECT_CREATE)
         {
         if(sparam == "beep" || sparam == "buy" || sparam == "sell") 
           {
           //--- Create another object
           CSmartLine*mSmartLine = new CSmartLine();
           // Make to new object the owner of the new line
           mSmartLine.SetName(sparam);
           //--- file the pointer value in the array[0]
           ArrayOfSmartLineS[NoLines]=mSmartLine;
           //--- ask the new object for it's line name
           MessageBox("new object: " + ArrayOfSmartLineS[NoLines].GetName());
           //
           NoLines++;
           };
         if(StringSubstr(sparam,0,10) == "SmartLine_")
           {
           for(int i=0;i<10;i++)  // Ask all exsisting objects to pick up the change if concerns
             {
             if(ArrayOfSmartLineS[i] != NULL)
                ArrayOfSmartLineS[i].checkForChange(sparam);
             }
           }

         }
}
//----------------------------------------------------------------------------
void OnTick(void)
{
      MqlTick last_tick;
  
      SymbolInfoTick(_Symbol,last_tick);
     
      for(int i=0;i<10;i++)  // Ask all exsisting objects
             {
             if(ArrayOfSmartLineS[i] != NULL)
                ArrayOfSmartLineS[i].checkForAction(last_tick.ask);
             }
}
//+------------------------------------------------------------------+
void OnDeinit(const int xReason)
{
      if(xReason == REASON_RECOMPILE   ||
         xReason == REASON_CHARTCHANGE ||
         xReason == REASON_PARAMETERS  ||
         xReason == REASON_ACCOUNT)    return;
     
//--- We must delete all created dynamic objects
      for(int i=0;i<10;i++)
         {
         //--- We can delete only the objects with pointers of POINTER_DYNAMIC type
         if(CheckPointer(ArrayOfSmartLineS[i])==POINTER_DYNAMIC)
            {
            //--- Notify of deletion
            MessageBox("Deleting object "+IntegerToString(i)+" named "+ArrayOfSmartLineS[i].GetName());
            //--- Delete an object by its pointer
            delete ArrayOfSmartLineS[i];
            ArrayOfSmartLineS[i] = NULL;
            }
         }   // Loop i=0;i<10;i++
  }

 

@Marco

Я не уверен, понял ли я вашу идею.

Вы имеете в виду, что я должен пойти со стандартными графическими объектами, написать класс, который наследует каждую вещь от него и увеличивает функциональность до диапазона, который я планирую для моих SmartLines ?!!?

Я думал об этой идее и мне интересно, позволяет ли MT5 создавать графические объекты на графике (через обычный интерфейс).

Помимо этой проблемы, мои объекты SmartLine должны срабатывать, когда цена меняется, и я не знаю, как обойти эту проблему.

Есть ли у вас какой-нибудь опыт в этой области?

Willbur

 
Willbur:

@Marco

Я не уверен, понял ли я вашу идею.

Вы имеете в виду, что я должен пойти со стандартными графическими объектами, написать класс, который наследует каждую вещь от него и увеличивает функциональность до диапазона, который я планирую для моих SmartLines ?!!?

Я думал об этой идее и мне интересно, позволяет ли MT5 создавать графические объекты на графике (через обычный интерфейс).

Помимо этой проблемы, мои объекты SmartLine должны срабатывать, когда цена меняется, и я понятия не имею, как обойти эту проблему.

Есть ли у вас опыт в этой области?

Willbur

Это был просто пример того, как нужно делать.

Всякий раз, когда я создаю GUI или графический интерфейс пользователя, я всегда создаю управляющие рамки в цикле.

Это означает, что кнопки управления просто создаются как B0,B1,B2,B3,B4,B5 и т.д., что означает "кнопка 0 кнопка 1 кнопка 2 и т.д.".

И я всегда использую IntegerToString(), чтобы добавить целое число к имени объекта.

Если вам нужен триггер, вы также можете использовать:

if((Ask+Bid)/2>ObjectGetDouble(0,"object name",OBJPROP_PRICE)
 {
  // Trigger 1
 }

else if((Ask+Bid)/2<ObjectGetDouble(0,"object name",OBJPROP_PRICE)
 {
  // Trigger 2
 }

Или другой вариант, поскольку это только для того, чтобы дать вам несколько идей.

Итак, вы берете цену Ask, Bid или Median и сравниваете ее с ценой Double, на которой сейчас находится ваша Hline.

Единственный предел - это ваше собственное воображение.

 

Марко, Вы все еще находитесь в своем EA-коде. Правда?

Это не то, о чем я говорю.

Идея заключалась в том, чтобы добавить функциональность к данному объекту grafic, написав свой собственный объект, который наследуется от оригинального объекта grafic и дополнить его функциями "buy" и "sell".

Если пойти таким путем, то мой объект SmartLine должен появиться в меню МТ5 рядом с линией тренда, стрелками, текстовым объектом и всем остальным.

Где вы можете выбрать его с помощью мыши, как другие графические объекты, и добавить его на график.

Если MT5 это позволяет, только тогда мы должны обсудить оставшийся вопрос, как объект может быть запущен терминальной программой при изменении цены.

Ни один из существующих графических объектов не способен реагировать на изменение цены (насколько я знаю).

Willbur

 

Мне не хотелось бы говорить, что вы делаете это совершенно неправильно, но это так, потому что это структурное программирование, а не ООП. Большая разница заключается в силе наследования и перегрузки. И кстати, вы не можете наследоваться от реальных графических объектов, но вы можете представить что угодно как объект кода и ссылаться на линию или что-то еще из этого объекта. Так обычно делается в любом классе, независимо от того, MFC это или MQL, все одинаково.

Если ваши строки являются объектами, то и обращайтесь с ними как с объектами. Не работайте с массивами снаружи, делайте это внутри класса коллекции и работайте с указателями. Посмотрите на CWndContainer и получите представление о нем. Этот класс представляет собой контейнер, который в основном управляет массивами указателей для объектов CWnd. Продвиньтесь на шаг вперед, ваша структура должна быть такой:

CObject в качестве базы для каждого объекта

CPriceTimeObjects как база для каждого объекта, основанного на цене/времени, например, линии, производного от CObject. Он контролирует создание, хранит время и цену и вызывает функцию OnCreate(), которая может быть использована следующим наследником. Он также имеет функцию Tick, которая вызывает виртуальную OnTick(), которая затем перегружается наследниками.

CTrendLine, как основа для линий тренда, наследуется от CPriceTimeObjects и обрабатывает OnCreate, где создает конечную линию, используя функцию ObjectCreate. Он также должен иметь обработчик OnTick() для реакции/отклика на события Tick, потому что он должен быть чувствителен к цене, насколько я понял.

Кроме этого, у вас есть класс-контейнер, который управляет массивом указателей, содержащим все объекты CTimePriceObject, которые вы хотите, он также наследуется от CTimePriceObject и передает OnTick() своим "детям". Контейнер имеет также функцию, которая обрабатывает OnChartEvent() для добавления линий или их удаления. Он также должен иметь функцию scan для сканирования всех существующих объектов на случай, если эксперт был добавлен после создания линий. Кроме того, она обрабатывает перегруженный OnTick() из CTimePrice, зацикливает массив, спрашивает каждый объект CTrendLine в нем, чувствует ли он себя как-то ответственным, чтобы реагировать, вызывая функцию Tick каждого дочернего объекта, который обрабатывается виртуальным OnTick. Почему именно это? Потому что CTrendLine перегружает эту функцию также из CTimePrice, и таким образом этот класс может быть унаследован другими наследниками с другими функциями.

В дальнейшем ваш код должен выглядеть следующим образом:

CTPContainer container;

::OnChartEvent(...)

container.ChartEvent(id, lparam, dparam, sparam) //... приводит к OnCreate() и OnDelete() у каждого CTrendLineObject. Контейнер решает, что делать, а не ваш советник.

::OnTick()

container.Tick() // ... приводит к OnTick() на каждом "дочернем" объекте CTrendLine

и так далее.

Такова ясная база ООП, которую можно легко дополнить полезными функциями, не трогая больше ни одного советника, использующего эти классы.

 
Я не люблю говорить, что вы делаете это совершенно неправильно, но вы делаете... <br / translate="no">

Вау ... спасибо за урок.

Почему-то кажется, что у вас правильный подход.

Я попробую изменить свой код в этом направлении, прежде чем вернуться с ним.

Willbur

 

Если вы сразу и последовательно перейдете на объектно-ориентированный код, вы никогда не пожалеете. Начало может быть труднее, чем обычный путь сверху вниз, но вы сможете развиваться на гораздо более высоком уровне, который включает в себя большую силу, больше возможностей, большую гибкость и более легкую совместимость с будущими изменениями.

MQL - это потрясающе, но без ООП вы никогда не поймете, насколько это действительно так.

Если у вас есть вопросы, просто напишите их, и я постараюсь помочь.