// Read-only virtual execution audit for confirmed SMA crosses.
// Fills use the next bar open; costs are a fixed estimate, not history.
// This is a simulation, NOT a Strategy Tester result or a trade signal.
#property strict
#property version "1.00"
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots 6
#property indicator_type1 DRAW_LINE
#property indicator_type2 DRAW_LINE
#property indicator_type3 DRAW_LINE
#property indicator_type4 DRAW_LINE
#property indicator_type5 DRAW_ARROW
#property indicator_type6 DRAW_ARROW
#property indicator_color1 clrLime
#property indicator_color2 clrDeepSkyBlue
#property indicator_color3 clrSilver
#property indicator_color4 clrOrangeRed
#property indicator_color5 clrDeepSkyBlue
#property indicator_color6 clrHotPink
#property indicator_width1 2
#property indicator_width2 1
#property indicator_width3 1
#property indicator_width4 1
#property indicator_width5 1
#property indicator_width6 1
#property indicator_label1 "Next-open net equity"
#property indicator_label2 "Next-open net realized balance"
#property indicator_label3 "Next-open gross realized balance"
#property indicator_label4 "Signal-close net balance (comparison)"
#property indicator_label5 "Up virtual fill"
#property indicator_label6 "Down virtual fill"

input int InpFastPeriod=10;
input int InpSlowPeriod=20;
input int InpLookback=1000; // Closed bars to audit
input double InpRoundTurnCostPoints=20.0; // Fixed estimate per completed leg

double NetEquity[];
double NetBalance[];
double GrossBalance[];
double SignalCloseNet[];
double UpFill[];
double DownFill[];
int fast_handle=INVALID_HANDLE;
int slow_handle=INVALID_HANDLE;
datetime last_bar=0;

