//+------------------------------------------------------------------+
//|                                                  FlameChart_1.mq5 |
//|        Adaptive Mean Reversion Z-Score + signal arrows (objects) |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_buffers 7
#property indicator_plots   4

#property indicator_label1  "Dynamic Z-Score"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSilver
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

// Буферные стрелки ПОКУПКИ (в окне индикатора)
#property indicator_label2  "Buy Arrow (buffer)"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrDeepSkyBlue
#property indicator_width2  1

// Буферные стрелки ПРОДАЖИ (в окне индикатора)
#property indicator_label3  "Sell Arrow (buffer)"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrRed
#property indicator_width3  1

// Буферные крестики ВЫХОДА (в окне индикатора)
#property indicator_label4  "Exit Cross (buffer)"
#property indicator_type4   DRAW_ARROW
#property indicator_color4  clrOrange
#property indicator_width4  1

// Уровни нормального распределения (+/- 2 сигмы)
#property indicator_level1   2.0
#property indicator_level2   0.0
#property indicator_level3  -2.0
#property indicator_levelcolor clrGray
#property indicator_levelstyle STYLE_DOT

// Буферы индикатора (сначала все INDICATOR_DATA, затем CALCULATIONS)
double ExtZScoreBuffer[];
double ExtBuyArrowBuf[];   // Буферные стрелки ПОКУПКИ (плот 1)
double ExtSellArrowBuf[];  // Буферные стрелки ПРОДАЖИ (плот 2)
double ExtExitCrossBuf[];  // Буферные крестики ВЫХОДА (плот 3)
double ExtMeanBuffer[];
double ExtStdDevBuffer[];
double ExtPriceBuffer[];

// Эффективный коэффициент адаптации (защита от InpFastAlpha = 0)
double ExtAlphaEffective = 0.3;
int    ExtSubWindow      = 0; // 0 = основное окно графика
int    ExtPosition       = 0; // состояние позиции: 0 = нет, 1 = лонг, -1 = шорт

// Префикс графических объектов (стрелок)
#define ARROW_PREFIX "FC1_"

// Входные параметры
input int    InpPeriod            = 20;     // Базовый период расчета
input double InpFastAlpha         = 0.3;    // Коэффициент адаптации среднего (0.1-0.5; при 0 используется 0.3)
input double InpEntryDev          = 2.0;    // Отклонение в сигмах для ВХОДА (+/- N * sigma)
input double InpExitDev           = 0.5;    // Отклонение в сигмах для ВЫХОДА (возврат к нулю)
input int    InpArrowWidth        = 1;      // Размер (ширина) стрелок входа
input int    InpMaxBars           = 1000;   // Отрисовка сигналов на последних N барах (0 = вся история)
input bool   InpShowSignals       = true;   // Стрелки/крестики ОБЪЕКТАМИ на графике (на барах)

input color  InpBuyArrowColor     = clrDeepSkyBlue; // Цвет стрелки ПОКУПКИ (▲ под баром)
input color  InpSellArrowColor    = clrRed;  // Цвет стрелки ПРОДАЖИ (▼ над баром)
input color  InpExitArrowColor    = clrOrange; // Цвет крестиков ВЫХОДА (X на пересечении 0)

//+------------------------------------------------------------------+
//| Удалить стрелки на заданном баре                                |
//+------------------------------------------------------------------+
void DeleteArrowAt(datetime bar_time)
{
  string time_str = TimeToString(bar_time, TIME_DATE | TIME_MINUTES);
  string up_name   = ARROW_PREFIX + "BUY_"  + time_str;
  string down_name = ARROW_PREFIX + "SELL_" + time_str;
  string exit_name = ARROW_PREFIX + "EXIT_" + time_str;
  if(ObjectFind(0, up_name)   >= 0) ObjectDelete(0, up_name);
  if(ObjectFind(0, down_name) >= 0) ObjectDelete(0, down_name);
  if(ObjectFind(0, exit_name) >= 0) ObjectDelete(0, exit_name);
}

