#property strict
#property copyright "Copyright 2026, Trade-Forex.info"
#property link      "https://trade-forex.info/"
#property version   "1.00"
#property description "Causal trend/range/shock regime diagnostics with two-bar hysteresis."
#property indicator_separate_window
#property indicator_minimum 0.0
#property indicator_maximum 1.0
#property indicator_buffers 9
#property indicator_plots   7

#property indicator_label1  "Regime band"
#property indicator_type1   DRAW_COLOR_HISTOGRAM2
#property indicator_color1  clrLimeGreen,clrDeepSkyBlue,clrGold,clrLightCoral
#property indicator_width1  5

#property indicator_label2  "Regime code (1 Trend, 2 Range, 3 Transition, 4 Shock)"
#property indicator_type2   DRAW_NONE
#property indicator_label3  "Median efficiency ratio"
#property indicator_type3   DRAW_NONE
#property indicator_label4  "Median sign entropy"
#property indicator_type4   DRAW_NONE
#property indicator_label5  "Volatility percentile"
#property indicator_type5   DRAW_NONE
#property indicator_label6  "Range expansion"
#property indicator_type6   DRAW_NONE
#property indicator_label7  "Raw regime code"
#property indicator_type7   DRAW_NONE

#include <TF_Indicators/Theme.mqh>
#include <TF_Indicators/RobustStatistics.mqh>
#include <TF_Indicators/RegimeMath.mqh>
#include <TF_Indicators/ChartObjects.mqh>
#include <TF_Indicators/AuditLog.mqh>

input int  InpMaxBars=5000;                  // Maximum bars to calculate (300..100000)
input bool InpShowDashboard=true;            // Show compact diagnostics panel
input bool InpEnableRegimeAlert=false;       // Alert on confirmed closed-bar transition
input bool InpDebugLog=false;                // Log history resets and data states

double BandLow[];
double BandHigh[];
double BandColor[];
double RegimeCode[];
double EfficiencyMedian[];
double EntropyMedian[];
double VolatilityPercentile[];
double RangeExpansion[];
double RawRegime[];

string g_prefix;
datetime g_last_alert_bar=0;
int g_last_alert_state=0;

string TF_RegimeName(const int state)
  {
   if(state==TF_REGIME_TREND) return("TREND");
   if(state==TF_REGIME_RANGE) return("RANGE");
   if(state==TF_REGIME_SHOCK) return("SHOCK");
   return("TRANSITION");
  }

color TF_RegimeColor(const int state)
  {
   if(state==TF_REGIME_TREND) return(TF_ColorTrend());
   if(state==TF_REGIME_RANGE) return(TF_ColorRange());
   if(state==TF_REGIME_SHOCK) return(TF_ColorShock());
   return(TF_ColorTransition());
  }

void TF_ClearBar(const int index)
  {
   BandLow[index]=EMPTY_VALUE;
   BandHigh[index]=EMPTY_VALUE;
   BandColor[index]=0.0;
   RegimeCode[index]=EMPTY_VALUE;
   EfficiencyMedian[index]=EMPTY_VALUE;
   EntropyMedian[index]=EMPTY_VALUE;
   VolatilityPercentile[index]=EMPTY_VALUE;
   RangeExpansion[index]=EMPTY_VALUE;
   RawRegime[index]=EMPTY_VALUE;
  }

void TF_DrawDashboard()
  {
   if(!InpShowDashboard || ArraySize(RegimeCode)<2 || RegimeCode[0]==EMPTY_VALUE) return;
   bool dark=TF_IsDarkChart(0);
   color text_color=TF_ColorText(dark);
   color background=TF_ColorBackground(dark);
   int state=(int)RegimeCode[0];
   TF_UpsertRectangleLabel(0,g_prefix+"PANEL",12,18,310,112,background,TF_RegimeColor(state));
   TF_UpsertLabel(0,g_prefix+"TITLE","TF Regime Persistence Map  v"+TF_SUITE_VERSION,24,27,text_color,10);
   TF_UpsertLabel(0,g_prefix+"STATE","PRELIMINARY: "+TF_RegimeName(state),24,50,TF_RegimeColor(state),10);
   TF_UpsertLabel(0,g_prefix+"METRICS1",StringFormat("ER %.3f   Entropy %.3f",EfficiencyMedian[0],EntropyMedian[0]),24,74,text_color,9);
   TF_UpsertLabel(0,g_prefix+"METRICS2",StringFormat("Vol pct %.1f   Expansion %.2f",VolatilityPercentile[0],RangeExpansion[0]),24,92,text_color,9);
   ChartRedraw(0);
  }

