In MT4, is there a simple way to get a UniqueID per *each* instance of an indicator? - page 2

 
Alain Verleyen:
So ? How each indicator instance will recognize its objects ?
Random integer generator
pips4life:

Hi MT4 programmers,

I have indicators that create objects.  For a few of my indicators, I might add more than one (of the same indicator) to a chart.

A problem will occur if the indicators both create and delete the same object names.  Therefore, the usual workaround is to use a user-defined property, like:
    extern int  UniqueID_per_indicator = 1; // When adding >1 of the same indicator, change the number

By now, with b600+ features, isn't there now some way to automatically get a simple (preferably short, 1-2 digit) unique ID# that the indicator itself can retrieve?

I just need a UniqueID.  I say "short" is better, because some object names I use actually get quite long and may exceed the 62 or 63 character limit for object names.

Note: Before anyone suggests to use a random integer generator, that really doesn't work well with  templates!  The first time added to a chart, it works fine.  But if you save the chart to a template, it saves all the indicator-created objects too.  But when you use this template again, the indicator will get a *new* uniqueID, and therefore, nothing will remove the stale objects from the chart (that came from the template).  They end up on top of each other.


This is such a basic need.  I see plenty of code with the external variable approach, but why bother the user to set such a simple (but necessarily unique) integer?

Any ideas?  Any code snippet for MT4?

Thanks,

pips4life (Kent)


P.S. Is this doable in MT5?  I haven't dived in there yet.

 
honest_knave:
Random integer generator

Not working in all cases. You need a unique and repeatable ID as said by pips4life.

If parameters are different you can use a signature based on them. But it may happen you need several instances of the same indicator/parameters.

 

Thanks for your comments also.

My default template has about 4 indicators.  Assuming that I have started MT4, created my charts, and then quit normally, at least once, this establishes a "baseline".

Next time I start MT4, all the charts seems to have the same indicator order.  But no, I'm not sure about it, because I don't often put 2+ of the same one on charts.  It happens, just not a lot.  But... it still seems like they come up with the same order again, so long as there was at least one "normal" shutdown.

When there's a crash, you lose everything since the last normal shutdown.  Any new charts, things added... gone.  But, shouldn't matter for purposes of this discussion, because, what is there is there, and it was already working.  I only need to add back whatever was lost by the crash, then, normal quit again, then I have a new baseline.   (Or maybe, Save Profile is another way to save the baseline?? I don't do that much).


Regarding your PS, you asked a question of honest knave.  He described a reasonable workaround, to delete the created object before creating the template.

You asked, "So ? How each indicator instance will recognize its objects ?"

Hmm, I think it's self evident. The basic operation of the indicators is to create objects when they start up.  Dropping a template -- which has zero objects (all were deleted), but does have the indicators -- onto a blank chart, it runs all indicators which creates all the needed objects.  And because there were no objects in the template, that means there are no stale objects based on a previous UniqueID.

 
pips4life:

Thanks for your comments also.

My default template has about 4 indicators.  Assuming that I have started MT4, created my charts, and then quit normally, at least once, this establishes a "baseline".

Next time I start MT4, all the charts seems to have the same indicator order.  But no, I'm not sure about it, because I don't often put 2+ of the same one on charts.  It happens, just not a lot.  But... it still seems like they come up with the same order again, so long as there was at least one "normal" shutdown.

When there's a crash, you lose everything since the last normal shutdown.  Any new charts, things added... gone.  But, shouldn't matter for purposes of this discussion, because, what is there is there, and it was already working.  I only need to add back whatever was lost by the crash, then, normal quit again, then I have a new baseline.   (Or maybe, Save Profile is another way to save the baseline?? I don't do that much).


Regarding your PS, you asked a question of honest knave.  He described a reasonable workaround, to delete the created object before creating the template.

You asked, "So ? How each indicator instance will recognize its objects ?"

Hmm, I think it's self evident. The basic operation of the indicators is to create objects when they start up.  Dropping a template -- which has zero objects (all were deleted), but does have the indicators -- onto a blank chart, it runs all indicators which creates all the needed objects.  And because there were no objects in the template, that means there are no stale objects based on a previous UniqueID.

I posted a link to a thread where all of that was already discussed.

Anyway, no worries. Thanks.

 
Alain Verleyen:
There was an interesting discussion some years ago, but no definitive solution I am afraid.


Good thread, and same topic.  But I did not find an answer there.

You understand my request perfectly, in your later post:  "You need a unique and repeatableID as said by pips4life."

That's it.

I can't think of any indicators where I would add 2 or more of the same thing without editing each so they have at least one unique parameter setting, but I suppose there are such examples.

But I don't want to parse each and every parameter and put that into a shortname, for example.  Way too much hassle.


If there is no function, I guess I'll have to stick with the lame but perfectly functional manual setting of:


   extern  int UniqueID_per_indicator = 1; // or 2, or 3, etc., different for each of this indicator added to a single chart.
   //
   string objectPrefix = StringConcatenate(""_",WindowExpertName(),"_",UniqueID,"_"); // Or any variation that is specific enough

 
pips4life:


Good thread, and same topic.  But I did not find an answer there.

You understand my request perfectly, in your later post:  "You need a unique and repeatableID as said by pips4life."

That's it.

I can't think of any indicators where I would add 2 or more of the same thing without editing each so they have at least one unique parameter setting, but I suppose there are such examples.

But I don't want to parse each and every parameter and put that into a shortname, for example.  Way too much hassle.


If there is no function, I guess I'll have to stick with the lame but perfectly functional manual setting of:


   extern  int UniqueID_per_indicator = 1; // or 2, or 3, etc., different for each of this indicator added to a single chart.
   //
   string objectPrefix = StringConcatenate(""_",WindowExpertName(),"_",UniqueID,"_"); // Or any variation that is specific enough

If you find a working function create a hash-value of all the parameters and compare the hash-values?
 
pips4life:


Good thread, and same topic.  But I did not find an answer there.

You understand my request perfectly, in your later post:  "You need a unique and repeatableID as said by pips4life."


Yes I understand it and on my side I would be interested by an universal solution, working even when the parameters are the same.

If your needs as only when parameters are different then follow Carl suggestion, here is a hash function.

 
pips4life:

P.S. Is this doable in MT5?  I haven't dived in there yet.

See GetMyUniqueName() here.

#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280
#include <crc64.mqh>       // https://www.mql5.com/en/blogs/post/683577
  
template <typename T>
ulong crc64( const T &Array[] )
{
  ulong crc = 0;

  return(::crc64(crc, _R(Array).Bytes, ::ArraySize(Array) * sizeof(T)));
}

int GetShortNames( string &ShortNames[], const long Chart_ID = 0, const int SubWindow = 0 )
{
  const int Total = ::ChartIndicatorsTotal(Chart_ID, SubWindow);

  ::ArrayResize(ShortNames, Total);

  for (int i = 0; i < Total; i++)
    ShortNames[i] = ::ChartIndicatorName(Chart_ID, SubWindow, i);

  return(Total);
}

// Возвращает свое "Короткое имя" - ShortName
string GetMyShortName( void )
{
  string Res = "";

  const int SubWindow = ::ChartWindowFind();

  string ShortNames[];

  GetShortNames(ShortNames, 0, SubWindow);

  const string TmpShortName = __FUNCSIG__ + (string)::MathRand();

  ::IndicatorSetString(INDICATOR_SHORTNAME, TmpShortName);

  string NewShortNames[];

  const int Total = GetShortNames(NewShortNames, 0, SubWindow);

  for (int i = 0; i < Total; i++)
    if (NewShortNames[i] == TmpShortName)
    {
      Res = ShortNames[i];

      ::IndicatorSetString(INDICATOR_SHORTNAME, Res);

      break;
    }

  return(Res);
}

// Возвращает свой хэндл
int GetMyHandle( void )
{
  const string ShortName = GetMyShortName();

  const string TmpShortName = __FUNCSIG__ + (string)::MathRand();

  ::IndicatorSetString(INDICATOR_SHORTNAME, TmpShortName);

  const int Res = ::ChartIndicatorGet(0, ::ChartWindowFind(), TmpShortName);

  ::IndicatorSetString(INDICATOR_SHORTNAME, ShortName);

  return(Res);
}

string GetMyUniqueName( void )
{
  const int handle = GetMyHandle();

  MqlParam Params[];
  ENUM_INDICATOR Type;

  const int Total = ::IndicatorParameters(handle, Type, Params);
  ::IndicatorRelease(handle);

  uchar Bytes[];

  for (int i = 1; i < Total; i++)
  {
    ::ArrayCopy(Bytes, _R(Params[i].double_value).Bytes, ::ArraySize(Bytes));
    ::ArrayCopy(Bytes, _R(Params[i].integer_value).Bytes, ::ArraySize(Bytes));
    ::ArrayCopy(Bytes, _R(Params[i].string_value).Bytes, ::ArraySize(Bytes));
  }

  return("::" + (string)::ChartID() + (string)crc64(Bytes) + ::MQLInfoString(MQL_PROGRAM_NAME));
}
 

It can be done, it just depends how much code you want to write.

You need some code to ensure your don't duplicate your random number suffix.

You need to make sure your code cleans up its objects in OnDeinit.

You need a way to clean up orphaned objects when the terminal doesn't shutdown correctly. You can do this by using temporary GVs of the terminal in conjunction with a cleanup routine in OnInit(). Loop through the objects, if you find my_object123 but there isn't a temporary GV "locking" 123 then delete the object.

 

Thanks everyone for your thoughtful replies.

fxsaber: When I get to MT5 -- and those who already are -- may benefit by the example.  Thanks.

honest_knave: I can imagine a flow that does make use of GV's,perhaps.  

However, here's a potential solution/flow:   I would prefer to make use of some control objects that exist or not, and take actions based on those.   Example: init() deletes all control objects of a pattern for it's own indicator.  Then the 1st tick (in start/OnCalculate) creates a control object that includes it's own random integer generator for UniqueID#.  Now you have only active control objects.  Wait 2-3 ticks to be sure, but then, assign the task to the lowest UniqueID# indicator, to go through all objects, pattern match, and delete all objects NOT matching any active UniqueID#.   


Alain, all:   I filed a Service Request "suggestion" with a proposal that would be universal.   I've no idea if it will be seriously considered.   If you like it, or see flaws, or can offer improvements, please comment.


=== 

PROPOSAL:

I want the very action of adding an indicator to a chart to create a unique and persistent integer ID# (on THAT chart), that will never change, no matter what other indicators I add or delete, whether the same name or a different name.

I don't mind reusing old (but presently unused) ID#'s.   If I add 5 indicators, say that each have unique and persistent ID's (0 1 2 3 4), I I delete the 3rd one, then "2" is now free to be re-used.  That's fine.  It's not a big deal if there are gaps in the number, however...

I'd really prefer this be a very short number, but obviously big enough to handle how ever many indicators are on the single chart.

static int chartCounter = 0;
int  freed_ID_numbers[];
int use_ID;

if( An indicator is added to a chart...)
  {  if ArrayLength( freed_ID_numbers==0) { use_ID=chartCounter; chartCounter++;  }
      else { use_ID = freed_ID_numbers[0];  (strip off the number [0] just used from the array. Shorten the array.); }   // Or use the last number in the array, doesn't matter which.
      
      store on the indicator instance, the UniqueID# = use_ID;
  }

if( An indicator is deleted from a chart...) 
  { size= ArraySize(freed_ID_numbers;  ArrayResize(freed_ID_numbers, 1+size); freed_ID_numbers[size]= (the ID# from the deleted indicator); }

There is no need to sort the "freed_ID_numbers" array.  The available ID numbers get used until there are no more gaps.

The "chartCounter" can grow as big as it needs to grow, BUT, because of re-using the freed up numbers, it will stay pretty short, almost always <100 or even <10 in practice.

If you don't want to mess with "freed_ID_numbers[]" as an array, there are probably other ways.

The unique indicator ID# needs a function, or an integer property handle to get it.

   IndicatorID(), or whatever.  (This is a per-chart ID#, and will vary on different charts, "0" on some, "1" or "2" etc. on others).  ("-1" perhaps for legacy templates??  If not already set in the template, it must get assigned, so I cannot imagine why or when it would return -1, as in no ID#.  Something wrong if so.


The unique indicator ID# should be stored as part of each indicator on a template!   It goes with the objects that might already exist on the template also.  Keep them consistent.  It will make it trivially easy to keep the indicator and template objects in sync with each other.

Perhaps "chartCounter" and "freed_ID_numbers[]" will need to be saved into the template.   Otherwise, they'll need to be reinitilized, or maybe just detect the max-used ID#, and start using chartCounter.  Build the "freed_ID_numbers[]", or don't bother.  Gaps are not a big deal.


WHY is "short" a big deal?  Maybe more so to me it is than others.  I create names with the ID# embedded, and some of my object names are already 63 characters long!  I can't afford extra digits for some.  (Plus, by the way, I could really use 128 or 256 even!  I store DATA in the object name (price and time for each point, in addiction to other things), and it works great! ).

If somebody creates objects based on a unique ID, plus a lot of chart property values, the names can get very long.

This proposal is to replace manual "UniqueID".   Most code is using, simple, "0" or "1".   If you make it up to MAX-integer, that's many more characters longer.   Maybe it will be OK,but maybe too long.

===   


Reason: