Индикаторы: AIS Polygonal Number

 

AIS Polygonal Number:

В этом индикаторе реализован алгоритм многоугольных чисел. Он позволяет получить разные варианты сглаживания временного ряда.

AIS Polygonal Number

Автор: Aleksej Poljakov

 
Как выяснилось, изменение Polygonal никак не влияет на конфигурацию индикатора. 
 
Andy An #:
Как выяснилось, изменение Polygonal никак не влияет на конфигурацию индикатора. 
попробуйте менять еще и период индикатора
 
Andy An #:
Как выяснилось, изменение Polygonal никак не влияет на конфигурацию индикатора. 

Вот мы знаем, что SMA запаздывает... А насколько это запаздывание происходит? Мы можем спокойно рассчитать запаздывание любого линейного индикатора.

Для примера берем SMA с периодом 3. Вместо цен подставляем номера отсчетов индикатора. Получается 1/3*1 + 1/3*2 +1/3*3 = 2. То есть, у нас получается что SMA запаздывает на половину своего периода.

Теперь переходим к EMA, тут у нас запаздывание минимально. Но беда в том, что эффективный период EMA меньше 20. А период равен 3. Откуда нам это известно. Берем коэффициенты такого индикатора (1/2, 1/4, 1/8 и т.д.). Как только значение коэффициента становится меньше 1/100000 дальнейшее наращивание периода становится бессмысленным - мы выходим за пределы точности.

Для этого индикатора, можно ввести проверку - как только переменная denom становится больше 100k дальнейшее увеличение периода индикатора не имеет смысла. Но с этим можно и побороться. Добавим еще один параметр начального сдвига s (не меньше 1). Меняем 37 строку на coef[period-1]=s. Тут у нас появляется возможностей побольше. Главное, чтобы выполнялось неравенство 100000*s>=denom.

 
Творческая переработка Вашего индикатора + попытка побороть найденные баги
//+------------------------------------------------------------------+
//|                                  Ramanujan_Partition_1729.mq5    |
//|                        Concept by Algorithmic Trading     |
//+------------------------------------------------------------------+
#property copyright "Creative Trading Math"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "Стратегия Полигонального Разбиения 1729 (Ramanujan)"
#property description "Без перерисовки. Индексация массивов как таймсерии."
#property indicator_chart_window
#property indicator_buffers 7
#property indicator_plots   7

//--- Плот 1: Macro-Trend (1729)
#property indicator_label1  "Macro 172, 9"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrPurple
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- Плот 2: Медленная (11)
#property indicator_label2  "Slow 11, 6"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrCrimson
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- Плот 3: Средняя (7)
#property indicator_label3  "Medium 7, 5"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrOrange
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//--- Плот 4: Быстрая (5)
#property indicator_label4  "Fast 5, 4"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrLimeGreen
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1

//--- Плот 5: Вход Long
#property indicator_label5  "Buy Signal"
#property indicator_type5   DRAW_ARROW
#property indicator_color5  clrDodgerBlue
#property indicator_width5  2

//--- Плот 6: Вход Short
#property indicator_label6  "Sell Signal"
#property indicator_type6   DRAW_ARROW
#property indicator_color6  clrRed
#property indicator_width6  2

//--- Плот 7: Выход (Нарушение симметрии)
#property indicator_label7  "Exit Signal"
#property indicator_type7   DRAW_ARROW
#property indicator_color7  clrGold
#property indicator_width7  1

//--- Входные параметры (Основаны на конгруэнтностях Рамануджана)
input int InpMacroPeriod = 172; // Macro Period (from 1729)
input int InpMacroPoly   = 9;   // Macro Polygonal Degree (9^3 + 10^3)

input int InpSlowPeriod  = 11;  // Slow Period p(11k+6)
input int InpSlowPoly    = 6;   // Slow Polygonal Degree

input int InpMedPeriod   = 7;   // Medium Period p(7k+5)
input int InpMedPoly     = 5;   // Medium Polygonal Degree

input int InpFastPeriod  = 5;   // Fast Period p(5k+4)
input int InpFastPoly    = 4;   // Fast Polygonal Degree

//--- Буферы индикатора
double MacroBuffer[];
double SlowBuffer[];
double MediumBuffer[];
double FastBuffer[];
double BuyBuffer[];
double SellBuffer[];
double ExitBuffer[];

//--- Массивы весовых коэффициентов
double WeightsMacro[];
double WeightsSlow[];
double WeightsMedium[];
double WeightsFast[];

//+------------------------------------------------------------------+
//| Математическое ядро: Расчет фигурных чисел                       |
//| (Генерация весов для "полигонального" сглаживания)               |
//+------------------------------------------------------------------+
double GetFigurateWeight(int n, int poly)
  {
   if(poly == 0) return 1.0;
   if(poly == 1) return (double)n;
   
   double weight = 1.0;
   // Комбинаторная формула обобщенного многоугольного числа: C(n+poly-1, poly)
   for(int i = 1; i <= poly; i++)
     {
      weight *= (double)(n + i - 1) / (double)i;
     }
   return weight;
  }

//+------------------------------------------------------------------+
//| Инициализация массивов весов (выполняется 1 раз для оптимизации) |
//+------------------------------------------------------------------+
void InitWeights(double &wArray[], int period, int poly)
  {
   ArrayResize(wArray, period);
   double sum = 0;
   for(int i = 0; i < period; i++)
     {
      // Индексация таймсерии: i=0 это самый новый бар окна (наивысший вес n=period)
      // i=period-1 это самый старый бар окна (наименьший вес n=1)
      int n = period - i; 
      double w = GetFigurateWeight(n, poly);
      wArray[i] = w;
      sum += w;
     }
   // Нормализуем веса, чтобы сумма равнялась 1.0
   if(sum > 0)
     {
      for(int i = 0; i < period; i++) wArray[i] /= sum;
     }
  }

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, MacroBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, SlowBuffer, INDICATOR_DATA);
   SetIndexBuffer(2, MediumBuffer, INDICATOR_DATA);
   SetIndexBuffer(3, FastBuffer, INDICATOR_DATA);
   SetIndexBuffer(4, BuyBuffer, INDICATOR_DATA);
   SetIndexBuffer(5, SellBuffer, INDICATOR_DATA);
   SetIndexBuffer(6, ExitBuffer, INDICATOR_DATA);

   // Настройка символов стрелок
   PlotIndexSetInteger(4, PLOT_ARROW, 233); // Стрелка вверх
   PlotIndexSetInteger(5, PLOT_ARROW, 234); // Стрелка вниз
   PlotIndexSetInteger(6, PLOT_ARROW, 251); // Крестик (нарушение симметрии)
   
   // Индексируем как таймсерии
   ArraySetAsSeries(MacroBuffer, true);
   ArraySetAsSeries(SlowBuffer, true);
   ArraySetAsSeries(MediumBuffer, true);
   ArraySetAsSeries(FastBuffer, true);
   ArraySetAsSeries(BuyBuffer, true);
   ArraySetAsSeries(SellBuffer, true);
   ArraySetAsSeries(ExitBuffer, true);

   // Предрасчет весов
   InitWeights(WeightsMacro, InpMacroPeriod, InpMacroPoly);
   InitWeights(WeightsSlow, InpSlowPeriod, InpSlowPoly);
   InitWeights(WeightsMedium, InpMedPeriod, InpMedPoly);
   InitWeights(WeightsFast, InpFastPeriod, InpFastPoly);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Вспомогательная функция: Применение весов к ценам                |
