//+------------------------------------------------------------------+
//|                                   Pip Digger's Night Scalper.mq4 |
//|                                      Copyright © 2013, PipDigger |
//|                                      http://worldwide-invest.org |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2013, PipDigger"
#property link      "http://worldwide-invest.org/"

#include <WinUser32.mqh>

#import "kernel32.dll"
int GetTimeZoneInformation(int& TIME_ZONE_INFORMATION[]);
void GetSystemTime(int& SYSTEMTIME[]);
#import

// EA name and version
string EA_Name									= "Pip Digger\'s Night Scalper";
string EA_version								= "0.7.4.2";

// externals
extern string EA_Configuration         = "==== EA Configuration ====";
extern int    Magic                    = -1;
extern string BotComment               = "pdns0.7.4.2";
extern bool   AutoConfig					= true;
extern string TradeSettings            = "==== Trade settings ====";
extern int	  MaxTrades						= 2;
extern double MaxSpread                = 0.0;
extern int    Slippage                 = 1;
extern string MoneyManagement          = "==== Money Management ====";
extern double LotSize                  = 0.0;
extern double Risk                     = 0.0;
extern bool   RiskBooster					= true;
extern double BoosterBase					= 0.1;
extern double BoosterPower					= 8;
extern string TimeSettings             = "==== Time settings ====";
extern bool   AutoGMT						= true;
extern bool   AdjustDST						= false;
extern int    GMT_Offset               = 0;
extern string GMT_StartHrs             = "21:30";
extern string GMT_StopHrs              = "23:16";
//extern int	  iGMT_ForceCloseHrs			= 0215;
extern string GMT_ForceCloseHrs			= "08:30";
extern bool   TradeMondayMorning			= true;
extern bool   TradeFridayNight			= false;
extern string OpenSettings             = "==== Open / close settings ====";
extern int    WPR_Period					= 12;
extern double WPR_Open_Level				= 92;
 bool   WPR_DblCycle					= false;
//extern int    WPR_Close_Period			= 10;
extern double WPR_Close_Level				= 16;
extern int    SL                       = 35;
extern double TP                       = 5.5;
extern double PartTP							= 4.5;
extern double PartCloseFraction			= 0.35;
extern bool   OpenBetterPriceOnly		= true;
extern double OpenExtraPips				= 0.5;
extern double OpenPipStep					= 2.0;

// globals
double MinLot, MaxLot, LotStep, StpLvl;
double Spread[101], SortSpread[101];
int LotDigits;
datetime bt = 0;
int ERR_GMT_Offset = 0;


int init()
{
   int i;
	
	MinLot = MarketInfo(Symbol(), MODE_MINLOT);
	MaxLot = MarketInfo(Symbol(), MODE_MAXLOT);
   LotStep = MarketInfo(Symbol(),MODE_LOTSTEP);
	StpLvl = MarketInfo(Symbol(), MODE_STOPLEVEL);
	LotDigits = MathLog(MarketInfo(Symbol(), MODE_LOTSTEP)) / MathLog(0.1);
	
   // Auto magic number generator
   if(Magic <= 0)
   	Magic = AutoMagic(-Magic);
   
   if(AutoConfig) {
   	if(!AutoSetParameters()) {
   	   Comment("Unsupported instrument! Aborting...");
   		MessageBox("Unsupported instrument! Aborting...", "Unsupported instrument", MB_OK|MB_ICONERROR);
   		while(true)
   			Sleep(1000);
   	}
   	if(Period() != PERIOD_M15) {
   	   Comment("Unsupported time frame! Put on M15 chart. Aborting...");
   		MessageBox("Unsupported time frame! Put on M15 chart. Aborting...", "Unsupported time frame", MB_OK|MB_ICONERROR);
   		while(true)
   			Sleep(1000);
   	}
   }
   
   // comment out the following to perform force close hrs optimization
   /*GMT_ForceCloseHrs = DoubleToStr(iGMT_ForceCloseHrs, 0);
   if(StringLen(GMT_ForceCloseHrs) == 3)
   	GMT_ForceCloseHrs = StringConcatenate("0", GMT_ForceCloseHrs);
   GMT_ForceCloseHrs = StringConcatenate(StringSubstr(GMT_ForceCloseHrs, 0, 2), ":", StringSubstr(GMT_ForceCloseHrs, 2, 2));*/
   
   MaxSpread *= Point;   
   
	if(Digits%2==1) {
		MaxSpread *= 10.0;
		Slippage	*= 10;
		SL *= 10;
		TP *= 10.0;
		PartTP *= 10.0;
		OpenExtraPips *= 10.0;
		OpenPipStep *= 10.0;
	}
	
	if(MaxTrades < 0)
		MaxTrades = 0;
   
   if(!RiskBooster) {
   	BoosterBase = 1.0;
   	BoosterPower = 0.0;
   }
   
   if(AutoGMT)
   	if(IsTesting()) {
   		AutoGMT = false;
   		Print("WARNING: AutoGMT does not work in backtester! Using manual GMT_Offset = ", GMT_Offset);
   	} else if(!IsDllsAllowed()) {
   		Comment("Please enable DLLs for AutoGMT to work! Aborting...");
   		MessageBox("Please enable DLLs for AutoGMT to work! Aborting...", "Please enable DLLs", MB_OK|MB_ICONERROR);
   		while(true)
   			Sleep(1000);
   	} else
   		GMT_Offset = GetGMT_Offset();
   
   if(!IsTesting())
   	AdjustDST = true;
	
	for(i = 0; i < 101; i++)
		Spread[i] = Ask - Bid;
	
	if(StringLen(BotComment) == 0)
		BotComment = EA_Name;
   
   return(0);
}

int deinit()
{
	return(0);
}

int start()
{
	static bool OpenBuy = false, OpenSell = false, CloseBuy = false, CloseSell = false;
	static double PrevAsk, PrevBid, BuyOpenRefPrc, SellOpenRefPrc, WPR;
	static double LastBuyPrc, LastSellPrc;
	double x;
	int i, nbuy, nsell;
	//int i, nbuy = -1, nsell = -1;
	static int ticket = -1;
	
	// run every new bar openening
	if(bt != Time[0]) {
		// check if DST status has changed
		if(AutoGMT)
			if(TimeDayOfWeek(bt) > 4 && DayOfWeek() < 2)
				GMT_Offset = GetGMT_Offset();
		
		bt = Time[0];
		
		OpenBuy = false;
		OpenSell = false;
		CloseBuy = false;
		CloseSell = false;
		
		WPR = iWPR(NULL, 0, WPR_Period, 1);
		
		// check for close conditions
		if(WPR > -WPR_Close_Level)
			CloseBuy = true;
		else if(WPR < -100 + WPR_Close_Level)
			CloseSell = true;
		if(IsForceCloseTime()) {
			CloseBuy = true;
			CloseSell = true;
		}
		
		// check for open conditions
		if(MaxTrades > 0) { // get number of open trades
			nbuy = 0;
			nsell = 0;
			for(i = OrdersTotal() - 1; i >= 0; i--)
				if(OrderSelect(i, SELECT_BY_POS))
					if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
						switch(OrderType()) {
							case OP_BUY:
								nbuy++;
								break;
							case OP_SELL:
								nsell++;
								break;
						}
		} // end get number of open trades
		if(WPR_DblCycle)
			WPR = WPR_DblCycle();
		if(WPR < -WPR_Open_Level && nbuy < MaxTrades && !CloseBuy) {
			OpenBuy = true;
			BuyOpenRefPrc = MathMin(PrevAsk, Ask);
		} else if(WPR > -100 + WPR_Open_Level && nsell < MaxTrades && !CloseSell) {
			OpenSell = true;
			SellOpenRefPrc = MathMax(PrevBid, Bid);
		}
	} // end run every new bar openening
	
	// save Ask and Bid ticks for next run of start()
	PrevAsk = Ask;
	PrevBid = Bid;
	
	// close open positions
	if(CloseBuy || CloseSell) {
		nbuy = 0;
		nsell = 0;
		for(i = OrdersTotal() - 1; i >= 0; i--)
			if(OrderSelect(i, SELECT_BY_POS))
				if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
					if(OrderType() == OP_BUY && CloseBuy) {
						nbuy++;
						if(OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, Blue))
							nbuy--;
					} else if(OrderType() == OP_SELL && CloseSell) {
						nsell++;
						if(OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, Red))
							nsell--;
					}
		if(nbuy == 0)
			CloseBuy = false;
		if(nsell == 0)
			CloseSell = false;
	}
	
	if(ERR_GMT_Offset>0) {				// in case GetGMT_Offset returned any errors...
		GMT_Offset = GetGMT_Offset();	// ... try again to get GMT Offset
		if(ERR_GMT_Offset>=10)			// if retry count reached...
			ERR_GMT_Offset = 0;			// ... give up
	}
	
	// open new positions
	if(IsTradeTime() && Ask - Bid <= MaxSpread) {
		if(OpenBuy) {
			nbuy = 0;
			LastBuyPrc = 10.0 * Ask;
			if(OpenBetterPriceOnly || RiskBooster)
				for(i = OrdersTotal() - 1; i >= 0; i--)
					if(OrderSelect(i, SELECT_BY_POS))
						if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == OP_BUY) {
							nbuy++;
							if(OpenBetterPriceOnly)
								LastBuyPrc = MathMin(LastBuyPrc, OrderOpenPrice());
						}
			if(Ask < BuyOpenRefPrc - OpenExtraPips * Point && Ask < LastBuyPrc - OpenPipStep * Point) {
				ticket = OrderSend(Symbol(), OP_BUY, GetLot(MathPow(BoosterBase, nbuy) * MathPow(-WPR / WPR_Open_Level, BoosterPower)), Ask, Slippage, 0.0, 0.0, BotComment, Magic, 0, Blue);
				if(ticket != -1) {
					OrderSelect(ticket, SELECT_BY_TICKET);
					LastBuyPrc = OrderOpenPrice();
					OpenBuy = false;
				}
			}
		}
		if(OpenSell) {
			nsell = 0;
			LastSellPrc = 0.0;
			if(OpenBetterPriceOnly || RiskBooster)
				for(i = OrdersTotal() - 1; i >= 0; i--)
					if(OrderSelect(i, SELECT_BY_POS))
						if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == OP_SELL) {
							nsell++;
							if(OpenBetterPriceOnly)
								LastSellPrc = MathMax(LastSellPrc, OrderOpenPrice());
						}
			if(Bid > SellOpenRefPrc + OpenExtraPips * Point && Bid > LastSellPrc + OpenPipStep * Point) {
				ticket = OrderSend(Symbol(), OP_SELL, GetLot(MathPow(BoosterBase, nsell) * MathPow((100 + WPR) / WPR_Open_Level, BoosterPower)), Bid, Slippage, 0.0, 0.0, BotComment, Magic, 0, Red);
				if(ticket != -1) {
					OrderSelect(ticket, SELECT_BY_TICKET);
					LastSellPrc = OrderOpenPrice();
					OpenSell = false;
				}
			}
		}
	}
	
   // set hard TP & SL for last opened trade
	if(OrderSelect(ticket, SELECT_BY_TICKET))
		if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
			switch(OrderType()) {
				case OP_BUY:
					if(OrderModify(ticket, OrderOpenPrice(), Bid - SL * Point, Ask + TP * Point, 0, Blue))
						ticket = -1;
					break;
				case OP_SELL:
					if(OrderModify(ticket, OrderOpenPrice(), Ask + SL * Point, Bid - TP * Point, 0, Red))
						ticket = -1;
					break;
			}
	
	// check for partial close
	if(PartTP > 0.0)
		for(i = OrdersTotal() - 1; i >= 0; i--)
			if(OrderSelect(i, SELECT_BY_POS))
				if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
					switch(OrderType()) {
						case OP_BUY:
							if(Bid - OrderOpenPrice() >= PartTP * Point) // PA beyond partial Close
								if(StringFind(OrderComment(), BotComment) != -1) // Order has original comment (not partially closed yet!)
									//OrderClose(OrderTicket(), NormalizeDouble(MathMin(MathMax(MinLot, OrderLots() * PartCloseFraction), MaxLot), LotDigits), Bid, Slippage, Blue);
									OrderClose(OrderTicket(), LotsRound(OrderLots() * PartCloseFraction), Bid, Slippage, Blue);
							break;
						case OP_SELL:
							if(OrderOpenPrice() - Ask >= PartTP * Point) // PA beyond partial Close
								if(StringFind(OrderComment(), BotComment) != -1) // Order has original comment (not partially closed yet!)
									//OrderClose(OrderTicket(), NormalizeDouble(MathMin(MathMax(MinLot, OrderLots() * PartCloseFraction), MaxLot), LotDigits), Ask, Slippage, Red);
									OrderClose(OrderTicket(), LotsRound(OrderLots() * PartCloseFraction), Ask, Slippage, Red);
							break;
					}
	
	// update HUD
	if(!IsTesting() || IsVisualMode())
		HUD();
	
	// update PrevAsk and PrevBid if tickvalues have changed
	if(RefreshRates()) {
		PrevAsk = MathMin(PrevAsk, Ask);
		PrevBid = MathMax(PrevBid, Bid);
	}
	
	return(0);
}

// return lotsize for next trade
double GetLot(double x = 1.0)
{
	if(LotSize > 0.0)
		x = LotSize;
	else
		x *= AccountFreeMargin() * (Risk/100.0) / (SL * MarketInfo(Symbol(), MODE_TICKVALUE) * Point / MarketInfo(Symbol(), MODE_TICKSIZE));
	
	return(LotsRound(x));
}

//+-----------------------------------------------------------------+
//| rounds arbitrary lots to valid lot size for trading,            |
//| respecting MODE_MINLOT, MODE_MAXLOT, MODE_LOTSTEP               |
//| mode = -1 -> round down                                         |
//| mode =  0 -> round to nearest                                   |
//| mode = +1 -> round up                                           |
//+-----------------------------------------------------------------+
double LotsRound(double lots, int mode = 0)
{
	switch(mode) {
		case -1:
			lots = MathFloor(lots / LotStep) * LotStep;
			break;
		case 1:
			lots = MathCeil(lots / LotStep) * LotStep;
			break;
		default:
			lots = MathRound(lots / LotStep) * LotStep;
	}
	
	if(lots < MinLot)
		lots = MinLot;
	else if(lots > MaxLot)
		lots = MaxLot;
	
	return(lots);
}

// return true if server time is between StartHrs and StopHrs (with GMT adjustment)
bool IsTradeTime()
{
	datetime GMT_Server, GMT_Start, GMT_Stop;
	int DST_Adjust = 0;
	
	if(AdjustDST && IsEDT())
		DST_Adjust = 3600;
	
	GMT_Server = TimeCurrent() - GMT_Offset * 3600;
	GMT_Start = StrToTime(GMT_StartHrs); // usually 22:00 = NY close (no DST)
	GMT_Stop = StrToTime(GMT_StopHrs); // usually 23:XX = 1h XXm after NY close (no DST), but could also be next GMT day!
	
	if(GMT_Stop < GMT_Start) { // Adjust GMT_Start or GMT_Stop because not on same day!
		GMT_Stop += 86400;
		if(GMT_Stop - DST_Adjust - GMT_Server >= 86400) {
			GMT_Start -= 86400;
			GMT_Stop -= 86400;
		}
	}
	
	if(GMT_Server >= GMT_Start - DST_Adjust)
		if((!TradeFridayNight && TimeDayOfWeek(GMT_Server) == 5) || (!TradeMondayMorning && TimeDayOfWeek(GMT_Server) <= 1))
			return(false);
	
	//Print("GMT_Server : ", TimeToStr(GMT_Server, TIME_DATE|TIME_MINUTES)," - GMT_Start : ", TimeToStr(GMT_Start, TIME_DATE|TIME_MINUTES), " - GMT_Stop : ", TimeToStr(GMT_Stop, TIME_DATE|TIME_MINUTES));
	
	if(GMT_Server >= GMT_Start - DST_Adjust && GMT_Server <= GMT_Stop - DST_Adjust)
		return(true);
	else
		return(false);
}

// return true if server time is between ForceCloseHrs and StartHrs (with GMT adjustment)
bool IsForceCloseTime()
{
	datetime GMT_Server, GMT_ForceClose, GMT_Start;
	int DST_Adjust = 0;
	
	if(AdjustDST && IsEDT())
		DST_Adjust = 3600;
	
	GMT_Server = TimeCurrent() - GMT_Offset * 3600;
	GMT_ForceClose = StrToTime(GMT_ForceCloseHrs);
	GMT_Start = StrToTime(GMT_StartHrs);
	
	if(GMT_Start < GMT_ForceClose)
		GMT_Start += 86400;
	
	if(GMT_Server >= GMT_ForceClose - DST_Adjust && GMT_Server < GMT_Start - DST_Adjust)
		return(true);
	else
		return(false);
}

// return average of n previous WPR values
double WPR_DblCycle()
{
	double WPR_2, WPR_1;
	
	WPR_2 = iWPR(NULL, 0, WPR_Period, 2);
	WPR_1 = iWPR(NULL, 0, WPR_Period, 1);
	
	if(WPR_2 < -50.0 && WPR_1 <= -WPR_Open_Level)
		WPR_1 = -MathSqrt(WPR_1 * WPR_2);
	else if(WPR_2 > -50.0 && WPR_1 >= -100 + WPR_Open_Level)
		WPR_1 = -MathSqrt(WPR_1 * WPR_2);
	else
		WPR_1 = -50.0;

	return(WPR_1);
}

// automatic number generator
int AutoMagic(int salt = 0)
{
   int m;
   string s;
   
	s = StringConcatenate(EA_Name, Symbol(), Period(), DoubleToStr(AccountNumber(), 0), AccountCompany(), DoubleToStr(MathMod(100.0 + MathAbs(salt), 100.0), 0));
      
   m = djb2(s);
   if(m < 0) // invert m if negative (original djb2 is for unsigned int!)
   	m = -m;
  	m = MathMod(m, 1000000) + (m - MathMod(m, 1000000)) / 1000000;
  	if(m > 1000000)
  		m = MathMod(m, 1000000);
  	
  	return(m);
}

// djb2 hash
int djb2(string key){
	int i, h = 5381, c;
	for (i = 0; i < StringLen(key); i++){
		c = StringGetChar(key, i);
		h = (h << 5) + h + c;
	}
	return(h);
}

// Get GMT Offset by GetTimeZoneInformation() - modified from CrapperQuotes source
int GMT_Off_GetTimeZoneInformation()
{
	int a[43],offlocmin,n;
	
	n=TimeCurrent()-TimeLocal(); // server time - local time, in seconds
	
	switch (GetTimeZoneInformation(a)) {
		case 0: // no daylight saving in local time zone, or unkown
			offlocmin=a[0];
			break;
		case 1: // local system is operating in standard time (no daylight saving time)
			offlocmin=a[0];
			break;
		case 2: // local system is operating in daylight saving time
			offlocmin=a[0]+a[42];
			break;
	}
	
	return(MathRound(n/3600.0-offlocmin/60.0));
}

// Get GMT Offset by GetSystemTime() - unknown source
int GMT_Off_GetSystemTime()
{
	double GMT_Diff;
	int SYSTEMTIME[4];
	int nYear, nMonth, nDay, nHour, nMin, nSec, nMilliSec;
	string sMonth, sDay, sHour, sMin, sSec;
	string sUTC_Time;
	
	GetSystemTime(SYSTEMTIME);
	nYear     = SYSTEMTIME[0]&0x0000FFFF;
	nMonth    = SYSTEMTIME[0]>>16;
	nDay      = SYSTEMTIME[1]>>16;
	nHour     = SYSTEMTIME[2]&0x0000FFFF;
	nMin      = SYSTEMTIME[2]>>16;
	nSec      = SYSTEMTIME[3]&0x0000FFFF;
	nMilliSec = SYSTEMTIME[3]>>16;
	
	sMonth = 100 + nMonth;
	sMonth = StringSubstr(sMonth, 1);
	sDay   = 100 + nDay;
	sDay   = StringSubstr(sDay, 1);
	sHour  = 100 + nHour;
	sHour  = StringSubstr(sHour, 1);
	sMin   = 100 + nMin;
	sMin   = StringSubstr(sMin, 1);
	sSec   = 100 + nSec;
	sSec   = StringSubstr(sSec, 1);
	
	sUTC_Time = StringConcatenate(nYear,".",sMonth,".",sDay," ",sHour,":",sMin,":",sSec);
	
	GMT_Diff = TimeCurrent() - StrToTime(sUTC_Time);
	
	return(MathRound(GMT_Diff / 3600.0));
}

// Get GMT Offset in several possible ways
int GetGMT_Offset()
{
	bool IsPlausible1=false,IsPlausible2=false,IsIdentical=false; 
	int GMT_Offset1, GMT_Offset2;
	
	GMT_Offset1 = GMT_Off_GetSystemTime();
	GMT_Offset2 = GMT_Off_GetTimeZoneInformation();
	
	if(-12<=GMT_Offset1 && GMT_Offset1<=12)
		IsPlausible1=true;
	if(-12<=GMT_Offset2 && GMT_Offset2<=12)
		IsPlausible2=true;
	if(GMT_Offset1==GMT_Offset2)
		IsIdentical=true;
	
	if(IsIdentical && IsPlausible1) {
		ERR_GMT_Offset = 0;
		return(GMT_Offset1);
	}
	
	ERR_GMT_Offset++; // if not identical or not plausible, then increase error counter and return most suitable value
	
	if(IsPlausible1 && !IsPlausible2)
		return(GMT_Offset1);
	else if(!IsPlausible1 && IsPlausible2)
		return(GMT_Offset2);
	else // leave unchanged
		return(GMT_Offset);
}


// return true if current server time is in DST days for EST/EDT
bool IsEDT()
{
	datetime servertime;
	datetime EDTstart[9], EDTend[9];
	int i;
	
	EDTstart[0]	= D'2007.03.11';
	EDTend[0]	= D'2007.11.04';
	EDTstart[1]	= D'2008.03.09';
	EDTend[1]	= D'2008.11.02';
	EDTstart[2]	= D'2009.03.08';
	EDTend[2]	= D'2009.11.01';
	EDTstart[3]	= D'2010.03.14';
	EDTend[3]	= D'2010.11.07';
	EDTstart[4]	= D'2011.03.13';
	EDTend[4]	= D'2011.11.06';
	EDTstart[5]	= D'2012.03.11';
	EDTend[5]	= D'2012.11.04';
	EDTstart[6]	= D'2013.03.10';
	EDTend[6]	= D'2013.11.03';
	EDTstart[7]	= D'2014.03.09';
	EDTend[7]	= D'2014.11.02';
	EDTstart[8]	= D'2015.03.08';
	EDTend[8]	= D'2015.11.01';
	
	servertime = TimeCurrent();
	
	for(i = 0; i < 9; i++)
		if(servertime > EDTstart[i] && servertime < EDTend[i])
			return(true);
	
	return(false);
}

// HUD
void HUD()
{
	int i;
	static int iS = 0;
	double avgSpread = 0.0;
	string cmt = "\n", autoparams = "";
	
	if(iS == 100)
		iS = 0;
	Spread[iS] = Ask - Bid;
	iS++;
	ArrayCopy(SortSpread, Spread);
	ArraySort(SortSpread);
	for(i = 0; i < 101; i++)
		avgSpread += Spread[i];
	avgSpread /= 101.0;
	
	if(AutoConfig)
		autoparams = " (auto configured)";
	
	cmt = StringConcatenate(cmt, EA_Name, " v", EA_version, "\n");
	cmt = StringConcatenate(cmt, "=====\n");
	cmt = StringConcatenate(cmt, "Instrument : ");
	if(StringFind(Symbol(),"USDCAD") != -1)
		cmt = StringConcatenate(cmt, "Loonie", autoparams,"\n");
	else if(StringFind(Symbol(),"GBPUSD") != -1)
		cmt = StringConcatenate(cmt, "Cable", autoparams,"\n");
	else if(StringFind(Symbol(),"EURCAD") != -1)
		cmt = StringConcatenate(cmt, "EURCAD", autoparams,"\n");
	else if(StringFind(Symbol(),"EURGBP") != -1)
		cmt = StringConcatenate(cmt, "Chunnel", autoparams,"\n");
	else
		cmt = StringConcatenate(cmt, Symbol(), " (unrecognized)\n");
	cmt = StringConcatenate(cmt, "=====\n");
	cmt = StringConcatenate(cmt, "Risk : ", Risk, "\n");
	cmt = StringConcatenate(cmt, "=====\n");
	cmt = StringConcatenate(cmt, "Magic : ", Magic, "\n");
	cmt = StringConcatenate(cmt, "=====\n");
	cmt = StringConcatenate(cmt, "Local Time : ", TimeToStr(TimeLocal(), TIME_MINUTES|TIME_SECONDS), "\n");
	cmt = StringConcatenate(cmt, "Server Time : ", TimeToStr(TimeCurrent(), TIME_MINUTES|TIME_SECONDS), "\n");
	cmt = StringConcatenate(cmt, "GMT Time : ", TimeToStr(TimeCurrent() - GMT_Offset * 3600, TIME_MINUTES|TIME_SECONDS), "\n");
	if(AutoGMT)
		cmt = StringConcatenate(cmt,"GMT Offset (auto) : ", GMT_Offset, "\n");
	else
		cmt = StringConcatenate(cmt,"GMT Offset (manual) : ", GMT_Offset, "\n");
	if(IsEDT()) {
		cmt = StringConcatenate(cmt,"NY market is observing EDT / DST\n");
		if(AdjustDST)
			cmt = StringConcatenate(cmt,"Trading hours adjusted for DST.\n");
		else
			cmt = StringConcatenate(cmt,"WARNING: Trading hours NOT adjusted for DST. ONLY FOR BACKTESTING!\n");
	} else
		cmt = StringConcatenate(cmt,"NY market is observing EST (no DST)\n");
	cmt = StringConcatenate(cmt, "=====\n");
	cmt = StringConcatenate(cmt,"Current / Max spread : ", DoubleToStr(Ask - Bid, Digits), " / ", DoubleToStr(MaxSpread, Digits) ,"\n");
	cmt = StringConcatenate(cmt,"Average spread : ", DoubleToStr(avgSpread, Digits), "\n");
	cmt = StringConcatenate(cmt,"Median spread : ", DoubleToStr(SortSpread[50], Digits), "\n");
	cmt = StringConcatenate(cmt, "=====\n");
	if(IsTradeTime())
		cmt = StringConcatenate(cmt,"Trade session OPEN.\n");
	else if(IsForceCloseTime())
		cmt = StringConcatenate(cmt,"Trade session CLOSED.\n");
	else
		cmt = StringConcatenate(cmt,"Trade session CLOSING.\n");

	Comment(cmt);
}

bool AutoSetParameters()
{
	if(StringFind(Symbol(), "USDCAD") != -1) { // Loonie
		MaxTrades				= 2;
		if(MaxSpread <= 0.0)
			MaxSpread			= 2.5;
		if(Risk == 0.0)
			Risk					= 4.0;
		else if(Risk < 0.0)
			Risk					*= -4.0;
		RiskBooster				= true;
		BoosterBase				= 0.15;
		BoosterPower			= 10.0;
		GMT_StartHrs			= "21:30";
		GMT_StopHrs				= "23:16";
		GMT_ForceCloseHrs		= "08:30";
		TradeMondayMorning	= true;
		TradeFridayNight		= false;
		WPR_Period				= 12;
		WPR_Open_Level			= 92.0;
		WPR_Close_Level		= 16.0;
		SL							= 35;
		TP							= 5.5;
		PartTP					= 4.5;
		PartCloseFraction		= 0.25;
		OpenBetterPriceOnly	= true;
		OpenExtraPips			= 0.5;
		OpenPipStep				= 3.0;
	} else if(StringFind(Symbol(), "GBPUSD") != -1) { // Cable
		MaxTrades				= 2;
		if(MaxSpread <= 0.0)
			MaxSpread			= 2.5;
		if(Risk == 0.0)
			Risk					= 3.0;
		else if(Risk < 0.0)
			Risk					*= -3.0;
		RiskBooster				= true;
		BoosterBase				= 0.15;
		BoosterPower			= 1.0;
		GMT_StartHrs			= "22:00";
		GMT_StopHrs				= "23:20";
		GMT_ForceCloseHrs		= "09:45";
		TradeMondayMorning	= true;
		TradeFridayNight		= false;
		WPR_Period				= 9;
		WPR_Open_Level			= 81.0;
		WPR_Close_Level		= 40.0;
		SL							= 35;
		TP							= 20.0;
		PartTP					= 7.0;
		PartCloseFraction		= 0.6;
		OpenBetterPriceOnly	= true;
		OpenExtraPips			= 1.0;
		OpenPipStep				= 10.0;
	} else if(StringFind(Symbol(), "EURCAD") != -1) { // EURCAD
		MaxTrades				= 2;
		if(MaxSpread <= 0.0)
			MaxSpread			= 4.0;
		if(Risk == 0.0)
			Risk					= 4.5;
		else if(Risk < 0.0)
			Risk					*= -4.5;
		RiskBooster				= true;
		BoosterBase				= 0.4;
		BoosterPower			= 2.0;
		GMT_StartHrs			= "22:00";
		GMT_StopHrs				= "23:20";
		GMT_ForceCloseHrs		= "08:30";
		TradeMondayMorning	= true;
		TradeFridayNight		= false;
		WPR_Period				= 6;
		WPR_Open_Level			= 83.0;
		WPR_Close_Level		= 28.0;
		SL							= 55;
		TP							= 9.0;
		PartTP					= 6.0;
		PartCloseFraction		= 0.6;
		OpenBetterPriceOnly	= true;
		OpenExtraPips			= 0.8;
		OpenPipStep				= 5.0;
	} else if(StringFind(Symbol(), "EURGBP") != -1) { // Chunnel
		MaxTrades				= 1;
		if(MaxSpread <= 0.0)
			MaxSpread			= 2.5;
		if(Risk == 0.0)
			Risk					= 4.0;
		else if(Risk < 0.0)
			Risk *= -4.0;
		RiskBooster				= true;
		BoosterBase				= 1.0;
		BoosterPower			= 1.5;
		GMT_StartHrs			= "22:30";
		GMT_StopHrs				= "00:05";
		GMT_ForceCloseHrs		= "08:30";
		TradeMondayMorning	= true;
		TradeFridayNight		= false;
		WPR_Period				= 12;
		WPR_Open_Level			= 93.0;
		WPR_Close_Level		= 27.0;
		SL							= 40;
		TP							= 12.0;
		PartTP					= 10.0;
		PartCloseFraction		= 0.4;
		OpenBetterPriceOnly	= true;
		OpenExtraPips			= 0.7;
		OpenPipStep				= 1.0;
	} else {
		Print("WARNING: Unsupported instrument, using manual defaults.");
		return(false);
	}
	
	return(true);
}

