Особенности языка mql5, тонкости и приёмы работы - страница 222

 
Maxim Kuznetsov #:

Это не новость, они так всегда от рождения работают.

только если данные больше чем __atomic__ надо доступ обкладывать/защищать критическими секциями (или mutex в терминал std::thread)

Ну вот я не знал такого поведения, и ожидал при импорте отдельный процесс.
Так как думал что глобальные указатели DLL будут новые, в каждой загруженной программе МТ5.
С чем и пришлось попотеть в понимании логики, недоумевая почему вылетает ошибка по памяти.
Но когда методом тыка(принта) понял причину, то по другому тогда строится весь архитектурный план работы с DLL.
Блин, а насчёт мютексов ты прав, что то не подумал. Спасибо за подсказку.

 
fxsaber #:ЗЫ Жаль, по подполю или методу не получается.

Да. Хотя если метод без параметров, то можно так:

// Сортировка массива структур и указателей на объекты по методу.
#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)

Применение:

#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);

  }
 

При частичном исполнении отложек меняется поле ORDER_TIME_SETUP_MSC.

Как следствие, DEAL_TIME_MSC может быть меньше ORDER_TIME_SETUP_MSC своего ордера.

 

Как насчет использования пользовательской функции сравнения:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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 #:

Вы правы, спасибо! Я перестарался на ровном месте. Оставлю ваш вариант для сортировки.


Применение.


ЗЗЫ Жаль, что по подполям или методам не работает.

Вот оптимизированный QuickSort из моей библиотеки, немного адаптированный под ваши требования.

Он может сортировать огромные массивы без переполнения стека (из-за неконтролируемой рекурсии).

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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);
  }
 
Bodolino #:

Привет и спасибо, что поделились!

Это было бы идеально, если бы работало. Однако, при вставке в скрипт .mq5 и выполнении, к сожалению, ваш код выдает следующую ошибку(и):

  • template declarations are not allowed in local classes ArraySortStruct.mq5 87 4
  • (после небольшого изменения вашего кода): объявления шаблонов разрешены только в глобальной области, пространстве имен или классе ArraySortStruct.mq5 90 4 )

Не могли бы вы исправить это? Для вас это, вероятно, просто, тогда как я не знаю, с чего начать :-)

 
Комментарии, не относящиеся к этой теме, были перенесены в "Любые вопросы новичков по MQL4 и MQL5, помощь и обсуждение по алгоритмам и кодам".
 

При использовании ArrayInsert можно экономить на памяти в случае, когда Array_Destination[] меньше, чем Array_Source[], из которого добавляются элементы.

Для этого надо дважды применить ArraySwap: до и после ArrayInsert.


Например, это актуально, когда работаешь с большими MqlTick-архивами и т.д.

 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Особенности языка mql5, тонкости и приёмы работы

fxsaber, 2022.02.20 15:00

Вы правы, Спасибо! Перемудрил я на ровном месте. Оставлю Ваш вариант для сортировки.

Применение.

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);
}

Была ошибка в метода Partition. Исправленный вариант.

// Сортировка массива структур и указателей на объекты по полю.
#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 #:

Была ошибка в метода Partition. Исправленный вариант.

Для частного случая, когда для сортировки берется числовое поле (самая частая ситуация), написал вариант, который быстрее предыдущего на несколько порядков. Но при этом потребляет памяти в два раза больше. Актуально для больших массивов.

// Сортировка массива структур и указателей на объекты по ЧИСЛОВОМУ полю.
#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)


Применение (с замером времени выполнения).

#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, 1e5);
  
  for (int i = Size - 1; i >= 0; i--)
    Array[i].Num = Size - i;
    
//  _BV2(ArraySortStruct(Array, Num));
  _BV2(ArraySortStruct2(Array, Num));
}


Результат.

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.
Причина обращения: