English Deutsch 日本語
preview
Разработка инструментария для анализа Price Action (Часть 37): Индикатор смещения настроений

Разработка инструментария для анализа Price Action (Часть 37): Индикатор смещения настроений

MetaTrader 5Примеры |
98 1
Christian Benjamin
Christian Benjamin

Введение

Перед нами Часть 37 серии Разработка инструментария для анализа Price Action. Моя главная цель всегда заключалась в разработке практических инструментов, которые помогают трейдерам интерпретировать поведение рынка и, где это уместно, автоматизировать отдельные аспекты такого анализа. На сегодняшний день мы уже рассмотрели автономные утилиты на MQL5, внешние интеграции и гибридные решения. Сегодня мы расширим этот инструментарий еще одним полезным и практичным дополнением.

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

Индикатор смещения настроений (Sentiment Tilt Meter, STM) решает эту проблему, агрегируя метрики направленности свечей по настраиваемым таймфреймам в единый сглаженный показатель в диапазоне от -100 до +100. Этот показатель отображается в компактной панели, в которой элементы не перекрывают друг друга. Постоянные аннотации, такие как стрелки и текстовые метки, точно привязаны ко времени закрытия бара, что позволяет независимо проверять каждый алерт по временной метке и уровню цены. 

STM задает явные параметризуемые правила – такие как пороги разворота, величина и длительность смены знака, критерии моментума и веса отдельных таймфреймов, – позволяя пользователю настраивать чувствительность и балансировать между подавлением шума и отзывчивостью.

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


Обзор стратегии

Рынки полны шума, и именно шум – главный враг последовательного принятия решений. Индикаторы наслаиваются, метки перекрываются, и очень быстро становится невозможно понять, что именно сдвинуло цену и почему. Индикатор смещения настроений (STM) создан, чтобы рассеять этот туман: дать единую проверяемую оценку краткосрочного перекоса рынка, показать основания на графике и позволить принимать дисциплинированные решения без догадок.

Под настроением здесь понимается краткосрочный перекос рынка – суммарное давление в сторону покупок или продаж в последних свечах. STM измеряет этот перекос непосредственно по Price Action, преобразуя каждую закрытую свечу в небольшую, но интерпретируемую оценку. Микропризнаки:

  • Соотношение тела свечи (Candle Body Ratio, CBR):

  • Процентное положение цены закрытия (Close Position Percent, CPP):


  • Расстояние с поправкой на волатильность (Volatility-Adjusted Distance, VAD):

где ATR – это недавнее базовое значение истинного диапазона, а 

Clamp ⁡ ( x , a , b ) =max ⁡ ( a , min ⁡ ( b , x ) ) \operatorname{clamp}(x,a,b)=\max(a,\min(b,x)) clamp(x,a,b)=max(a,min(b,x)).

Мини-оценка для каждой свечи представляет собой взвешенную сумму. Например, значение:

ограничивается диапазоном [ − 1 , + 1 ] [-1, +1] [−1,+1], а затем умножается на простые коэффициенты уверенности и спокойного рынка. Оценки для каждого таймфрейма усредняются по выборке, а затем объединяются между таймфреймами с учетом пользовательских весов:

Наконец, оценки экспоненциально сглаживаются:

Ниже показаны графики цены и оценки:

График оценки показывает сглаженное значение настроения во времени: на нем каждое необработанное пересечение нулевой линии (смена знака) отмечено маленьким значком ×, а принятые сигналы смены направления – после применения фильтров величины, устойчивости и моментума – отмечены крупными точками. Эта визуализация помогает легко выявлять небольшие шумовые колебания, которые могли бы вызывать ложные сигналы, а также реальные смены направления, прошедшие через фильтры.

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

Чтобы считать результат приемлемым, советник должен генерировать сигналы, которые можно сразу отследить по времени и цене, демонстрировать надежные результаты в тестах на исторических данных и коротких тестах на реальных данных, а также минимизировать количество очевидных ложных срабатываний до уровня, который вы считаете пригодным для торговли. Если все это соблюдено, STM выполняет свою задачу: он дает четкую, настраиваемую и проверяемую оценку краткосрочного рыночного перекоса, на которую можно опираться в рамках дисциплинированного процесса.


Реализация на MQL5

Прежде чем приступить к программированию, выделите две минуты на правильную настройку среды – это заметно сэкономит время в дальнейшем. Убедитесь, что MetaTrader 5 и MetaEditor установлены. MetaEditor можно запустить как отдельное приложение или открыть через MetaTrader 5; оба варианта подходят.

Чтобы подготовить советник:

  1. откройте MetaEditor;
  2. перейдите в Файл → Новый файл → Советник (шаблон);
  3. вставьте исходный код советника во вновь созданный файл;
  4. сохраните файл под понятным и содержательным именем;
  5. скомпилируйте код, нажав F7.

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

Вернувшись в MetaTrader 5:

  • запустите советник в тестере стратегий или поместите его прямо на график;
  • следите за панелью, журналами и сигналами;
  • проверьте вкладки "Эксперты" и "Журнал" на наличие сообщений времени выполнения и отладочной информации.

Такая настройка гарантирует, что среда будет корректно подготовлена для эффективной разработки и тестирования. Далее мы шаг за шагом создадим советник Sentiment Tilt Meter (STM) и разберем каждый этап процесса разработки.

Блок заголовка и метаданных задает идентификационные данные советника, сведения об авторских правах, ссылку на профиль автора и режим компиляции (#property strict). Эти строки носят чисто декларативный характер, но важны: они встраивают сведения о версии и авторстве в скомпилированную программу и включают более строгие проверки на этапе компиляции. Сразу после этого советник подключает стандартную торговую библиотеку Trade.mqh, чтобы код получил доступ к классу CTrade и другим вспомогательным средствам для торговли; хотя в этом файле CTrade g_trade используется редко (или вообще не используется в показанном коде), подключение библиотеки заранее готовит код к отправке ордеров без последующего рефакторинга.

//+------------------------------------------------------------------+
//|                                                            STM EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/ru/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ru/users/lynnchris"
#property version   "1.0"
#property strict

#include <Trade\Trade.mqh>

Блок входных параметров определяет все пользовательские настройки. Входные параметры сгруппированы по назначению: выбор таймфрейма, настройка сигнального алгоритма, визуальное оформление и стиль панели, звуковые и push-уведомления, а также поведение стрелок и маркеров. Основной таймфрейм и дополнительные таймфреймы позволяют советнику объединять сигналы с нескольких таймфреймов, а веса (InpWeightPrimary, InpWeightExtra1, InpWeightExtra2) задают пропорции этого объединения. Параметр сглаживания (InpSmoothAlpha) управляет экспоненциальным сглаживанием необработанных объединенных оценок, а переменные настройки порогов (например, InpFlipThreshold, InpPositiveZone, InpNegativeZone) определяют, когда советник считает смену направления значимой и когда он переходит в положительную или отрицательную зону.

Визуальные параметры управляют панелью на графике: ее размером, углом привязки, шрифтами, цветами, количеством столбцов гистограммы и внутренними отступами. Параметры стрелок и маркеров позволяют включать сигналы на графике и настраивать их, а несколько защитных проверок (минимальное число баров, величина смены знака, требования к моментуму) уменьшают шум и число ложных разворотов. Все входные параметры явно типизированы, чтобы они отображались в окне настроек советника и могли изменяться без редактирования кода.

// --- User inputs (layout / visuals tuned) ----------------------------
input ENUM_TIMEFRAMES InpTFPrimary = PERIOD_M5;
input bool InpUseMultiTF = true;
input ENUM_TIMEFRAMES InpExtraTF1 = PERIOD_M15;
input ENUM_TIMEFRAMES InpExtraTF2 = PERIOD_H1;
input int InpLookback = 20;
input int InpSSWindow = 5;
input int InpATRPeriod = 14;
input double InpATRMultiplier = 1.0;
input double InpVolQuietThreshold = 0.4;
input int InpHistogramMaxBars = 24;
input int InpPanelPadding = 10;
input int InpPanelCorner = CORNER_LEFT_UPPER;
input int InpPanelX = 5;
input int InpPanelY = 25;
input string InpFont = "Arial";
input int InpTitleFontSize = 12;
input int InpScoreFontSize = 18;
input int InpSmallFontSize = 9;
input color InpPanelColor = clrBlack;
input int InpPanelAlpha = 200;
input bool InpEnableSound = true;
input string InpSoundFile = "alert.wav";
input bool InpEnablePush = false;
input double InpWeightPrimary = 0.6;
input double InpWeightExtra1 = 0.2;
input double InpWeightExtra2 = 0.2;
input double InpSmoothAlpha = 0.28;
input double InpFlipThreshold = 60.0;
input double InpPositiveZone = 30.0;
input double InpNegativeZone = -30.0;
input int InpMinBarsForSignal = 3;

// arrows / marker options
input bool InpShowArrows = true;
input int  InpArrowFontSize = 16;
input int  InpArrowOffsetPoints = 8;
input int  InpMaxSignalsToKeep = 50;
input bool InpSignalOnSignFlip = true;
input double InpMinSignFlipAbs = 6.0;
input int    InpSignFlipHoldBars = 1;
input bool   InpRequireMomentum = true;
input double InpMinMomentum = 0.5;
//+------------------------------------------------------------------+
Далее идут константы и глобальные переменные. Советник создает уникальный g_prefix для каждого графика (символ + идентификатор графика), чтобы задать пространство имен для объектов на графике и избежать конфликтов с другими советниками или объектами, созданными вручную. Предусмотрены хэндлы индикатора ATR для каждого таймфрейма, буфер гистограммы (g_hist_buffer) и индексы для управления кольцевым хранением. Глобальные переменные также включают в себя состояние сглаженного значения (g_smoothed_score, g_prev_smoothed), последнее значение, по которому отслеживаются резкие смены, и текущее значение g_zone_state (1, -1 или 0). CTradeg_trade объявлен на случай, если советник позже расширят для размещения ордеров. Хранение g_last_signal_text и g_last_signal_time позволяет интерфейсу показывать на панели последний сигнал.
// globals
long g_chart_id = 0;
string g_prefix = "";
string ui_bg_name = "";
string ui_shadow_name = "";
string ui_title_name = "";
string ui_score_name = "";
string ui_zone_name = "";
string ui_hist_base = "";
string ui_recent_name = "";
string ui_advice_name = "";
string ui_signal_name = "";
int g_atr_handle_primary = INVALID_HANDLE;
int g_atr_handle_extra1 = INVALID_HANDLE;
int g_atr_handle_extra2 = INVALID_HANDLE;
double g_hist_buffer[];
int g_hist_idx = 0;
int g_hist_count = 0;
double g_smoothed_score = 0.0;
double g_prev_smoothed = 0.0;
double g_last_alert_score = 0.0;
int g_zone_state = 0;
CTrade g_trade;
string g_last_signal_text = "None";
datetime g_last_signal_time = 0;
const string BASE_HIST = "STM_HBAR_";
//+------------------------------------------------------------------+
Ниже идут несколько небольших вспомогательных процедур, которые делают интерфейс и код отображения более надежными. ARGB_uint формирует беззнаковое целое значение ARGB из альфа-канала и цвета MQL, чтобы можно было аккуратно задавать полупрозрачные фоны прямоугольников. EstimateTextWidth – это простая оценка пиксельной ширины строки на основе размера шрифта и подобранного коэффициента ширины символов; она используется, чтобы избежать наложения меток при размещении значка зоны и строки Recent. Такие приближения вполне разумны для панели на графике, где точное позиционирование не критично, а наложения нежелательны.
// ARGB helper - returns a color as unsigned int
uint ARGB_uint(int a, color c)
  {
   uint u = ((uint)c) & 0x00FFFFFF;
   return ((((uint)a) & 0xFF) << 24) | u;
  }

// approximate text width estimator (pixels)
int EstimateTextWidth(string txt,int fontSize)
  {
   if(StringLen(txt) <= 0)
      return 6;
   double factor = 0.58;
   int w = (int)MathRound(StringLen(txt) * fontSize * factor);
   return MathMax(8, w);
  }
//+------------------------------------------------------------------+
DrawText и DrawCell – это безопасные обертки для создания и настройки текстовых меток на графике и меток-прямоугольников. Они централизуют шаблон создания объектов и задания их свойств (угол привязки, смещение по x/y, шрифт, флаг выделения, а также порядок наложения по оси Z), чтобы все UI-метки подчинялись единым правилам. Использование этих помощников позволяет избежать дублирования шаблонного кода и обеспечивает единообразное поведение при перестроении или обновлении панели.
// safe DrawText - sets font explicitly and uses exact x/y
void DrawText(string id,string txt,int x,int y,color clr,int sz)
  {
   if(ObjectFind(g_chart_id,id) < 0)
      ObjectCreate(g_chart_id,id,OBJ_LABEL,0,0,0);
   ObjectSetInteger(g_chart_id,id,OBJPROP_CORNER,InpPanelCorner);
   ObjectSetInteger(g_chart_id,id,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(g_chart_id,id,OBJPROP_YDISTANCE,y);
   ObjectSetInteger(g_chart_id,id,OBJPROP_COLOR,(int)clr);
   ObjectSetInteger(g_chart_id,id,OBJPROP_FONTSIZE,sz);
   ObjectSetString(g_chart_id,id,OBJPROP_FONT,InpFont);
   ObjectSetString(g_chart_id,id,OBJPROP_TEXT,txt);
   ObjectSetInteger(g_chart_id,id,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(g_chart_id,id,OBJPROP_ZORDER,0);
  }

// small helper to draw rectangle label cell
void DrawCell(string id,int x,int y,int w,int h,color bg,color br)
  {
   if(ObjectFind(g_chart_id,id) < 0)
      ObjectCreate(g_chart_id,id,OBJ_RECTANGLE_LABEL,0,0,0);
   ObjectSetInteger(g_chart_id,id,OBJPROP_CORNER,InpPanelCorner);
   ObjectSetInteger(g_chart_id,id,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(g_chart_id,id,OBJPROP_YDISTANCE,y);
   ObjectSetInteger(g_chart_id,id,OBJPROP_XSIZE,w);
   ObjectSetInteger(g_chart_id,id,OBJPROP_YSIZE,h);
   ObjectSetInteger(g_chart_id,id,OBJPROP_BGCOLOR,(int)bg);
   ObjectSetInteger(g_chart_id,id,OBJPROP_BORDER_COLOR,(int)br);
   ObjectSetInteger(g_chart_id,id,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(g_chart_id,id,OBJPROP_ZORDER,1);
  }
CreateUIObjects и DeleteUIObjects управляют жизненным циклом всех элементов интерфейса. CreateUIObjects заранее создает объекты для фона, тени, заголовка, оценки, зоны, строки Recent и меток гистограммы, а также задает шрифты по умолчанию и порядок наложения. DeleteUIObjects удаляет их при деинициализации – это полезная практика, благодаря которой после удаления советника график остается аккуратным. Предварительное создание фиксированного числа объектов меток гистограммы (на основе InpHistogramMaxBars) позволяет избежать частого создания объектов во время выполнения, что повышает производительность и стабильность.
void CreateUIObjects()
  {
   int default_w = 200;
   int default_h = 80;
   int scol = (int)ARGB_uint(InpShadowAlpha, InpShadowColor);
   DrawCell(ui_shadow_name, InpPanelX + 3, InpPanelY + 3, default_w, default_h, (color)scol, InpGridClr);
   DrawCell(ui_bg_name, InpPanelX, InpPanelY, default_w, default_h, InpPanelBG, InpGridClr);

   DrawText(ui_title_name, "", InpPanelX + InpPanelPadding, InpPanelY + InpPanelPadding, InpTxtClr, InpTitleFontSize);
   DrawText(ui_score_name, "", InpPanelX + InpPanelPadding, InpPanelY + InpPanelPadding + InpTitleFontSize + 4, InpTxtClr, InpScoreFontSize);
   DrawText(ui_zone_name, "", InpPanelX + default_w - InpPanelPadding - 80, InpPanelY + InpPanelPadding + 4, InpTxtClr, InpSmallFontSize);
   DrawText(ui_advice_name, "", InpPanelX + InpPanelPadding, InpPanelY + InpPanelPadding + InpTitleFontSize + InpScoreFontSize + 8, InpTxtClr, InpSmallFontSize);
   DrawText(ui_recent_name, "", InpPanelX + InpPanelPadding, InpPanelY + default_h - InpPanelPadding - 18, InpTxtClr, InpSmallFontSize);
   DrawText(ui_signal_name, "", InpPanelX + InpPanelPadding + 120, InpPanelY + InpPanelPadding + InpTitleFontSize + InpScoreFontSize + 8, InpTxtClr, InpSmallFontSize);

   for(int i = 0; i < InpHistogramMaxBars; i++)
     {
      string name = ui_hist_base + IntegerToString(i);
      if(ObjectFind(g_chart_id, name) < 0)
        {
         ObjectCreate(g_chart_id, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(g_chart_id, name, OBJPROP_CORNER, InpPanelCorner);
         ObjectSetString(g_chart_id, name, OBJPROP_FONT, InpFont);
         ObjectSetInteger(g_chart_id, name, OBJPROP_FONTSIZE, InpSmallFontSize);
         ObjectSetInteger(g_chart_id, name, OBJPROP_SELECTABLE, false);
         ObjectSetInteger(g_chart_id, name, OBJPROP_ZORDER,0);
        }
     }
  }

void DeleteUIObjects()
  {
   if(g_chart_id == 0) return;
   if(ObjectFind(g_chart_id, ui_shadow_name) >= 0) ObjectDelete(g_chart_id, ui_shadow_name);
   if(ObjectFind(g_chart_id, ui_bg_name) >= 0) ObjectDelete(g_chart_id, ui_bg_name);
   if(ObjectFind(g_chart_id, ui_title_name) >= 0) ObjectDelete(g_chart_id, ui_title_name);
   if(ObjectFind(g_chart_id, ui_score_name) >= 0) ObjectDelete(g_chart_id, ui_score_name);
   if(ObjectFind(g_chart_id, ui_zone_name) >= 0) ObjectDelete(g_chart_id, ui_zone_name);
   if(ObjectFind(g_chart_id, ui_recent_name) >= 0) ObjectDelete(g_chart_id, ui_recent_name);
   if(ObjectFind(g_chart_id, ui_advice_name) >= 0) ObjectDelete(g_chart_id, ui_advice_name);
   if(ObjectFind(g_chart_id, ui_signal_name) >= 0) ObjectDelete(g_chart_id, ui_signal_name);

   for(int i = 0; i < InpHistogramMaxBars; i++)
     {
      string name = ui_hist_base + IntegerToString(i);
      if(ObjectFind(g_chart_id, name) >= 0) ObjectDelete(g_chart_id, name);
     }
  }

В RefreshUI фактически вычисляются и отрисовываются макет панели и гистограмма. Она измеряет высоту шрифта и резервирует место для заголовка, оценки, мелкого текста и строки гистограммы; затем динамически вычисляет ширину панели так, чтобы гистограмма помещалась, но при этом соблюдалась разумная минимальная ширина. Она рисует прямоугольники тени и фона, вычисляет положение значка зоны так, чтобы он не накладывался на заголовок (с помощью EstimateTextWidth), и выбирает цвет для сглаженной оценки (scoreCol) на основе порогов. Строка "Recent:" формируется по последним барам основного таймфрейма (сравнение цены закрытия и открытия, а также их диапазонов в пунктах) и при переполнении усекается с многоточием.

Для гистограммы она вычисляет количество блочных символов, визуально передающих величину, привязывает каждый такой символ к предварительно созданному объекту метки и выбирает цвета в зависимости от знака. Эта процедура содержит защитные проверки: она проверяет доступное пространство и при необходимости уменьшает интервалы, чтобы избежать наложения меток и при этом сохранить панель компактной и читаемой.

void RefreshUI(double rawScore, double smoothScore)
  {
   int title_h = InpTitleFontSize + 6;
   int score_h = InpScoreFontSize + 8;
   int small_h = InpSmallFontSize + 4;
   int hist_area_h = 22;
   int gap_between_sections = 6;

   int hist_px_step = 7;
   int hist_px_width = InpHistogramMaxBars * hist_px_step;
   int panel_width = MathMax(340, hist_px_width + InpPanelPadding*2 + 30);
   int extra_recent_space = small_h + 12;
   int panel_height = InpPanelPadding*2 + title_h + score_h + small_h + hist_area_h + (gap_between_sections * 4) + extra_recent_space;

   int scol = (int)ARGB_uint(InpShadowAlpha, InpShadowColor);
   DrawCell(ui_shadow_name, InpPanelX + 3, InpPanelY + 3, panel_width, panel_height, (color)scol, InpGridClr);
   DrawCell(ui_bg_name, InpPanelX, InpPanelY, panel_width, panel_height, InpPanelBG, InpGridClr);

   int x_start = InpPanelX + InpPanelPadding;
   int y_title = InpPanelY + InpPanelPadding;
   string titleText = StringFormat("Sentiment Tilt Meter — %s  [%s]", Symbol(), TFToText(InpTFPrimary));
   DrawText(ui_title_name, titleText, x_start, y_title, InpTxtClr, InpTitleFontSize);

   // ... zone, score, advice, recent and histogram painting (omitted here for brevity)
   // The full EA code contains the rest and iterates label objects to draw histogram blocks.
  }

TFToText – это простой вспомогательный метод, который сопоставляет ENUM_TIMEFRAMES с короткими понятными текстовыми метками (M1, M5, M15, H1 и т.д.). Он используется в заголовке, чтобы пользователь всегда видел, на какой основной таймфрейм опирается советник.
string TFToText(ENUM_TIMEFRAMES tf)
  {
   switch(tf)
     {
      case PERIOD_M1:  return "M1";
      case PERIOD_M5:  return "M5";
      case PERIOD_M15: return "M15";
      case PERIOD_M30: return "M30";
      case PERIOD_H1:  return "H1";
      case PERIOD_H4:  return "H4";
      case PERIOD_D1:  return "D1";
      case PERIOD_W1:  return "W1";
      case PERIOD_MN1: return "MN";
      default:         return IntegerToString((int)tf);
     }
  }

OnInit выполняет инициализацию: он получает идентификатор графика и формирует префикс объектов, изменяет размер и инициализирует буфер гистограммы, создает хэндлы индикатора ATR (iATR) для запрошенных таймфреймов (только если включен мультитаймфреймовый режим), вычисляет начальное необработанное объединенное значение настроения (ComputeFusedSentiment) и задает сглаженное значение и состояние зоны. Затем OnInit создает объекты интерфейса, рисует начальную панель через RefreshUI и запускает таймер на 1 секунду (EventSetTimer(1)), чтобы OnTimer мог замечать новые бары основного таймфрейма. В этой схеме сглаживание инициализируется первым необработанным значением, чтобы первое отображаемое сглаженное значение было стабильным. Обратите внимание: код вызывает UpdateZoneState уже на раннем этапе, чтобы начальная зона определялась корректно. 

OnDeinit выполняет очистку при удалении советника: останавливает таймер, удаляет объекты интерфейса и убирает все маркеры сигналов, созданные этим советником (с именем в формате g_prefix + "SIG_"). Он также освобождает хэндлы индикатора ATR с помощью IndicatorRelease. Это позволяет аккуратно освобождать ресурсы, не оставлять на графике лишние метки и не допускать утечки хэндлов индикатора.

int OnInit()
  {
   g_chart_id = ChartID();
   g_prefix = Symbol() + "_" + IntegerToString((int)g_chart_id) + "_";
   ui_bg_name = g_prefix + "BG";
   ui_shadow_name = g_prefix + "SHDW";
   ui_title_name = g_prefix + "TITLE";
   ui_score_name = g_prefix + "SCORE";
   ui_zone_name = g_prefix + "ZONE";
   ui_hist_base = g_prefix + BASE_HIST;
   ui_recent_name = g_prefix + "RECENT";
   ui_advice_name = g_prefix + "ADVICE";
   ui_signal_name = g_prefix + "LASTSIG";

   ArrayResize(g_hist_buffer, InpHistogramMaxBars);
   ArrayInitialize(g_hist_buffer, 0.0);
   g_hist_idx = 0;
   g_hist_count = 0;

   g_atr_handle_primary = iATR(Symbol(), InpTFPrimary, InpATRPeriod);
   if(InpUseMultiTF)
     {
      g_atr_handle_extra1 = iATR(Symbol(), InpExtraTF1, InpATRPeriod);
      g_atr_handle_extra2 = iATR(Symbol(), InpExtraTF2, InpATRPeriod);
     }

   double raw_init = ComputeFusedSentiment();
   g_smoothed_score = raw_init;
   g_prev_smoothed = g_smoothed_score;
   g_last_alert_score = raw_init;
   UpdateZoneState(raw_init);

   CreateUIObjects();
   RefreshUI(raw_init, g_smoothed_score);

   EventSetTimer(1);
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   EventKillTimer();
   DeleteUIObjects();

   // delete signal markers created by this EA (prefix-based)
   string name;
   int total = ObjectsTotal(g_chart_id);
   for(int i = total - 1; i >= 0; --i)
     {
      name = ObjectName(g_chart_id, i);
      if(StringFind(name, g_prefix + "SIG_") == 0)
         ObjectDelete(g_chart_id, name);
     }

   if(g_atr_handle_primary != INVALID_HANDLE)
      IndicatorRelease(g_atr_handle_primary);
   if(g_atr_handle_extra1 != INVALID_HANDLE)
      IndicatorRelease(g_atr_handle_extra1);
   if(g_atr_handle_extra2 != INVALID_HANDLE)
      IndicatorRelease(g_atr_handle_extra2);
  }
Советник использует OnTimer как основной механизм опроса. Он проверяет временную метку текущего бара на основном таймфрейме (iTime(Symbol(), InpTFPrimary, 0)) и, если она отличается от последнего обработанного значения, вызывает HandleNewPrimaryBar. Этот подход позволяет не выполнять обработку на каждом тике таймера и срабатывать только один раз на каждый новый завершенный бар: это хороший прием, когда нужны сигналы по барам, но вместо OnTick используется таймер.
datetime g_last_primary_time = 0;
void OnTimer()
  {
   datetime t = iTime(Symbol(), InpTFPrimary, 0);
   if(t == g_last_primary_time) return;
   g_last_primary_time = t;
   HandleNewPrimaryBar();
  }

void HandleNewPrimaryBar()
  {
   double raw = ComputeFusedSentiment();
   double alpha = MathMax(0.0, MathMin(1.0, InpSmoothAlpha));
   g_prev_smoothed = g_smoothed_score;
   g_smoothed_score = alpha * raw + (1.0 - alpha) * g_smoothed_score;
   g_hist_buffer[g_hist_idx] = g_smoothed_score;
   g_hist_idx = (g_hist_idx + 1) % InpHistogramMaxBars;
   if(g_hist_count < InpHistogramMaxBars) g_hist_count++;
   RefreshUI(raw, g_smoothed_score);
   ProcessAlerts(raw, g_smoothed_score);
  }

HandleNewPrimaryBar вычисляет новое необработанное объединенное значение настроения через ComputeFusedSentiment, применяет экспоненциальное сглаживание с заданным коэффициентом alpha (g_smoothed_score = alpha * raw + (1-alpha) * prev), сохраняет сглаженное значение в кольцевой буфер гистограммы, обновляет индексы и счетчик, обновляет интерфейс и вызывает ProcessAlerts, чтобы проверить, должно ли это новое сглаженное значение породить сообщения или сигналы. Кольцевой буфер реализован через g_hist_idx по модулю InpHistogramMaxBars, а g_hist_count отслеживает число действительных записей. Это сохраняет скользящую историю для визуальной гистограммы и проверок удержания знака.

ComputeFusedSentiment – это агрегирующая функция верхнего уровня для расчета итоговой оценки. Она нормализует абсолютные значения трех весовых коэффициентов так, чтобы их сумма была равна 1 (с безопасным резервным вариантом: использовать только основной таймфрейм, если сумма равна нулю). Затем она вызывает ComputeTFScore для основного таймфрейма и, если это включено, для двух дополнительных таймфреймов. Наконец, она вычисляет взвешенное среднее и ограничивает объединенный результат диапазоном [-100, 100]. Это поддерживает согласованность интерфейса и пороговой логики независимо от исходного масштаба оценок отдельных таймфреймов.

double ComputeFusedSentiment()
  {
   double w1 = MathAbs(InpWeightPrimary);
   double w2 = MathAbs(InpWeightExtra1);
   double w3 = MathAbs(InpWeightExtra2);
   double sum = w1 + w2 + w3;
   if(sum <= 0.0) { w1 = 1.0; w2 = 0.0; w3 = 0.0; sum = 1.0; }
   w1 /= sum; w2 /= sum; w3 /= sum;

   double s1 = ComputeTFScore(Symbol(), InpTFPrimary, InpLookback, InpSSWindow, g_atr_handle_primary);
   if(!InpUseMultiTF) return s1;
   double s2 = ComputeTFScore(Symbol(), InpExtraTF1, InpLookback, InpSSWindow, g_atr_handle_extra1);
   double s3 = ComputeTFScore(Symbol(), InpExtraTF2, InpLookback, InpSSWindow, g_atr_handle_extra2);
   double fused = s1 * w1 + s2 * w2 + s3 * w3;
   return MathMax(-100.0, MathMin(100.0, fused));
  }

double ComputeTFScore(string sym, ENUM_TIMEFRAMES tf, int lookback, int ssWindow, int atrHandle)
  {
   MqlRates rates[];
   int needed = MathMax(lookback, ssWindow) + 6;
   int copied = CopyRates(sym, tf, 0, needed, rates);
   if(copied <= 0) return 0.0;

   double atr = 0.0001;
   if(atrHandle != INVALID_HANDLE)
     {
      double abuf[];
      if(CopyBuffer(atrHandle, 0, 1, 1, abuf) > 0)
         atr = abuf[0] * InpATRMultiplier;
     }
   else
     {
      int rc = MathMin(10, copied - 1);
      double ar = 0.0;
      for(int i = 1; i <= rc; i++) ar += (rates[i].high - rates[i].low);
      if(rc > 0) ar /= rc;
      if(ar > 0) atr = ar * 0.6;
     }

   int limit = MathMin(ssWindow, copied - 1);
   double avgRange = 0.0;
   int vr = MathMin(10, copied - 1);
   for(int k = 1; k <= vr; k++) avgRange += (rates[k].high - rates[k].low);
   if(vr > 0) avgRange /= vr;

   double sumSS = 0.0;
   int count = 0;
   for(int i = 1; i <= limit; i++)
     {
      double open = rates[i].open;
      double close = rates[i].close;
      double high = rates[i].high;
      double low = rates[i].low;
      double range = high - low;
      if(range <= 0.0) continue;
      double dir = (close > open) ? 1.0 : -1.0;
      double cbr = (MathAbs(close - open) / range) * dir;
      double cpp = (((close - low) / range) - 0.5) * 2.0 * dir;
      double vad = 0.0;
      if(atr > 0.0) vad = (range / atr) - 1.0;
      vad = MathMax(-1.0, MathMin(3.0, vad));
      double ss = cbr * 0.4 + cpp * 0.3 + vad * 0.3;
      ss = MathMax(-1.0, MathMin(1.0, ss));

      bool conf = false;
      if((i - 1) >= 0 && (i - 1) <= copied - 1)
        {
         double nOpen = rates[i - 1].open;
         double nClose = rates[i - 1].close;
         if(dir > 0.0 && nClose > nOpen) conf = true;
         if(dir < 0.0 && nClose < nOpen) conf = true;
        }

      double confFactor = conf ? 1.0 : 0.6;
      double volFactor = 1.0;
      if(atr > 0.0 && avgRange / atr < InpVolQuietThreshold) volFactor = 0.6;
      double finalSS = ss * confFactor * volFactor;
      sumSS += finalSS;
      count++;
     }
   if(count == 0) return 0.0;
   double avgSS = sumSS / count;
   return avgSS * 100.0;
  }

ComputeTFScore – именно здесь вычисляются базовые сигнальные признаки для конкретного таймфрейма. Она копирует блок последних MqlRates баров с запрошенного таймфрейма и вычисляет прокси индикатора ATR: если хэндл ATR существует, копируется буфер индикатора; в противном случае используется запасной вариант – простое среднее истинного диапазона. Код вычисляет avgRange, а затем перебирает до ssWindow прошлых баров, чтобы получить для каждого бара три нормализованных признака: cbr (закрытие относительно открытия в пределах диапазона – отражает направление и силу), cpp (положение цены закрытия внутри бара, нормализованное и смещенное с учетом направления) и vad (расстояние с поправкой на волатильность – диапазон относительно ATR минус единица). Каждый признак взвешивается (0,4, 0,3 и 0,3 соответственно), ограничивается диапазоном [-1,1], а затем умножается на два коэффициента уверенности: confFactor, который проверяет, подтвердил ли предыдущий бар направление, и volFactor, который уменьшает вклад, если рынок необычно спокоен относительно ATR.

Итоговое значение настроения для бара finalSS накапливается и усредняется по окну, а затем умножается на 100, чтобы получить удобный квазипроцентный диапазон. Если коротко: эта процедура сводит направленное смещение, положение цены закрытия внутри бара, относительную величину диапазона и простое подтверждение между барами в единую оценку для данного таймфрейма.

RecentSmoothedSignHold – это вспомогательная функция, которую логика смены знака использует, чтобы последние сглаженные значения всех проверяемых баров имели один и тот же новый знак. Она проходит назад по кольцевому g_hist_buffer и возвращает false, если хотя бы одно из нужных недавних сглаженных значений не проходит проверку знака. Это предотвращает мерцание сигнала из-за одного случайного бара.

ProcessAlerts – это механизм принятия решений для сообщений и сигналов на графике. Сначала функция проверяет, накоплено ли достаточно истории (InpMinBarsForSignal), а затем – не произошел ли резкий значительный разворот (когда абсолютная разница с g_last_alert_score превышает InpFlipThreshold); в этом случае отправляется алерт, обновляется состояние зоны и при необходимости рисуется текстовый маркер BUY/SELL. Если большого разворота нет, функция вычисляет новую зону по InpPositiveZone/InpNegativeZone. Если InpSignalOnSignFlip включен, функция также проверяет смену знака (positive→negative или negative→positive) и применяет три критерия подтверждения: минимальную абсолютную величину нового сглаженного значения (InpMinSignFlipAbs), удержание нового знака в последних барах (InpSignFlipHoldBars) и необязательную проверку моментума (InpRequireMomentum + InpMinMomentum). Лишь когда все проверки пройдены, функция принимает смену знака как истинный сигнал, рисует маркер на графике, обновляет g_last_alert_score и устанавливает g_zone_state.

Если зона меняется обычным образом (при входе в положительную или отрицательную зону), код аналогично отправляет алерты, записывает последний сигнал и время и рисует маркер. В результате получается многоуровневая консервативная сигнальная стратегия, которая предпочитает более четкие структурные изменения хаотичному шуму.

bool RecentSmoothedSignHold(int bars, int desiredSign)
  {
   if(bars <= 1) return true;
   if(g_hist_count < bars) return false;
   int max = InpHistogramMaxBars;
   int idx = (g_hist_idx - 1 + max) % max;
   for(int i = 0; i < bars; i++)
     {
      double v = g_hist_buffer[(idx - i + max) % max];
      if(desiredSign == 1 && v <= 0.0) return false;
      if(desiredSign == -1 && v >= 0.0) return false;
     }
   return true;
  }

void ProcessAlerts(double rawScore, double smoothScore)
  {
   if(g_hist_count < InpMinBarsForSignal)
     {
      PrintFormat("%s: waiting hist_count=%d (need %d)", __FUNCTION__, g_hist_count, InpMinBarsForSignal);
      return;
     }

   // large flip detection
   if(MathAbs(smoothScore - g_last_alert_score) >= InpFlipThreshold)
     {
      string m = StringFormat("STM Flip: %.1f -> %.1f on %s", g_last_alert_score, smoothScore, Symbol());
      SendAlert(m);
      g_last_alert_score = smoothScore;
      UpdateZoneState(smoothScore);

      int newZoneFlip = 0;
      if(smoothScore >= InpPositiveZone) newZoneFlip = 1;
      else if(smoothScore <= InpNegativeZone) newZoneFlip = -1;
      if(newZoneFlip != 0)
        {
         g_last_signal_text = (newZoneFlip == 1) ? "BUY" : "SELL";
         g_last_signal_time = TimeCurrent();
         DrawSignalOnChart(newZoneFlip, smoothScore);
         PruneOldSignals(InpMaxSignalsToKeep);
        }
      RefreshUI(rawScore, g_smoothed_score);
      return;
     }

  }
UpdateZoneState – это простая функция, которая присваивает g_zone_state значение 1, -1 или 0 в зависимости от сглаженной оценки и заданных порогов положительной и отрицательной зоны. SendAlert централизует отправку алертов: воспроизводит звук, если включена соответствующая опция, отправляет push-уведомление, если включена соответствующая опция, и вызывает Alert() (локальное диалоговое окно терминала) и Print() для лога. Такая централизация упрощает дальнейшее расширение функциональности (например, за счет электронной почты или вебхуков).
void UpdateZoneState(double smoothScore)
  {
   if(smoothScore >= InpPositiveZone) g_zone_state = 1;
   else if(smoothScore <= InpNegativeZone) g_zone_state = -1;
   else g_zone_state = 0;
  }

void SendAlert(string msg)
  {
   if(InpEnableSound) PlaySound(InpSoundFile);
   if(InpEnablePush) SendNotification(msg);
   Alert(msg);
   Print(__FILE__, ": ", msg);
  }

DrawSignalOnChart отвечает за размещение текста BUY/SELL и глифа стрелки точно на последнем закрытом баре основного таймфрейма (индекс 1). Она формирует уникальное имя объекта из временной метки и g_hist_idx, чтобы разные сигналы не смешивались, вычисляет небольшой ценовой сдвиг для размещения текстовой метки выше или ниже стрелки, оставляя саму стрелку точно на уровне цены, и создает два текстовых объекта графика: один для текстовой метки, другой для глифа стрелки. Советник поддерживает простой выбор глифа стрелки и задает шрифты, цвета, порядок наложения и запрет на выделение, чтобы эти аннотации не мешали ручной работе трейдера с графиком. После создания сигнала функция вызывает PruneOldSignals, чтобы график не засорялся слишком большим количеством маркеров. 

PruneOldSignals выполняет простую служебную очистку: перечисляет все объекты на графике, выбирает те, чьи имена начинаются с префикса советника + SIG_, извлекает временную метку, закодированную в имени объекта, сортирует объекты по этой временной метке (реализовано многократным удалением минимального значения) и удаляет самые старые, пока не останется только keep объектов. Это гарантирует, что на графике останутся только последние InpMaxSignalsToKeep маркеров, предотвращает его захламление и сохраняет низкими затраты ресурсов на перебор объектов при разумных значениях лимита keep.

void DrawSignalOnChart(int zone, double score)
  {
   datetime tm = iTime(Symbol(), InpTFPrimary, 1);
   double price = iClose(Symbol(), InpTFPrimary, 1);
   if(tm == 0 || price == 0.0) return;

   double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
   double textOffsetPts = MathMax(1, InpArrowOffsetPoints);
   double textOffset = point * textOffsetPts * 1.5;

   string name = g_prefix + "SIG_" + IntegerToString((int)tm) + "_" + IntegerToString(g_hist_idx);
   string arrName = g_prefix + "SIG_ARR_" + IntegerToString((int)tm) + "_" + IntegerToString(g_hist_idx);

   if(ObjectFind(g_chart_id, name) >= 0) ObjectDelete(g_chart_id, name);
   if(ObjectFind(g_chart_id, arrName) >= 0) ObjectDelete(g_chart_id, arrName);

   double text_price = (zone == 1) ? price + textOffset : price - textOffset;
   if(ObjectCreate(g_chart_id, name, OBJ_TEXT, 0, tm, text_price))
     {
      string txt = (zone == 1) ? "BUY" : ((zone == -1) ? "SELL" : "NEUTRAL");
      color col = (zone == 1) ? clrLime : ((zone == -1) ? clrRed : clrSilver);
      ObjectSetString(g_chart_id, name, OBJPROP_TEXT, txt);
      ObjectSetInteger(g_chart_id, name, OBJPROP_COLOR, (int)col);
      ObjectSetInteger(g_chart_id, name, OBJPROP_FONTSIZE, 12);
      ObjectSetString(g_chart_id, name, OBJPROP_FONT, InpFont);
      ObjectSetInteger(g_chart_id, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(g_chart_id, name, OBJPROP_ZORDER, 2);
     }

   if(InpShowArrows)
     {
      string arrowTxt = (zone == 1) ? "▲" : ((zone == -1) ? "v" : "■");
      if(ObjectCreate(g_chart_id, arrName, OBJ_TEXT, 0, tm, price))
        {
         color acol = (zone == 1) ? clrLime : ((zone == -1) ? clrRed : clrSilver);
         ObjectSetString(g_chart_id, arrName, OBJPROP_TEXT, arrowTxt);
         ObjectSetInteger(g_chart_id, arrName, OBJPROP_COLOR, (int)acol);
         ObjectSetInteger(g_chart_id, arrName, OBJPROP_FONTSIZE, InpArrowFontSize);
         ObjectSetString(g_chart_id, arrName, OBJPROP_FONT, InpFont);
         ObjectSetInteger(g_chart_id, arrName, OBJPROP_SELECTABLE, false);
         ObjectSetInteger(g_chart_id, arrName, OBJPROP_ZORDER, 3);
        }
     }

   PruneOldSignals(InpMaxSignalsToKeep);
  }

void PruneOldSignals(int keep)
  {
   if(keep <= 0) return;
   int total = ObjectsTotal(g_chart_id);
   string names[];
   int times[];
   int n = 0;
   string sigPrefix = g_prefix + "SIG_";
   for(int i = 0; i < total; i++)
     {
      string nm = ObjectName(g_chart_id, i);
      if(StringFind(nm, sigPrefix) == 0)
        {
         string rest = StringSubstr(nm, StringLen(sigPrefix));
         int pos = StringFind(rest, "_");
         if(pos > 0)
           {
            string tsStr = StringSubstr(rest, 0, pos);
            int ts = (int)StringToInteger(tsStr);
            ArrayResize(names, n+1);
            ArrayResize(times, n+1);
            names[n] = nm;
            times[n] = ts;
            n++;
           }
        }
     }
   if(n <= keep) return;

   while(n > keep)
     {
      int minIdx = 0;
      for(int j = 1; j < n; j++) if(times[j] < times[minIdx]) minIdx = j;
      ObjectDelete(g_chart_id, names[minIdx]);
      for(int k = minIdx; k < n-1; k++)
        {
         names[k] = names[k+1];
         times[k] = times[k+1];
        }
      ArrayResize(names, n-1);
      ArrayResize(times, n-1);
      n--;
     }
  }

Наконец, в советнике реализован ряд защитных и ориентированных на удобство пользователя решений: уникальные префиксы имен объектов для предотвращения конфликтов, резервный расчет ATR при сбое хэндлов индикатора, ограничение объединенных оценок фиксированным диапазоном, сглаживание с настраиваемым коэффициентом alpha для уменьшения шума и многоуровневые критерии принятия сигнала (порог разворота, величина/удержание/моментум смены знака, вход в зону). Код интерфейса предотвращает наложение элементов при размещении значка зоны и строки Recent, а гистограмма использует текстовые блоки вместо графики, что делает ее легкой и совместимой с более ранними сборками MetaTrader 5.


Тестирование и результаты

В этом разделе мы рассмотрим результаты тестирования нашего советника, включая как результаты тестирования на исторических данных, так и наблюдения в ходе тестирования на реальном рынке.

Ниже показана GIF-анимация теста на исторических данных индекса Crash 300. Панель представляет собой компактную и удобную для проверки сводку состояния STM: в верхней строке указаны название индикатора, инструмент (Crash 300 Index) и активный таймфрейм (M5). Под ней отображается крупная цветовая числовая оценка настроения по шкале от -100 до +100, а краткая метка направления (например, BULL, BEAR, NEUTRAL) сразу дает качественное представление о рынке. Непосредственно под оценкой панель показывает последний сигнал (например, "Bias: Long BUY @ 16:30"), чтобы можно было проверить направление и точное время бара. Горизонтальная полоса силы показывает величину, а компактная строка "Recent" выводит последние метрики свечей и тем самым помогает интерпретировать оценку в контексте. Все элементы привязаны к закрытым барам, поэтому каждый алерт и каждая метка однозначно соотносятся с конкретными временем и ценой.

Во время тестирования в реальном режиме на Crash 1000 Index советник выдал сигнал BUY, который при проверке по времени закрытого бара подтвердился после короткого периода удержания. Панель показывала явное положительное смещение, а маркер BUY был привязан к точному моменту закрытия бара и уровню цены, поэтому и само движение, и его время на графике полностью поддаются проверке. Этот результат показывает, что STM способен выявлять устойчивое краткосрочное бычье смещение: сглаживание и проверки моментума отфильтровали шум и снизили риск раннего ложного сигнала в условиях рыночной пилы, при том что управление сделкой (размер позиции, стопы, цели по прибыли) по-прежнему оставалось за трейдером.

  • Тестирование на Crash 300 Index


Заключение

Советник Sentiment Tilt Meter (STM) сделал именно то, для чего был создан: четко и надежно указывал на краткосрочное смещение во время тестов на Crash 300 и Crash 1000. Сигналы легко проверить – каждая стрелка и каждая метка привязаны к закрытому бару и цене, поэтому можно без догадок понять, что произошло и когда. Сглаживание и проверки моментума не давали индикатору дергаться на мелких движениях, а взвешивание по таймфреймам позволяет делать больший акцент на горизонтах, которые важны именно для вашего стиля.

Этот инструмент – генератор сигналов и средство их фиксации, а не полноценная торговая система. Он покажет, когда возникает рыночное смещение, но не будет за вас рассчитывать размер позиции, выставлять стопы или управлять выходом из сделки. Результаты будут различаться в зависимости от инструмента и торговой сессии; в этих тестах индексы Crash показали себя хорошо, но для форекса, акций или фьючерсов может потребоваться иная настройка.

Используйте STM как фильтр или как слой подтверждения. Логируйте сигналы в CSV, запускайте тесты на исторических данных по нескольким инструментам и короткие тесты на реальных данных и только после этого рассматривайте автоматизацию входов. Если вы торгуете по его сигналам, встраивайте их в простой риск-план – фиксированный R, четкие правила установки стопа и проверка на более высоком таймфрейме, – чтобы одна шумная сессия не могла свести на нет преимущество, накопленное за месяц.

Если коротко, STM дает ясную и проверяемую оценку краткосрочного рыночного смещения. Он ускоряет принятие решений и помогает сохранять дисциплину. Прежде чем торговать на реальном счете, дополните советник средствами контроля риска и многоэтапными проверками.







Chart Projector
Analytical Comment
Analytics Master
Analytics Forecaster 
Volatility Navigator
Mean Reversion Signal Reaper
Signal Pulse 
Metrics Board 
External Flow
VWAP
Heikin Ashi   FibVWAP  
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR) 
Скрипт Quarters Drawer
Intrusion Detector
TrendLoom Tool  Quarters Board 
ZigZag Analyzer  Correlation Pathfinder  Market Structure Flip Detector Tool
Correlation Dashboard   Currency Strength Meter 
PAQ Analysis Tool 
Dual EMA Fractal Breaker
Pin bar, Engulfing and RSI divergence
Liquidity Sweep Opening Range Breakout Tool Boom and Crash Interceptor CCI Zer-Line EA
Candlestick Recognition Candlestick Detection using TA-Lib Candle Range Tool MetaTrader 5 Data Ingestor Model Training and Deployment Use of Python Lib
Sentiment Tilt Meter          

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19137

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Mustafa Nail Sertoglu
Mustafa Nail Sertoglu | 25 авг. 2025 в 21:40
Спасибо, что поделились новыми идеями по управлению торговлей; проводите здоровые дни с теми, кто вас любит.
Нейросети в трейдинге: Оценка риска по несогласованности представлений (Основные компоненты) Нейросети в трейдинге: Оценка риска по несогласованности представлений (Основные компоненты)
В статье реализована адаптация ReGEN-TAD под MQL5: риск трактуется как согласованность двух путей анализа — трансформера (контекст) и рекуррентной сети (динамика). Введён модуль токенизации разности, который формирует токен риска в общем пространстве признаков и передаёт его в последующие решения. Практический итог — готовые блоки для интеграции оценки уверенности в советники и для последующего обучения и тестирования.
Автоматизация греков Блэка-Шоулза: Расширенный скальпинг и микроструктурная торговля Автоматизация греков Блэка-Шоулза: Расширенный скальпинг и микроструктурная торговля
Гамма и Дельта изначально разрабатывались как инструменты управления рисками для хеджирования опционной экспозиции, но со временем они превратились в мощные инструменты для продвинутого скальпинга, моделирования потока ордеров и торговли на основе рыночной микроструктуры. Сегодня они служат индикаторами ценовой чувствительности и поведения ликвидности в режиме реального времени, позволяя трейдерам с удивительной точностью прогнозировать краткосрочную волатильность.
Событийная архитектура в MQL5: как превратить советник в полноценную торговую систему Событийная архитектура в MQL5: как превратить советник в полноценную торговую систему
Статья посвящена событийной архитектуре в MQL5 и описывает переход от монолитной модели OnTick к распределённой обработке. Разбираются предопределённые и пользовательские события, сервисы и обмен сообщениями между программами, а также типовые архитектурные ошибки. На практическом примере показано, как организовать взаимодействие индикаторов и советника, чтобы снизить нагрузку, повысить читаемость и упростить сопровождение.
Разработка торговой стратегии: Стратегия следования за трендом на основе Индекса цветочной волатильности Разработка торговой стратегии: Стратегия следования за трендом на основе Индекса цветочной волатильности
Неустанное стремление расшифровать рыночные ритмы привело трейдеров и аналитиков, занимающихся количественным анализом, к разработке бесчисленных математических моделей. В данной статье представлен Индекс цветочной волатильности (FVI) — новый подход, превращающий математическую элегантность кривых розы (Rose Curves), также известных как розы Гранди, в функциональный торговый инструмент. Благодаря этой работе мы показали, как математические модели могут быть адаптированы к практическим торговым механизмам, способным поддерживать как анализ, так и принятие решений в реальных рыночных условиях.