int OnInit()
  {
   if(InpMaxBars<300 || InpMaxBars>100000)
     {
      Print("TF Regime Persistence Map: InpMaxBars must be between 300 and 100000.");
      return(INIT_PARAMETERS_INCORRECT);
     }
   SetIndexBuffer(0,BandLow,INDICATOR_DATA);
   SetIndexBuffer(1,BandHigh,INDICATOR_DATA);
   SetIndexBuffer(2,BandColor,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(3,RegimeCode,INDICATOR_DATA);
   SetIndexBuffer(4,EfficiencyMedian,INDICATOR_DATA);
   SetIndexBuffer(5,EntropyMedian,INDICATOR_DATA);
   SetIndexBuffer(6,VolatilityPercentile,INDICATOR_DATA);
   SetIndexBuffer(7,RangeExpansion,INDICATOR_DATA);
   SetIndexBuffer(8,RawRegime,INDICATOR_DATA);
   ArraySetAsSeries(BandLow,true);
   ArraySetAsSeries(BandHigh,true);
   ArraySetAsSeries(BandColor,true);
   ArraySetAsSeries(RegimeCode,true);
   ArraySetAsSeries(EfficiencyMedian,true);
   ArraySetAsSeries(EntropyMedian,true);
   ArraySetAsSeries(VolatilityPercentile,true);
   ArraySetAsSeries(RangeExpansion,true);
   ArraySetAsSeries(RawRegime,true);
   for(int plot=0;plot<7;plot++) PlotIndexSetDouble(plot,PLOT_EMPTY_VALUE,EMPTY_VALUE);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,273);
   IndicatorSetString(INDICATOR_SHORTNAME,"TF Regime Persistence Map v"+TF_SUITE_VERSION);
   IndicatorSetInteger(INDICATOR_DIGITS,4);
   g_prefix=StringFormat("TF_RPM_%s_%I64d_%I64u_",TF_SUITE_VERSION,ChartID(),GetMicrosecondCount());
   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[])
  {
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(tick_volume,true);
   ArraySetAsSeries(volume,true);
   ArraySetAsSeries(spread,true);

   const int required=273;
   if(rates_total<required)
     {
      TF_Audit(InpDebugLog,"Regime","insufficient history: "+IntegerToString(rates_total)+" / "+IntegerToString(required));
      return(0);
     }
   if(prev_calculated==0)
     {
      TF_AuditHistoryReset(InpDebugLog,"Regime",rates_total);
      ArrayInitialize(BandLow,EMPTY_VALUE);
      ArrayInitialize(BandHigh,EMPTY_VALUE);
      ArrayInitialize(RegimeCode,EMPTY_VALUE);
      ArrayInitialize(EfficiencyMedian,EMPTY_VALUE);
      ArrayInitialize(EntropyMedian,EMPTY_VALUE);
      ArrayInitialize(VolatilityPercentile,EMPTY_VALUE);
      ArrayInitialize(RangeExpansion,EMPTY_VALUE);
      ArrayInitialize(RawRegime,EMPTY_VALUE);
     }

   int oldest=MathMin(rates_total-required,InpMaxBars-1);
   int start=(prev_calculated==0 ? oldest : MathMin(1,oldest));
   for(int i=start;i>=0 && !IsStopped();i--)
     {
      double e20=TF_EfficiencyRatio(close,i,20);
      double e50=TF_EfficiencyRatio(close,i,50);
      double e100=TF_EfficiencyRatio(close,i,100);
      double h20=TF_SignEntropy(close,i,20);
      double h50=TF_SignEntropy(close,i,50);
      double h100=TF_SignEntropy(close,i,100);
      double efficiency=TF_Median3(e20,e50,e100);
      double entropy=TF_Median3(h20,h50,h100);
      double vol_percentile=TF_VolatilityRank(close,i);
      double expansion=TF_RangeExpansion(high,low,close,i);
      int raw=TF_Classify(efficiency,entropy,vol_percentile,expansion);
      int state=raw;
      if(i<oldest && RegimeCode[i+1]!=EMPTY_VALUE)
        {
         int previous_state=(int)RegimeCode[i+1];
         if(raw!=TF_REGIME_SHOCK && raw!=previous_state)
           {
            int previous_raw=(RawRegime[i+1]==EMPTY_VALUE ? previous_state : (int)RawRegime[i+1]);
            if(previous_raw!=raw) state=previous_state;
           }
        }
      BandLow[i]=0.0;
      BandHigh[i]=1.0;
      BandColor[i]=(double)(state-1);
      RegimeCode[i]=(double)state;
      EfficiencyMedian[i]=efficiency;
      EntropyMedian[i]=entropy;
      VolatilityPercentile[i]=vol_percentile;
      RangeExpansion[i]=expansion;
      RawRegime[i]=(double)raw;
     }

   TF_DrawDashboard();
   if(InpEnableRegimeAlert && rates_total>1 && RegimeCode[1]!=EMPTY_VALUE && time[1]!=g_last_alert_bar)
     {
      int confirmed=(int)RegimeCode[1];
      if(g_last_alert_state!=0 && confirmed!=g_last_alert_state)
         Alert(_Symbol," ",EnumToString((ENUM_TIMEFRAMES)_Period),": confirmed regime ",TF_RegimeName(confirmed));
      g_last_alert_state=confirmed;
      g_last_alert_bar=time[1];
     }
   return(rates_total);
  }

void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_CHART_CHANGE) TF_DrawDashboard();
  }

void OnDeinit(const int reason)
  {
   TF_DeleteObjectsByPrefix(0,g_prefix);
   ChartRedraw(0);
  }
