Features of the mql5 language, subtleties and tricks - page 222

 
Maxim Kuznetsov #:

This is nothing new, it's the way it's always worked from birth.

Only if the data is larger than __atomic__ should access be wrapped/protected with critical sections (or mutex to terminal std::thread)

Well I didn't know this behaviour, and was expecting a separate process when importing.
I thought that global DLL pointers will be new, in each loaded MT5 program.
With what I had to sweat to understand the logic, wondering why the error will pop up in memory.
But when I figured out the reason by try (print) method, then the whole architectural plan of working with DLL will be built differently.
Man, you're right about mutexes, I didn't think about it. Thanks for the tip.

 
fxsaber #: ZZY It's a pity you can't do it by subfield or method.

Yes. Although if the method has no parameters, you can do so:

// Сортировка массива структур и указателей на объекты по методу.
#define  ArraySortStruct_DefineMethod(METHOD)                                     \
namespace SortOnMethod_##METHOD                                                  \
{                                                                                \
  class SORT                                                                     \
  {                                                                              \
  private:                                                                       \
    template <typename T>                                                        \
    static void Swap( T &Array[], const int i, const int j )                     \
    {                                                                            \
      const T Temp = Array[i];                                                   \
                                                                                 \
      Array[i] = Array[j];                                                       \
      Array[j] = Temp;                                                           \
                                                                                 \
      return;                                                                    \
    }                                                                            \
                                                                                 \
    template <typename T>                                                        \
    static int Partition( T &Array[], const int Start, const int End )           \
    {                                                                            \
      int Marker = Start;                                                        \
                                                                                 \
      for (int i = Start; i <= End; i++)                                         \
        if (Array[i].##METHOD() <= Array[End].##METHOD())                        \
        {                                                                        \
          SORT::Swap(Array, i, Marker);                                          \
                                                                                 \
          Marker++;                                                              \
        }                                                                        \
                                                                                 \
       return(Marker - 1);                                                       \
    }                                                                            \
                                                                                 \
    template <typename T>                                                        \
    static void QuickSort( T &Array[], const int Start, const int End )          \
    {                                                                            \
      if (Start < End)                                                           \
      {                                                                          \
        const int Pivot = Partition(Array, Start, End);                          \
                                                                                 \
        SORT::QuickSort(Array, Start, Pivot - 1);                                \
        SORT::QuickSort(Array, Pivot + 1, End);                                  \
      }                                                                          \
                                                                                 \
      return;                                                                    \
    }                                                                            \
                                                                                 \
  public:                                                                        \
    template <typename T>                                                        \
    static void Sort( T &Array[], int Count = WHOLE_ARRAY, const int Start = 0 ) \
    {                                                                            \
      if (Count == WHOLE_ARRAY)                                                  \
        Count = ::ArraySize(Array);                                              \
                                                                                 \
      SORT::QuickSort(Array, Start, Start + Count - 1);                          \
                                                                                 \
      return;                                                                    \
    }                                                                            \
  };                                                                             \
}

#define  ArraySortStructMet(ARRAY, METHOD) SortOnMethod_##METHOD::SORT::Sort(ARRAY)

Application:

#include <fxsaber\TypeToBytes.mqh>

struct MqlRates2: public MqlRates
  {
   double            Open() {return open;}
  };

ArraySortStruct_DefineMethod(Open)

void OnStart()
  {
   MqlRates Rates[];

   CopyRates(_Symbol, PERIOD_CURRENT, 0, 5, Rates); // Взяли бары

   MqlRates2 Rates2[];
   _ArrayCopy(Rates2, Rates);


   Print("\nСортируем Rates2 по open-цене.");
   ArraySortStructMet(Rates2, Open);
   ArrayPrint(Rates2);

  }
 

With partial execution of orders the fieldORDER_TIME_SETUP_MSC changes.

As a consequence, DEAL_TIME_MSC can be less than ORDER_TIME_SETUP_MSC of its order.

 

What about using a custom compare function:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
typedef double(*TComparer)(MqlRates &a, MqlRates &b);

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class SORT
  {
private:
   template <typename T>
   static void       Swap(T &Array[], const int i, const int j)
     {
      const T Temp = Array[i];

      Array[i] = Array[j];
      Array[j] = Temp;

      return;
     }

   template <typename T>
   static int        Partition(T &Array[], const int Start, const int End, TComparer Compare)
     {
      int Marker = Start;

      for(int i = Start; i <= End; i++)
         if(Compare(Array[i], Array[End]) <= 0)
           {
            SORT::Swap(Array, i, Marker);

            Marker++;
           }

      return(Marker - 1);
     }

   template <typename T>
   static void       QuickSort(T &Array[], const int Start, const int End, TComparer Compare)
     {
      if(Start < End)
        {
         const int Pivot = Partition(Array, Start, End, Compare);

         SORT::QuickSort(Array, Start, Pivot - 1, Compare);
         SORT::QuickSort(Array, Pivot + 1, End, Compare);
        }

      return;
     }

public:
   template <typename T>
   static void       Sort(T &Array[], TComparer Compare, int Count = WHOLE_ARRAY, const int Start = 0)
     {
      if(Count == WHOLE_ARRAY)
         Count = ::ArraySize(Array);

      SORT::QuickSort(Array, Start, Start + Count - 1, Compare);

      return;
     }
  };

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double compare_open(MqlRates &a, MqlRates &b)          { return a.open - b.open;     }
double compare_high(MqlRates &a, MqlRates &b)          { return a.high - b.high;     }
double compare_low(MqlRates &a, MqlRates &b)           { return a.low  - b.low;      }
double compare_close(MqlRates &a, MqlRates &b)         { return a.close - b.close;   }
double compare_time(MqlRates &a, MqlRates &b)          { return (double)(a.time - b.time); }
double compare_tick_volume(MqlRates &a, MqlRates &b)   { return (double)(a.tick_volume - b.tick_volume); }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   MqlRates Rates[];

   CopyRates(_Symbol, PERIOD_CURRENT, 0, 5, Rates);
   ArrayPrint(Rates);

   SORT::Sort(Rates, compare_open);
   ArrayPrint(Rates);
  }
 
fxsaber #:

You're right, thank you! I overdid it on a flat spot. I'll leave your variant for sorting.


Application.


ZZY It's a pity, by subfield or method, it doesn't work.

Here is the optimized QuickSort from my library, slightly adapted to your requirements.

It can sort huge arrays without stack overflow (due to uncontrolled recursion).

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
typedef double(*TComparer)(MqlRates &a, MqlRates &b);

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class SORT
  {
private:
   //+------------------------------------------------------------------+
   //| Swaps two variables or two array elements.                       |
   //+------------------------------------------------------------------+
   template<typename T>
   static void       Swap(T &var1, T &var2)
     {
      T temp = var1;
      var1 = var2;
      var2 = temp;
     }
   //+------------------------------------------------------------------+
   //| Insertion Sort                                                   |
   //+------------------------------------------------------------------+
   /**
    * Sort the input array in-place in ascending order.
    * Insertion sort is the fastest sort algorithm for very small
    * arrays (<10-20 elements), or when the input is almost sorted.
    */
   template<typename T>
   static void       InsertionSort(T &arr[], int lo, int hi, TComparer Compare)
     {
      for(int i = lo + 1; i <= hi; i++)
        {
         if(Compare(arr[i], arr[i - 1]) < 0)
           {
            T key = arr[i];
            int j = i - 1;
            while(j >= lo && Compare(arr[j], key) > 0)
              {
               arr[j + 1] = arr[j];
               j--;
              }
            arr[j + 1] = key;
           }
        }
     }
   //+------------------------------------------------------------------+
   //| Quick Sort (Optimized)                                           |
   //+------------------------------------------------------------------+
   /**
    * Sort the input array in-place in ascending order.
    * Generally, the fastest comparison-based sort algorithm for arrays.
    * (esp. if large size).
    * Average time complexity: O(n log n)
    * Worst-case time complexity: O(n log n)
    * Stable    : no
    *
    * Improvements:
    *
    * 1. Use insertion sort for small partitions, takes linear time O(n + I) with nearly sorted data.
    * 2. Choose pivot as the middle element, to avoid the worst case O(n^2) on already-sorted data.
    * 3. Hoare's parition scheme, which is more efficient than Lomuto's partition scheme
    *    because it does three times fewer swaps on average, and it creates efficient
    *    partitions even when all values are equal (duplicate sort keys).
    * 4. Sort small partitions using recursion and do tail recursion elimination for large partitions.
    *    This guarantees O(logn) space complexity and avoids stack overflow with huge arrays.
    */
   template<typename T>
   static void       QuickSort(T &arr[], int lo, int hi, TComparer Compare)
     {
      // Use a while loop for tail recursion elimination.
      while(lo < hi)
        {
         // Insertion sort is faster for small partitions.
         if(hi - lo < 16)
           {
            InsertionSort(arr, lo, hi, Compare);
            return;
           }

         // Pick pivot as the middle element.
         int mid = lo + (hi - lo) / 2;
         T pivot = arr[mid];

         // Hoare’s partition scheme.
         int i = lo;
         int j = hi;
         while(i <= j)
           {
            while(Compare(arr[i], pivot) < 0)
               i++;
            while(Compare(arr[j], pivot) > 0)
               j--;
            if(i <= j)
              {
               Swap(arr[i], arr[j]);
               i++;
               j--;
              }
           }
         // now, a[lo..j] <= a[i..hi]

         // Sort the small partition first using recursion and do tail recursion elimination for
         // the large partition.
         int l_size = j - lo;
         int r_size = hi - i;
         if(l_size < r_size)
           {
            if(l_size > 0)
              {
               QuickSort(arr, lo, j, Compare);
              }
            lo = i;
           }
         else
           {
            if(r_size > 0)
              {
               QuickSort(arr, i, hi, Compare);
              }
            hi = j;
           }
        }
     }

public:
   template<typename T>
   static void       Sort(T &Array[], TComparer Compare, int Count = WHOLE_ARRAY, const int Start = 0)
     {
      if(Count == WHOLE_ARRAY)
         Count = ::ArraySize(Array);

      SORT::QuickSort(Array, Start, Start + Count - 1, Compare);
     }
  };

