ShareEA/HighLowBreakOut.mq5

353 lines
13 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:24:11 +02:00
//+------------------------------------------------------------------+
//| 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 <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//| 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 && currentTick.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;
}
}
}
}