//+------------------------------------------------------------------+
//|                                        scooby-doo Shag'emAll.mq4 |
//|                                  Copyright © 2009, Steve Hopwood |
//|                              http://www.hopwood3.freeserve.co.uk |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2009, Steve Hopwood"
#property link      "http://www.hopwood3.freeserve.co.uk"
#include <WinUser32.mqh>
#include <stdlib.mqh>
#define  NL    "\n"
//Trading direction
#define long "Long"
#define short "Short"
#define none "None"

/*
void DisplayUserFeedback()
int start()

----Trading functions----
bool DoesTradeExist(string symbol, int type)
void MakeTradingDecision(string symbol, string direction)
bool SendTrade(int type, string symbol)


----Pairs traded----
void CleanUpInputString()
void CalculatePairsPassed()

----SMA and Channel----
void GetSMA(string symbol)
void GetTradeDirection(string symbol)


*/



extern string     GVI="-----General trade inputs-----";
extern string     PairsToTrade="GBPJPY,USDCHF,GBPUSD,EURUSD,USDJPY,AUDUSD,USDCAD,EURGBP,EURCHF,EURJPY,GBPCHF,EURAUD,NZDJPY,CHFJPY,CADJPY,AUDJPY";
extern double     Lot=0.01;
extern int        MagicNumber=34567;
extern string     TradeComment="X5 Shag";
extern int        MaxTradesAllowed=20;
extern bool       CriminalIsECN=false;
extern int        MinChannelDepth=40;
extern string     wei="----Week ending inputs----";
extern bool       FridayClose=true;
extern bool       SaturdayClose=false;
extern int        CloseHour = 12;
extern string     oae="----Odds and ends----";
extern int        DisplayGapSize=30;


//Trading
string            TradeDirection;


//Pair extraction
string            InputString;//Holds the contents PairsToTrade for tidying yp etc
int               NoOfPairs;// Holds the number of pairs passed by the user via the inputs screen
string            TradePair[]; //Array to hold the pairs traded by the user

//SMA & Channel
double            Sma200;
double            ChannelHigh, ChannelLow;
int               ChannelDepth;


//Misc stuff
string            ScreenMessage, Gap;
bool              RobotDisabled;
string            DisabledMessage;



void DisplayUserFeedback()
{

   ScreenMessage = "";
   int cc;
   
   ScreenMessage = StringConcatenate(ScreenMessage, Gap, "Trading ", PairsToTrade, NL);
   ScreenMessage = StringConcatenate(ScreenMessage, Gap, "Lot size = ", Lot, NL);
   ScreenMessage = StringConcatenate(ScreenMessage, Gap, "Magic number: ", MagicNumber, NL);
   ScreenMessage = StringConcatenate(ScreenMessage, Gap, "Trade comment: ", TradeComment, NL);
   ScreenMessage = StringConcatenate(ScreenMessage, Gap, "MaxTradesAllowed: ", MaxTradesAllowed, NL);
   if (CriminalIsECN) ScreenMessage = StringConcatenate(ScreenMessage, Gap, "CriminalIsECN is true ", NL);
   else ScreenMessage = StringConcatenate(ScreenMessage, Gap, "CriminalIsECN is false ", NL);
   if (FridayClose) ScreenMessage= StringConcatenate(ScreenMessage, Gap, "Friday close = ", DoubleToStr(CloseHour, 2), NL);
   if (SaturdayClose) ScreenMessage= StringConcatenate(ScreenMessage, Gap, "Saturday close = ", DoubleToStr(CloseHour, 2), NL);
   ScreenMessage= StringConcatenate(ScreenMessage, Gap, "MinChannelDepth = ", MinChannelDepth, NL);
   
   
   //Display pairs information CriminalIsECN
   for (cc = 0; cc < NoOfPairs; cc++)
   {
      GetSMA(TradePair[cc] );
      GetTradeDirection(TradePair[cc] );
      double bid = MarketInfo(TradePair[cc], MODE_BID);
      int digits = MarketInfo(TradePair[cc], MODE_DIGITS);
      if (TradeDirection != none)  ScreenMessage = StringConcatenate(ScreenMessage, Gap, TradePair[cc], ":  200 SMA = ", DoubleToStr(Sma200, digits),
                      ":  Channel: High = ", DoubleToStr(ChannelHigh, digits), "  Low = ", DoubleToStr(ChannelLow, digits), 
                      "  Depth = ", DoubleToStr(ChannelDepth,0), ": Direction = ", TradeDirection, ": ", DoubleToStr(bid, digits), NL );
//      ScreenMessage = StringConcatenate(ScreenMessage, Gap, TradePair[cc], ":  200 SMA = ", DoubleToStr(Sma200, digits),
//                      ":  Channel: High = ", DoubleToStr(ChannelHigh, digits), "  Low = ", DoubleToStr(ChannelLow, digits), 
//                      "  Depth = ", DoubleToStr(ChannelDepth,0), ": Direction = ", TradeDirection, ": ", DoubleToStr(bid, digits), NL );
   }//for (cc = 0; cc < NoOfPairs; cc==)
   
   Comment(ScreenMessage);
   
}//End void DisplayUserFeedback()

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
{
//----
   
   int cc;
   
   Gap="";
   if (DisplayGapSize >0)
   {
      for (cc=0; cc< DisplayGapSize; cc++)
      {
         Gap = StringConcatenate(Gap, " ");
      }   
   }

      //Check magic number.
   if (!IsDemo() )
   {
      if (MagicNumber == 349567)
      {
         MessageBox("You cannot run this robot until changed the MagicNumber input from the default of 34567." + NL 
                    + "Please reload the robot with a new MagicNumber input.");
         Comment("                This robot can do nothing. Please reload it");
         DisabledMessage = "You need to change the MagicNumber input";
         RobotDisabled = true;
         return;
      }//if (MagicNumber == 12345)
   }//if (!IsDemo)
      
   //Insufficient length
   string mn = DoubleToStr(MagicNumber,0);
   if (StringLen(mn) < 2)
   {
      MessageBox("Your MagicNumber input needs to be at least 5 digits long, Your MagicNumber is " + mn + NL 
                 + "Please reload the robot with the MagicNumber input containing at least 5 digits - the more the better.");
      Comment("                This robot can do nothing. Please reload it");
      DisabledMessage = "You need to make the MagicNumber input at 5 digits long - more is better";
      RobotDisabled = true;
      return;
   }//if (StringLen(mn) < 5)

          
   //Extract the pairs traded by the user
   // Cleanup first
   InputString=PairsToTrade;
   CleanUpInputString();
   
   // Extract the number of paraaters passed by the user
   CalculatePairsPassed();
   
   string AddChar = StringSubstr(Symbol(),6,4);
   
   // Resize the array appropriately
   string NewArray = ArrayResize(TradePair, NoOfPairs);
   
   int Index = 0;//For searching InputString
   int LastIndex = 0;//Points the the most recent Index
   for (cc = 0; cc < NoOfPairs; cc ++)
   {
      Index = StringFind(InputString, ",",LastIndex);
      if (Index > -1)
      {
         TradePair[cc] = StringSubstr(InputString, LastIndex,Index-LastIndex);
         TradePair[cc] = StringTrimLeft(TradePair[cc]);
         TradePair[cc] = StringTrimRight(TradePair[cc]);
         TradePair[cc] = StringConcatenate(TradePair[cc], AddChar);
         
         LastIndex = Index+1;
         
      }//if (Index > -1)            
   }//for (int cc; cc<NoOfPairs; cc ++)
   
         
   DisplayUserFeedback();
   
//----
   return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
{
//----
   Comment("");
//----
   return(0);
}

void GetSMA(string symbol)
{
   Sma200 = iMA(symbol, 15, 200, 0, MODE_SMA, PRICE_CLOSE, 0);
   ChannelHigh = iMA(symbol, 240, 1, 0, MODE_SMA, PRICE_HIGH, 1);
   ChannelLow = iMA(symbol, 240, 1, 0, MODE_SMA, PRICE_LOW, 1);

   int multiplier, digits;
   digits = MarketInfo(symbol, MODE_DIGITS);
      
   if (digits == 2) multiplier = 100;
   if (digits == 3) multiplier = 1000;
   if (digits == 4) multiplier = 10000;
   if (digits == 5) multiplier = 100000;

   ChannelDepth = (ChannelHigh - ChannelLow) * multiplier;

}//End void GetSMA(string symbol)


void CleanUpInputString()
{
   // Does any tidying up of the user inputs
   
   //Remove unwanted spaces
   InputString = StringTrimLeft(InputString);
   InputString = StringTrimRight(InputString);

   //Add final comma if ommitted by user
   if (StringSubstr(InputString, StringLen(InputString)-1) != ",") 
      InputString = StringConcatenate(InputString,",");
      
   
}//void CleanUpInputString

void CalculatePairsPassed()
{
   // Calculates the numbers of paramaters passed in MagicNumber and TradeComment.
   
   int Index = 0;//For searching NoTradePairs
   int LastIndex;//Points the the most recent Index
   
   while(Index > -1)
      {
         Index = StringFind(InputString, ",",LastIndex);
         if (Index > -1)
         {
            NoOfPairs ++;
            LastIndex = Index+1;            
         }//if (Index > -1)
      }//while(int cc > -1)
   
   
   
}//End void CalculatePairsPassed()

bool DoesTradeExist(string symbol)
{
   //Returns true if there is already a trade in place, else false
   
   if (OrdersTotal() == 0) return(false); //nothing to do
   
   for (int cc = OrdersTotal() - 1; cc >= 0; cc--)
   {
      if (!OrderSelect(cc, SELECT_BY_POS) ) continue;
      if (OrderMagicNumber() == MagicNumber && OrderSymbol() == symbol) return(true);   
   }//for (int cc = OrdersTotal() - 1; cc >= 0, cc--)
   
   //Got this far, so no trade exists
   return(false);

}//bool DoesTradeExist(string symbol)

void GetTradeDirection(string symbol)
{
   //Sets the trade direction
   
   if (ChannelDepth < MinChannelDepth)
   {
      TradeDirection = none;
      return;
   }//if (ChannelDepth < MinChannelDepth)
   
   
   //Long
   if (ChannelHigh > Sma200 && ChannelLow > Sma200)
   {
      TradeDirection = long;
      return;
   }//if (ChannelHigh > Sma200 && ChannelLow > Sms200)
   
   //Short
   if (ChannelHigh < Sma200 && ChannelLow < Sma200)
   {
      TradeDirection = short;
      return;
   }//if (ChannelHigh > Sma200 && ChannelLow > Sms200)
   
   //None
   TradeDirection = none;

}//void GetTradeDirection(string symbol)

bool SendTrade(int type, string symbol)
{
   // Attempts to place a trade according to type, i.e.
   // 0 = OP_BUY
   // 1 = OP_SELL
   // 4 = OP_BUYSTOP
   // 5 = OP_SELLSTOP

   // Check spread
   /*if (MarketInfo(symbol, MODE_SPREAD) > MaxSpreadAllowed)
   {
      string rr;
      if (type == 0) rr = "Buy ";
      else rr = "Sell";
      Alert(symbol, " scooby-doo basket ", rr, " cancelled; spread too wide");
      return(false);// Spread too large, so abort send
   }//if (MarketInfo(NULL, MODE_SPREAD) > MaxSpreadAllowed)
   */
   
   GetSMA(symbol);//Somehow, the channel low and high are getting lost somewhere, so re-set them
   
   double digits = MarketInfo(symbol, MODE_DIGITS);
   double point = MarketInfo(symbol, MODE_POINT);
   int sl = ChannelDepth / 2;
   int PendingPipsFromPrice = ChannelDepth / 10;
   int TpOffset = ChannelDepth / 10;
   
   RefreshRates();
   
   // Buy trade
   if (type == 4)
   {
      double TradePrice=NormalizeDouble(ChannelLow + (PendingPipsFromPrice * point), digits);
      double Stop = NormalizeDouble(TradePrice - (sl * point), digits);
      double Take = NormalizeDouble(ChannelHigh - (TpOffset * point), digits);
      //double Take = ChannelHigh;
      double market = MarketInfo(symbol, MODE_ASK);
      //Alert("Channel ", ChannelDepth, ": Low ", ChannelLow, ": Price ", TradePrice, ": Stop ", Stop, ": High ", ChannelHigh, ": Take ", Take);
      //return;
   }//if (type == 0)   
   
   
   // Sell trade
   if (type == 5)
   {
      TradePrice = NormalizeDouble(ChannelHigh - (PendingPipsFromPrice * point), digits);
      Stop = NormalizeDouble(TradePrice + (sl * point), digits);
      Take = NormalizeDouble(ChannelLow + (TpOffset * point), digits);
      //Take = ChannelLow;
      market = MarketInfo(symbol, MODE_BID);
      //Alert("Channel ", ChannelDepth, ": High ", ChannelHigh, ": Price ", TradePrice, ": Stop ", Stop, ": Low ", ChannelLow, ": Take ", Take);
      //return;
   }//if (type == 1)

   //Trade expiry time
   datetime OrderExpiryTime = TimeCurrent();
   OrderExpiryTime += (1 * 60) * 60;
   //Alert(TimeToStr(OrderExpiryTime, TIME_MINUTES));
   
   //Non ECN crook
   if (!CriminalIsECN) int ticket = OrderSend(symbol,type, Lot, TradePrice, 50, Stop, Take, TradeComment, MagicNumber,OrderExpiryTime, CLR_NONE);
   
   //Is a 2 stage criminal
   if (CriminalIsECN)
   {
     ticket = OrderSend(symbol, type, Lot, TradePrice, 50, 0, 0, TradeComment, MagicNumber, OrderExpiryTime, CLR_NONE);
	  if (Stop != 0)
	  {
		  if (ticket > 0)
		  {
			  bool result = OrderModify(ticket, OrderOpenPrice(),	Stop, Take, OrderExpiration(), CLR_NONE);
			  if (!result)
			  {
			      int err=GetLastError();
               Alert(Symbol(), " ", type," TP order modify failed with error(",err,"): ",ErrorDescription(err));
               Print("Order send failed with error(",err,"): ",ErrorDescription(err));      
			  }//if (!result)
		  }//if (ticket > 0)	  
	  }//if (Take != 0)
	  else Print("Skipping OrderModify because no SL or TP specified.");
      
   }//if (CriminalIsECN)

   //Error trapping for both   
   if (ticket < 0)
   {
      err=GetLastError();
      Alert("Shag'emAll ", symbol, " ", type," order send failed with error(",err,"): ",ErrorDescription(err),
                     " Market=", DoubleToStr(market, digits), ": Entry=", DoubleToStr(TradePrice, digits),
                     ": TP=", Take, ": Sl=",Stop,
                     " Lots=",Lot, ": CH=", ChannelHigh, ": CL=", ChannelLow);
      Print("Order send failed with error(",err,"): ",ErrorDescription(err));
      if (err == 146) Sleep(1000);//Trade context busy error, so sleep 1 second to give it a break
      return(false);
   }//if (ticket < 0)
   else return(true);

}//End void SendTrade(type)

void MakeTradingDecision(string symbol, string direction)
{
   //Uses the information gathered about trade direction and channel range to decide whether to trade or not
   
   //Insert news filter code here
   
   
   RefreshRates();
   //Long trade
   if (direction == long)
   {
      double ask = MarketInfo(symbol, MODE_ASK);
      
      //Examine the filters.
      //Channel
      if (ask > ChannelLow) return;
      
      //ChannelHigh direction.
      double ma1, ma2;
      ma1 = iMA(symbol, 30, 8, 0, MODE_SMA, PRICE_LOW, 1);
      ma2 = iMA(symbol, 30, 24, 0, MODE_SMA, PRICE_CLOSE, 1);
      if (ma1 > ma2 ) return;
      
      
      //Got this far, so trade should be sent
      SendTrade(4, symbol);
      
   }//if (direction == long)
   
   //Short trade
   if (direction == short)
   {
      double bid = MarketInfo(symbol, MODE_BID);
      
      //Examine the filters.
      //Channel.
      if (bid < ChannelHigh) return;
      
      //ChannelLow direction
      ma1 = iMA(symbol, 30, 8, 0, MODE_SMA, PRICE_HIGH, 1);
      ma2 = iMA(symbol, 30, 24, 0, MODE_SMA, PRICE_CLOSE, 1);
      if (ma1 < ma2 ) return;
      
      
      //Got this far, so trade should be sent
      SendTrade(5, symbol);
      
   }//if (direction == long)
   

}//void MakeTradingDecision(string symbol, string direction)


//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
{
//----

   if (RobotDisabled)
   {
      Comment("The robot has been suspended. Reason: ", DisabledMessage);
      return;
   }//if (RobotDisabled )

   if (IsTradeContextBusy() )
   {
      Comment("Trade context is busy because the criminal is too lazy, dim or both to provide a decent server. Please wait for the cretin to stop pissing around.");
      return;
   }//if (IsTradeContextBusy)

   if (OrdersTotal() >= MaxTradesAllowed)
   {
      Comment("Maximum of ", MaxTradesAllowed, " trades are open");
      return;
   }//if (OrdersTotal() >= MaxTradesAllowed)
   
   //Check Friday close
   if (FridayClose)
   {
      if (TimeHour(TimeLocal() ) >= CloseHour && TimeDayOfWeek(TimeLocal() ) == 5)
      {
         Comment(Gap + "Trading suspended for Friday close");
         return;
      }//if (TimeHour(TimeLocal()) >= CloseHour && DayOfWeek() == 5)
   }//if (FridayClose)
   
   //Check Saturday close for Aitipodeans
   if (SaturdayClose)
   {
      if (TimeHour(TimeLocal() ) >= CloseHour && TimeDayOfWeek(TimeLocal() ) == 6)
      {
         Comment(Gap + "Trading suspended for Saturday close");
         return;
      }//if (TimeHour(TimeLocal()) >= CloseHour && DayOfWeek() == 5)
   }//if (Saturday)
   
   //Loop around the pairs list
   for (int cc = 0; cc < NoOfPairs; cc++)
   {
      bool TradeExists = DoesTradeExist(TradePair[cc] );
      if (!TradeExists)
      {
         GetSMA(TradePair[cc] );
         GetTradeDirection(TradePair[cc] );
         if (TradeDirection == long || TradeDirection == short)
         {
            MakeTradingDecision(TradePair[cc], TradeDirection);
         }//if (TradeDirection == long || TradeDirection == short)
            
      }//if (!TradeExists)
   }//for (cc = 0; cc < NoOfPairs; cc==)

   
   DisplayUserFeedback();
//----
   return(0);
}
//+------------------------------------------------------------------+