//+------------------------------------------------------------------+
//|                              ADR_Continuation_TickEA_PANEL.mq4    |
//|  Tick EA derived from ADR_Continuation_Prob (original)            |
//|  Adds OnTimer() panel so you ALWAYS see activity                  |
//|  Shows live ratio, ref open, ADR, session state, and stats         |
//+------------------------------------------------------------------+
#property strict

//================= Inputs ===========================
enum AverageType { DAILY, WEEKLY };

input string      _hdr             = "<< SETTINGS >>";
input AverageType ChooseType       = DAILY;   // D1 o W1 como sesión de referencia
input int         DaysToAverage    = 5;       // ADR: nº de sesiones previas completas
input double      MinThreshold     = 0.50;    // 0.50 = 50% ADR
input double      MaxThreshold     = 0.75;    // 0.75 = 75% ADR (objetivo)
input int         LimitPeriods     = 0;       // 0=sin límite; >0 deja de contar tras N sesiones
input bool        ShowHourlyTop    = true;    // muestra top-5 horas por tasa de continuación
input int         HourlyMinSamples = 10;      // muestras mínimas/hora para aparecer en el top
input bool        AlertsOn         = false;   // alertas al tocar Min / confirmar Reversion / Continuation
input int         PanelRefreshSec  = 1;       // refresco panel (segundos)
input bool        DebugPrints      = true;    // escribe en Journal al cambiar de sesión o tocar Min/Max

//================= Internos =========================
int    RefTF = PERIOD_D1;

double Pip = 0.0;
int    gDays = 5;
double gMin  = 0.50;
double gMax  = 0.75;

// Estadística global
int Samples        = 0;
int Continuations  = 0;
int Reversals      = 0;
int PeriodsChecked = 0;

// Estadística por dirección
int SamplesUp = 0, SamplesDn = 0;
int ContUp    = 0, ContDn    = 0;
int RevUp     = 0, RevDn     = 0;

// Desglose horario
int HourSamples[24];
int HourCont[24];
int HourRev[24];

// Estado por sesión
bool     MinHit=false, MaxHit=false, OppositeMarked=false;
int      Dir=0;           // 1=up, 2=down
int      HitHour=-1;      // hora del primer toque
datetime CurrentRefTs=0;  // apertura del periodo (D1/W1) actual
double   RefOpen=0.0;
double   ADR=0.0;

// Live values
double   LivePrice=0.0;
double   LiveRatio=0.0;
double   LiveSigned=0.0;

//================= Helpers ==========================
void ResetSessionFlags()
{
   MinHit=false;
   MaxHit=false;
   OppositeMarked=false;
   Dir=0;
   HitHour=-1;
}

double ADR_Avg()
{
   double sum=0.0;
   int cnt=0;
   int total = iBars(Symbol(), RefTF);

   for(int k=1; k<=gDays; k++)
   {
      int idx = k;
      if(idx >= total) break;

      double hi = iHigh(Symbol(), RefTF, idx);
      double lo = iLow(Symbol(),  RefTF, idx);
      if(hi<=0.0 || lo<=0.0) continue;

      double rng = (hi-lo)/Pip;
      if(rng>0.0){ sum += rng; cnt++; }
   }
   if(cnt==0) return 0.0;
   return sum/(double)cnt;
}

void BuildTopHours(string &out)
{
   out = "";
   if(!ShowHourlyTop) return;

   int hours[24];
   double rates[24];
   int m=0;

   ArrayInitialize(hours, 0);
   ArrayInitialize(rates, 0.0);

   for(int h=0; h<24; h++)
   {
      if(HourSamples[h] >= HourlyMinSamples && HourSamples[h] > 0)
      {
         hours[m]=h;
         rates[m]=100.0*(double)HourCont[h]/(double)HourSamples[h];
         m++;
      }
   }

   if(m<=0)
   {
      out = "Top hours: no hour meets min samples ("+IntegerToString(HourlyMinSamples)+")";
      return;
   }

   int top = (m<5 ? m : 5);

   for(int a=0; a<top; a++)
   {
      int best=a;
      for(int b=a+1; b<m; b++)
         if(rates[b] > rates[best]) best=b;

      if(best!=a)
      {
         double tr=rates[a]; rates[a]=rates[best]; rates[best]=tr;
         int th=hours[a]; hours[a]=hours[best]; hours[best]=th;
      }
   }

   out = "Top hours by continuation (min "+IntegerToString(HourlyMinSamples)+" samples):";
   for(int k=0; k<top; k++)
   {
      int h2=hours[k];
      out += "  " + IntegerToString(h2) + "h=" + DoubleToString(rates[k],1) +
             "% (" + IntegerToString(HourCont[h2]) + "/" + IntegerToString(HourSamples[h2]) + ")";
   }
}

string StateString()
{
   if(!MinHit) return "Waiting Min";
   if(MaxHit) return "Continuation (MaxHit)";
   if(OppositeMarked) return "Reversion (back < Min)";
   return "MinHit (pending)";
}

string DirString()
{
   if(Dir==1) return "UP";
   if(Dir==2) return "DOWN";
   return "-";
}

void UpdatePanel()
{
   string sp="\n";
   string hdr = (RefTF==PERIOD_D1 ? "DAILY" : "WEEKLY");

   double pCont = (Samples>0) ? (100.0*(double)Continuations/(double)Samples) : 0.0;
   double pRev  = (Samples>0) ? (100.0*(double)Reversals/(double)Samples)      : 0.0;

   double pContUp = (SamplesUp>0) ? (100.0*(double)ContUp/(double)SamplesUp) : 0.0;
   double pContDn = (SamplesDn>0) ? (100.0*(double)ContDn/(double)SamplesDn) : 0.0;

   string s0 = "ADR Continuation TickEA (MT4)  Ref:"+hdr;
   string sA = "ADR N="+IntegerToString(gDays)+"  Min="+DoubleToString(gMin,2)+"  Max="+DoubleToString(gMax,2);
   string sB = "RefOpen="+DoubleToString(RefOpen,Digits)+"  ADR="+DoubleToString(ADR,1)+"p";
   string sC = "Price="+DoubleToString(LivePrice,Digits)+"  Ratio="+DoubleToString(LiveRatio,3)+"  Signed="+DoubleToString(LiveSigned,3);
   string sD = "State: "+StateString()+"  Dir: "+DirString()+"  HitHour: "+(HitHour>=0?IntegerToString(HitHour):"-");
   string s1 = "Samples="+IntegerToString(Samples)+"  Cont="+IntegerToString(Continuations)+"  Rev="+IntegerToString(Reversals);
   string s2 = "P(continue)="+DoubleToString(pCont,2)+"%   P(reversal)="+DoubleToString(pRev,2)+"%";
   string s3 = "Up: Samples="+IntegerToString(SamplesUp)+"  Cont="+IntegerToString(ContUp)+"  P="+DoubleToString(pContUp,2)+"%";
   string s4 = "Dn: Samples="+IntegerToString(SamplesDn)+"  Cont="+IntegerToString(ContDn)+"  P="+DoubleToString(pContDn,2)+"%";

   string sH="";
   if(ShowHourlyTop) BuildTopHours(sH);

   if(ShowHourlyTop)
      Comment(s0,sp,sA,sp,sB,sp,sC,sp,sD,sp,s1,sp,s2,sp,s3,sp,s4,sp,sH);
   else
      Comment(s0,sp,sA,sp,sB,sp,sC,sp,sD,sp,s1,sp,s2,sp,s3,sp,s4);
}

//================= Init/Deinit ======================
int OnInit()
{
   Pip = (Digits % 2 == 1) ? Point*10.0 : Point;

   RefTF = (ChooseType == WEEKLY) ? PERIOD_W1 : PERIOD_D1;

   gDays = (DaysToAverage < 1 ? 1 : DaysToAverage);
   gMin  = MinThreshold;
   gMax  = MaxThreshold;
   if(gMax < gMin){ double tmp=gMin; gMin=gMax; gMax=tmp; }

   for(int h=0; h<24; h++){ HourSamples[h]=0; HourCont[h]=0; HourRev[h]=0; }
   ResetSessionFlags();

   CurrentRefTs = iTime(Symbol(), RefTF, 0);
   RefOpen      = iOpen(Symbol(), RefTF, 0);
   ADR          = ADR_Avg();

   EventSetTimer(MathMax(1, PanelRefreshSec));

   if(DebugPrints)
      Print("INIT OK  RefTF=", (RefTF==PERIOD_D1?"D1":"W1"),
            " RefOpen=", DoubleToString(RefOpen,Digits),
            " ADR=", DoubleToString(ADR,1),
            " Min=", DoubleToString(gMin,2),
            " Max=", DoubleToString(gMax,2));

   UpdatePanel();
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   EventKillTimer();
   Comment("");
}