int OnInit()
{
   if(InpFastPeriod<1 || InpSlowPeriod<=InpFastPeriod || InpSlowPeriod>10000 ||
      InpLookback<50 || InpLookback>10000 ||
      !MathIsValidNumber(InpRoundTurnCostPoints) ||
      InpRoundTurnCostPoints<0.0 || InpRoundTurnCostPoints>100000.0)
      return INIT_PARAMETERS_INCORRECT;

   if(!SetIndexBuffer(0,NetEquity,INDICATOR_DATA) ||
      !SetIndexBuffer(1,NetBalance,INDICATOR_DATA) ||
      !SetIndexBuffer(2,GrossBalance,INDICATOR_DATA) ||
      !SetIndexBuffer(3,SignalCloseNet,INDICATOR_DATA) ||
      !SetIndexBuffer(4,UpFill,INDICATOR_DATA) ||
      !SetIndexBuffer(5,DownFill,INDICATOR_DATA))
      return INIT_FAILED;

   ArraySetAsSeries(NetEquity,true);
   ArraySetAsSeries(NetBalance,true);
   ArraySetAsSeries(GrossBalance,true);
   ArraySetAsSeries(SignalCloseNet,true);
   ArraySetAsSeries(UpFill,true);
   ArraySetAsSeries(DownFill,true);
   for(int plot=0;plot<6;plot++)
      PlotIndexSetDouble(plot,PLOT_EMPTY_VALUE,EMPTY_VALUE);
   PlotIndexSetInteger(2,PLOT_LINE_STYLE,STYLE_DASH);
   PlotIndexSetInteger(3,PLOT_LINE_STYLE,STYLE_DOT);
   PlotIndexSetInteger(4,PLOT_ARROW,233);
   PlotIndexSetInteger(5,PLOT_ARROW,234);
   IndicatorSetInteger(INDICATOR_DIGITS,2);
   IndicatorSetString(INDICATOR_SHORTNAME,StringFormat(
      "CrossMA Virtual Audit (%d,%d) SIMULATION - NOT STRATEGY TESTER",
      InpFastPeriod,InpSlowPeriod));

   fast_handle=iMA(_Symbol,_Period,InpFastPeriod,0,MODE_SMA,PRICE_CLOSE);
   slow_handle=iMA(_Symbol,_Period,InpSlowPeriod,0,MODE_SMA,PRICE_CLOSE);
   if(fast_handle==INVALID_HANDLE || slow_handle==INVALID_HANDLE)
      return INIT_FAILED;
   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   if(fast_handle!=INVALID_HANDLE)
      IndicatorRelease(fast_handle);
   if(slow_handle!=INVALID_HANDLE)
      IndicatorRelease(slow_handle);
}

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(close,true);
   const bool reset=(prev_calculated<=0 || prev_calculated>rates_total);
   if(reset)
   {
      ArrayInitialize(NetEquity,EMPTY_VALUE);
      ArrayInitialize(NetBalance,EMPTY_VALUE);
      ArrayInitialize(GrossBalance,EMPTY_VALUE);
      ArrayInitialize(SignalCloseNet,EMPTY_VALUE);
      ArrayInitialize(UpFill,EMPTY_VALUE);
      ArrayInitialize(DownFill,EMPTY_VALUE);
      last_bar=0;
   }
   if(rates_total<InpSlowPeriod+3)
      return 0;

   const int limit=MathMin(InpLookback,rates_total-InpSlowPeriod-2);
   if(limit<1)
      return 0;
   const int ma_count=limit+3;
   if(BarsCalculated(fast_handle)<ma_count || BarsCalculated(slow_handle)<ma_count)
      return prev_calculated;

   double fast[];
   double slow[];
   ArraySetAsSeries(fast,true);
   ArraySetAsSeries(slow,true);
   if(CopyBuffer(fast_handle,0,0,ma_count,fast)!=ma_count ||
      CopyBuffer(slow_handle,0,0,ma_count,slow)!=ma_count)
      return prev_calculated;
   for(int i=0;i<ma_count;i++)
   {
      if(fast[i]==EMPTY_VALUE || slow[i]==EMPTY_VALUE ||
         !MathIsValidNumber(fast[i]) || !MathIsValidNumber(slow[i]))
         return prev_calculated;
   }
   for(int i=0;i<=limit+1;i++)
   {
      if(open[i]<=0.0 || close[i]<=0.0 ||
         !MathIsValidNumber(open[i]) || !MathIsValidNumber(close[i]))
         return prev_calculated;
   }
   if(!reset && last_bar==time[0])
      return rates_total;

   NetEquity[0]=EMPTY_VALUE;
   NetBalance[0]=EMPTY_VALUE;
   GrossBalance[0]=EMPTY_VALUE;
   SignalCloseNet[0]=EMPTY_VALUE;
   UpFill[0]=EMPTY_VALUE;
   DownFill[0]=EMPTY_VALUE;
   for(int i=limit+1;i<rates_total;i++)
   {
      NetEquity[i]=EMPTY_VALUE;
      NetBalance[i]=EMPTY_VALUE;
      GrossBalance[i]=EMPTY_VALUE;
      SignalCloseNet[i]=EMPTY_VALUE;
      UpFill[i]=EMPTY_VALUE;
      DownFill[i]=EMPTY_VALUE;
   }

   int position_dir=0;
   int completed_legs=0;
   double next_open_pos_price=0.0;
   double signal_close_pos_price=0.0;
   double gross_balance=0.0;
   double signal_close_gross_balance=0.0;

   // Process oldest to newest. Bar i executes a signal confirmed on bar i+1.
   for(int i=limit;i>=1;i--)
   {
      const bool cross_up=(fast[i+1]>slow[i+1] && fast[i+2]<slow[i+2]);
      const bool cross_down=(fast[i+1]<slow[i+1] && fast[i+2]>slow[i+2]);
      const int signal_dir=cross_up ? 1 : (cross_down ? -1 : 0);
      UpFill[i]=EMPTY_VALUE;
      DownFill[i]=EMPTY_VALUE;

      if(signal_dir!=0 && signal_dir!=position_dir)
      {
         if(position_dir!=0)
         {
            gross_balance+=position_dir*(open[i]-next_open_pos_price)/_Point;
            signal_close_gross_balance+=
               position_dir*(close[i+1]-signal_close_pos_price)/_Point;
            completed_legs++;
         }
         position_dir=signal_dir;
         next_open_pos_price=open[i];
         signal_close_pos_price=close[i+1];
         const double marker=gross_balance-
            completed_legs*InpRoundTurnCostPoints;
         if(cross_up)
            UpFill[i]=marker;
         else
            DownFill[i]=marker;
      }

      if(position_dir==0)
      {
         NetEquity[i]=EMPTY_VALUE;
         NetBalance[i]=EMPTY_VALUE;
         GrossBalance[i]=EMPTY_VALUE;
         SignalCloseNet[i]=EMPTY_VALUE;
         continue;
      }

      const double net_balance=gross_balance-
         completed_legs*InpRoundTurnCostPoints;
      const double signal_close_net=signal_close_gross_balance-
         completed_legs*InpRoundTurnCostPoints;
      const double open_leg_points=position_dir*(close[i]-next_open_pos_price)/_Point;
      NetEquity[i]=net_balance+open_leg_points-InpRoundTurnCostPoints;
      NetBalance[i]=net_balance;
      GrossBalance[i]=gross_balance;
      SignalCloseNet[i]=signal_close_net;
   }
   last_bar=time[0];
   return rates_total;
}
