//+------------------------------------------------------------------+
//|                                                   3barHiLoEA.mq4 |
//|                                    Copyright © 2007, MQL Service |
//|                                        http://www.mqlservice.com |
//+------------------------------------------------------------------+
//| $Id: //mqlservice/mt4files/experts/3barHiLoEA.mq4#3 $
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MQL Service"
#property link      "http://www.mqlservice.com"
#include <stdlib.mqh>

extern double    Lots         = 0.1;
extern int       StartTimeGMT = 400;       // Start trading at 4:00 GMT
extern int       StopTimeGMT  = 900;       // Stop trading at  9:00 GMT
extern int       Hi_Timeframe = PERIOD_H4; // Higher timeframe to test
extern int       Lo_Timeframe = PERIOD_H1; // Lower timeframe to test
extern int       NoBars       = 3;         // Number of bars
extern int       Pips_offset  = 1;         // Offset for entry above 3 Bars
extern int       DaysExit     = 1;         // Exit the same day close (2-next day close, etc).
extern int       StopLoss     = 30;   // 0 deactivates Stop Loss
extern int       TrailingStop = 0;    // 0 deactivates Trailing Stop
extern int       TakeProfit   = 0;    // 0 deactivates Take Profit
extern int       GainForBE    = 0;    // How many pips will trigger Break Even
extern int       PipsBE       = 0;    // Level at which Break Even will be put
extern int       Slippage     = 3;

#define MAGIC 200707012

int init()
{
  Comment("Waiting for the first tick...");
  return(0);
}

int deinit()
{
  Comment(WindowExpertName()," finished.");
  return(0);
}

int start()
{
  if(Bars<100)
  {
    Comment("Waiting for bars...");
    return(0);
  }

  int _gmt_offset = MathRound(TimeZoneServer());
  datetime _StartTime = StartTimeGMT + _gmt_offset;
  datetime _StopTime  = StopTimeGMT  + _gmt_offset;
  return(_3barHiLo(Symbol(), Period(), MAGIC, Lots, StopLoss, TrailingStop, TakeProfit, GainForBE, PipsBE, Slippage,
                   _StartTime, _StopTime, Hi_Timeframe, Lo_Timeframe, NoBars, Pips_offset, DaysExit));
}

int _3barHiLo(string symbol, int period, int magic, double lots, int stoploss, int trailingstop, int takeprofit, 
              int gainforbe, int pipsbe, int slippage, int starttime, int stoptime,
              int hi_timeframe=PERIOD_H4, int lo_timeframe=PERIOD_H1, int nobars=3, int pips_offset=1, int daysexit=0)
{
  // Internals
  int _Digits = MarketInfo(symbol, MODE_DIGITS), i;
  if(_Digits == 0) _Digits = 4;
  double _Point = MarketInfo(symbol, MODE_POINT);
  if(NormalizeDouble(_Point, _Digits) == 0.0) _Point = Point;
  double _Bid = MarketInfo(symbol, MODE_BID);
  double _Ask = MarketInfo(symbol, MODE_ASK);
  int   _iBid = MathRound(_Bid/_Point);
  string _cm = "Time GMT: " + TimeToStr(TimeGMT());
  bool _can_open = true;

  // TRADING TIMES If not in trading times close all positions and exit
  datetime _now = TimeCurrent();
  datetime _st = MathFloor(_now/86400)*86400 + MathRound(starttime/100)*3600+MathMod(starttime, 100)*60;
  datetime _en = MathFloor(_now/86400)*86400 + MathRound(stoptime/100)*3600+MathMod(stoptime, 100)*60;
  while(_st <= _now) _st += 86400;
  while(_en <= _now) _en += 86400;
  if(_st<_en) _can_open = false;
  // END OF TRADING TIMES

  // Signals
  static bool _long  = false;
  static bool _short = false;
  bool _3bl = false;
  bool _3bs = false;
  i=0;
  while(!(_3bl || _3bs || i>Bars-5))
  {
    // Braek 3 bar low reversal
    if(MathRound(iHigh(symbol, hi_timeframe, i)/_Point) >= MathRound(iHigh(symbol, hi_timeframe, iHighest(symbol, hi_timeframe, MODE_HIGH, nobars, 1+i))/_Point))
    if(MathRound(iHigh(symbol, hi_timeframe, i+2)/_Point) < MathRound(iHigh(symbol, hi_timeframe, i+1)/_Point))
    if(MathRound(iHigh(symbol, hi_timeframe, i+2)/_Point) < MathRound(iHigh(symbol, hi_timeframe, i+3)/_Point))
    if(MathRound(iLow(symbol, hi_timeframe, i+2)/_Point)  < MathRound(iLow(symbol, hi_timeframe, i+1)/_Point))
    if(MathRound(iLow(symbol, hi_timeframe, i+2)/_Point)  < MathRound(iLow(symbol, hi_timeframe, i+3)/_Point)) _3bl = true;
    // Break 3 bar high reversal
    if(MathRound(iLow(symbol, hi_timeframe, i)/_Point) <= MathRound(iLow(symbol, hi_timeframe, iLowest(symbol, hi_timeframe, MODE_LOW, nobars, 1+i))/_Point))
    if(MathRound(iHigh(symbol, hi_timeframe,i+2)/_Point)  > MathRound(iHigh(symbol, hi_timeframe, i+1)/_Point))
    if(MathRound(iHigh(symbol, hi_timeframe, i+2)/_Point) > MathRound(iHigh(symbol, hi_timeframe, i+3)/_Point))
    if(MathRound(iLow(symbol, hi_timeframe, i+2)/_Point)  > MathRound(iLow(symbol, hi_timeframe, i+1)/_Point))
    if(MathRound(iLow(symbol, hi_timeframe, i+2)/_Point)  > MathRound(iLow(symbol, hi_timeframe, i+3)/_Point)) _3bs = true;
    i++;
  }
  if(_3bl)
    if(_iBid >= MathRound(iHigh(symbol, lo_timeframe, iHighest(symbol, lo_timeframe, MODE_HIGH, nobars,1))/_Point)+pips_offset)
      _long = _can_open;
  if(_3bs)
    if(_iBid <= MathRound(iLow(symbol, lo_timeframe, iLowest(symbol, lo_timeframe, MODE_LOW, nobars,1))/_Point)-pips_offset)
      _short = _can_open;
  if(!_ip(OP_BUY, symbol, magic))
  if(_long) Print("Bid: ", _iBid,"; H4: ",MathRound(iHigh(symbol, hi_timeframe, iHighest(symbol, hi_timeframe, MODE_HIGH, nobars, 1))/_Point),"; H1: ",
                  MathRound(iHigh(symbol, lo_timeframe, iHighest(symbol, lo_timeframe, MODE_HIGH, nobars,1))/_Point));
  if(!_ip(OP_SELL, symbol, magic))
  if(_short) Print("Bid: ",_iBid,"; H4: ",MathRound(iLow(symbol, hi_timeframe, iLowest(symbol, hi_timeframe, MODE_LOW, nobars, 1))/_Point),"; H1: ",
                   MathRound(iLow(symbol, lo_timeframe, iLowest(symbol, lo_timeframe, MODE_LOW, nobars,1))/_Point));
  // Signals

  // S&R
  bool _send_ok = true;
  if(_long){
    if(_ip(OP_SELL, symbol, magic))
      _OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), slippage, Red);
    if(!_ip(OP_BUY, symbol, magic))
      _send_ok = _OrderSend(symbol, OP_BUY, _nv(symbol, lots), _Ask, slippage, _sl(OP_BUY, symbol, _Bid, stoploss), _tp(OP_BUY, symbol, _Bid, takeprofit),
                            WindowExpertName(), magic, 0, Blue) > 0;
  }
  if(_short){
    if(_ip(OP_BUY, symbol, magic))
      _OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), slippage, Blue);
    if(!_ip(OP_SELL, symbol, magic))
      _send_ok = _OrderSend(symbol, OP_SELL, _nv(symbol, lots), _Bid, slippage, _sl(OP_SELL, symbol, _Ask, stoploss), _tp(OP_SELL, symbol, _Ask, takeprofit),
                            WindowExpertName(), magic, 0, Red) > 0;
  }
  // S&R
  
  if(_ip(OP_BUY, symbol, magic))
  {
    int diff = MathFloor((TimeCurrent()-MathFloor(OrderOpenTime()/86400)*86400)/86400.0);
    if(diff>=daysexit) _fa(symbol, magic);
  }
  if(_ip(OP_SELL, symbol, magic))
  {
    diff = MathFloor((TimeCurrent()-MathFloor(OrderOpenTime()/86400)*86400)/86400.0);
    if(diff>=daysexit) _fa(symbol, magic);
  }

  // TrailingStop
  if(trailingstop > 0)
    for(i=0; i < OrdersTotal(); i++)
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
        if(OrderSymbol() == symbol)
          if(OrderMagicNumber() == magic)
            if(OrderType() == OP_BUY){
              if(MathRound((OrderClosePrice()-OrderStopLoss())/_Point) > trailingstop)
                if(!OrderModify(OrderTicket(), OrderOpenPrice(), OrderClosePrice()-trailingstop*_Point, OrderTakeProfit(),
                                OrderExpiration(), Blue))
                  Print("OrderModify(OP_BUY) error - ", ErrorDescription(GetLastError()));    
            }else if(OrderType() == OP_SELL){
              if((MathRound((OrderStopLoss()-OrderClosePrice())/_Point) > trailingstop)||(OrderStopLoss()<_Bid))
                if(!OrderModify(OrderTicket(), OrderOpenPrice(), OrderClosePrice()+trailingstop*_Point, OrderTakeProfit(),
                                OrderExpiration(), Red))
                  Print("OrderModify(OP_SELL) error - ", ErrorDescription(GetLastError()));    
            }
      }else
        Print("OrderSelect() error - ", ErrorDescription(GetLastError()));
  // TrailingStop

  // BreakEven
  if(gainforbe > 0)
    for(i=0; i < OrdersTotal(); i++)
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
        if(OrderSymbol() == symbol)
          if(OrderMagicNumber() == magic)
            if(OrderType() == OP_BUY){
              if(MathRound((OrderClosePrice()-OrderOpenPrice())/_Point) >= gainforbe)
              if(MathRound((OrderStopLoss()-OrderOpenPrice())/_Point) < pipsbe)
                if(!OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()+pipsbe*_Point, OrderTakeProfit(),
                                OrderExpiration(), Blue))
                  Print("OrderModify(OP_BUY) error - ", ErrorDescription(GetLastError()));    
            }else if(OrderType() == OP_SELL){
              if(MathRound((OrderOpenPrice()-OrderClosePrice())/_Point) >= gainforbe)
              if(MathRound((OrderOpenPrice()-OrderStopLoss())/_Point) < pipsbe)
                if(!OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()-pipsbe*_Point, OrderTakeProfit(),
                                OrderExpiration(), Red))
                  Print("OrderModify(OP_SELL) error - ", ErrorDescription(GetLastError()));    
            }
      }else
        Print("OrderSelect() error - ", ErrorDescription(GetLastError()));
  // BreakEven

  if(_send_ok){
    _long = false;
    _short = false;
  }
  Comment(_cm);
  return(0);
}

bool _ip(int type, string symbol, int magic)
{
  for(int i=OrdersTotal()-1; i >= 0; i--)
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
    if(OrderType() == type)
    if(OrderSymbol() == symbol)
    if(OrderMagicNumber() == magic)
      return(true);
    }else
      Print("OrderSelect() error - ", ErrorDescription(GetLastError()));
  return(false);
}

void _fa(string symbol, int magic, bool manualmode=false) // Flat all
{
  for(int i=OrdersTotal()-1; i >= 0; i--)
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
    if(OrderSymbol() == symbol)
    if(OrderMagicNumber() == magic||manualmode)
      if(OrderType() <= OP_SELL)
        _OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), Slippage, Gray);
      else
        _OrderDelete(OrderTicket());
    }else
      Print("OrderSelect() error - ", ErrorDescription(GetLastError()));
}

double _sl(int type, string symbol, double price, int stoploss, int offset=0)
{
  if(type == OP_BUY||type == OP_BUYSTOP||type == OP_BUYLIMIT)
    if(stoploss > 0)
      return(price-(stoploss+offset)*MarketInfo(symbol, MODE_POINT));
    else
      return(0.0);
  else if(type == OP_SELL||type == OP_SELLSTOP||type == OP_SELLLIMIT)
    if(stoploss > 0)
      return(price+(stoploss+offset)*MarketInfo(symbol, MODE_POINT));
    else
      return(0.0);    
  return(0.0);
}

double _tp(int type, string symbol, double price, int takeprofit, int offset=0)
{
  if(type == OP_BUY)
    if(takeprofit > 0||type == OP_BUYSTOP||type == OP_BUYLIMIT)
      return(price+(takeprofit+offset)*MarketInfo(symbol, MODE_POINT));
    else
      return(0.0);
  else if(type == OP_SELL||type == OP_SELLSTOP||type == OP_SELLLIMIT)
    if(takeprofit > 0)
      return(price-(takeprofit+offset)*MarketInfo(symbol, MODE_POINT));
    else
      return(0.0);
  return(0.0);
}

double _nv(string symbol, double lots, bool return_zero=false){
  // Adjust trade volume to broker. Take into account minimum & maximum position size.
  double step   = MarketInfo(symbol, MODE_LOTSTEP);
  double min    = MarketInfo(symbol, MODE_MINLOT);
  double max    = MarketInfo(symbol, MODE_MAXLOT);
  if(step > 0)
  if(max  > 0)
    if(return_zero)
      return(MathMin(MathRound(lots/step)*step, max));
    else if(min > 0) // When you don't want return 0 lots (default)
      return(MathMax(MathMin(MathRound(lots/step)*step, max), min));    
  return(lots);
}

#include <stderror.mqh>

int _OrderSend(string symbol, int cmd, double lots, double price, int slippage, double stoploss, double takeprofit,
               string comment, int magic, datetime expiration, color cl)
{
  int ticket = OrderSend(symbol, cmd, lots, price, slippage, stoploss, takeprofit, comment, magic, expiration, cl);
  if(ticket < 0){
    int err = GetLastError();
    Print("ERROR OrderSend #",err,": ", ErrorDescription(err),_strcmd(cmd, symbol, price));
  }
  return(ticket);
}

string _strcmd(int cmd, string symbol, double price)
{
  int _mode;
  int _d = MarketInfo(symbol, Digits);
  string _str = "";
  switch(cmd)
  {
    case OP_BUY: 
      _mode = MODE_ASK;
      _str  = "; BUY @"+DoubleToStr(price, _d);
      break;
    case OP_SELL:
      _mode = MODE_BID;
      _str  = "; BUY @"+DoubleToStr(price, _d);
      break;
    default:
      break;
  }
    
  _str = _str +"; market @" + DoubleToStr(MarketInfo(symbol, _mode), _d);
  return(_str);
}

bool _OrderClose(int ticket, double lots, double price, int slippage, color cl=CLR_NONE)
{
  bool result = OrderClose(ticket, lots, price, slippage, cl);
  if(!result){
    int err = GetLastError();
    Print("ERROR OrderClose #",err,": ", ErrorDescription(err));
  }
  return(result);
}

bool _OrderDelete(int ticket)
{
  bool result = OrderDelete(ticket);
  if(!result){
    int err = GetLastError();
    Print("ERROR OrderDelete #",err,": ", ErrorDescription(err));
  }
  return(result);
}

#import "kernel32.dll"
int  GetTimeZoneInformation(int& TZInfoArray[]);
#import

#define TIME_ZONE_ID_UNKNOWN   0
#define TIME_ZONE_ID_STANDARD  1
#define TIME_ZONE_ID_DAYLIGHT  2

// Local timezone in hours, adjusting for daylight saving
double TimeZoneLocal()
{
	int TZInfoArray[43];

	switch(GetTimeZoneInformation(TZInfoArray))
	{
	case TIME_ZONE_ID_UNKNOWN: 
		Print("Error obtaining PC timezone from GetTimeZoneInformation in kernel32.dll. Returning 0");
		return(0);

	case TIME_ZONE_ID_STANDARD:
		return(TZInfoArray[0]/(-60.0));
	
	case TIME_ZONE_ID_DAYLIGHT:
		return((TZInfoArray[0]+TZInfoArray[42])/(-60.0));
		
	default:
		Print("Unkown return value from GetTimeZoneInformation in kernel32.dll. Returning 0");
		return(0);
	}
}

// Server timezone in hours
double TimeZoneServer()
{
	int ServerToLocalDiffMinutes = (TimeCurrent()-TimeLocal())/60;
	
	// round to nearest 30 minutes to allow for inaccurate PC clock
	int nHalfHourDiff = MathRound(ServerToLocalDiffMinutes/30.0);
	ServerToLocalDiffMinutes = nHalfHourDiff*30;
	return(TimeZoneLocal() + ServerToLocalDiffMinutes/60.0);
}

// Uses local PC time, local PC timezone, and server time to calculate GMT time at arrival of last tick
datetime TimeGMT()
{
	// two ways of calculating
	// 1. From PC time, which may not be accurate
	// 2. From server time. Most accurate except when server is down on weekend
	datetime dtGmtFromLocal = TimeLocal() - TimeZoneLocal()*3600;
	datetime dtGmtFromServer = TimeCurrent() - TimeZoneServer()*3600;

	// return local-derived value if server value is out by more than 5 minutes, eg during weekend
	if (dtGmtFromLocal > dtGmtFromServer + 300)
	{
		return(dtGmtFromLocal);
	}
	else
	{
		return(dtGmtFromServer);
	}	
}
//+---- Programmed by Michal Rutka @ MQLService.com -----------------+