//+------------------------------------------------------------------+
double CalcPolyMA(int index, int period, const double &price[], const double &weights[])
  {
   double sum = 0;
   for(int i = 0; i < period; i++)
     {
      sum += price[index + i] * weights[i];
     }
   return sum;
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   if(rates_total < InpMacroPeriod + 5) return 0;

   // Устанавливаем доступ к ценам как к таймсериям
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);

   // Определение границ расчета
   int limit = rates_total - prev_calculated;
   if(prev_calculated > 0) limit++; // Пересчитываем текущий бар и предыдущий закрытый
   if(prev_calculated == 0) limit = rates_total - InpMacroPeriod - 2;

   for(int i = limit; i >= 0; i--)
     {
      // Расчет линий индикатора
      MacroBuffer[i]  = CalcPolyMA(i, InpMacroPeriod, close, WeightsMacro);
      SlowBuffer[i]   = CalcPolyMA(i, InpSlowPeriod, close, WeightsSlow);
      MediumBuffer[i] = CalcPolyMA(i, InpMedPeriod, close, WeightsMedium);
      FastBuffer[i]   = CalcPolyMA(i, InpFastPeriod, close, WeightsFast);

      // Инициализация пустых значений для стрелок
      BuyBuffer[i]  = EMPTY_VALUE;
      SellBuffer[i] = EMPTY_VALUE;
      ExitBuffer[i] = EMPTY_VALUE;

      // --- ЛОГИКА СИГНАЛОВ (Только на закрытых барах i > 0 для защиты от перерисовки) ---
      if(i > 0 && i < rates_total - 2)
        {
         // Определение пересечений Быстрой (5) и Средней (7) линий
         bool isCrossUp = (FastBuffer[i] > MediumBuffer[i]) && (FastBuffer[i+1] <= MediumBuffer[i+1]);
         bool isCrossDn = (FastBuffer[i] < MediumBuffer[i]) && (FastBuffer[i+1] >= MediumBuffer[i+1]);

         // Макро-условия для фильтрации входов (Полная симметрия Веера и тренд 1729)
         bool alignUp = (MediumBuffer[i] > SlowBuffer[i]) && (close[i] > MacroBuffer[i]);
         bool alignDn = (MediumBuffer[i] < SlowBuffer[i]) && (close[i] < MacroBuffer[i]);

         // Динамический отступ для красивой отрисовки стрелок (не зависит от таймфрейма)
         double atrProxy = MathMax(high[i] - low[i], 10 * _Point); 

         // 1. Сигнал на покупку
         if(isCrossUp && alignUp)
           {
            BuyBuffer[i] = low[i] - atrProxy * 0.5;
           }
         // 2. Сигнал на продажу
         else if(isCrossDn && alignDn)
           {
            SellBuffer[i] = high[i] + atrProxy * 0.5;
           }
         // 3. Сигнал раннего выхода (нарушение симметрии при удержании Long)
         else if(isCrossDn && close[i] > MacroBuffer[i])
           {
            ExitBuffer[i] = high[i] + atrProxy * 0.2;
           }
         // 4. Сигнал раннего выхода (нарушение симметрии при удержании Short)
         else if(isCrossUp && close[i] < MacroBuffer[i])
           {
            ExitBuffer[i] = low[i] - atrProxy * 0.2;
           }
        }
     }

   return(rates_total);
  }
//+------------------------------------------------------------------+

Discover new MetaTrader 5 opportunities with MQL5 community and services
Discover new MetaTrader 5 opportunities with MQL5 community and services
  • 2026.02.23
  • www.mql5.com
MQL5: language of trade strategies built-in the MetaTrader 5 Trading Platform, allows writing your own trading robots, technical indicators, scripts and libraries of functions
 
Aleksej Poljakov #:

Вот мы знаем, что SMA запаздывает... А насколько это запаздывание происходит? Мы можем спокойно рассчитать запаздывание любого линейного индикатора.

Для примера берем SMA с периодом 3. Вместо цен подставляем номера отсчетов индикатора. Получается 1/3*1 + 1/3*2 +1/3*3 = 2. То есть, у нас получается что SMA запаздывает на половину своего периода.

Теперь переходим к EMA, тут у нас запаздывание минимально. Но беда в том, что эффективный период EMA меньше 20. А период равен 3. Откуда нам это известно. Берем коэффициенты такого индикатора (1/2, 1/4, 1/8 и т.д.). Как только значение коэффициента становится меньше 1/100000 дальнейшее наращивание периода становится бессмысленным - мы выходим за пределы точности.

Для этого индикатора, можно ввести проверку - как только переменная denom становится больше 100k дальнейшее увеличение периода индикатора не имеет смысла. Но с этим можно и побороться. Добавим еще один параметр начального сдвига s (не меньше 1). Меняем 37 строку на coef[period-1]=s. Тут у нас появляется возможностей побольше. Главное, чтобы выполнялось неравенство 100000*s>=denom.

