﻿//+------------------------------------------------------------------+
//|                                         CustomChartGenerator.mq5 |
//|                                  Copyright 2026,MetaQuotes Ltd. |
//|                                           https://www.mql5.com   |
//+------------------------------------------------------------------+
#property copyright "MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrNONE

#include <CBarAggregator.mqh>

//+------------------------------------------------------------------+
//| типы поддерживаемых графиков                                     |
//+------------------------------------------------------------------+
enum ENUM_CUSTOM_CHART_MODE
  {
   CMT_RENKO=0,      // Ренко
   CMT_RANGE=1,      // Рейндж
   CMT_EQVOL_TICK=2, // Экви-объём тиковый
   CMT_EQVOL_REAL=3   // Экви-объём реальный
  };

//+------------------------------------------------------------------+
//| входные параметры                                                |
//+------------------------------------------------------------------+
input ENUM_CUSTOM_CHART_MODE InputMode=CMT_RENKO;      // режим генерации
input double                 InputBoxSize=100;            // размер бара (пункты) для renko/range
input long                   InputVolLimit=1000;           // лимит объема для equal-volume
input string                 InputSuffix="";             // суффикс имени символа
input datetime               InputStartTime=0;              // начало истории (0-последние 7 дней)

//+------------------------------------------------------------------+
//| глобальные переменные                                            |
//+------------------------------------------------------------------+
string   custom_symbol_name;
CBarAggregator aggregator;
ulong    last_tick_time_msc;
double   dummy_buffer[];

//+------------------------------------------------------------------+
//| инициализация программы                                          |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0,dummy_buffer,INDICATOR_DATA);
   PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_NONE);

   string mode_str="";
   double threshold=0;
   ENUM_VOLUME_MODE vol_mode=VOLUME_MODE_TICK;

   switch(InputMode)
     {
      case CMT_RENKO:
         mode_str="Renko";
         threshold=InputBoxSize;
         break;
      case CMT_RANGE:
         mode_str="Range";
         threshold=InputBoxSize;
         break;
      case CMT_EQVOL_TICK:
         mode_str="EqVolT";
         threshold=(double)InputVolLimit;
         vol_mode=VOLUME_MODE_TICK;
         break;
      case CMT_EQVOL_REAL:
         mode_str="EqVolR";
         threshold=(double)InputVolLimit;
         vol_mode=VOLUME_MODE_REAL;
         break;
     }

   string suffix=(InputSuffix=="") ? "" : "_"+InputSuffix;
   custom_symbol_name=_Symbol+"_"+mode_str+"_"+DoubleToString(threshold,0)+suffix;

   if(!CustomSymbolCreate(custom_symbol_name,"",_Symbol))
     {
      if(!SymbolInfoInteger(custom_symbol_name,SYMBOL_CUSTOM))
        {
         Print("ошибка создания символа: ",GetLastError());
         return INIT_FAILED;
        }
     }

   SymbolSelect(custom_symbol_name,true);
   CloneProperties(_Symbol,custom_symbol_name);

// принудительная очистка истории
   CustomRatesDelete(custom_symbol_name,0,LONG_MAX);
   CustomTicksDelete(custom_symbol_name,0,LONG_MAX);
   aggregator.Reset();

   int agg_type=0;
   if(InputMode==CMT_EQVOL_TICK || InputMode==CMT_EQVOL_REAL)
      agg_type=2;
   else
      agg_type=(int)InputMode;

   aggregator.Init(custom_symbol_name,threshold,agg_type,vol_mode);

   datetime start_time=InputStartTime;
   if(start_time==0)
      start_time=TimeCurrent()-7*24*60*60;

   Print("начало генерации истории от ",TimeToString(start_time));

   LoadHistory(start_time);
   aggregator.FlushBuffer();

   Print("генерация завершена. символ: ",custom_symbol_name);

   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| приход нового тика                                               |
//+------------------------------------------------------------------+
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[])
  {
   ProcessNewTicks();
   return rates_total;
  }

//+------------------------------------------------------------------+
//| загрузка истории                                                 |
//+------------------------------------------------------------------+
void LoadHistory(datetime start_time)
  {
   ulong cursor=(ulong)start_time*1000;
   ulong stop_cursor=(ulong)TimeCurrent()*1000;
   MqlTick ticks[];
   int copied;

   Print("Загрузка истории порциями от ",TimeToString(start_time));

   while(cursor<stop_cursor && !IsStopped())
     {
      ulong next_cursor=cursor+(ulong)PeriodSeconds(PERIOD_D1)*1000;
      if(next_cursor>stop_cursor)
         next_cursor=stop_cursor;

      copied=CopyTicksRange(_Symbol,ticks,COPY_TICKS_ALL,cursor,next_cursor);

      if(copied>0)
        {
         for(int i=0;i<copied;i++)
            aggregator.ProcessTick(ticks[i]);

         cursor=ticks[copied-1].time_msc+1;
        }
      else
        {
         cursor=next_cursor;
        }

      Sleep(10);
     }

   last_tick_time_msc=cursor;
   Print("Загрузка завершена.");
  }

//+------------------------------------------------------------------+
//| обработка нового тика                                            |
//+------------------------------------------------------------------+
void ProcessNewTicks()
  {
   if(last_tick_time_msc==0)
      return;

   MqlTick ticks[];
   int copied=CopyTicksRange(_Symbol,ticks,COPY_TICKS_ALL,last_tick_time_msc,0);

   if(copied>0)
     {
      for(int i=0;i<copied;i++)
        {
         aggregator.ProcessTick(ticks[i]);
         last_tick_time_msc=ticks[i].time_msc;
        }
      aggregator.FlushBuffer();
     }
  }

//+------------------------------------------------------------------+
//| клонирование свойств из реального символа                        |
//+------------------------------------------------------------------+
void CloneProperties(const string src,const string dst)
  {
   ENUM_SYMBOL_INFO_DOUBLE dbl_props[]= {SYMBOL_POINT,SYMBOL_TRADE_TICK_VALUE,SYMBOL_TRADE_CONTRACT_SIZE};
   for(int i=0;i<ArraySize(dbl_props);i++)
      CustomSymbolSetDouble(dst,dbl_props[i],SymbolInfoDouble(src,dbl_props[i]));

   ENUM_SYMBOL_INFO_INTEGER int_props[]= {SYMBOL_DIGITS,SYMBOL_TRADE_CALC_MODE,SYMBOL_TRADE_EXEMODE,SYMBOL_TRADE_STOPS_LEVEL};
   for(int i=0;i<ArraySize(int_props);i++)
      CustomSymbolSetInteger(dst,int_props[i],SymbolInfoInteger(src,int_props[i]));

   ENUM_SYMBOL_INFO_STRING str_props[]= {SYMBOL_CURRENCY_BASE,SYMBOL_CURRENCY_PROFIT,SYMBOL_CURRENCY_MARGIN,SYMBOL_DESCRIPTION};
   for(int i=0;i<ArraySize(str_props);i++)
      CustomSymbolSetString(dst,str_props[i],SymbolInfoString(src,str_props[i]));
  }
//+------------------------------------------------------------------+
