//+------------------------------------------------------------------+
//|                                                Arrows Binary v1  |
//+------------------------------------------------------------------+

#property version   "1.00"
#property strict

#property indicator_chart_window
#property indicator_buffers 3
#property indicator_color1 Red
#property indicator_color2 Lime

// Input parameters
extern int    RiskLevel             = 2;
extern double ArrowSpacing          = 1.0;
extern int    MaxBarsBackToShow     = 2000;
extern bool   UseTradingTimeFilter  = false;
extern int    TradingStartHour      = 6;
extern int    TradingEndHour        = 20;
extern bool   UseMTFMAFilter        = false;
extern int    MTFMA_Timeframe       = PERIOD_CURRENT;
extern int    MTFMA_Period          = 50;
extern int    MTFMA_Shift           = 0;
extern int    MTFMA_Method          = MODE_SMA;
extern int    MTFMA_AppliedPrice    = PRICE_CLOSE;

extern bool   EnablePopup           = true;
extern bool   EnableSound           = true;
extern bool   EnablePush            = false;
extern string AlertSoundName        = "alert.wav";

extern bool   ShowSimulationPanel   = true;
extern int    PanelCorner           = 3;
extern int    PanelXDistance        = 10;
extern int    PanelYDistance        = 10;
extern int    PanelFontSize         = 10;
extern color  PanelTextColor        = White;

extern int    ExpiryBars            = 1;

// Indicator buffers
double sellSignalBuffer[];
double buySignalBuffer[];
double wprBuffer[];

// Globals
double upperThreshold, lowerThreshold;
datetime lastAlertTimeUp  = 0;
datetime lastAlertTimeDown= 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   IndicatorBuffers(3);
   SetIndexBuffer(0, sellSignalBuffer);
   SetIndexStyle(0, DRAW_ARROW);
   SetIndexArrow(0, 234);

   SetIndexBuffer(1, buySignalBuffer);
   SetIndexStyle(1, DRAW_ARROW);
   SetIndexArrow(1, 233);

   SetIndexBuffer(2, wprBuffer);

   ArraySetAsSeries(sellSignalBuffer, true);
   ArraySetAsSeries(buySignalBuffer, true);
   ArraySetAsSeries(wprBuffer, true);
   ArrayInitialize(sellSignalBuffer, EMPTY_VALUE);
   ArrayInitialize(buySignalBuffer, EMPTY_VALUE);

   return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
   ObjectsDeleteAll(0, "BO_");
   return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   int countedBars = IndicatorCounted();
   if(countedBars < 0) return(-1);
   if(countedBars > 0) countedBars--;

   int barsToProcess = Bars - countedBars;
   barsToProcess = MathMin(barsToProcess, MaxBarsBackToShow);

   upperThreshold = 67.0 + RiskLevel;
   lowerThreshold = 33.0 - RiskLevel;

   for(int i = barsToProcess; i >= 1; i--)
      ProcessBar(i);

   // Alerts
   int shift = 1;
   if(buySignalBuffer[shift] != EMPTY_VALUE && Time[shift] != lastAlertTimeUp)
   {
      lastAlertTimeUp = Time[shift];
      SendAlerts("UP arrow");
   }
   if(sellSignalBuffer[shift] != EMPTY_VALUE && Time[shift] != lastAlertTimeDown)
   {
      lastAlertTimeDown = Time[shift];
      SendAlerts("DOWN arrow");
   }

   // Simulation stats
   int totalWins=0, totalLosses=0;
   int currWin=0, currLoss=0, maxWin=0, maxLoss=0;

   for(int x = Bars-1; x >= ExpiryBars; x--)
   {
      bool isWin=false, isLoss=false;
      if(sellSignalBuffer[x] != EMPTY_VALUE)
      {
         double entry = Close[x], exit = Close[x-ExpiryBars];
         if(exit < entry) isWin = true; else isLoss = true;
      }
      else if(buySignalBuffer[x] != EMPTY_VALUE)
      {
         double entry = Close[x], exit = Close[x-ExpiryBars];
         if(exit > entry) isWin = true; else isLoss = true;
      }
      if(isWin)
      {
         totalWins++; currWin++; currLoss = 0;
         if(currWin > maxWin) maxWin = currWin;
      }
      else if(isLoss)
      {
         totalLosses++; currLoss++; currWin = 0;
         if(currLoss > maxLoss) maxLoss = currLoss;
      }
   }

   if(ShowSimulationPanel)
      DrawPanel(totalWins, totalLosses, maxWin, maxLoss, ExpiryBars);
   else
      ObjectDelete("BO_BG");

   return(0);
}

//+------------------------------------------------------------------+
//| Process individual bar                                           |
//+------------------------------------------------------------------+
void ProcessBar(int idx)
{
   if(UseTradingTimeFilter)
   {
      int hr = TimeHour(Time[idx]);
      if(hr < TradingStartHour || hr >= TradingEndHour)
      {
         sellSignalBuffer[idx] = EMPTY_VALUE;
         buySignalBuffer[idx]  = EMPTY_VALUE;
         return;
      }
   }

   double avgRange = CalculateAverageRange(idx, 10);
   int wprPer = DetermineWPRPeriod(idx, avgRange);
   wprBuffer[idx] = NormalizeWPR(iWPR(NULL,0,wprPer,idx));

   sellSignalBuffer[idx] = EMPTY_VALUE;
   buySignalBuffer[idx]  = EMPTY_VALUE;

   double mtfMA = 0;
   if(UseMTFMAFilter)
      mtfMA = iMA(NULL, MTFMA_Timeframe, MTFMA_Period, MTFMA_Shift, MTFMA_Method, MTFMA_AppliedPrice, idx);

   if(wprBuffer[idx] < lowerThreshold)
   {
      if(!UseMTFMAFilter || Close[idx] < mtfMA)
         CheckForSignalCross(idx, upperThreshold, sellSignalBuffer, High[idx] + avgRange*ArrowSpacing);
   }
   if(wprBuffer[idx] > upperThreshold)
   {
      if(!UseMTFMAFilter || Close[idx] > mtfMA)
         CheckForSignalCross(idx, lowerThreshold, buySignalBuffer, Low[idx] - avgRange*ArrowSpacing);
   }
}

//+------------------------------------------------------------------+
//| Draw stats panel                                                 |
//+------------------------------------------------------------------+
void DrawPanel(int wins,int losses,int maxWinStreak,int maxLossStreak,int expiry)
{
   string pfx = "BO_";
   int x=PanelXDistance, y=PanelYDistance, w=190, h=125;
   color bg=clrDarkGray, border=clrSilver, txt=PanelTextColor;
   int fs=PanelFontSize;
   
   ObjectsDeleteAll(0,pfx);
   ObjectCreate(0,pfx+"BG",OBJ_RECTANGLE_LABEL,0,0,0);
   ObjectSetInteger(0,pfx+"BG",OBJPROP_XDISTANCE,x);
   ObjectSetInteger(0,pfx+"BG",OBJPROP_YDISTANCE,y);
   ObjectSetInteger(0,pfx+"BG",OBJPROP_XSIZE,w);
   ObjectSetInteger(0,pfx+"BG",OBJPROP_YSIZE,h);
   ObjectSetInteger(0,pfx+"BG",OBJPROP_BGCOLOR,bg);
   ObjectSetInteger(0,pfx+"BG",OBJPROP_BORDER_COLOR,border);
   ObjectSetInteger(0,pfx+"BG",OBJPROP_CORNER,PanelCorner);

   ObjectCreate(0,pfx+"Title",OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,pfx+"Title",OBJPROP_XDISTANCE,x+5);
   ObjectSetInteger(0,pfx+"Title",OBJPROP_YDISTANCE,y+5);
   ObjectSetString(0,pfx+"Title",OBJPROP_TEXT,"Arrows Binary v1");
   ObjectSetInteger(0,pfx+"Title",OBJPROP_COLOR,txt);
   ObjectSetInteger(0,pfx+"Title",OBJPROP_FONTSIZE,fs+2);

   ObjectCreate(0,pfx+"Wins",OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,pfx+"Wins",OBJPROP_XDISTANCE,x+5);
   ObjectSetInteger(0,pfx+"Wins",OBJPROP_YDISTANCE,y+25);
   ObjectSetString(0,pfx+"Wins",OBJPROP_TEXT,"Wins: "+IntegerToString(wins));
   ObjectSetInteger(0,pfx+"Wins",OBJPROP_COLOR,clrLime);
   ObjectSetInteger(0,pfx+"Wins",OBJPROP_FONTSIZE,fs);

   ObjectCreate(0,pfx+"Losses",OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,pfx+"Losses",OBJPROP_XDISTANCE,x+5);
   ObjectSetInteger(0,pfx+"Losses",OBJPROP_YDISTANCE,y+40);
   ObjectSetString(0,pfx+"Losses",OBJPROP_TEXT,"Losses: "+IntegerToString(losses));
   ObjectSetInteger(0,pfx+"Losses",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,pfx+"Losses",OBJPROP_FONTSIZE,fs);

   double total=wins+losses;
   double rate= total>0 ? wins/total*100 : 0;
   ObjectCreate(0,pfx+"WinRate",OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,pfx+"WinRate",OBJPROP_XDISTANCE,x+5);
   ObjectSetInteger(0,pfx+"WinRate",OBJPROP_YDISTANCE,y+55);
   ObjectSetString(0,pfx+"WinRate",OBJPROP_TEXT,StringFormat("Win Rate: %.2f%%",rate));
   ObjectSetInteger(0,pfx+"WinRate",OBJPROP_COLOR,txt);
   ObjectSetInteger(0,pfx+"WinRate",OBJPROP_FONTSIZE,fs);

   ObjectCreate(0,pfx+"Streaks",OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,pfx+"Streaks",OBJPROP_XDISTANCE,x+5);
   ObjectSetInteger(0,pfx+"Streaks",OBJPROP_YDISTANCE,y+70);
   ObjectSetString(0,pfx+"Streaks",OBJPROP_TEXT,"Max Wins: "+IntegerToString(maxWinStreak)+" | Max Losses: "+IntegerToString(maxLossStreak));
   ObjectSetInteger(0,pfx+"Streaks",OBJPROP_COLOR,txt);
   ObjectSetInteger(0,pfx+"Streaks",OBJPROP_FONTSIZE,fs);

   ObjectCreate(0,pfx+"Expiry",OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,pfx+"Expiry",OBJPROP_XDISTANCE,x+5);
   ObjectSetInteger(0,pfx+"Expiry",OBJPROP_YDISTANCE,y+85);
   ObjectSetString(0,pfx+"Expiry",OBJPROP_TEXT,"Expiry Bars: "+IntegerToString(expiry));
   ObjectSetInteger(0,pfx+"Expiry",OBJPROP_COLOR,txt);
   ObjectSetInteger(0,pfx+"Expiry",OBJPROP_FONTSIZE,fs);
}

//+------------------------------------------------------------------+
//| Alerts sending                                                   |
//+------------------------------------------------------------------+
void SendAlerts(string signal)
{
   string tf = TimeFrameToString(Period());
   string msg = "Arrows Binary v1: "+ Symbol()+" ["+tf+"] "+signal+" detected.";
   if(EnablePopup) Alert(msg);
   if(EnableSound) PlaySound(AlertSoundName);
   if(EnablePush)  SendNotification(msg);
}

//+------------------------------------------------------------------+
//| Convert timeframe to string                                      |
//+------------------------------------------------------------------+
string TimeFrameToString(int p)
{
   switch(p)
   {
      case PERIOD_M1:  return "M1";
      case PERIOD_M5:  return "M5";
      case PERIOD_M15: return "M15";
      case PERIOD_M30: return "M30";
      case PERIOD_H1:  return "H1";
      case PERIOD_H4:  return "H4";
      case PERIOD_D1:  return "D1";
      case PERIOD_W1:  return "W1";
      case PERIOD_MN1: return "MN1";
      default:         return "Unknown";
   }
}

//+------------------------------------------------------------------+
//| Helper functions (range, WPR period, volatile/trend checks, etc.)|
//+------------------------------------------------------------------+
double CalculateAverageRange(int idx,int per)
{
   double sum=0;
   for(int i=0;i<per;i++) if(idx+i<Bars) sum+=High[idx+i]-Low[idx+i];
   return sum/per;
}

int DetermineWPRPeriod(int idx,double avg)
{
   int per=RiskLevel*2+3;
   if(CheckVolatileCandles(idx,6,avg)) per=3;
   if(CheckStrongTrend(idx,9,avg)) per=4;
   return per;
}

bool CheckVolatileCandles(int idx,int lb,double avg)
{
   for(int i=0;i<lb;i++)
      if(idx+i<Bars&&MathAbs(Open[idx+i]-Close[idx+i+1])>=2.0*avg) return true;
   return false;
}

bool CheckStrongTrend(int idx,int lb,double avg)
{
   for(int i=0;i<lb;i++)
      if(idx+i+3<Bars&&MathAbs(Close[idx+i+3]-Close[idx+i])>=4.6*avg) return true;
   return false;
}

double NormalizeWPR(double v)
{ return v+100.0; }

void CheckForSignalCross(int idx,double thr,double &buf[],double lvl)
{
   int la=1;
   while(idx+la<Bars && wprBuffer[idx+la]>=lowerThreshold && wprBuffer[idx+la]<=upperThreshold) la++;
   if(idx+la>=Bars) return;
   if((thr==upperThreshold && wprBuffer[idx+la]>upperThreshold) ||
      (thr==lowerThreshold && wprBuffer[idx+la]<lowerThreshold)) buf[idx]=lvl;
}
//+------------------------------------------------------------------+
