2025-01-17 11:54:59 -05:00
|
|
|
//+------------------------------------------------------------------+
|
2025-01-17 11:55:39 -05:00
|
|
|
//| 13.Bollinger_Bands_EA.mq5 |
|
2025-04-16 11:54:33 -04:00
|
|
|
//| Author: Santiago Cruz |
|
2025-01-17 11:54:59 -05:00
|
|
|
//| https://www.mql5.com/en/users/algo-trader/ |
|
|
|
|
//+------------------------------------------------------------------+
|
2025-04-16 11:54:33 -04:00
|
|
|
#property copyright "Santiago Cruz"
|
|
|
|
#property link "https://www.mql5.com/en/users/algo-trader/"
|
2025-01-17 11:54:59 -05:00
|
|
|
#property version "1.00"
|
|
|
|
|
|
|
|
#include <Trade\Trade.mqh>
|
|
|
|
|
|
|
|
input group "=== Trading Inputs ==="
|
|
|
|
input string TradeComment = "Bollinger Bands EA";
|
|
|
|
static input long EA_Magic = 250117;
|
|
|
|
static input double LotSize = 0.01; //Lot Size
|
|
|
|
input int Stop_Loss = 100; //Stop Loss In Points
|
|
|
|
input int Take_Profit = 200; //Take Profit In Points
|
|
|
|
input int BB_Period = 21; //BB Period
|
|
|
|
input double BB_Deviation = 2.0; //Deviation
|
|
|
|
|
|
|
|
//---Global Variables
|
|
|
|
int BB_Handle;
|
|
|
|
double BB_UpperBuffer[];
|
|
|
|
double BB_BaseBuffer[];
|
|
|
|
double BB_LowerBuffer[];
|
|
|
|
datetime openTimeBuy = 0;
|
|
|
|
datetime openTimeSell = 0;
|
|
|
|
MqlTick currentTick;
|
|
|
|
CTrade trade;
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Expert initialization function |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
int OnInit()
|
|
|
|
{
|
|
|
|
//---Check User Input Parameters
|
|
|
|
if(EA_Magic<=0 && LotSize<=0 && Stop_Loss<0 && Take_Profit<0)
|
|
|
|
{
|
|
|
|
Alert("Incorrect Input Parameters.");
|
|
|
|
return INIT_PARAMETERS_INCORRECT;
|
|
|
|
}
|
|
|
|
if(BB_Period<=1 && BB_Deviation<=0)
|
|
|
|
{
|
|
|
|
Alert("Incorrect Bollinger Bands Parameters");
|
|
|
|
return INIT_PARAMETERS_INCORRECT;
|
|
|
|
}
|
|
|
|
|
|
|
|
trade.SetExpertMagicNumber(EA_Magic);
|
|
|
|
|
|
|
|
//---Create Bollinger Bands Handle
|
|
|
|
BB_Handle = iBands(_Symbol,PERIOD_CURRENT,BB_Period,1,BB_Deviation,PRICE_CLOSE);
|
|
|
|
if(BB_Handle == INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
Alert("Failed to create Bollinger Band Indicator Handle.");
|
|
|
|
return INIT_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---Bollinger Bands Buffers
|
|
|
|
ArraySetAsSeries(BB_UpperBuffer,true);
|
|
|
|
ArraySetAsSeries(BB_BaseBuffer,true);
|
|
|
|
ArraySetAsSeries(BB_LowerBuffer,true);
|
|
|
|
|
|
|
|
return(INIT_SUCCEEDED);
|
|
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Expert deinitialization function |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void OnDeinit(const int reason)
|
|
|
|
{
|
|
|
|
//---Release Bollinger Bands Indicator Handle
|
|
|
|
if(BB_Handle != INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
IndicatorRelease(BB_Handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Expert tick function |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void OnTick()
|
|
|
|
{
|
|
|
|
//---Check Current Tick
|
|
|
|
if(!IsNewBar())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---Get Current Tick
|
|
|
|
if(!SymbolInfoTick(_Symbol,currentTick))
|
|
|
|
{
|
|
|
|
Print("Failed to get Current Tick!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---Get Bollinger Bands Indicator Values
|
|
|
|
int BB_Value = CopyBuffer(BB_Handle,0,0,1,BB_BaseBuffer)
|
|
|
|
+ CopyBuffer(BB_Handle,1,0,1,BB_UpperBuffer)
|
|
|
|
+ CopyBuffer(BB_Handle,2,0,1,BB_LowerBuffer);
|
|
|
|
|
|
|
|
if(BB_Value != 3)
|
|
|
|
{
|
|
|
|
Print("Failed to get Bollinger Bands Indicator Values!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Comment("BB Up[0]: ",BB_UpperBuffer[0],"\n",
|
|
|
|
"BB Base[0]: ",BB_BaseBuffer[0],"\n",
|
|
|
|
"BB Low[0]: ",BB_LowerBuffer[0]);
|
|
|
|
|
|
|
|
//---Count Open Positions
|
|
|
|
int cntBuy,cntSell;
|
|
|
|
if(!CountOpenPositions(cntBuy,cntSell))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---Check For Lower Band Cross to Open a Buy Position
|
|
|
|
if(cntBuy==0 && currentTick.ask<=BB_LowerBuffer[0] && openTimeBuy!=iTime(_Symbol,PERIOD_CURRENT,0))
|
|
|
|
{
|
|
|
|
openTimeBuy = iTime(_Symbol,PERIOD_CURRENT,0);
|
|
|
|
double SL = currentTick.bid - Stop_Loss*_Point;
|
|
|
|
double TP = Take_Profit == 0 ? 0 : currentTick.bid + Take_Profit*_Point;
|
|
|
|
if(!NormalizePrice(SL,SL)){return;}
|
|
|
|
if(!NormalizePrice(TP,TP)){return;}
|
|
|
|
trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,LotSize,currentTick.ask,SL,TP,TradeComment);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---Check For Upper Band Cross to Open a Sell Position
|
|
|
|
if(cntSell==0 && currentTick.bid>=BB_UpperBuffer[0] && openTimeSell!=iTime(_Symbol,PERIOD_CURRENT,0))
|
|
|
|
{
|
|
|
|
openTimeSell = iTime(_Symbol,PERIOD_CURRENT,0);
|
|
|
|
double SL = currentTick.ask + Stop_Loss*_Point;
|
|
|
|
double TP = Take_Profit == 0 ? 0 : currentTick.ask - Take_Profit*_Point;
|
|
|
|
if(!NormalizePrice(SL,SL)){return;}
|
|
|
|
if(!NormalizePrice(TP,TP)){return;}
|
|
|
|
trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,LotSize,currentTick.bid,SL,TP,TradeComment);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---Check For Close Position Buy/Sell with Base Band
|
|
|
|
if(!CountOpenPositions(cntBuy,cntSell)){return;}
|
|
|
|
if(cntBuy>0 && currentTick.bid>=BB_BaseBuffer[0]){ClosePositions(1);}
|
2025-01-17 12:01:32 -05:00
|
|
|
if(cntSell>0 && currentTick.ask<=BB_BaseBuffer[0]){ClosePositions(2);}
|
2025-01-17 11:54:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Check For New Bars |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
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 &countBuy, int &countSell)
|
|
|
|
{
|
|
|
|
countBuy = 0;
|
|
|
|
countSell = 0;
|
|
|
|
int total = PositionsTotal();
|
|
|
|
for(int i=total-1;i>=0;i--)
|
|
|
|
{
|
|
|
|
ulong positionTicket = PositionGetTicket(i);
|
|
|
|
if(positionTicket<=0){Print("Failed to get position ticket."); return false;}
|
|
|
|
if(!PositionSelectByTicket(positionTicket)){Print("Failed to select position Ticket."); return false;}
|
|
|
|
long magic;
|
|
|
|
if(!PositionGetInteger(POSITION_MAGIC,magic)){Print("Failed to get position Magic Number."); return false;}
|
|
|
|
if(magic == EA_Magic)
|
|
|
|
{
|
|
|
|
long type;
|
|
|
|
if(!PositionGetInteger(POSITION_TYPE,type)){Print("Failed to get position Type Buy/Sell."); return false;}
|
|
|
|
if(type == POSITION_TYPE_BUY){countBuy++;}
|
|
|
|
if(type == POSITION_TYPE_SELL){countSell++;}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Normalize Price |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool NormalizePrice(double &price, double &normalizedPrize){
|
|
|
|
double tickSize = 0;
|
|
|
|
if(!SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE,tickSize))
|
|
|
|
{
|
|
|
|
Print("Failed to get Trade Tick Size!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
normalizedPrize = NormalizeDouble(MathRound(price/tickSize)*tickSize,_Digits);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Close All Positions |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool ClosePositions(int all_buy_sell)
|
|
|
|
{
|
|
|
|
int total = PositionsTotal();
|
|
|
|
for(int i=total-1;i>=0;i--)
|
|
|
|
{
|
|
|
|
ulong positionTicket = PositionGetTicket(i);
|
|
|
|
if(positionTicket<=0){Print("Failed to get position ticket."); return false;}
|
|
|
|
if(!PositionSelectByTicket(positionTicket)){Print("Failed to select position ticket."); return false;}
|
|
|
|
long magic;
|
|
|
|
if(!PositionGetInteger(POSITION_MAGIC,magic)){Print("Failed to get position Magic Number."); return false;}
|
|
|
|
if(magic == EA_Magic)
|
|
|
|
{
|
|
|
|
long type;
|
|
|
|
if(!PositionGetInteger(POSITION_TYPE,type)){Print("Failed to get position Type Buy/Sell."); return false;}
|
|
|
|
if(all_buy_sell == 1 && type == POSITION_TYPE_SELL){continue;}
|
|
|
|
if(all_buy_sell == 2 && type == POSITION_TYPE_BUY){continue;}
|
|
|
|
trade.PositionClose(positionTicket);
|
|
|
|
if(trade.ResultRetcode() != TRADE_RETCODE_DONE)
|
|
|
|
{
|
|
|
|
Print("Failed to close Position Ticket: ",(string)positionTicket," result" ,(string)trade.ResultRetcode(),":",trade.CheckResultRetcodeDescription());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|