Библиотеки: TicksShort - страница 2

 
amrali #:
Мне понравилось переписывать компрессор в качестве умственного упражнения, чтобы узнать что-то новое о различных доступных алгоритмах сжатия данных временных рядов.

Можно ускорить декомпрессию, если избавиться от 70% NormalizeDouble. Причине такой возможности в этом.

int Amount = 0;
int Count = 0;

int PriceToInteger( const double Price, const double point )
{
  return((int)(Price / point + 0.1));
}

void OnTick()
{
  static MqlTick PrevTick = {};
  
  MqlTick Tick;
  
  if (SymbolInfoTick(_Symbol, Tick))
  {
    if (PrevTick.bid && (PrevTick.bid != Tick.bid))
    {
      if (Tick.bid == PrevTick.bid + (PriceToInteger(Tick.bid, _Point) - PriceToInteger(PrevTick.bid, _Point)) * _Point)
        Count++;

      Amount++;      
    }
    
    PrevTick = Tick;
  }
}

double OnTester()
{
  return(Count * 100.0 / Amount); // > 70%.
}

На dBid выделять 5 битов + 1 бит, как флаг, нужно ли делать нормализацию или нет. Такой подход минимально ухудшит компрессию (максимальная дельта станет в два раза ниже), но, возможно, даст 10-20% дополнительной производительности.

 

Also I see in the profiler that the two calls to NormalizeDouble() are alone consuming about 15% of the total running time .


 
fxsaber #:

Такой подход минимально ухудшит компрессию (максимальная дельта станет в два раза ниже), но, возможно, даст 10-20% дополнительной производительности.

Реализовал.


Было.

Compressed 778443660 bytes into 77844582 bytes ==> 10.00%
Compress performance: 4000 MB/s
Compress performance: 69.9 Ticks (millions)/sec.
Compress performance criterion: 699.1
Decompress performance: 2430 MB/s
Decompress performance: 42.5 Ticks (millions)/sec.
Decompress performance criterion: 424.7
Correct = true


Стало.

Добавить макрос.

#define TICKS_SHORT_ALTERNATIVE


Compressed 778446960 bytes into 77844912 bytes ==> 10.00%
Compress performance: 3647 MB/s
Compress performance: 63.7 Ticks (millions)/sec.
Compress performance criterion: 637.3
Decompress performance: 3050 MB/s
Decompress performance: 53.3 Ticks (millions)/sec.
Decompress performance criterion: 533.1
Correct = true


Сравнение в мат. режиме.

EAToMath
EAToMath
  • 2025.07.09
  • www.mql5.com
Тестирование на истории в математическом режиме MT5-тестера.
 
fxsaber # :

Implemented.

MOD1: Deacrease calls to NormalizeDouble

Calls to NormalizeDouble can be effectively decreased to the minimum possible (even less than you updated version) if you switch to +ve powers of 10, I mean MathPow(10, digits) like 10, 100, 1000 instead of -ve powers, 0.1, 0.01, etc. The round-off error will be less when you divide N/1000 than N*0.001 

    static const double Power10[] = {1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7};
Файлы:
 

MOD2: Totally Abandoning NormalizeDouble

if you wish to keep your calculations using the -ve powers of 10 as is:

    static const double Power10[] = {1, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7};

I suggest using this NEW function (very fast) to replace NormalizeDouble for the prices that are not normalized correctly due to -ve powers of 10.