//+------------------------------------------------------------------+
//| Нарисовать стрелку-объект: dir = +1 (вверх/покупка), -1 (вниз)  |
//+------------------------------------------------------------------+
void DrawArrow(datetime bar_time, double price, int dir)
{
  string name = ARROW_PREFIX + (dir > 0 ? "BUY_" : "SELL_") +
                TimeToString(bar_time, TIME_DATE | TIME_MINUTES);
  if(ObjectFind(0, name) >= 0) return; // уже есть
  if(ObjectCreate(0, name, OBJ_ARROW, ExtSubWindow, bar_time, price)) {
    ObjectSetInteger(0, name, OBJPROP_ARROWCODE, dir > 0 ? 233 : 234);
    ObjectSetInteger(0, name, OBJPROP_COLOR,     dir > 0 ? InpBuyArrowColor : InpSellArrowColor);
    ObjectSetInteger(0, name, OBJPROP_WIDTH,     InpArrowWidth);
    ObjectSetInteger(0, name, OBJPROP_ANCHOR,    (dir > 0) ? ANCHOR_TOP : ANCHOR_BOTTOM);
    ObjectSetInteger(0, name, OBJPROP_BACK,      false);
  }
}

//+------------------------------------------------------------------+
//| Нарисовать крестик выхода-объект (пересечение нуля)             |
//+------------------------------------------------------------------+
void DrawExitCross(datetime bar_time, double price, int exit_dir)
{
  string name = ARROW_PREFIX + "EXIT_" + TimeToString(bar_time, TIME_DATE | TIME_MINUTES);
  if(ObjectFind(0, name) >= 0) return; // уже есть
  if(ObjectCreate(0, name, OBJ_ARROW, ExtSubWindow, bar_time, price)) {
    ObjectSetInteger(0, name, OBJPROP_ARROWCODE, 251); // символ выхода (Wingdings)
    ObjectSetInteger(0, name, OBJPROP_COLOR,     InpExitArrowColor);
    ObjectSetInteger(0, name, OBJPROP_WIDTH,     3);
    ObjectSetInteger(0, name, OBJPROP_ANCHOR,    (exit_dir > 0) ? ANCHOR_BOTTOM : ANCHOR_TOP);
    ObjectSetInteger(0, name, OBJPROP_BACK,      false);
  }
}