//================= Timer ============================
void OnTimer()
{
   if(RefOpen>0.0 && ADR>0.0)
   {
      LivePrice  = Bid;
      LiveRatio  = MathAbs((LivePrice-RefOpen)/Pip)/ADR;
      LiveSigned = ((LivePrice-RefOpen)/Pip)/ADR;
   }
   UpdatePanel();
}

//================= Core (ticks) =====================
void OnTick()
{
   if(LimitPeriods > 0 && PeriodsChecked >= LimitPeriods)
      return;

   datetime refTs = iTime(Symbol(), RefTF, 0);
   if(refTs == 0) return;

   if(refTs != CurrentRefTs)
   {
      CurrentRefTs = refTs;
      RefOpen = iOpen(Symbol(), RefTF, 0);
      ADR = ADR_Avg();
      ResetSessionFlags();
      PeriodsChecked++;

      if(DebugPrints)
         Print("NEW SESSION  ", (RefTF==PERIOD_D1?"DAY":"WEEK"),
               " RefOpen=", DoubleToString(RefOpen,Digits),
               " ADR=", DoubleToString(ADR,1), "p  PeriodsChecked=", PeriodsChecked);

      if(AlertsOn)
         Alert(Symbol(), " -> New ", (RefTF==PERIOD_D1 ? "day" : "week"),
               " open=", DoubleToString(RefOpen, Digits),
               " ADR=", DoubleToString(ADR,1), "p");
   }

   if(RefOpen<=0.0 || ADR<=0.0) return;

   LivePrice  = Bid;
   LiveRatio  = MathAbs((LivePrice-RefOpen)/Pip)/ADR;
   LiveSigned = ((LivePrice-RefOpen)/Pip)/ADR;

   if(!MinHit && LiveRatio >= gMin && LiveRatio < gMax)
   {
      MinHit = true;
      Dir = (LiveSigned>0.0) ? 1 : (LiveSigned<0.0 ? 2 : 0);
      HitHour = TimeHour(TimeCurrent());

      if(Dir != 0)
      {
         Samples++;
         if(Dir==1) SamplesUp++; else SamplesDn++;
         if(HitHour>=0 && HitHour<24) HourSamples[HitHour]++;
      }

      if(DebugPrints)
         Print("MIN HIT  Dir=", (Dir==1?"UP":(Dir==2?"DOWN":"-")),
               " Ratio=", DoubleToString(LiveRatio,3),
               " Price=", DoubleToString(LivePrice,Digits),
               " Hour=", HitHour);

      if(AlertsOn && Dir!=0)
         Alert(Symbol(), " -> Min hit ", (Dir==1?"UP":"DOWN"),
               " ratio=", DoubleToString(LiveRatio,2));
   }

   if(MinHit && !MaxHit && !OppositeMarked)
   {
      if(MathAbs(LiveSigned) < gMin)
      {
         Reversals++;
         if(Dir==1) RevUp++; else if(Dir==2) RevDn++;
         if(HitHour>=0 && HitHour<24) HourRev[HitHour]++;
         OppositeMarked = true;

         if(DebugPrints)
            Print("REVERSION  back < Min  Signed=", DoubleToString(LiveSigned,3),
                  " Price=", DoubleToString(LivePrice,Digits));

         if(AlertsOn) Alert(Symbol(), " -> REVERSION confirmed (back < Min).");
      }
   }

   if(MinHit && !MaxHit && Dir != 0)
   {
      if( (Dir==1 && LiveSigned >= gMax) ||
          (Dir==2 && LiveSigned <= -gMax) )
      {
         Continuations++;
         if(Dir==1) ContUp++; else ContDn++;
         if(HitHour>=0 && HitHour<24) HourCont[HitHour]++;
         MaxHit = true;

         if(DebugPrints)
            Print("CONTINUATION  >= Max  Signed=", DoubleToString(LiveSigned,3),
                  " Price=", DoubleToString(LivePrice,Digits));

         if(AlertsOn) Alert(Symbol(), " -> CONTINUATION confirmed (>= Max).");
      }
   }
}
//+------------------------------------------------------------------+
