//+-----------------------------------------------------+
//| INDICATOR                          AudioAlerts.mq4  |
//| copy to [experts\indicators] and recompile          |
//| status messages printed in Experts tab of terminal  |
//+-----------------------------------------------------+
//| 'The Force' trading system created by ForexJedi.    |
//| Alerts for 'The Force' are NOT FILTERED by design.  |
//| Every qualified signal candle triggers an alert.    |
//+-----------------------------------------------------+
//| This is free experimental software.                 |
//| No guarantees are expressed or implied.             |
//| Feedback welcome via Forex Factory private message. |
//+-----------------------------------------------------+
#property copyright "Copyright © 2009 Robert Dee"
#property link      "www.forexfactory.com/member.php?u=12983"

#define INDICATOR_VERSION    20090302
#define INDICATOR_NAME       "AudioAlerts"

#property indicator_chart_window
#property indicator_buffers 4
#property indicator_color1 DarkBlue  // BUY alert arrows
#property indicator_color2 Red       // SELL alert arrows
#property indicator_color3 DarkBlue  // BULLISH painted candle body
#property indicator_color4 Red       // BEARISH painted candle body
#property indicator_width1 5
#property indicator_width2 5
#property indicator_width3 3
#property indicator_width4 3

// user settings
extern bool   BasicForceAlerts    = True;         // macd + stochastics + emas (includes all stoch OB & OS restrictions)
extern bool   AdvancedForceAlerts = True;         // macd + stochastics + emas (ignores stoch OB & OS restrictions also ignores divergence)
extern bool   ShowChartArrows     = False;        // printing of alert arrows on the price chart (default = disabled)
extern bool   PaintSignalCandles  = True;         // painting of signal candle bodies on the price chart (default = enabled)
extern bool   PaintOutsideCandles = False;        // paint 'outside' candle bodies, obull closes above prev highs, obear closes below prev lows
extern bool   LineAlerts          = False;        // alert when price touches a line (any trend line or horizontal line object on the price chart)
extern bool   AlertPopups         = True;         // enable alert popup window
extern string AlertSoundFile      = "alert2.wav"; // the WAV file must be located in the MT4\sounds folder, ("" = disable sounds)
extern int    AlertSoundRepeats   = 3;            // number of times the WAV file will be repeated after the first alert sound

// display buffers
double   BuyAlerts[];
double   SellAlerts[];
double   CandleBodyLow[];
double   CandleBodyHigh[];

// global variables
int      lastalert, alertsoundcount;
bool     osreset, obreset;
datetime signalcandletime, lastcandletime, lastlinealert;
int      lastsignal;
#define  NONE         0
#define  BASICBUY     1
#define  BASICSELL    2
#define  ADVANCEDBUY  3
#define  ADVANCEDSELL 4

// future features, still under construction
bool   TunnelMode          = False;       // monitor bull moves and bear moves across tunnel levels
int    Levels              = 6;           // how many levels above and below the tunnel midline
int    LevelPips           = 40;          // number of pips for each tunnel level, 40 pips suitable for EURUSD
bool   ShowLevelTags       = False;       // show a price tag when price retraces inside the same tunnel level
bool   ShowLevelPrices     = False;       // after retrace and tag then show the entry price on the very next signal

// tunnel mode variables
double   Upper[];
double   Lower[];
int      level, lastlevel;
bool     leveltagged, levelpriced;
bool     bullmove, bearmove;


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
if(ShowChartArrows == True) SetIndexStyle(0,DRAW_ARROW); else SetIndexStyle(0,DRAW_NONE);
SetIndexArrow(0,SYMBOL_ARROWUP);
SetIndexEmptyValue(0,0);
if(ShowChartArrows == True) SetIndexStyle(1,DRAW_ARROW); else SetIndexStyle(1,DRAW_NONE);
SetIndexArrow(1,SYMBOL_ARROWDOWN);
SetIndexEmptyValue(1,0);
SetIndexStyle(2,DRAW_HISTOGRAM);
SetIndexStyle(3,DRAW_HISTOGRAM);
SetIndexBuffer(0,BuyAlerts);
SetIndexBuffer(1,SellAlerts);
SetIndexBuffer(2,CandleBodyLow);
SetIndexBuffer(3,CandleBodyHigh);
IndicatorDigits(Digits);
IndicatorShortName(INDICATOR_NAME);
SetIndexLabel(0,"");
SetIndexLabel(1,"");
SetIndexLabel(2,"");
SetIndexLabel(3,"");
// cleanup buffers
int i = 0;
while(i < Bars)
   {
   BuyAlerts[i] = 0;
   SellAlerts[i] = 0;
   CandleBodyLow[i] = 0;
   CandleBodyHigh[i] = 0;
   i++;
   }
ArrayResize(Upper,Levels+1); // initialise array
ArrayResize(Lower,Levels+1); // initialise array
Print("version V"+INDICATOR_VERSION+" Copyright © 2009 Robert Dee");   
}

//+------------------------------------------------------------------+
//| Paints the candle body at position (shift) using a histogram
//+------------------------------------------------------------------+
void PaintCandleBody(int shift)
   {
   CandleBodyLow[shift]  = Close[shift];
   CandleBodyHigh[shift] = Open[shift];
   }

//+------------------------------------------------------------------+
//| Prints price labels with price, time, and colour as inputs
//+------------------------------------------------------------------+
void PrintPriceLabel(double labelprice, datetime labeltime, color labelcolor)
   {
   string objname = "_pricelabel "+DoubleToStr(labelprice,Digits)+" "+TimeToStr(labeltime,TIME_MINUTES);
   if(ObjectFind(objname) == 0) return; // object already exists, do no more
   ObjectCreate(objname,OBJ_ARROW,0,labeltime,labelprice);
   ObjectSet(objname,OBJPROP_ARROWCODE,5);
   ObjectSet(objname,OBJPROP_COLOR,labelcolor);
   }

//+------------------------------------------------------------------+
//| Indicator start() function called every time price changes (tic)
//+------------------------------------------------------------------+
int start()
{
int    count;
int    multi       = MathPow(10,Digits);               // calc multiplier for this currency
int    daycandles  = (3600*24)/(Time[0]-Time[1]);      // candles per day in this timeframe
int    spread      = MarketInfo(Symbol(),MODE_SPREAD); // spread for this pair
double macd1, macd2, stoch1, stoch2, stochsig1, stochsig2, oema5, cema5, emashigh, emaslow;
double lwma, prevhighs, prevlows, levelprice, objvalue;
string objname, objdesc, msg;

// find starting position at beginning of chart
int shift;
int limit;
int counted_bars=IndicatorCounted();
if(counted_bars<0) return(-1);
if(counted_bars>0) counted_bars--;
limit=Bars-counted_bars;
shift = limit;
while(shift >= 0)
   {
   /////////////////////////////
   // DATA from CLOSED CANDLES
   macd1     = iMACD(NULL,0,12,26,1,PRICE_CLOSE,MODE_MAIN,shift+1);
   macd2     = iMACD(NULL,0,12,26,1,PRICE_CLOSE,MODE_MAIN,shift+2);
   stoch1    = iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_MAIN,shift+1);
   stoch2    = iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_MAIN,shift+2);
   stochsig1 = iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_SIGNAL,shift+1);
   stochsig2 = iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_SIGNAL,shift+2);
   oema5     = iMA(NULL,0,5,0,MODE_EMA,PRICE_OPEN,shift+1);
   cema5     = iMA(NULL,0,5,0,MODE_EMA,PRICE_CLOSE,shift+1);
   prevhighs = High[iHighest(NULL,0,MODE_HIGH,3,shift+2)];       // highest high previous candles
   prevlows  = Low[iLowest(NULL,0,MODE_LOW,3,shift+2)];          // lowest  low  previous candles

   //////////////////////////////////
   // BASIC FORCE STOCH RESET RULES
   if(stoch1 < 20 && stochsig1 < 20) {osreset = True; obreset = False;} // oversold reset allows new BuyAlerts
   if(stoch1 > 80 && stochsig1 > 80) {obreset = True; osreset = False;} // overbought reset allows new SellAlerts
   if(lastsignal == BASICBUY  && cema5 < oema5-1*Point) lastsignal = NONE;
   if(lastsignal == BASICSELL && cema5 > oema5+1*Point) lastsignal = NONE;
   if(lastsignal == ADVANCEDBUY  && cema5 < oema5-1*Point) lastsignal = NONE;
   if(lastsignal == ADVANCEDSELL && cema5 > oema5+1*Point) lastsignal = NONE;

   /////////////////////////////////
   // BASIC FORCE BUY ALERT RULES
   if(BasicForceAlerts == True)
   if(osreset == True)
   if(macd1 > macd2)
   if(stoch1 >= stochsig1+1 && stoch1 > stoch2 && stoch1 > 20 && stoch1 < 80)
   if(cema5 >= oema5+0.5*Point)
   if(Close[shift+1] > Open[shift+1])
      {
      BuyAlerts[shift] = Low[shift]-5*Point;
      if(PaintSignalCandles == True) PaintCandleBody(shift+1);
      osreset = False;
      lastsignal = BASICBUY;
      signalcandletime = Time[shift+1];
      }

   /////////////////////////////////
   // BASIC FORCE SELL ALERT RULES
   if(BasicForceAlerts == True)
   if(obreset == True)
   if(macd1 < macd2)
   if(stoch1 <= stochsig1-1 && stoch1 < stoch2 && stoch1 < 80 && stoch1 > 20)
   if(cema5 <= oema5-0.5*Point)   
   if(Close[shift+1] < Open[shift+1])
      {
      SellAlerts[shift] = High[shift]+5*Point;
      if(PaintSignalCandles == True) PaintCandleBody(shift+1);
      obreset = False;
      lastsignal = BASICSELL;
      signalcandletime = Time[shift+1];
      }

   ///////////////////////////////////
   // ADVANCED FORCE BUY ALERT RULES
   if(AdvancedForceAlerts == True)
   if(lastsignal != ADVANCEDBUY && lastsignal != BASICBUY)
   if(macd1 > macd2)
   if(stoch1 >= stochsig1+1 && stoch1 > stoch2 && stoch1 < 80)
   if(cema5 >= oema5+0.5*Point)   
   if(Close[shift+1] > Open[shift+1])
      {
      BuyAlerts[shift] = Low[shift]-5*Point;
      if(PaintSignalCandles == True) PaintCandleBody(shift+1);
      osreset = False;
      lastsignal = ADVANCEDBUY;
      signalcandletime = Time[shift+1];
      }

   ///////////////////////////////////
   // ADVANCED FORCE SELL ALERT RULES
   if(AdvancedForceAlerts == True)
   if(lastsignal != ADVANCEDSELL && lastsignal != BASICSELL)
   if(macd1 < macd2)
   if(stoch1 <= stochsig1-1 && stoch1 < stoch2 && stoch1 > 20)
   if(cema5 <= oema5-0.5*Point)   
   if(Close[shift+1] < Open[shift+1])
      {
      SellAlerts[shift] = High[shift]+5*Point;
      if(PaintSignalCandles == True) PaintCandleBody(shift+1);
      obreset = False;
      lastsignal = ADVANCEDSELL;
      signalcandletime = Time[shift+1];
      }

   /////////////////////
   // PAINT OBULL RULES
   if(PaintOutsideCandles == True)
   //if(Low[shift+1] < Low[shift+2])    // low is below previous candle low (classic outside bar)
   if(Close[shift+1] > prevhighs)     // and close above previous candle high
   if(Close[shift+1] > Open[shift+1]) // and bullish candle body
      PaintCandleBody(shift+1);

   /////////////////////
   // PAINT OBEAR RULES
   if(PaintOutsideCandles == True)
   //if(High[shift+1] > High[shift+2])   // high is above previous candle high (classic outside bar)
   if(Close[shift+1] < prevlows)       // and close below previous candle low
   if(Close[shift+1] < Open[shift+1])  // and bearish candle body
      PaintCandleBody(shift+1);

   //////////////////////
   // TUNNEL MODE RULES
   if(TunnelMode == True)
      {
      emashigh  = MathMax(oema5,cema5);                             // upper ema value
      emaslow   = MathMin(oema5,cema5);                             // lower ema value
      lwma      = iMA(NULL,0,96,24,MODE_LWMA,PRICE_CLOSE,shift+1);  // tunnel midline
      for(count=0;count<=Levels;count++)                            // load all the tunnel levels
         {Upper[count]=lwma+(count*LevelPips*Point);Lower[count]=lwma-(count*LevelPips*Point);}
      for(count=0;count<=Levels;count++)                            // find the current tunnel level
         {
         if(emaslow  > Upper[count]+1*Point)level = +1+count;
         if(emashigh < Lower[count]-1*Point)level = -1-count;
         }
      // NEW TUNNEL LEVEL TAGGING
      if(ShowLevelTags == True && shift < WindowFirstVisibleBar() + WindowBarsPerChart())   
      if(level == lastlevel && leveltagged == False)                // if the level has not changed and has not been tagged
         {
         // BULLMOVE TAGGING
         if(level > 0) levelprice = Upper[level-1];
         if(level < 0) levelprice = Lower[0-level];
         if(bullmove == True)                                       // bullmove
         if(prevlows > levelprice+2*Point)                          // and previous lows are above levelprice
         if(Low[shift] < levelprice+2*Point)                        // and this candle low is near levelprice
            {
            PrintPriceLabel(Low[shift],Time[shift],Blue);
            leveltagged = True;
            }
         // BEARMOVE TAGGING
         if(level > 0) levelprice = Upper[level];
         if(level < 0) levelprice = Lower[-level-1];
         if(bearmove == True)                                       // bearmove 
         if(prevhighs < levelprice-2*Point)                         // and previous highs are below levelprice
         if(High[shift] > levelprice-2*Point)                       // and this candle high is near levelprice
            {
            PrintPriceLabel(High[shift],Time[shift],Red);
            leveltagged = True;
            }         
         }
      // NEW TUNNEL LEVEL RULES
      if(level != lastlevel)                                        // if the level has changed
         {
         if(level > lastlevel) {bullmove = True; bearmove = False;} // if the level has increased
         if(level < lastlevel) {bearmove = True; bullmove = False;} // if the level has decreased
         leveltagged = False;
         levelpriced = False;
         lastlevel = level;
         }
      }

   /////////////////////
   // SHOW LEVEL PRICES
   if(ShowLevelPrices == True && shift < WindowFirstVisibleBar() + WindowBarsPerChart())   
      {
      if(bullmove == True && leveltagged == True && levelpriced == False)
         {
         if(BuyAlerts[shift]  != 0 && lastsignal == BASICBUY)     {PrintPriceLabel(Open[shift],Time[shift],Blue); levelpriced = True;}
         if(BuyAlerts[shift]  != 0 && lastsignal == ADVANCEDBUY)  {PrintPriceLabel(Open[shift],Time[shift],Blue); levelpriced = True;}
         }
      if(bearmove == True && leveltagged == True && levelpriced == False)
         {
         if(SellAlerts[shift] != 0 && lastsignal == BASICSELL)    {PrintPriceLabel(Open[shift],Time[shift],Red); levelpriced = True;}
         if(SellAlerts[shift] != 0 && lastsignal == ADVANCEDSELL) {PrintPriceLabel(Open[shift],Time[shift],Red); levelpriced = True;}
         }
      }

   shift--;                     // move forward to the next candle
   } // end of while() loop

/////////////////////
// LINE ALERT RULES
if(LineAlerts == True && lastlinealert != Time[0]) // only one line alert per candle
   {
   shift = ObjectsTotal()-1; // objects index starts at zero
   while(shift >= 0)
      {
      // get data from the object
      objname  = ObjectName(shift);
      objdesc  = ObjectDescription(objname); if(objdesc == "") objdesc = objname; objdesc = StringTrimLeft(objdesc);
      objvalue = NormalizeDouble(ObjectGetValueByShift(objname,0),Digits);
      if(ObjectType(objname) == OBJ_HLINE) objvalue = NormalizeDouble(ObjectGet(objname,OBJPROP_PRICE1),Digits);
      // line alert rules
      if(ObjectType(objname) == OBJ_TREND || ObjectType(objname) == OBJ_HLINE) // trend line or horizontal line
      if(Bid <= objvalue+1*Point && Bid >= objvalue-1*Point)                   // 1 pip above or below the line
         {
         Alert("Line Alert for '"+objdesc+"' at " + DoubleToStr(objvalue,Digits));
         PlaySound(AlertSoundFile);
         lastlinealert = Time[0];
         break;
         }
      shift--; // move to next object in the list
      }
    } // end of line alert while() loop

///////////////////////////////////////////////////////////////////////
// a status message is printed in the experts log for each new candle
if(lastcandletime != Time[0])
   {
   if(AlertPopups == True)
      {
      if(BuyAlerts[0] != 0  && lastsignal == BASICBUY)     {Alert("Basic Force BUY candle "+TimeToStr(signalcandletime,TIME_MINUTES)+" "+Symbol());PlaySound(AlertSoundFile);}
      if(SellAlerts[0] != 0 && lastsignal == BASICSELL)    {Alert("Basic Force SELL candle "+TimeToStr(signalcandletime,TIME_MINUTES)+" "+Symbol());PlaySound(AlertSoundFile);}
      if(BuyAlerts[0] != 0  && lastsignal == ADVANCEDBUY)  {Alert("Advanced Force BUY candle "+TimeToStr(signalcandletime,TIME_MINUTES)+" "+Symbol());PlaySound(AlertSoundFile);}
      if(SellAlerts[0] != 0 && lastsignal == ADVANCEDSELL) {Alert("Advanced Force SELL candle "+TimeToStr(signalcandletime,TIME_MINUTES)+" "+Symbol());PlaySound(AlertSoundFile);}
      }
   msg = "status";
   if(lastsignal == NONE) msg = msg + " NO ALERTS";
   if(lastsignal == BASICBUY) msg = msg + " BasicForceBUY " +TimeToStr(signalcandletime,TIME_MINUTES);
   if(lastsignal == BASICSELL) msg = msg + " BasicForceSELL " +TimeToStr(signalcandletime,TIME_MINUTES);
   if(lastsignal == ADVANCEDBUY) msg = msg + " AdvancedForceBUY " +TimeToStr(signalcandletime,TIME_MINUTES);
   if(lastsignal == ADVANCEDSELL) msg = msg + " AdvancedForceSELL " +TimeToStr(signalcandletime,TIME_MINUTES);
   if(osreset == True) msg = msg + " OSreset";
   if(obreset == True) msg = msg + " OBreset";
   if(TunnelMode == True)
      {
      if(bullmove == True) msg = msg + " (bullmove level "+level;
      if(bearmove == True) msg = msg + " (bearmove level "+level;
      if(leveltagged == True) msg = msg + " tagged";
      if(levelpriced == True) msg = msg + " priced";
      msg = msg + ")";
      }
   Print(msg);
   lastcandletime = Time[0];
   alertsoundcount = 0;  // reset the counter with each new candle
   }

////////////////////////////////////////////////////////////////////////
// AlertSoundFile played again depending on value of AlertSoundRepeats
if(Seconds() < lastalert) lastalert = Seconds();     // reset counter each minute
if(AlertSoundFile != "" && Seconds() >= lastalert+5) // five seconds delay then repeating on next price tick
   {
   if(BuyAlerts[0]  != 0 && alertsoundcount < AlertSoundRepeats) {PlaySound(AlertSoundFile);lastalert = Seconds();alertsoundcount++;}
   if(SellAlerts[0] != 0 && alertsoundcount < AlertSoundRepeats) {PlaySound(AlertSoundFile);lastalert = Seconds();alertsoundcount++;}
   if(lastlinealert == Time[0] && alertsoundcount < AlertSoundRepeats) {PlaySound(AlertSoundFile);lastalert = Seconds();alertsoundcount++;}
   }

return(0);
} // end of start()

//+------------------------------------------------------------------+
void deinit()
{
// cleanup buffers
int i = 0;
while(i < Bars)
   {
   BuyAlerts[i] = 0;
   SellAlerts[i] = 0;
   CandleBodyLow[i] = 0;
   CandleBodyHigh[i] = 0;
   i++;
   }
if(TunnelMode == True) ObjectsDeleteAll(0,OBJ_ARROW); // cleanup all the price labels
}