double DoubleAdvance(const double value, const long distance)
{
  if(!distance) return value;
  union _d {double value; long bits;} dbl;
  dbl.value = value;
  dbl.bits += distance;
  return dbl.value;
}
  bool Decompress( const TICK_SHORT &TickIn, MqlTick &TickOut )
  {
    ...
    ...
    
      #else // #ifndef TICKS_SHORT_ALTERNATIVE
        //this.PriceBid += ShiftBid;
        //TickOut.bid = (this.bid = NormBid ? this.bid + ShiftBid * this.Pow
        //                                  : ::NormalizeDouble(this.PriceBid * this.Pow, 7/* this.digits*/));
        //
        //this.PriceAsk += ShiftAsk;
        //TickOut.ask = (this.ask = NormAsk ? this.ask + ShiftAsk * this.Pow
        //                                  : ::NormalizeDouble(this.PriceAsk * this.Pow, 7/* this.digits*/));

       //##########################################################################################
        TickOut.bid = DoubleAdvance(((this.PriceBid += ShiftBid) * this.Pow), ((bool)NormBid - 1));
        TickOut.ask = DoubleAdvance(((this.PriceAsk += ShiftAsk) * this.Pow), ((bool)NormAsk - 1));
       //##########################################################################################

Both Mod1 and Mod2 are faster than you last update.

 
amrali #:

MOD1: Уменьшить количество вызовов NormalizeDouble

Количество вызовов NormalizeDouble можно эффективно сократить до минимума (даже меньше, чем в вашей обновленной версии), если переключиться на положительные степени числа 10 , я имею в виду MathPow(10, цифры), например, 10, 100, 1000 вместо отрицательных степеней, 0,1, 0,01 и т. д. Ошибка округления будет меньше при делении N/1000, чем N*0,001.

Я изменил этот код на такой

      static const double Power10[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8};
      
      if (Tick.bid == PriceToInteger(Tick.bid, _Point) / Power10[_Digits])
        Count++;

//      if (Tick.bid == PrevTick.bid + (PriceToInteger(Tick.bid, _Point) - PriceToInteger(PrevTick.bid, _Point)) * _Point)
//        Count++;

И получил в OnTester результат > 96%! Спасибо, что показали эту особенность.

 
amrali #:

MOD2: Полный отказ от NormalizeDouble

если вы хотите сохранить свои вычисления с использованием отрицательных степеней числа 10 как есть:

Я предлагаю использовать эту НОВУЮ функцию (очень быструю) для замены NormalizeDouble для цен, которые не нормализованы правильно из-за отрицательных степеней числа 10.

К сожалению, Ваша функция не всегда правильно срабатывает.

int PriceToInteger( const double Price, const double point )
{
  return((int)(Price / point + 0.1));
}

double DoubleAdvance(const double value, const long distance)
{
  if(!distance) return value;
  union _d {double value; long bits;} dbl;
  dbl.value = value;
  dbl.bits += distance;
  return dbl.value;
}

bool Check( const double Price, const double point )
{  
  const double Price2 = PriceToInteger(Price, point) * point;
  
  return(Price == DoubleAdvance(Price2, (Price == Price2) - 1));
}

void OnStart()
{
  const int digits = 3;
  
  for (int i = 0; i < 1e7; i++)
  {
    static const double Points[] = {1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7};
    
    if (!Check(NormalizeDouble(i * Points[digits], digits), Points[digits]))
    {
      Print(DoubleToString(i * Points[digits], digits)); // 1.281
      
      break;
    }
  }
}
 
fxsaber #:

Я изменил этот код на такой

И получил в OnTester результат > 96%! Спасибо, что показали эту особенность.

Сделал такое сравнение.

int PriceToInteger( const double Price, const double point )
{
  return((int)(Price / point + 0.1));
}

bool Check1( const double Price, const double point )
{   
  return(Price == PriceToInteger(Price, point) * point);
}

bool Check2( const double Price, const double point )
{   
  return(Price == PriceToInteger(Price, point) / (double)PriceToInteger(1, point));
}

void OnStart()
{
  static const double Points[] = {1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7};
  const int digits = 5;
  const double point = Points[digits];

  const int Amount = 1e7;
  int Count1 = 0;
  int Count2 = 0;
  
  for (int i = 0; i < Amount; i++)
  {    
    const double Price = NormalizeDouble(i * point, digits);
    
    if (Check1(Price, point))
      Count1++;

    if (Check2(Price, point))
      Count2++;
  }
  
  Print(Count1 * 100.0 / Amount); // 47.64052
  Print(Count2 * 100.0 / Amount); // 98.91764
}

Деление на положительную степень дает гораздо больше нормализованных значений, чем умножение на отрицательную степень. Еще раз спасибо!

 
amrali #:

Mod1 и Mod2 работают быстрее, чем в последнем обновлении.

Я написал свой вариант Mod1 и попробовал Ваш вариант.

К сожалению, каждый из них оказался по какой-то причине медленнее на моей машине, чем выложенный в кодобазу.

Видимо, операция деления значительно дороже операции умножения.

 
fxsaber #:

К сожалению, Ваша функция не всегда правильно срабатывает.

Для Digits <= 5 проблема только у этих чисел.

1.68, 1.93, 1.281, 1.539, 1.543, 1.547, 1.551, 1.555, 1.559, 1.668, 1.672, 1.676, 1.680,
            1.684, 1.793, 1.797, 1.801, 1.805, 1.809, 1.922, 1.926, 1.930, 1.934, 2.562


Совсем не возникает проблемы, если делать такой вызов.

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

Библиотеки: TicksShort

fxsaber, 2025.07.24 05:26

bool Check( const double Price, const double point )
{  
  const double Price2 = PriceToInteger(Price, point) * point;
  
  return((Price == DoubleAdvance(Price2, (Price == Price2) - 1)) || (Price == DoubleAdvance(Price2, 1 - (Price == Price2))));
}

Но в как определиться точно с одним из этих двух условий - не соображу.