//+------------------------------------------------------------------+
//| Обработать закрытый бар i: вход по стрелке, выход - ОДИН        |
//| крестик на позицию при пересечении нуля. Рисует объектами       |
//| (на барах) и/или буферами (в окне индикатора)                   |
//+------------------------------------------------------------------+
void ProcessBar(int i, const datetime &time[], const double &high[], const double &low[])
{
  if(i < 1 || i < InpPeriod) return; // прогрев первых баров
  bool draw_obj = InpShowSignals;
  bool draw_buf = true; // буферные сигналы ВСЕГДА включены
  if(!draw_obj && !draw_buf) return; // все отрисовки отключены
  // Сброс буферных сигналов на этом баре
  ExtBuyArrowBuf[i]  = EMPTY_VALUE;
  ExtSellArrowBuf[i] = EMPTY_VALUE;
  ExtExitCrossBuf[i] = EMPTY_VALUE;
  if(draw_obj) DeleteArrowAt(time[i]); // сброс объектов на баре
  bool buy_signal  = false;
  bool sell_signal = false;
  double prev_z = ExtZScoreBuffer[i-1];
  bool prev_ok  = (prev_z != EMPTY_VALUE); // бар вне окна не даёт ложных сигналов
  // --- Один сигнал на ПЕРЕСЕЧЕНИИ уровня ---
  if(prev_ok && ExtZScoreBuffer[i] >  InpEntryDev && prev_z <= InpEntryDev) sell_signal = true; // ▼ ПРОДАЖА
  if(prev_ok && ExtZScoreBuffer[i] < -InpEntryDev && prev_z >= -InpEntryDev) buy_signal  = true; // ▲ ПОКУПКА
  // --- Отрисовка ВХОДА ---
  if(draw_obj) {
    if(sell_signal) DrawArrow(time[i], high[i], -1);
    if(buy_signal)  DrawArrow(time[i], low[i],  1);
  }
  if(draw_buf) {
    if(sell_signal) ExtSellArrowBuf[i] =  ExtZScoreBuffer[i]; // ● на линии (продажа)
    if(buy_signal)  ExtBuyArrowBuf[i]  = ExtZScoreBuffer[i]; // ● на линии (покупка)
  }
  // --- Открытие позиции по первому сигналу входа (если позиции нет) ---
  if(ExtPosition == 0) {
    if(buy_signal)       ExtPosition =  1; // лонг
    else if(sell_signal) ExtPosition = -1; // шорт
  }
  // --- ВЫХОД: один крестик на позицию при пересечении нуля ---
  bool exit_now = false;
  // Лонг закрывается, когда линия пересекает 0 снизу вверх
  if(prev_ok && ExtPosition == 1 && ExtZScoreBuffer[i] > 0.0 && prev_z <= 0.0) exit_now = true;
  // Шорт закрывается, когда линия пересекает 0 сверху вниз
  else if(prev_ok && ExtPosition == -1 && ExtZScoreBuffer[i] < 0.0 && prev_z >= 0.0) exit_now = true;
  if(exit_now) {
    if(draw_obj) DrawExitCross(time[i], ExtPriceBuffer[i], ExtPosition); // объект на цене закрытия (1=из лонга, -1=из шорта)
    if(draw_buf) ExtExitCrossBuf[i] = ExtZScoreBuffer[i];   // ● на линии (выход)
    ExtPosition = 0;
  }
}

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
  // Все INDICATOR_DATA буферы идут первыми, затем INDICATOR_CALCULATIONS
  SetIndexBuffer(0, ExtZScoreBuffer, INDICATOR_DATA);
  SetIndexBuffer(1, ExtBuyArrowBuf,  INDICATOR_DATA);
  SetIndexBuffer(2, ExtSellArrowBuf, INDICATOR_DATA);
  SetIndexBuffer(3, ExtExitCrossBuf, INDICATOR_DATA);
  SetIndexBuffer(4, ExtMeanBuffer,    INDICATOR_CALCULATIONS);
  SetIndexBuffer(5, ExtStdDevBuffer,  INDICATOR_CALCULATIONS);
  SetIndexBuffer(6, ExtPriceBuffer,   INDICATOR_CALCULATIONS);
  PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
  PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
  PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
  PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);
  // Коды символов (Wingdings): 108 = ● точка входа, 251 = ● выход
  PlotIndexSetInteger(1, PLOT_ARROW, 108);
  PlotIndexSetInteger(2, PLOT_ARROW, 108);
  PlotIndexSetInteger(3, PLOT_ARROW, 251);
  // Цвета и размеры буферных сигналов (из настроек)
  PlotIndexSetInteger(1, PLOT_LINE_COLOR, InpBuyArrowColor);
  PlotIndexSetInteger(2, PLOT_LINE_COLOR, InpSellArrowColor);
  PlotIndexSetInteger(3, PLOT_LINE_COLOR, InpExitArrowColor);
  PlotIndexSetInteger(1, PLOT_LINE_WIDTH, InpArrowWidth);
  PlotIndexSetInteger(2, PLOT_LINE_WIDTH, InpArrowWidth);
  PlotIndexSetInteger(3, PLOT_LINE_WIDTH, 3);
  IndicatorSetString(INDICATOR_SHORTNAME, "Adaptive Mean Reversion Z-Score");
  ExtSubWindow = 0; // стрелки - в основное окно графика
  // Защита: при InpFastAlpha <= 0.01 среднее замораживается и индикатор не работает.
  if(InpFastAlpha <= 0.01) {
    ExtAlphaEffective = 0.3;
    Print("FlameChart_1: ВНИМАНИЕ! InpFastAlpha = ", InpFastAlpha,
          " - недопустимое значение. Используется 0.3. Поставьте 0.1-0.5 в настройках!");
  } else {
    ExtAlphaEffective = InpFastAlpha;
  }
  return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
  ObjectsDeleteAll(0, ARROW_PREFIX);
}