//+------------------------------------------------------------------+
//|                                     AIS_Polygonal_Number_v2.mq5  |
//|                        Оригинальная идея: Aleksej Poljakov       |
//|                        Добавлен авторский фикс предела точности  |
//+------------------------------------------------------------------+
#property copyright "Aleksej Poljakov / Fixed & Optimized"
#property link      "https://www.mql5.com/ru/code/69753"
#property version   "2.00"
#property description "Полигональная Скользящая Средняя с параметром сдвига (s)"

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

#property indicator_label1  "Polygonal MA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- Входные параметры
input int    InpPeriod    = 20;    // Период индикатора (Period)
input int    InpPolygonal = 5;     // Порядок полинома (Polygonal)
input double InpShiftS    = 1.0;   // Начальный сдвиг (s) - Авторский фикс

//--- Буферы и глобальные массивы
double ExtLineBuffer[];
double ExtWeights[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Привязка буфера для отрисовки линии на графике
   SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA);
   ArraySetAsSeries(ExtLineBuffer, true);
   
   if(InpPeriod <= 1)
     {
      Print("Ошибка: Период должен быть больше 1");
      return(INIT_FAILED);
     }
     
   // Запускаем предрасчет весов 1 раз при старте
   CalculateWeights();
   
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Генерация весов (Дискретное интегрирование ) |
//+------------------------------------------------------------------+
void CalculateWeights()
  {
   int period = InpPeriod;
   int poly = InpPolygonal;
   double s = InpShiftS; // Тот самый параметр s
   
   ArrayResize(ExtWeights, period);
   
   // 1. Базовая инициализация (Poly = 0 соответствует SMA)
   for(int i = 0; i < period; i++)
     {
      ExtWeights[i] = 1.0;
     }
     
   // 2. ИДЕЯ АВТОРА: "Меняем 37 строку на coef[period-1]=s"
   // Мы задаем искусственный вес для самой старой свечи в периоде.
   ExtWeights[period - 1] = s;
   
   // 3. Интегрирование (Генерация полигональных чисел)
   // Прибавляем к каждому элементу значение предыдущего (более старого) бара
   for(int p = 1; p <= poly; p++)
     {
      for(int i = period - 2; i >= 0; i--)
        {
         ExtWeights[i] = ExtWeights[i] + ExtWeights[i + 1];
        }
     }
     
   // 4. Расчет знаменателя (denom)
   double denom = 0.0;
   for(int i = 0; i < period; i++)
     {
      denom += ExtWeights[i];
     }
     
   // Логируем результат для контроля предела точности 100k
   PrintFormat("[AIS Polygonal] Period: %d, Poly: %d, s: %.1f | Denom = %G", 
               period, poly, s, denom);
     
   // 5. Нормализация (приводим сумму всех весов к 1.0)
   if(denom > 0)
     {
      for(int i = 0; i < period; i++)
        {
         ExtWeights[i] /= denom;
        }
     }
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   // Проверка на достаточное количество истории
   if(rates_total < InpPeriod) return(0);
   
   // Индексация массива цен как таймсерии (0 - текущий бар)
   ArraySetAsSeries(close, true);
   
   // Определение границ цикла для оптимизации вычислений
   int limit = rates_total - prev_calculated;
   if(prev_calculated > 0) limit++; // Пересчет нулевого (текущего) и первого бара
   if(prev_calculated == 0) limit = rates_total - InpPeriod - 1; // Первый запуск
   
   // Основной цикл расчета скользящей средней
   for(int i = limit; i >= 0; i--)
     {
      double sum = 0.0;
      
      // Накладываем предрассчитанные полигональные веса на цены
      for(int j = 0; j < InpPeriod; j++)
        {
         // i - сдвиг по графику в прошлое, j - окно периода
         sum += close[i + j] * ExtWeights[j]; 
        }
        
      ExtLineBuffer[i] = sum;
     }
     
   return(rates_total);
  }
//+------------------------------------------------------------------+


AIS Polygonal Number
AIS Polygonal Number
  • 2026.02.22
  • www.mql5.com
В этом индикаторе реализован алгоритм многоугольных чисел. Он позволяет получить разные варианты сглаживания временного ряда.
 
Andy An #:
Творческая переработка Вашего индикатора + попытка побороть найденные баги

Отличная переработка. Мне нравится.

Маленько освобожусь, и выведу еще более интересную формулу из этого индикатора. Сейчас индикатор просто сглаживает цены, но можно немного переделать его, чтобы он сглаживал тренд.