//+------------------------------------------------------------------+ //| HighLowBreakOut.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include //+------------------------------------------------------------------+ //| Input | //+------------------------------------------------------------------+ static input long InpMagicnumber = 54321; //magicnumber static input double InpLots = 0.01; //lot size input int InpBars = 20; // bars for high/low input int InpIndexFilter = 0; // index filter in % (0=off) input int InpSizeFilter = 0; //channel size filter in points(0 = off) input int InpStopLoss = 200; // stop loss in points input bool InpTrailingSL = true; // trailing stop loss? input int InpTakeProfit = 0; //take profit in points (0 = off) //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ double high = 0; //highest price of the last N bars double low = 0; // lowest price of the last N bars int highIdx=0; //index of highest bar int lowIdx=0; //index of lowest bar MqlTick currentTick, previousTick; CTrade trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //check for user input if(!CheckInputs()){return INIT_PARAMETERS_INCORRECT;} //set the magic number trade.SetExpertMagicNumber(InpMagicnumber); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ ObjectDelete(NULL,"high"); ObjectDelete(NULL,"low"); ObjectDelete(NULL,"text"); ObjectDelete(NULL,"indexFilter"); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //check for the new bar open tick if(!IsNewBar()){return;} //get tick previousTick = currentTick; //previousTick become last tick if(!SymbolInfoTick(_Symbol,currentTick)){Print("Failed to get current tick"); return;} //ymbolinfotick() will renew the current tick // count open positions int cntBuy, cntSell; if(!CountOpenPositions(cntBuy,cntSell)){return;} // check for buy position if(cntBuy==0 & high!=0 && previousTick.ask=high && CheckIndexFilter(highIdx) && CheckSizeFilter()){ //calculate the stoploss/takeprofit double sl = InpStopLoss==0 ? 0 : currentTick.bid - InpStopLoss *_Point; double tp = InpTakeProfit == 0 ? 0 : currentTick.ask + InpTakeProfit *_Point; if(!NormalizePrice(sl)){return;} if(!NormalizePrice(tp)){return;} trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,InpLots,currentTick.ask,sl,tp,"highlowBreakoutEA"); } // check for sell position if(cntSell==0 & low!=0 && previousTick.bid>low && currentTick.bid<=low && CheckIndexFilter(lowIdx) && CheckSizeFilter()){ //calculate the stoploss/takeprofit double sl = InpStopLoss==0 ? 0 : currentTick.ask + InpStopLoss *_Point; double tp = InpTakeProfit == 0 ? 0 : currentTick.bid - InpTakeProfit *_Point; if(!NormalizePrice(sl)){return;} if(!NormalizePrice(tp)){return;} trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,InpLots,currentTick.bid,sl,tp,"highlowBreakoutEA"); } //update stop loss (trailing stop) if(InpStopLoss>0 && InpTrailingSL){ UpdateStopLoss(InpStopLoss*_Point); } // calculate high/low highIdx = iHighest(_Symbol,PERIOD_CURRENT,MODE_HIGH,InpBars,1); //ihighest = highest bar in a period lowIdx = iLowest(_Symbol,PERIOD_CURRENT,MODE_LOW,InpBars,1); high = iHigh(_Symbol,PERIOD_CURRENT,highIdx); //ihigh = the high price of 1 bar low = iLow(_Symbol,PERIOD_CURRENT,lowIdx); DrawObjects(); } //+------------------------------------------------------------------+ //| Function | //+------------------------------------------------------------------+ //check User input bool CheckInputs(){ if(InpMagicnumber<=0){ Alert("Wrong inputs : Magicnumber <=0"); return false; } if(InpLots<=0){ Alert("Wrong inputs : lotsize <= 0"); } if(InpBars<=0){ Alert("Wrong inputs : bars <= 0"); } if(InpSizeFilter<0){ Alert("Wrong inputs : InpSizeFilter < 0"); } if(InpIndexFilter<0 || InpIndexFilter>50){ Alert("Wrong inpuits : InpIndexFilter "); } if(InpStopLoss<=0){ Alert("Wrong input : InpStopLoss <= 0 , if no stoploss the trade will last forever!"); } if(InpTakeProfit<0){ Alert("Wrong inpuits : InpTakeProfit < 0"); } if(InpTrailingSL==0 && InpTakeProfit==0){ Alert("Trailing SL and Takeprofit set to 0!! the deal won't make profit!!"); } return true; } // check if high/low is inside valid index range bool CheckIndexFilter(int index){ if(InpIndexFilter>0 && (index<=round(InpBars*InpIndexFilter*0.01) || index>InpBars-round(InpBars*InpIndexFilter*0.01))){ return false; } return true; } // check channel size bool CheckSizeFilter(){ if(InpSizeFilter>0 && (high-low)>InpSizeFilter*_Point){ return false; } return true; } void DrawObjects(){ datetime time1 = iTime(_Symbol,PERIOD_CURRENT,InpBars); datetime time2 = iTime(_Symbol,PERIOD_CURRENT,1); //high ObjectDelete(NULL,"high"); ObjectCreate(NULL,"high",OBJ_TREND,0,time1,high,time2,high); ObjectSetInteger(NULL,"high",OBJPROP_WIDTH,3); ObjectSetInteger(NULL,"high",OBJPROP_COLOR,CheckIndexFilter(highIdx) && CheckSizeFilter() ? clrLime : clrRed); //low ObjectDelete(NULL,"low"); ObjectCreate(NULL,"low",OBJ_TREND,0,time1,low,time2,low); ObjectSetInteger(NULL,"low",OBJPROP_WIDTH,3); ObjectSetInteger(NULL,"low",OBJPROP_COLOR,CheckIndexFilter(lowIdx) && CheckSizeFilter() ? clrLime : clrRed); // index filter ObjectDelete(NULL,"indexFilter"); if(InpIndexFilter>0){ datetime timeIF1 = iTime(_Symbol,PERIOD_CURRENT,(int)InpBars-round(InpBars*InpIndexFilter*0.01)); datetime timeIF2 = iTime(_Symbol,PERIOD_CURRENT,(int)round(InpBars*InpIndexFilter*0.01)); ObjectCreate(NULL,"indexFilter",OBJ_RECTANGLE,0,timeIF1,low,timeIF2,high); ObjectSetInteger(NULL,"indexFilter",OBJPROP_BACK,false); //background ObjectSetInteger(NULL,"indexFilter",OBJPROP_FILL,true); //background color switch ObjectSetInteger(NULL,"indexFilter",OBJPROP_COLOR,clrLime); //select background color } // text ObjectDelete(NULL,"text"); ObjectCreate(NULL,"text",OBJ_TEXT,0,time2,low); ObjectSetInteger(NULL,"text",OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER); ObjectSetInteger(NULL,"text",OBJPROP_COLOR,clrLime); ObjectSetString(NULL,"text",OBJPROP_TEXT,"Bars:"+(string)InpBars+ " index filter"+DoubleToString(round(InpBars*InpIndexFilter*0.01),0)+ " high index"+(string)highIdx+ " low index"+(string)lowIdx)+ " size:"+DoubleToString((high-low)/_Point,0); } // check if we have a bar open tick bool IsNewBar(){ static datetime previousTime = 0; datetime currentTime = iTime(_Symbol,PERIOD_CURRENT,0); if(previousTime!=currentTime){ previousTime=currentTime; return true; } return false; } // count open positions bool CountOpenPositions(int &cntBuy, int &cntSell) { cntBuy = 0; cntSell = 0; int total = PositionsTotal(); for(int i= total-1; i>=0; i--){ ulong ticket = PositionGetTicket(i); if(ticket<=0){Print("Failed to get position ticket"); return false;} if(!PositionSelectByTicket(ticket)){Print("Failed to select position"); return false;} long magic; if(!PositionGetInteger(POSITION_MAGIC,magic)) {Print("Failed to get position magicnumber"); return false;} if(magic==InpMagicnumber){ long type; if(!PositionGetInteger(POSITION_TYPE,type)) {Print("Failed to get position type"); return false;} if(type==POSITION_TYPE_BUY) {cntBuy++ ;} if(type==POSITION_TYPE_SELL) {cntSell++ ;} } } return true; } // round up the digit for the trade bool NormalizePrice(double &price){ double tickSize = 0; if(!SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE,tickSize)){ Print("Failed to get tick size"); return false; } price = NormalizeDouble(MathRound(price/tickSize)*tickSize,_Digits); return true; } /* no close position needed in this EA // close positions bool ClosePositions(int all_buy_sell){ int total = PositionsTotal(); for(int i = total-1; i>=0; i--){ ulong ticket = PositionGetTicket(i); if(ticket<=0){Print("Failed to get position ticket"); return false;} if(!PositionSelectByTicket(ticket)){Print("Failed to select position"); return false;} long magic; if(!PositionGetInteger(POSITION_MAGIC,magic)) {Print("Failed to get position magicnumber"); return false;} if(magic==InpMagicnumber){ long type; if(!PositionGetInteger(POSITION_TYPE,type)){Print("Failed to get position type"); return false;} if(all_buy_sell==1 && type==POSITION_TYPE_SELL){continue;} //1 = buy position if(all_buy_sell==2 && type==POSITION_TYPE_BUY){continue;} //2 = sell position trade.PositionClose(ticket); if(trade.ResultRetcode()!=TRADE_RETCODE_DONE){ Print("Failed to close position. Ticket:", (string)ticket," result:",(string)trade.ResultRetcode(),":",trade.CheckResultRetcodeDescription()); } } } return true; } */ void UpdateStopLoss(double slDistance){ //loop through open positions int total = PositionsTotal(); for(int i=total-1;i>=0;i--){ ulong ticket = PositionGetTicket(i); if(ticket<=0){Print("Failed to get position ticket"); return;} if(!PositionSelectByTicket(ticket)){Print("Failed to select position by ticket"); return;} ulong magicnumber; if(!PositionGetInteger(POSITION_MAGIC,magicnumber)) {Print("Failed to get position magicnumber ");return;} if(InpMagicnumber==magicnumber){ //get type long type; if(!PositionGetInteger(POSITION_TYPE,type)){Print("Failed to get position type"); return;} //get current sl and tp double currSL, currTP; if(!PositionGetDouble(POSITION_SL,currSL)){Print("Failed to get position stop loss"); return;} if(!PositionGetDouble(POSITION_TP,currTP)){Print("Failed to get position stop loss"); return;} // calculate stop loss double currPrice = type==POSITION_TYPE_BUY ? currentTick.bid : currentTick.ask; int n = type==POSITION_TYPE_BUY ? 1 : -1; double newSL = currPrice - slDistance * n; if(!NormalizePrice(newSL)){return;} // check if new stop loss is closer to current price than existing stop loss if((newSL*n) < (currSL*n) || NormalizeDouble(MathAbs(newSL-currPrice),_Digits)<_Point) { //Print("No new stop loss needed"); continue; } //check for stop level long level = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); if(level!=0 && MathAbs(currPrice-newSL)<=level*_Point){ Print("New stop loss inside stop level"); continue; } // modify position with new stop loss if(!trade.PositionModify(ticket,newSL,currTP)){ Print("Failed to modify position, ticket:",(string)ticket,"currSL:",(string)currSL, "newSL:",(string)newSL," currTP:", (string)currTP); return; } } } }