//+------------------------------------------------------------------+
//| 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[])
{
  bool first_run = (prev_calculated == 0);
  if(rates_total < InpPeriod + 1) return(0);
  // Окно расчёта: последние InpMaxBars баров (0 = вся история)
  int limit      = (InpMaxBars > 0) ? InpMaxBars : rates_total;
  int calc_start = rates_total - limit;   // первый бар окна
  if(calc_start < 1) calc_start = 1;
  int sig_start  = MathMax(calc_start, InpPeriod); // сигналы не раньше прогрева
  int start;
  if(first_run) {
    // Первый расчёт: только окно InpMaxBars, остальное пусто
    ArrayInitialize(ExtZScoreBuffer, EMPTY_VALUE);
    ArrayInitialize(ExtBuyArrowBuf,  EMPTY_VALUE);
    ArrayInitialize(ExtSellArrowBuf, EMPTY_VALUE);
    ArrayInitialize(ExtExitCrossBuf, EMPTY_VALUE);
    ObjectsDeleteAll(0, ARROW_PREFIX);
    ExtPosition = 0;
    ExtMeanBuffer[calc_start - 1] = close[calc_start - 1]; // затравка рекурсии
    start = calc_start;
  } else {
    // Инкрементально: только вновь закрытые бары (хвосты за окном остаются)
    start = prev_calculated - 1;
    if(start < calc_start) start = calc_start;
  }
  ExtBuyArrowBuf[rates_total-1]=EMPTY_VALUE;
  ExtSellArrowBuf[rates_total-1]=EMPTY_VALUE;
  ExtExitCrossBuf[rates_total-1]=EMPTY_VALUE;
  for(int i = start; i < rates_total; i++) {
    double price = close[i];
    ExtPriceBuffer[i] = price;
    // 1. Быстрое адаптивное среднее (динамический "ноль")
    ExtMeanBuffer[i] = ExtMeanBuffer[i-1] + ExtAlphaEffective * (price - ExtMeanBuffer[i-1]);
    // 2. Стандартное отклонение (Sigma)
    double sum_sq = 0.0;
    int count = 0;
    for(int j = 0; j < InpPeriod && (i - j) >= 0; j++) {
      double diff = close[i - j] - ExtMeanBuffer[i - j];
      sum_sq += diff * diff;
      count++;
    }
    ExtStdDevBuffer[i] = MathSqrt(sum_sq / (count > 0 ? count : 1));
    // 3. Z-Score
    if(ExtStdDevBuffer[i] > 0)
      ExtZScoreBuffer[i] = (price - ExtMeanBuffer[i]) / ExtStdDevBuffer[i];
    else
      ExtZScoreBuffer[i] = 0;
  }
  // 4. Стрелки и крестики (только закрытые бары окна)
  if(first_run) {
    for(int i = sig_start; i < rates_total - 1; i++)
      ProcessBar(i, time, high, low);
  } else {
    for(int i = MathMax(start, sig_start); i < rates_total - 1; i++)
      ProcessBar(i, time, high, low);
  }
  // ДИАГНОСТИКА при первом полном расчете (только в окне отрисовки)
  if(first_run) {
    double min_z =  DBL_MAX;
    double max_z = -DBL_MAX;
    int    green_count = 0;
    int    red_count   = 0;
    datetime last_green_time = 0;
    datetime last_red_time   = 0;
    for(int i = sig_start; i < rates_total; i++) {
      double z = ExtZScoreBuffer[i];
      if(z == EMPTY_VALUE) continue;
      if(z < min_z) min_z = z;
      if(z > max_z) max_z = z;
      if(z < -InpEntryDev) {
        green_count++;
        last_green_time = time[i];
      }
      if(z >  InpEntryDev) {
        red_count++;
        last_red_time = time[i];
      }
    }
  }
  return(rates_total);
}
//+------------------------------------------------------------------+