//+------------------------------------------------------------------+
//| Compares two fields and returns a value indicating whether one   |
//| is equal to, less than, or greater than the other.               |
//+------------------------------------------------------------------+
double compare_open(MqlRates &a, MqlRates &b)          { return a.open - b.open;     }
double compare_high(MqlRates &a, MqlRates &b)          { return a.high - b.high;     }
double compare_low(MqlRates &a, MqlRates &b)           { return a.low  - b.low;      }
double compare_close(MqlRates &a, MqlRates &b)         { return a.close - b.close;   }
double compare_time(MqlRates &a, MqlRates &b)          { return (double)(a.time - b.time); }
double compare_tick_volume(MqlRates &a, MqlRates &b)   { return (double)(a.tick_volume - b.tick_volume); }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   MqlRates Rates[];

   CopyRates(_Symbol, PERIOD_CURRENT, 0, 5, Rates);
   ArrayPrint(Rates);

   SORT::Sort(Rates, compare_open);
   ArrayPrint(Rates);

   SORT::Sort(Rates, compare_tick_volume);
   ArrayPrint(Rates);
  }
 
 

When using ArrayInsert, you can save on memory when Array_Destination[] is smaller than Array_Source[] from which elements are added.

To do this, you should apply ArraySwap twice: before and after ArrayInsert.


For example, this is relevant when working with large MqlTick archives, etc.

 

