Untitled

mail@pastecode.io avatar
unknown
plain_text
2 years ago
20 kB
1
Indexable
Never
//+------------------------------------------------------------------+
//| include                                                          |
//+------------------------------------------------------------------+

#include <Trade\Trade.mqh>
#include <GraphicalPanelDL.mqh>

//+------------------------------------------------------------------+
//| inputs                                                           |
//+------------------------------------------------------------------+

input group "==== General Inputs ===="
input long InpMagicNumber = 10111;  //magic number

enum LOT_MODE_ENUM
   {
      LOT_MODE_FIXED,         //fixed lot
      LOT_MODE_MONEY,         //lots based on money
      LOT_MODE_PCT_ACCOUNT    //lots based on % of account
   };
   
input LOT_MODE_ENUM InpLotMode = LOT_MODE_FIXED; //lot mode

input double InpLots = 0.01;        //lots / money / percent
input int InpStopLoss = 150;        //StopLoss in % of the range, 0 = off
input int InpTakeProfit = 200;      //Take Profit in % of the range, 0 = off

input group "==== Range Inputs ===="
input int InpRangeStart = 90;       // range start time in minutes after midnight
input int InpRangeDuration = 270;   //range duration in minutes startes from RangeStart
input int InpRangeClose = 1200;     // range close time, -1=off

enum BREAK_MODE_ENUM                //OCO one breakout close another trade
{
   ONE_SIGNAL,                      //One breakout per range
   TWO_SIGNALS                      //High and low breakout still active after one is activated
};

input BREAK_MODE_ENUM InpBreakoutMode = ONE_SIGNAL; //breakout mode

input group "==== Day Of week filter ===="
input bool InpMonday = true;        //range on Monday
input bool InpTuesday = true;       //range on Tuesday
input bool InpWednesday = true;     //range on Wednesday
input bool InpThursday = true;      //range on Thursday
input bool InpFriday = true;        //range on Friday


//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+

struct RANGE_STRUCT
{
  datetime start_time;     //start of range 
  datetime end_time;       //end of range
  datetime close_time;     //close time
  double high;             //high of range
  double low;              //low of range
  bool f_entry;            //flag if we are inside the range
  bool f_high_breakout;    //flag if a high breakout occures
  bool f_low_breakout;     //flag if a low breakot occures
  
  RANGE_STRUCT() : start_time(0),end_time(0),close_time(0),high(0),low(DBL_MAX),f_entry(false),f_high_breakout(false),f_low_breakout(false) {};
};

RANGE_STRUCT range;
MqlTick prevTick, lastTick;
CTrade trade;
CGraphicalPanel panel;

int OnInit()
{
     //check user inputs
     if(!CheckInputs())
     {
         return INIT_PARAMETERS_INCORRECT;
     }
     
     //set magic number
     trade.SetExpertMagicNumber(InpMagicNumber);
     
     //calculate new range if input change
   if(_UninitReason == REASON_PARAMETERS && CountOpenPositions() == 0) //calculate range if there is no positions
     {
         CalculateRange(); 
         
         //update panel data
         panel.Update();
     }
     
     //draw objects
     DrawObject();
     
     //create panel
     if(!panel.OnInit()){return INIT_FAILED;}
     
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   // delete object
   ObjectsDeleteAll(NULL,"range");
   //destroy panel
   panel.Destroy(reason);
}


//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+

void OnTick()
{
   // get current tick
   prevTick = lastTick;                //previous tick variable
   SymbolInfoTick(_Symbol,lastTick);   //last tick variable
   
   // range calculation
   if(InpRangeClose >= 0 && lastTick.time >= range.start_time && lastTick.time < range.end_time)
     {
         //set flag
         range.f_entry = true;
         //new high
         if(lastTick.ask > range.high)
           {
               range.high = lastTick.ask;
               DrawObject();
           }
         //new low
         if(lastTick.bid < range.low)
           {
               range.low = lastTick.bid;
               DrawObject();
           }
     }
   
   // close positions
   if(lastTick.time >= range.close_time)
     {
         if(!ClosePositions()){return;}
     }
   
   // calculate new range if...
   if(((InpRangeClose >= 0 && lastTick.time >= range.close_time)//close time reached
      || (range.f_high_breakout && range.f_low_breakout)        //both brakouts flags are true
      || (range.end_time ==0)                                   //range not calculated yet
      || (range.end_time !=0 && lastTick.time > range.end_time && range.f_entry)) // there was a range calculated but no tick inside
      && CountOpenPositions()==0)
     {
         CalculateRange();
     }
     
   //check for brakouts
   CheckBreakouts();
}

//+------------------------------------------------------------------+
//| Chart Event Handler                                              |
//+------------------------------------------------------------------+

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   panel.PanelChartEvent(id,lparam,dparam,sparam);
}

