Dealing with a simple dynamic array

 

Hello, I'm struggling with MQ4L's arrays trying to achieve what I'd call a pretty simple operation in other languages.

Any advice on this greatly appreciated!


I've got a dynamic array that will be altered in the onCalculate() function of my indicator.

Everytime price reaches a value that is dividable by 10, this value should be added to the array, or, if already present, be removed.

(I'm actually using a different logic but this is for simplicity).


double prices[];


if (Bid % 10 == 0) {

    // What I want in pseudo-code

    if (prices.contains(Bid)) {

        prices.remove(Bid)

    } else {

        prices.append(Bid)

    }

}


Also, on a side note, is it possible to create an array with elements of my own class?


Thanks a lot!



 
  1. if (Bid % 10 == 0) {
    Bid is 1.23456. Your test will either be always logically true or always logically false. Modulo must use integers. Doubles rarely compare equal. See also The == operand. - MQL4 forum
  2. You could instead use points or pips. See adjusting SL, TP, and slippage; for 4/5 digit brokers and for JPY pairs.
    double   pip          = StringFind(_Symbol,"JPY") < 0 ? 0.01 : 0.0001;
    int      pipsToPoints = int(pip / _Point);
    int      pipDigits    = (int)MathLog10(pipsToPoints);
    int      slippage     = 3 * pipsToPoints;
    
    int      price        = (int) Bid / pip;
    ...Add(price)
    

  3. double prices[];
    Array has no size. You must enlarge it, store it, and write a function to search it.
  4. You can use <arrays/array*.mqh> classes (cArray) which have Add and Search* methods.


 
whroeder1:
  1. Bid is 1.23456. Your test will either be always logically true or always logically false. Modulo must use integers. Doubles rarely compare equal. See also The == operand. - MQL4 forum
  2. You could instead use points or pips. See adjusting SL, TP, and slippage; for 4/5 digit brokers and for JPY pairs.
  3. Array has no size. You must enlarge it, store it, and write a function to search it.
  4. You can use <arrays/array*.mqh> classes (cArray) which have Add and Search* methods.



Thank you for the answer, whroeder1!


My fault, I forgot mentioning that I was using this on SP500 and DAX, so the modulo should work (It's just for this example, anyways!)


I will try to work it out with your suggestions and report back, with the solution, hopefully.

 

whroeder1:

You can use <arrays/array*.mqh> classes (cArray) which have Add and Search* methods.


Please, could you point me to how to access these?


I can only find these in the docs:

https://docs.mql4.com/array/arraybsearch


Thanks!

ArrayBsearch - Array Functions - MQL4 Reference
ArrayBsearch - Array Functions - MQL4 Reference
  • docs.mql4.com
ArrayBsearch - Array Functions - MQL4 Reference
 
ZetaTrader: I forgot mentioning that I was using this on SP500 and DAX, so the modulo should work (It's just for this example, anyways!)
What is the tick size of those two? If it isn't exactly 1, 2, or 5 your modulo will not work.


ZetaTrader: Please, could you point me to how to access these?
Really? I used this and immediately found this.
 
ZetaTrader:


Please, could you point me to how to access these?

Once you start including external libraries, I would consider using a hashtable/dictionary instead of an array. In any programming language, a search on a hashtable ought to be faster a search on an array. For example, doing ContainsKey() on the following hashtable is faster than doing Search() on one of the CArray classes. Similarly, for comparison, in the .NET languages, HashTable.ContainsKey() is much faster than Array.Contains(). 

https://www.mql5.com/en/forum/170566#comment_4106277

 
whroeder1:
What is the tick size of those two? If it isn't exactly 1, 2, or 5 your modulo will not work.


Really? I used this and immediately found this.


Thank you, this seems to be what I'm looking for.


However, does this work with MQL4?

I'm trying to figure out how to include this into my project.

The compiler won't complain about the #include statement, but how would I declare my array as a cArray?

 
JC:

Once you start including external libraries, I would consider using a hashtable/dictionary instead of an array. In any programming language, a search on a hashtable ought to be faster a search on an array. For example, doing ContainsKey() on the following hashtable is faster than doing Search() on one of the CArray classes. Similarly, for comparison, in the .NET languages, HashTable.ContainsKey() is much faster than Array.Contains(). 

https://www.mql5.com/en/forum/170566#comment_4106277

Are you sure about that? Last time I tested the dictionary from one of the mql cookbooks it was slower.
 
ZetaTrader:


double prices[];


if (Bid % 10 == 0) {

    // What I want in pseudo-code

    if (prices.contains(Bid)) {

        prices.remove(Bid)

    } else {

        prices.append(Bid)

    }

}


Also, on a side note, is it possible to create an array with elements of my own class?


Question 1: https://www.mql5.com/en/docs/standardlibrary/datastructures/carraydouble

#include<Arrays\ArrayDouble.mqh>
CArrayDouble prices;
//---
   if (Bid % 10 == 0) 
   {
      int index = prices.SearchLinear(Bid);
      if (index > -1) 
         prices.Delete(index);
      else 
         prices.Add(Bid);
   }

Question 2: Yes.

enum SORT_BY{SORT_BY_DATA1,SORT_BY_DATA2};

class MyClass : public CObject
{
public:
   double   data1,
            data2;
            MyClass(double d1,double d2):data1(d1),data2(d2){}
   void     Run() { Print("MyData = ",data1);}
   //override the virtual compare method from CObject for object sorting
   int      Compare(const CObject *node,const int mode=0) const override;
};

int MyClass::Compare(const CObject *node,const int mode=0) const override
{
   MyClass *that = (MyClass*)node;
   if(mode == SORT_BY_DATA1)
   {
      if(this.data1 > that.data1)
         return 1;
      else if(this.data1 < that.data1)
         return -1;
      else 
         return 0;
   }
   if(mode == SORT_BY_DATA2)
   {
      if(this.data2 > that.data2)
         return 1;
      else if(this.data2 < that.data2)
         return -1;
      else 
         return 0;
   }
   return 0;
}
/////////////////////////////////////////////////////////////////////////////////
class MyClassList : public CArrayObj
{
public:
   MyClass  *operator[](const int index) const { return (MyClass*)this.At(index);}
};
/////////////////////////////////////////////////////////////////////////////////
void Test()
{
   MyClassList list;
   for(int i=1;i<11;i++)
      list.Add(new MyClass(1.4*i,2.3*i));
   
   list.Sort((int)SORT_BY_DATA1);
   for(int i=0;i<list.Total();i++)
      list[i].Run();
      
Documentation on MQL5: Standard Library / Data Collections / CArrayDouble
Documentation on MQL5: Standard Library / Data Collections / CArrayDouble
  • www.mql5.com
Standard Library / Data Collections / CArrayDouble - Reference on algorithmic/automated trading language for MetaTrader 5
 
nicholishen:
Are you sure about that? Last time I tested the dictionary from one of the mql cookbooks it was slower.

I'd say it depends on two main things. You may be able to think of others...

  • The number of items to be searched
  • Whether/how often the array can be relied on to be pre-sorted, versus the number of times you have to do .Sort() before being able to do .Search()

Assuming a pre-sorted array, the following code, on my computer, is faster using CArrayInt for roughly <250 items, and faster using a hashtable for >250 items.

For more realistic usage, the balance would shift in favour of the hashtable depending on how often the array needed to be re-sorted because of additions.

#property strict

#include <hashtable-templated.mqh>
#include <arrays/ArrayInt.mqh>

input int   ArrayElements = 300;
input int   TestIterations = 1000000;
input int   SearchVal = 49;

void TestArray()
{
   CArrayInt arr;
   for (int i = 0; i < ArrayElements; i++) {
      arr.Add(i);
   }
   // Although array is already sorted, must be explicitly sorted or 
   // else Search() exits early with a negative result
   arr.Sort();
   
   ulong tmStart = GetMicrosecondCount();
   for (int i = 0; i < TestIterations; i++) {
      bool bTest = (arr.Search(SearchVal) >= 0);
   }
   ulong tmEnd = GetMicrosecondCount();
   
   Print("Array method, microseconds taken: " , (tmEnd - tmStart));
}

void TestHashTable()
{
   BasicHashTable<int, int> bht;
   for (int i = 0; i < ArrayElements; i++) {
      bht.Set(i, i);
   }

   ulong tmStart = GetMicrosecondCount();
   for (int i = 0; i < TestIterations; i++) {
      bool bTest = bht.ContainsKey(SearchVal);
   }
   ulong tmEnd = GetMicrosecondCount();
   
   Print("Hashtable method, microseconds taken: " , (tmEnd - tmStart));
}

void OnStart()
{
   TestArray();
   TestHashTable();
}
 
JC:

I'd say it depends on two main things. You may be able to think of others...

... Also depends on the value being searched for. The example value in the above code - 49 - is fairly ideal for the array. Changing it to e.g. 1 means that the ArrayInt.Search() has to do much more work, and becomes slower, on my computer, than the hashtable at only about 25 items in the list.
Reason: