Orphaned Screen Objects?

 

Hi there,

I have an EA that determines trend and at certain points, it draws a row of dots above (short) or below (long) pullbacks in the trend. At the close of each bar, if the trend is maintained, another dot is added to the end of the row of dots at the last price level. If there is a subsequent pullback in the same direction of the trend, the line of dots are moved up below the higher high, or above, the lower low. Pretty standard stuff.

However (and there's always a "but" ...), whilst this works most of the time, occasionally, I get what I can only describe as "orphaned" dots. If I remove the EA, the "known" dots are removed, leaving the orphans behind. There are only two ways of adding dots and they are public methods on the CDotManager. Each dot is a CDot instance which creates and wraps the dot screen object. I have looked for days to see why this might be happening but right now, I'm out of ideas. I've included the sources for the Manager and Dot instance classes. If anyone could take a look at this code or might have a general idea of what could be causing these "orphans", I greatly appreciate it! :)

//+------------------------------------------------------------------+
//|                                                 gwDotManager.mqh |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
//---
#include <Arrays\ArrayObj.mqh>
#include <gwConfig.mqh>
#include <gwDot.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CDotManager : public CArrayObj
  {
private:
   //---
   double            _referencePrice;
   double            _chartScaleChange;
   datetime          _referenceTime;
   datetime          _lastTime;
   bool              _higherHighs;
   bool              _initMode;
   //---
   CConfig          *_config;

public:
                     CDotManager(CConfig *config);
                    ~CDotManager();
   void              SetDots(bool higherHighs,datetime time1,double price1,int period);
   void              UpdateDots();
   double            GetReferencePrice();
   void              OnChartScaleChange(double chartScale);
   datetime          GetReferenceTime();
   bool              IsHigherHighs();
   bool              PriceHasReversedThroughDots();

  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CDotManager::CDotManager(CConfig *config)
  {
   _config=config;
   _initMode=config.FromInit;
   _chartScaleChange=-1;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CDotManager::~CDotManager()
  {
   CArrayObj::Clear();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDotManager::OnChartScaleChange(double chartScale)
  {

   if(_chartScaleChange!=-1) return;

   _chartScaleChange=chartScale;

   for(int i=0;i<this.Total();i++)
     {

      if(this.At(i)==NULL
         || CheckPointer(this.At(i))==POINTER_INVALID)
         continue;

      CDot *dot=this.At(i);
      dot.OnChartScaleChange(chartScale);

     }

   _chartScaleChange=-1;

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDotManager::UpdateDots()
  {

   if(_initMode && !_config.FromInit)
     {

      // Was in init mode and now _config.FromInit has been set false.
      // This is the sign that the dots can be activated.

      _initMode=false;

      for(int i=0; i<this.Total();i++)
        {

         if(this.At(i)==NULL
            || CheckPointer(this.At(i))==POINTER_INVALID)
            continue;

         CDot *dot=this.At(i);

         if(dot.GetPeriod()==Period())
           {
            dot.Activate();
           }

        }

     }

   int i=-1,shift=CMyUtils::_bi(_lastTime);

   for(i=shift;i>0;i--)
     {
      this.Add(
               new CDot(
               _config,
               _higherHighs,
               Time[i],
               _referencePrice,
               Period())
               );
     }

   if(i!=-1)
     {
      _lastTime=Time[i];
     }

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDotManager::SetDots(
                          bool higherHighs,
                          datetime time1,
                          double price1,
                          int period)
  {

   if(this.Total()>0)
     {
      CArrayObj::Clear();
     }

   _higherHighs=higherHighs;
   _referenceTime=time1;
   _referencePrice=price1;

   int i=-1,shift=CMyUtils::_bi(time1);

   for(i=shift;i>0;i--)
     {
      this.Add(
               new CDot(
               _config,
               _higherHighs,
               Time[i],
               _referencePrice,
               period));
     }

   if(i!=-1)
     {
      _lastTime=Time[i];
     }

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CDotManager::GetReferencePrice()
  {
   return _referencePrice;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
datetime CDotManager::GetReferenceTime()
  {
   return _referenceTime;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CDotManager::IsHigherHighs()
  {
   return _higherHighs;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CDotManager::PriceHasReversedThroughDots()
  {

   bool higherHigh=_higherHighs;
   bool reversed=false;
   datetime referenceTime=_referenceTime;
   int shift=CMyUtils::_bi(referenceTime);

   for(int i=shift;i>0;i--)
     {

      double close=Close[i];

      if(higherHigh)
        {
         if(close<_referencePrice)
           {
            reversed=true;
            break;
           }
        }
      else
        {
         if(close>_referencePrice)
           {
            reversed=true;
            break;
           }
        }

     }

   return reversed;

  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                                       gwDots.mqh |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
//---
#include <Object.mqh>
#include <gwConfig.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CDot : public CObject
  {
private:
   bool              _created;
   bool              _higherHighs;
   string            _name;
   void              _deleteDot();
   void              _setName(string token);
   void              _createDot();
   datetime          _time1;
   double            _price1;
   double            _chartScale;
   double            _getAdjustedPriceLevel();
   int               _period;

   CConfig          *_config;

public:
                     CDot(CConfig *config,bool higherHighs,datetime time1,double price1,int period);
                    ~CDot();
   void              Activate();
   void              OnChartScaleChange(double chartScale);
   int               GetPeriod();

  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CDot::CDot(
           CConfig *config,
           bool higherHighs,
           datetime time1,
           double price1,
           int period)
  {

   _config=config;
   _higherHighs=higherHighs;
   _time1=time1;
   _price1=price1;
   _period=period;
   _setName("__CDot__");
   _chartScale=CMyUtils::GetChartScale();

   if(!_config.FromInit)
     {
      _createDot();
     }

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CDot::~CDot()
  {
   _deleteDot();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDot::OnChartScaleChange(double chartScale)
  {

   if(_created)
     {

      _chartScale=chartScale;

      if(ObjectSetDouble(0,_name,OBJPROP_PRICE,_getAdjustedPriceLevel()))
        {
         WindowRedraw();
        }
      else
        {
         Print(__FUNCTION__,", Error: ",GetLastError());
        }

     }

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDot::Activate()
  {
   _createDot();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CDot::GetPeriod()
  {
   return _period;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDot::_deleteDot()
  {

   if(_created && !ObjectDelete(0,_name))
     {

#ifdef LoggingOn
      Print("Error: unable to delete Dot! Error code #",GetLastError());
#endif

     }

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDot::_setName(string token)
  {
   _name=token+string(_time1)+"_"+string(_price1)+"_"+string(_period);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDot::_createDot()
  {

   _created=ObjectCreate(0,_name,OBJ_ARROW,0,_time1,_getAdjustedPriceLevel());

   if(_created)
     {
      //--- set the text
      ObjectSetInteger(0,_name,OBJPROP_ARROWCODE,159);
      ObjectSetInteger(0,_name,OBJPROP_WIDTH,1);
      ObjectSetInteger(0,_name,OBJPROP_COLOR,_higherHighs?clrBlue:clrRed);
     }
   else
     {

#ifdef LoggingOn
      Print("Error: unable to create dot! Error code #",GetLastError());
#endif

     }

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CDot::_getAdjustedPriceLevel()
  {

   double price=_price1;

   if(_higherHighs)
     {
      price-=(_config.DotsHighOffset*_chartScale);
     }
   else
     {
      price+=(_config.DotsLowOffset*_chartScale);
     }

   return price;

  }
//+------------------------------------------------------------------+
 
Geester:

Hi there,

I have an EA that determines trend and at certain points, it draws a row of dots above (short) or below (long) pullbacks in the trend. At the close of each bar, if the trend is maintained, another dot is added to the end of the row of dots at the last price level. If there is a subsequent pullback in the same direction of the trend, the line of dots are moved up below the higher high, or above, the lower low. Pretty standard stuff.

However (and there's always a "but" ...), whilst this works most of the time, occasionally, I get what I can only describe as "orphaned" dots. If I remove the EA, the "known" dots are removed, leaving the orphans behind. There are only two ways of adding dots and they are public methods on the CDotManager. Each dot is a CDot instance which creates and wraps the dot screen object. I have looked for days to see why this might be happening but right now, I'm out of ideas. I've included the sources for the Manager and Dot instance classes. If anyone could take a look at this code or might have a general idea of what could be causing these "orphans", I greatly appreciate it! :)


I would derive from CChartObjectsArrow and implement a static int to create a unique id for the object name. 

class CDot : public CChartObjectArrow
{
   static int  s_instances;
   int         m_instance;
 public:
   CDot(){ m_instance = ++s_instances;}
   bool Create(datetime time,double price)
   {
      string name = "__dot_"+string(m_instance)+"__";
      if(!CChartObjectArrow::Create(0,name,0,time,price,(char)159))
         return false;
   }
};
int CDot::s_instances=0;
 
nicholishen:

I would derive from CChartObjectsArrow and implement a static int to create a unique id for the object name. 


Hi @nicholishen - thanks for your comments and code sample. Are you thinking that it's possibly the naming of the arrows that are contributing to the issue I'm seeing?


 
Geester:

Hi @nicholishen - thanks for your comments and code sample. Are you thinking that it's possibly the naming of the arrows that are contributing to the issue I'm seeing?



Not sure. I just know that MQL has already implemented the proper chart object handling in the std lib, so no need to reinvent the wheel.... 

 
nicholishen:

Not sure. I just know that MQL has already implemented the proper chart object handling in the std lib, so no need to reinvent the wheel.... 


Hey @nicholishen - updated my code with the suggestions and so far so good. You  are absolutely right - I've been able to remove quite a bit of code that just isn't needed now. Feels cleaner and more precise. Fingers crossed! :)

Thanks again.

 
nicholishen:

Not sure. I just know that MQL has already implemented the proper chart object handling in the std lib, so no need to reinvent the wheel.... 


Hi @nicholishen - just an update to say that I managed to sort the problem. The new uniquely named dot objects didn't actually fix the problem but they allowed me to solve it. There was a bug in my code that was printing one dot too many on each iteration of a new bar loop. Because it was printing dots in the same places, it was using the same names down to my original naming structure. I got the same issue with the new style dots but, they all disappeared when the collection cleared, instead of leaving "orphans". Knowing this, I was able to track the error in my code. All good now! :)

Thanks again. --G

Reason: