//+------------------------------------------------------------------+
//|                                                      Tony112.mq4 |
//|                                   Copyright © 2011, Josh Rodatus |
//|                                                                  |
//+------------------------------------------------------------------+

#property copyright "Copyright © 2011, Josh Rodatus"
#property link		""

#define MAGIC	20111127

extern bool    brokerIsECN = true;
extern int		gmtOffset = 3;
extern double	risk = 0.01;
extern double	maxSpread = 1.0;
extern double  pendingLong = 0.0;
extern double  pendingShort = 0.0;
extern double	movementThreshold = 1.0;
extern double	moveSLto15 = 0.5;
extern double	moveSLto10 = 1.0;
extern double	moveSLto05 = 1.5;
extern double	moveSLto00 = 2.0;
extern int		takeProfit = 140;

int londonHour, NYHour;
double lotSize;
double digitMult;
bool tradedThisMinute;

double TS_levels[5];
double TS_correspondingSL[5] = {-2.0,-1.5,-1.0,-0.5,0.0};
int TS_level;
int TS_ticket;

int init() {
	if (Digits == 5) digitMult = 10.0;
	else digitMult = 1.0;
	
	londonHour = 8 + gmtOffset;
	NYHour = 13 + gmtOffset;
	Print("Will trade at "+londonHour+":00 and "+NYHour+":00 server time.");
	tradedThisMinute = false;
	ComputeLotSize();
	Print("Current server spread: "+(MarketInfo(Symbol(),MODE_SPREAD)/digitMult));
	double minSL = (MarketInfo(Symbol(),MODE_STOPLEVEL)+MarketInfo(Symbol(),MODE_SPREAD))/digitMult;
	Print("Minimum server SL distance: "+minSL);
	if (TS_correspondingSL[0]*(-1.0) <= minSL) {
	   Print("Virtual SL will kick in as necessary.");
	}
	
	if (pendingLong != 0.0) {
	   if (pendingLong <= Ask) {
	      Print("Cannot set pending long below current Ask price. Will ignore.");
	      pendingLong = 0.0;
	   } else {
	      Print("Will also trade long above "+pendingLong);
	   }
	}
	
	if (pendingShort != 0.0) {
	   if (pendingShort >= Bid) {
	      Print("Cannot set pending short above current Bid price. Will ignore.");
	      pendingShort = 0.0;
	   } else {
	      Print("Will also trade short below "+pendingShort);
	   }
	}
	
	TSInit();
	
	return (0);
}

int deinit() {
	return (0);
}

void ComputeLotSize() {
   Print("Margin required per lot: $"+MarketInfo(Symbol(),MODE_MARGINREQUIRED));
   double maxLots = NormalizeDouble(AccountFreeMargin()/MarketInfo(Symbol(),MODE_MARGINREQUIRED), 2)-0.01;
   double lotsCanRisk = NormalizeDouble((risk*AccountBalance()/TS_correspondingSL[0]*(-1.0))/(digitMult), 2);
   Print("Hence, max lots for margin is "+maxLots+". Can risk "+lotsCanRisk+ " lots.");
   lotSize = MathMin(maxLots,lotsCanRisk);
	Print("Using lot size "+lotSize);
}

void TSInit() {
	TS_levels[0] = moveSLto15;
	TS_levels[1] = moveSLto10;
	TS_levels[2] = moveSLto05;
	TS_levels[3] = moveSLto00;
	TS_levels[4] = 0.0;
	
	TS_ticket = -1;
}

int TSAvail() {
	if (TS_ticket == -1) return (1);
	
	if (OrderSelect(TS_ticket, SELECT_BY_TICKET) == false) return (-1);
	if (OrderCloseTime() != 0) {
		TS_ticket = -1;
		return (1);
	}
	
	return (0);
}

int TSOpen(int ticket) {
	if (TSAvail() == false) return (-1);
	
	TS_level = 0;
	TS_ticket = ticket;
	
	return (0);
}

int TSCheck() {
	if (TSAvail() == true) return (0);		// no ticket open
	
	if (OrderSelect(TS_ticket, SELECT_BY_TICKET) == false) { Print("TSCheck(): cannot select ticket "+TS_ticket); return (-1); }
	
	double PLpips = ((OrderClosePrice() - OrderOpenPrice()) / Point) / digitMult;
	if (OrderType() == OP_SELL) {
		PLpips *= -1.0;
	}
	
	double SLpips = TS_correspondingSL[TS_level];
	if (PLpips <= SLpips) {
	   Print("TSCheck(): Virtual stop loss "+SLpips+" triggered at "+PLpips);
	   if (closeTrade(TS_ticket) == -1) {
	      Print("TSCheck(): closeTrade() reported error.");
	      return (-1);
	   }
	   TS_ticket = -1;
	}
	
	while (PLpips >= TS_levels[TS_level] && TS_level < 4) {
	   TS_level++;
	   Print("TSCheck(): Moving stop loss to "+TS_correspondingSL[TS_level]);
	}
	if (TS_correspondingSL[TS_level] == SLpips) return (0);
	
	SLpips = TS_correspondingSL[TS_level];
	double newSL;
	if (OrderType() == OP_BUY) {
	   newSL = OrderOpenPrice()+(SLpips*digitMult*Point);
	} else {
	   newSL = OrderOpenPrice()-(SLpips*digitMult*Point);
	}
	newSL = NormalizeDouble(newSL,Digits);
	if (stoplossModOK(newSL, OrderType() == OP_BUY)) {
	   if (OrderModify(TS_ticket,OrderOpenPrice(),newSL,OrderTakeProfit(),0,Blue) == false) {
	      Print("TSCheck(): OrderModify reported error "+GetLastError());
	      return (-1);
	   }
	} else {
	   Print("Server rejects desired stop loss: virtual SL of "+SLpips+" will kick in.");
	}
	
	if (TS_level == 4) {
	   Print("TSCheck(): Trade reached last TS level, dropping from manager.");
	   TS_ticket = -1;
	   TS_level = 0;
	}
	
	return (0);
}

int checkPendingLevels() {
   if (TSAvail() == false) return (0);
   
   int ticket = 0;
   if (pendingLong != 0.0 && Ask >= pendingLong) {
      Print("Pending long hit at "+pendingLong+": opening trade.");
      ticket = openTrade(1);
      pendingLong = 0.0;
   } else if (pendingShort != 0.0 && Bid <= pendingShort) {
      Print("Pending short hit at "+pendingShort+": opening trade.");
      ticket = openTrade(-1);
      if (ticket != -1) TSOpen(ticket);
      pendingShort = 0.0;
   }
   if (ticket == -1) { Print("Unable to trade: openTrade() reported error "+GetLastError()); if (GetLastError()==4109) Print("Must set \"Allow live trading\""); return (-1); }
   
   return (0);
}

bool stoplossModOK(double fullSL, bool useAsk) {
   double price = Bid;
   if (useAsk) price = Ask;
   return (MathAbs(price - fullSL)/Point/digitMult >= (MarketInfo(Symbol(),MODE_STOPLEVEL)+MarketInfo(Symbol(),MODE_SPREAD))/digitMult);
}

bool stoplossOK(double SLpips) {
   return (MathAbs(SLpips) >= (MarketInfo(Symbol(),MODE_STOPLEVEL)+MarketInfo(Symbol(),MODE_SPREAD))/digitMult);
}

bool spreadOK() {
	return (MarketInfo(Symbol(),MODE_SPREAD) <= maxSpread*digitMult);
}

int MathSign(double value) {
	if (value < 0.0) return (-1);
	else return (1);
}

int openTrade(int direction) {
   if (spreadOK() == false) { Print("openTrade(): Unable to trade: spread = "+(MarketInfo(Symbol(),MODE_SPREAD)/digitMult)); return (-1); }
   
   ComputeLotSize();
   
   int ticket;
   double SLpips = TS_correspondingSL[0];
   double SL = 0.0;
   double TP = 0.0;
   if (stoplossOK(SLpips)) {
	   SL = NormalizeDouble((SLpips*digitMult*Point),Digits);
	} else {
	   Print("Server rejects desired stop loss: virtual SL of "+SLpips+" will kick in.");
	}
   RefreshRates();
	if (direction == 1) {
	   if (SL != 0.0) SL = Ask+SL;
	   TP = Ask+(takeProfit*digitMult*Point);
	   if (brokerIsECN) {
		   ticket = OrderSend(Symbol(),OP_BUY,lotSize,Ask,3,0.0,0.0,NULL,MAGIC,0,Blue);
		   if (ticket == -1) return (-1);
	      if (OrderModify(ticket,Ask,SL,TP,0,Blue) == 1) return (ticket);
	   } else {
	      return (OrderSend(Symbol(),OP_SELL,lotSize,Ask,3,SL,TP,NULL,MAGIC,0,Blue));
	   } 
	} else if (direction == -1) {
	   if (SL != 0.0) SL = Bid-SL;
	   TP = Bid-(takeProfit*digitMult*Point);
	   if (brokerIsECN) {
		   ticket = OrderSend(Symbol(),OP_SELL,lotSize,Bid,3,0.0,0.0,NULL,MAGIC,0,Blue);
		   if (ticket == -1) return (-1);
	      if (OrderModify(ticket,Bid,SL,TP,0,Blue) == 1) return (ticket);
	   } else {
	      return (OrderSend(Symbol(),OP_SELL,lotSize,Bid,3,SL,TP,NULL,MAGIC,0,Blue));
	   } 
	} else {
	   Print("openTrade(): Unable to trade: unknown trade direction.");
		return (-2);
	}
}

int closeTrade(int ticket) {
	if (OrderSelect(ticket, SELECT_BY_TICKET) == false) return (-1);
	
	bool done = false;
	while (!done) {
	   while (IsTradeContextBusy()) Sleep(100);
      done = OrderClose(ticket,lotSize,OrderClosePrice(),3,Brown);
   }
	
	return (0);
}

int start() {
	if (TSCheck() == -1) { Print("Unable to check trailing stop."); return (-1); }
	
   if (TSAvail() != true) return (0);
	
	checkPendingLevels();
	
	datetime time = Time[0];
	if (TimeHour(time) != londonHour && TimeHour(time) != NYHour) return (0);
	if (TimeMinute(time) != 0) { tradedThisMinute = false; return (0); }
	if (tradedThisMinute) return (0);
	
	if (Volume[0] == 1) Print("Tony112 waiting for movement.");
	
	if (MathAbs(Close[0] - Open[0]) >= movementThreshold*digitMult*Point) {
	   Print("Movement reached "+movementThreshold+" pip threshold: opening trade.");
		int ticket = openTrade(MathSign(Close[0] - Open[0]));
		if (ticket == -1) { Print("Unable to trade: openTrade() reported error "+GetLastError()); if (GetLastError()==4109) Print("Must set \"Allow live trading\""); return (-1); }
		tradedThisMinute = true;
		if (TSOpen(ticket) != 0) { Print("Unable to trade: TSOpen() returned non-zero."); closeTrade(ticket); return (-1); }
	}
	
	return (0);
}