Forum on trading, automated trading systems and testing trading strategies

Peculiarities of mql5 language, subtleties and techniques of work

fxsaber, 2022.02.20 15:00

You are right, Thank you! I overmudryl on a straight place. I will leave your variant for sorting.

Application.

ArraySortStruct_Define(open)
ArraySortStruct_Define(high)
ArraySortStruct_Define(time)

void OnStart()
{
  MqlRates Rates[];
  
  CopyRates(_Symbol, PERIOD_CURRENT, 0, 5, Rates); // Взяли бары
  
  Print("\nБары без сортировки - как получили.");
  ArrayPrint(Rates);
  
  Print("\nСортируем по open-цене.");
  ArraySortStruct(Rates, open);
  ArrayPrint(Rates);
  
  Print("\nСортируем по high-цене.");
  ArraySortStruct(Rates, high);
  ArrayPrint(Rates);
  
  Print("\nСортируем по времени.");
  ArraySortStruct(Rates, time);
  ArrayPrint(Rates);
}

There was an error in the Partition method. Corrected version.

// Сортировка массива структур и указателей на объекты по полю.
#define  ArraySortStruct_Define(FIELD)                                            \
namespace SortOnField_##FIELD                                                    \
{                                                                                \
  class SORT                                                                     \
  {                                                                              \
  private:                                                                       \
    template <typename T>                                                        \
    static void Swap( T &Array[], const int i, const int j )                     \
    {                                                                            \
      const T Temp = Array[i];                                                   \
                                                                                 \
      Array[i] = Array[j];                                                       \
      Array[j] = Temp;                                                           \
                                                                                 \
      return;                                                                    \
    }                                                                            \
                                                                                 \
    template <typename T>                                                        \
    static int Partition2 ( T &Array[], const int Start, const int End )         \
    {                                                                            \
      const T Pivot = Array[End];                                                \
      int i = (Start - 1);                                                       \
                                                                                 \
      for (int j = Start; j < End; j++)                                          \
        if (Array[j].##FIELD < Pivot.##FIELD)                                    \
          SORT::Swap(Array, ++i, j);                                             \
                                                                                 \
      SORT::Swap(Array, i + 1, End);                                             \
                                                                                 \
      return(i + 1);                                                             \
    }                                                                            \
                                                                                 \
    template <typename T>                                                        \
    static void QuickSort( T &Array[], const int Start, const int End )          \
    {                                                                            \
      if (Start < End)                                                           \
      {                                                                          \
        const int Pivot = SORT::Partition2(Array, Start, End);                   \
                                                                                 \
        SORT::QuickSort(Array, Start, Pivot - 1);                                \
        SORT::QuickSort(Array, Pivot + 1, End);                                  \
      }                                                                          \
                                                                                 \
      return;                                                                    \
    }                                                                            \
                                                                                 \
  public:                                                                        \
    template <typename T>                                                        \
    static void Sort( T &Array[], int Count = WHOLE_ARRAY, const int Start = 0 ) \
    {                                                                            \
      if (Count == WHOLE_ARRAY)                                                  \
        Count = ::ArraySize(Array);                                              \
                                                                                 \
      SORT::QuickSort(Array, Start, Start + Count - 1);                          \
                                                                                 \
      return;                                                                    \
    }                                                                            \
  };                                                                             \
}

#define  ArraySortStruct(ARRAY, FIELD) SortOnField_##FIELD::SORT::Sort(ARRAY)
 
fxsaber #:

There was an error in the Partition method. Corrected version.

For a special case when a numeric field is taken for sorting (the most common situation), I wrote a variant that is several orders of magnitude faster than the previous one. But it consumes twice as much memory. It is relevant for large arrays.

// Сортировка массива структур и указателей на объекты по ЧИСЛОВОМУ полю.
#define  ArraySortStruct2_Define(FIELD)                               \
namespace SortOnField_##FIELD                                        \
{                                                                    \
  class SORT2                                                        \
  {                                                                  \
  private:                                                           \
    template <typename T, typename T2>                               \
    static void Sort( T &Array[], const T2& )                        \
    {                                                                \
      T2 SortIndex[][2];                                             \
                                                                     \  
      const int Size = ::ArrayResize(SortIndex, ::ArraySize(Array)); \
                                                                     \
      for (int i = Size - 1; i >= 0; i--)                            \
      {                                                              \
        SortIndex[i][0] = (T2)Array[i].##FIELD;                      \
        SortIndex[i][1] = (T2)i;                                     \
      }                                                              \
                                                                     \
      ::ArraySort(SortIndex);                                        \
                                                                     \
      T Sort_Array[];                                                \
                                                                     \
      for (int i = ::ArrayResize(Sort_Array, Size) - 1; i >= 0; i--) \
        Sort_Array[i] = Array[(int)SortIndex[i][1]];                 \
                                                                     \
      ::ArraySwap(Sort_Array, Array);                                \
                                                                     \
      return;                                                        \
    }                                                                \
                                                                     \
  public:                                                            \
    template <typename T>                                            \
    static void Sort( T &Array[] )                                   \
    {                                                                \
      if (::ArraySize(Array))                                        \
        SORT2::Sort(Array, Array[0].##FIELD);                        \
                                                                     \
      return;                                                        \
    }                                                                \
  };                                                                 \
}

#define  ArraySortStruct2(ARRAY, FIELD) SortOnField_##FIELD::SORT2::Sort(ARRAY)


Application (with measuring the execution time).

#include <fxsaber\Benchmark\Benchmark.mqh> // https://www.mql5.com/ru/code/31279
#define _BV2(A) _BV(A, 100) // Алертим все, что исполняется дольше 100 микросекунд.

struct STRUCT : public MqlTick
{
  double Num;
};

ArraySortStruct_Define(Num)
ArraySortStruct2_Define(Num)

void OnStart()
{
  STRUCT Array[];
  
  const int Size = ArrayResize(Array, 1 e5);
  
  for (int i = Size - 1; i >= 0; i--)
    Array[i].Num = Size - i;
    
//  _BV2(ArraySortStruct(Array, Num));
  _BV2(ArraySortStruct2(Array, Num));
}


Result.

Alert: Bench_Stack = 0, 100 <= Time[Test9.mq5 129 in OnStart: SortOnField_Num::SORT::Sort(Array)] = 34574468 mcs.

Alert: Bench_Stack = 0, 100 <= Time[Test9.mq5 130 in OnStart: SortOnField_Num::SORT2::Sort(Array)] = 10586 mcs.
 
fxsaber #:

For a particular case when a numeric field is used for sorting (the most common situation), I wrote a variant that is several orders of magnitude faster than the previous one. But it consumes twice as much memory. It is relevant for large arrays.


Application (with measuring of execution time).


Result.

What do you think of this option?

#include <QuickSort.mqh>

#include <fxsaber\Benchmark\Benchmark.mqh> // https://www.mql5.com/ru/code/31279
#define _BV2(A) _BV(A, 100) // Алертим все, что исполняется дольше 100 микросекунд.

struct STRUCT : public MqlTick
{
  double Num;
};

typedef bool (*FuncLess)( const STRUCT&, const STRUCT& );

bool Less( const STRUCT& struct1, const STRUCT& struct2 )
{
   return struct1.Num < struct2.Num;
}

void function1( STRUCT& array[] )
{
   QuickSort< STRUCT, FuncLess >( array, Less );
}

void OnStart()
{
  STRUCT Array[];
  
  const int Size = ArrayResize(Array, 1 e5);
  
  for (int i = Size - 1; i >= 0; i--)
    Array[i].Num = Size - i;
    
//  _BV2(ArraySortStruct(Array, Num));
//  _BV2(ArraySortStruct2(Array, Num));
  
  _BV2( function1( Array ) );
  
}
Reason: