//+------------------------------------------------------------------+
//|                                                       Rhythm.mq4 |
//|                                     Copyright © 2007, MQLService |
//|                                        http://www.mqlservice.com |
//| $Id: //mqlservice/mt4files/experts/Rhythm.mq4#6 $
//+------------------------------------------------------------------+
/*
Alan Strawbridge [strawstock2@yahoo.com]

Many thanks to Tkimble on the ForexFactory for the simple strategy that this
EA is designed to manage.

Name: Rhythm    Pair: Any Pair You like.
 
This is a simple Stop @ Reverse system. It should be able to be used with
Daily and weekly trading

Once trade is initiated the stop loss and reversals are equal. 
Example: the stop is placed at 40 PIPs and the reversal is likewise placed at 40 PIPs. 
When the stop is activated, the reversal is simultaneously activated.  There is no limit on how
many reversals there can be in a day unless Max Trades variable is used
 
EA- Variables:

Overrule Direction: = True/False EA makes next day trade decision on the basis of the previous Days trend  
(i.e.; previous day up, Place "Buy" market order, previous day down, enter "Sell" market order) 
Example: Yesterdays open/close positive = LongEntry) (Yesterdays open/close negative = ShortEntry) 
00:00 >< 23:55  Set it to False if you want to choose your own initial entry direction

Direction Long: =True/False  this is the manual way to either go Long or Short for your opening trade.  
If using this variable Overrule Direction should be set to True

EntryTime Hour: = Entry will execute on the Open of the hour chosen. 00:00 thru 23:00

ExitTime Hour: =  00:00 thru 23:00 Exit will execute on the Open of the preceding bar from exit hour chosen  
Example: exit set for 17:00 exit will execute on the open of the 18:00 bar.

Max Trades: = 0=False or 1,2,3,4 etc  Set this variable if you want to limit on how many whipsaws you want in a day or week. 
EA will not take anymore trades for current day or week once variable is reached

TrailStop Once: = True/False (If set to True, Stop and Reverse order will move positive one time at 
the value of the Stop loss variable)  Example: I am long at 1.2000 and I have my stop set at 1.1960 (-40) 
If price was to advance to 1.2040 then my Stop & Reverse orders would move positive to 1.2000 This would be 
the only positive move they would make.

Trailing Stop: = True/False (If set to True, Stop and Reverse order will move
every time price moves positive at the value of the Stop loss variable) 
 
Stoploss: = Default 40  "Very important". It is the Value that all Stops will use. 
(i.e.; TrailStop Once, Trailing Stop) It is the value that all Stop and Reverse orders are placed.

ProfitTarget: = 0=False, Default 120  If Profit Target or Time exit is not used than trade will remain 
open until manually closed or when Profit Target or Time exit is initiated and executed in the future. 

Lots: = Default  0.1 Mini

*/
#property copyright "Copyright © 2007, MQLService"
#property link      "http://www.mqlservice.com"
#include <stdlib.mqh>
#include <stderror.mqh>

//---- input parameters
extern bool      OverruleDirection = false;
extern bool      DirectionLong     = false;
extern int       EntryTimeHour     = 0;
extern int       ExitTimeHour      = 23; // Set it equal of lower of
extern int       MaxTrades         = 0;  // Set <= 0 to disable
extern bool      TrailOnceStop     = false;
extern bool      TrailingStop      = false;

extern double    Lots       = 0.1;
extern int       StopLoss   = 40;
extern int       TakeProfit = 120;
extern int       Magic      = 361;

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

int deinit()
{
  Comment("");
  return(0);
}

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

  return(Rhythm(Symbol(),Period(), Magic, Lots, StopLoss, TakeProfit, MaxTrades));
}

bool Visual = true;

#define MAX_CNT 120 // 1 minute

int Rhythm(string sym, int per, int magic, double vol, int sl, int tp, int mt)
{
  // Internals
  int _Digits = MarketInfo(sym, MODE_DIGITS);
  if(_Digits == 0) _Digits = 4;
  double _Point = MarketInfo(sym, MODE_POINT);
  if(NormalizeDouble(_Point, _Digits) == 0.0) _Point = Point;
  double _Bid = MarketInfo(sym, MODE_BID);
  double _Ask = MarketInfo(sym, MODE_ASK);
  
  static int dir = -1;
  static int reverse = 0;
  // Find trend (override possible and skip Sundays for Monday trend)
  if(dir < 0){
    if(OverruleDirection)
      if(DirectionLong)
        dir = OP_BUY;
      else
        dir = OP_SELL;
    else
    {
      if(DayOfWeek() == 1)
        int bs = iBarShift(sym, PERIOD_D1, TimeCurrent()-72*3600);
      else
        bs = 1;
      if(iOpen(sym, PERIOD_D1, bs) < iClose(sym, PERIOD_D1, bs))
        dir = OP_BUY;
      else
        dir = OP_SELL;
    }
    if(dir == OP_BUY)
      reverse = MathRound(_Ask/_Point);
    else
      reverse = MathRound(_Bid/_Point);    
  }
  
  // Calculate how many trades today and if one ended with TP
  bool ct = _Rhythm_CanTrade(sym, magic, mt) && IsTradeAllowed();
  bool tt = _Rhythm_tt(per, EntryTimeHour, ExitTimeHour);
  ct = ct && tt;

  if(tt)
  {
    if(Visual) Comment("Trading times");
    // Check for active position
    int cnt = 0;
    while(!_Rhythm_ActivePosition(sym, magic) && ct)
    {
      int tic = -1;
      if(dir == OP_BUY){
        if(ct)
        { 
          _Ask = MarketInfo(sym, MODE_ASK);
          if(MathRound(_Ask/_Point) >= reverse)
            tic = OrderSend(sym, dir, vol, _Ask, 1, _sl(dir, sym, sl), _tp(dir, sym, tp),
                            "Rhythm", magic, 0, Blue);
          else
            break;
          _ce();
        }
      }else{
        if(ct)
        {
          _Bid = MarketInfo(sym, MODE_BID);
          if(MathRound(_Bid/_Point) <= reverse)
            tic = OrderSend(sym, dir, vol, _Bid, 1, _sl(dir, sym, sl), _tp(dir, sym, tp),
                            "Rhythm", magic, 0, Red);
          else
            break;
          _ce();
        }
      }
      Sleep(500);
      cnt++;
      if(cnt >= MAX_CNT)
        break;      
      // Recalculate conditions
      ct = _Rhythm_CanTrade(sym, magic, mt) && IsTradeAllowed();
      ct = ct && _Rhythm_tt(per, EntryTimeHour, ExitTimeHour);
    }
    // Find reverse:
    if(_Rhythm_ActivePosition(sym, magic))
    {
      reverse = MathRound(OrderStopLoss()/_Point);
      if(OrderType() == OP_BUY)
        dir = OP_SELL;
      else
        dir = OP_BUY;
    }
    if(Visual) Comment("Trading times. Reverse at ", DoubleToStr(reverse*_Point, _Digits));
  }
  
  if(!tt)
  {
    if(Visual) Comment("Outside of trading times");
    reverse = 0;
    dir     = -1;
    //close all open trades
    for(int i=OrdersTotal()-1; i >= 0; i--)
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
      if(OrderSymbol() == sym)
      if(OrderMagicNumber() == magic)
      if(!OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 1)) _ce();
    }
  }
  
  if(_Rhythm_ActivePosition(sym, magic))
  {
    // manage sl/reverse
    if(TrailingStop){
      if(OrderType() == OP_BUY){
        double nsl = OrderClosePrice()-sl*_Point;
        if(MathRound((_Bid-OrderStopLoss())/_Point)-sl > 0)
          if(OrderModify(OrderTicket(), OrderOpenPrice(), nsl, OrderTakeProfit(), OrderExpiration(), Blue))
            reverse = MathRound(nsl/_Point);
          else _ce();
      }else{
        nsl = OrderClosePrice()+sl*_Point;
        if(MathRound((OrderStopLoss()-_Ask)/_Point)-sl > 0)
          if(OrderModify(OrderTicket(), OrderOpenPrice(), nsl, OrderTakeProfit(), OrderExpiration(), Red))
            reverse = MathRound(nsl/_Point);
          else _ce();
      }
    }else if(TrailOnceStop){
      nsl = OrderOpenPrice();
      if(OrderType() == OP_BUY){
        if(MathRound((_Bid-nsl)/_Point)-sl > 0)
        if(MathRound((nsl-OrderStopLoss())/_Point) != 0)
        if(OrderModify(OrderTicket(), OrderOpenPrice(), nsl, OrderTakeProfit(), OrderExpiration(), Blue))
          reverse = MathRound(nsl/_Point);
        else _ce();
      }else{
        if(MathRound((nsl-_Ask)/_Point) - sl > 0)
        if(MathRound((nsl-OrderStopLoss())/_Point) != 0)
        if(OrderModify(OrderTicket(), OrderOpenPrice(), nsl, OrderTakeProfit(), OrderExpiration(), Blue))
          reverse = MathRound(nsl/_Point);
        else _ce();        
      }
    }
  }
  
  return(0);
}

bool _Rhythm_CanTrade(string symbol, int magic, int max)
{
  int today_trades = 0;
  datetime today = iTime(symbol, PERIOD_D1, 0);
  for(int i=OrdersHistoryTotal()-1; i >= 0; i--)
    if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)){
      if(OrderSymbol() == symbol)
      if(OrderMagicNumber() == magic)
      if(OrderOpenTime() >= today){
        today_trades++;

        if(MaxTrades > 0)
        if(today_trades >= MaxTrades)
          return(false);
          
        if(TakeProfit > 0)
        if(StringFind(OrderComment(), "[tp]") >= 0) 
          return(false);
      }
    }else
      Print("OrderSelect() error - ", ErrorDescription(GetLastError()));
  return(true);
}

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

bool _Rhythm_tt(int period, int openh, int closeh)
{
  // Trade times
  datetime st = MathFloor(TimeCurrent()/86400)*86400 + openh*3600;
  datetime en = MathFloor(TimeCurrent()/86400)*86400 + closeh*3600+period*60;
  datetime now = TimeCurrent();
  while(st > now) st -= 86400;
  while(en > now) en -= 86400;
  if(st<en) return(false);
  return(true);
}

bool _ce()
{
  int err=GetLastError();
  if(err != ERR_NO_ERROR){
    Print("Error #", err, " ", ErrorDescription(err));
    return(true);
  }
  return(false);
}

double _sl(int type, string symbol, int stoploss)
{
  if(type == OP_BUY)
    if(stoploss > 0)
      return(MarketInfo(symbol, MODE_BID)-stoploss*MarketInfo(symbol, MODE_POINT));
    else
      return(0.0);
  else if(type == OP_SELL)
    if(stoploss > 0)
      return(MarketInfo(symbol, MODE_ASK)+stoploss*MarketInfo(symbol, MODE_POINT));
    else
      return(0.0);
}

double _tp(int type, string symbol, int takeprofit)
{
  if(type == OP_BUY)
    if(takeprofit > 0)
      return(MarketInfo(symbol, MODE_BID)+takeprofit*MarketInfo(symbol, MODE_POINT));
    else
      return(0.0);
  else if(type == OP_SELL)
    if(takeprofit > 0)
      return(MarketInfo(symbol, MODE_ASK)-takeprofit*MarketInfo(symbol, MODE_POINT));
    else
      return(0.0);
}

//+---- Programmed by Michal Rutka ----------------------------------+