//check user inputs
bool CheckInputs()
{
   if(InpMagicNumber <= 0 )
   {
      Alert("Magic number <= 0");
      return false;
   }
   if(InpLotMode == LOT_MODE_FIXED && (InpLots <= 0 || InpLots > 10))
   {
      Alert("Lots <= 0 or > 10");
      return false;
   }
   if(InpLotMode == LOT_MODE_MONEY && (InpLots <= 0 || InpLots > 1000))
   {
      Alert("Lots <= 0 or > 1000");
      return false;
   }
   if(InpLotMode == LOT_MODE_PCT_ACCOUNT && (InpLots <= 0 || InpLots > 5))
   {
      Alert("Lots <= 0 or > 5");
      return false;
   }
   if((InpLotMode == LOT_MODE_MONEY || InpLotMode == LOT_MODE_PCT_ACCOUNT) && InpStopLoss == 0)
   {
      Alert("Selected Lot Mode needs a stop loss");
      return false;
   }
   if(InpStopLoss <= 0 || InpStopLoss > 1000)
   {
      Alert("Stop Loss < 0 or > 1000");
      return false;
   }
   if(InpTakeProfit < 0 || InpTakeProfit > 1000)
   {
      Alert("Take Profit < 0 or > 1000");
      return false;
   }
   if(InpRangeClose < 0 && InpStopLoss == 0)
   {
      Alert("Close time and stopl loss is OFF");
      return false;
   }
   if(InpRangeStart < 0 || InpRangeStart >= 1440)
   {
      Alert("Start time <= 0 or >= 1440");
      return false;
   }
   if(InpRangeDuration <= 0 || InpRangeDuration >= 1440)
   {
      Alert("Input range duration <= 0 or >= 1440");
      return false;
   }
   if(InpRangeClose > 1440 || (InpRangeStart+InpRangeDuration)%1440 == InpRangeClose)
   {
      Alert("Close Time >= 1440 or EndTime == CloseTime");
      return false;
   }
   if(InpMonday + InpTuesday + InpWednesday + InpThursday + InpFriday == 0)
   {
      Alert("Range is prohibited an all days of the week");
      return false;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| calculate new range                                              |
//+------------------------------------------------------------------+

void CalculateRange()
{
   //reset range to the default stage
   range.start_time = 0;
   range.end_time = 0;
   range.close_time =0;
   range.high = 0;
   range.low = DBL_MAX;
   range.f_high_breakout = false;
   range.f_low_breakout = false;
   range.f_entry = false;
   
   //calculate range start time
   int time_cycle = 86400;
   range.start_time = (lastTick.time - (lastTick.time % time_cycle)) + InpRangeStart*60;
   for(int i=0;i<8;i++)
     {
         MqlDateTime tmp;
         TimeToStruct(range.start_time,tmp);
         int dow = tmp.day_of_week;
         //check if we trade today, if not then shift to the next day
         if(lastTick.time >= range.start_time || dow ==6 || dow == 0 || (dow==1 && !InpMonday) || (dow==2 && !InpTuesday) || (dow==3 && !InpWednesday) || (dow==4 && !InpThursday) || (dow==5 && !InpFriday)) //if Saturday or Sunday Shift to Monday
           {
               range.start_time += time_cycle;
           }
     }
     
   //calculate range end time
   range.end_time = range.start_time + InpRangeDuration *60;
   for(int i=0;i<2;i++)
     {
         MqlDateTime tmp;
         TimeToStruct(range.start_time,tmp);
         int dow = tmp.day_of_week;
         if(dow ==6 || dow == 0)
           {
               range.end_time += time_cycle;
           }
     }
     
     //calculate close time and reset the range
     if(InpRangeClose>=0)
       {
         range.close_time = (range.end_time - (range.end_time % time_cycle)) + InpRangeClose*60;
         for(int i=0;i<3;i++)
        {
            MqlDateTime tmp;
            TimeToStruct(range.close_time,tmp);
            int dow = tmp.day_of_week;
            if(range.close_time <= range.end_time || dow ==6 || dow == 0) //if Saturday or Sunday Shift to Monday
              {
                  range.close_time += time_cycle;
              }
         }
      }
     
      //draw objects
      DrawObject();
   
}


//+------------------------------------------------------------------+
//| count open positions                                             |
//+------------------------------------------------------------------+

int CountOpenPositions()
{
   int counter = 0;
   int total = PositionsTotal();
   for(int i=total-1;i>=total;i--)
     {
         ulong ticket = PositionGetTicket(i);
         if(ticket <= 0)
           {
               Print("Failed to get Position Ticket"); return -1;
           }
         if(!PositionSelectByTicket(ticket))
           {
               Print("Failed to select position by ticket"); return -1;
           }
         ulong magicnumber;
         if(!PositionGetInteger(POSITION_MAGIC,magicnumber))
           {
               Print("Failed to grt position magicnumber"); return -1;
           }
         if(InpMagicNumber == magicnumber)
           {
               counter ++;
           }
      }
   
   return counter;
}


//+------------------------------------------------------------------+
//| check for breakouts                                              |
//+------------------------------------------------------------------+

void CheckBreakouts()
{
   // check if we are after the range
   if(lastTick.time >= range.end_time && range.end_time >= 0 && range.f_entry)
     {
         // check if we have high breakout
         if(!range.f_high_breakout && lastTick.ask >= range.high) //if we have no breakout flag yet
         {
               range.f_high_breakout = true;                      //flag prevent open multiple positions
               if(InpBreakoutMode == ONE_SIGNAL)
                 {
                     range.f_low_breakout = true;                 //if one signal mode is ON set flag for oposite trade to TRUE to prevent open a trade
                 }
               
               //calculate stop loss and take profit
               // wyjasnienie pytajnika: jezeli pierwszy warunek ?to zrob to :w przeciwnym wypadku to
               double sl = InpStopLoss == 0 ? 0 : NormalizeDouble(lastTick.bid - ((range.high - range.low) * InpStopLoss * 0.01),_Digits);
               double tp = InpTakeProfit == 0 ? 0 : NormalizeDouble(lastTick.bid + ((range.high - range.low) * InpTakeProfit * 0.01),_Digits);
               
               //calculate lots
               double lots;
               if(!CalculateLots(lastTick.bid-sl,lots)){return;}
               
               //open buy position
               trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,lots,lastTick.ask,sl,tp,"TimeRange EA");
         }
         // check if we have low breakout
         if(!range.f_low_breakout && lastTick.bid >= range.low)   //if we have no breakout flag yet
         {
               range.f_low_breakout = true;                       //flag prevent open multiple positions
               if(InpBreakoutMode == ONE_SIGNAL)                  //if one signal mode is ON set flag for oposite trade to TRUE to prevent open a trade
                 {
                     range.f_high_breakout = true;
                 }
               
               
               //calculate stop loss and take profit
               
               double sl = InpStopLoss == 0 ? 0 : NormalizeDouble(lastTick.ask + ((range.high - range.low) * InpStopLoss * 0.01),_Digits);
               double tp = InpTakeProfit == 0 ? 0 : NormalizeDouble(lastTick.ask - ((range.high - range.low) * InpTakeProfit * 0.01),_Digits);
               
               //calculate lots
               double lots;
               if(!CalculateLots(sl-lastTick.ask,lots)){return;}
               
               //open sell position
               trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,lots,lastTick.bid,sl,tp,"TimeRange EA");
         }
     }
}

//calculate lots
bool CalculateLots(double slDistance, double &lots)
{
   lots = 0.0;
   if(InpLotMode == LOT_MODE_FIXED)
   {
      lots = InpLots;
   }
   else
   {
      double tickSize   = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
      double tickValue  = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE);
      double volumeStep = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
      
      double riskMoney  = InpLotMode == LOT_MODE_MONEY ? InpLots : AccountInfoDouble(ACCOUNT_EQUITY) * InpLots * 0.01;
      double moneyVolumeStep = (slDistance / tickSize) * tickValue * volumeStep;
      
      lots = MathFloor(riskMoney/moneyVolumeStep) * volumeStep;
   }
   
   //check calculated lots
   if(!CheckLots(lots)){return false;}
   
   
   return true;
}

//check lots for min, max and step
bool CheckLots(double &lots)
   {
      double min = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
      double max = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
      double step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
      
      if(lots<min)
         {
            Print("Lot size will be set to the minimum alowable volume");
            lots = min;
            return true;
         }
      if(lots>max)
         {
            Print("Lotsize > maximum allowable volume. Lots: ",lots," max: ",max);
            return false;
         }
      lots = (int)MathFloor(lots/step) * step;
      
      return true;
   }

//+------------------------------------------------------------------+
//| Close all positions                                              |
//+------------------------------------------------------------------+

bool ClosePositions()
{
   int total = PositionsTotal();
   for(int i=total-1;i>=total;i--)
     {
         if(total != PositionsTotal())
         {
            total = PositionsTotal(); i = total; continue;
         }
         ulong ticket = PositionGetTicket(i); //select position
         if(ticket <= 0)
         {
            Print("Failed to get position ticket"); return false;
         }
         if(!PositionSelectByTicket(ticket))
         {
            Print("Failed to select position by ticket"); return false; 
         }
         long magicnumber;
         if(!PositionGetInteger(POSITION_MAGIC,magicnumber))
         {
            Print("Failed to get position by magic number");
         }
         if(magicnumber == InpMagicNumber)
         {
            trade.PositionClose(ticket);
            if(trade.ResultRetcode() != TRADE_RETCODE_DONE)
            {
            Print("Failed to close position. Result: "+(string)trade.ResultRetcode()+":"+trade.ResultRetcodeDescription() );
            }
         }
     }
   
   return true;
}


//+------------------------------------------------------------------+
//| Draw objects on Chart                                            |
//+------------------------------------------------------------------+

void DrawObject()
{
   // start
   ObjectDelete(NULL,"range start");
   if(range.start_time > 0)
     {
         ObjectCreate(NULL,"range start",OBJ_VLINE,0,range.start_time,0);
         ObjectSetString(NULL,"range start",OBJPROP_TOOLTIP,"start of the range \n"+TimeToString(range.start_time,TIME_DATE|TIME_MINUTES));
         ObjectSetInteger(NULL,"range start",OBJPROP_COLOR,clrBlue);
         ObjectSetInteger(NULL,"range start",OBJPROP_WIDTH,2);
         ObjectSetInteger(NULL,"range start",OBJPROP_BACK,true);
     }
   // end
   ObjectDelete(NULL,"range end");
   if(range.end_time > 0)
     {
         ObjectCreate(NULL,"range end",OBJ_VLINE,0,range.start_time,0);
         ObjectSetString(NULL,"range end",OBJPROP_TOOLTIP,"end of the range \n"+TimeToString(range.start_time,TIME_DATE|TIME_MINUTES));
         ObjectSetInteger(NULL,"range end",OBJPROP_COLOR,clrBlue);
         ObjectSetInteger(NULL,"range end",OBJPROP_WIDTH,2);
         ObjectSetInteger(NULL,"range end",OBJPROP_BACK,true);
     }
     
   // close time
   ObjectDelete(NULL,"range close");
   if(range.close_time > 0)
     {
         ObjectCreate(NULL,"range close",OBJ_VLINE,0,range.start_time,0);
         ObjectSetString(NULL,"range close",OBJPROP_TOOLTIP,"close of the range \n"+TimeToString(range.start_time,TIME_DATE|TIME_MINUTES));
         ObjectSetInteger(NULL,"range close",OBJPROP_COLOR,clrRed);
         ObjectSetInteger(NULL,"range close",OBJPROP_WIDTH,2);
         ObjectSetInteger(NULL,"range close",OBJPROP_BACK,true);
     }
     
   // high
   ObjectsDeleteAll(NULL,"range high");
   if(range.high > 0)
     {
         ObjectCreate(NULL,"range high",OBJ_TREND,0,range.start_time,range.high,range.end_time,range.high);
         ObjectSetString(NULL,"range high",OBJPROP_TOOLTIP,"high of the range \n"+DoubleToString(range.high,_Digits));
         ObjectSetInteger(NULL,"range high",OBJPROP_COLOR,clrBlue);
         ObjectSetInteger(NULL,"range high",OBJPROP_WIDTH,2);
         ObjectSetInteger(NULL,"range high",OBJPROP_BACK,true); 

         ObjectCreate(NULL,"range high ",OBJ_TREND,0,range.end_time,range.high,InpRangeClose>=0 ? range.close_time : INT_MAX,range.high);
         ObjectSetString(NULL,"range high ",OBJPROP_TOOLTIP,"high of the range \n"+DoubleToString(range.high,_Digits));
         ObjectSetInteger(NULL,"range high ",OBJPROP_COLOR,clrBlue);
         ObjectSetInteger(NULL,"range high ",OBJPROP_BACK,true);
         ObjectSetInteger(NULL,"range high ",OBJPROP_STYLE,STYLE_DOT);
     
     }
     
   // low
   ObjectsDeleteAll(NULL,"range low");
   if(range.low < DBL_MAX)
     {
         ObjectCreate(NULL,"range low",OBJ_TREND,0,range.start_time,range.low,range.end_time,range.low);
         ObjectSetString(NULL,"range low",OBJPROP_TOOLTIP,"low of the range \n"+DoubleToString(range.low,_Digits));
         ObjectSetInteger(NULL,"range low",OBJPROP_COLOR,clrBlue);
         ObjectSetInteger(NULL,"range low",OBJPROP_WIDTH,2);
         ObjectSetInteger(NULL,"range low",OBJPROP_BACK,true);
         
         ObjectCreate(NULL,"range low ",OBJ_TREND,0,range.end_time,range.low,InpRangeClose>=0 ? range.close_time : INT_MAX,range.low);
         ObjectSetString(NULL,"range low ",OBJPROP_TOOLTIP,"low of the range \n"+DoubleToString(range.low,_Digits));
         ObjectSetInteger(NULL,"range low ",OBJPROP_COLOR,clrBlue);
         ObjectSetInteger(NULL,"range low ",OBJPROP_STYLE,STYLE_DOT);
     }
     
     //refresh chart
     ChartRedraw();
}