Как выяснилось, изменение 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); } //+------------------------------------------------------------------+
- 2026.02.23
- www.mql5.com
Вот мы знаем, что 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); } //+------------------------------------------------------------------+
- 2026.02.22
- www.mql5.com
Отличная переработка. Мне нравится.
Маленько освобожусь, и выведу еще более интересную формулу из этого индикатора. Сейчас индикатор просто сглаживает цены, но можно немного переделать его, чтобы он сглаживал тренд.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
AIS Polygonal Number:
В этом индикаторе реализован алгоритм многоугольных чисел. Он позволяет получить разные варианты сглаживания временного ряда.
Автор: Aleksej Poljakov