USER
//+------------------------------------------------------------------+
//| BB_Cross_EA.mq4 |
//| Created by OpenAI - Assistant |
//| |
//+------------------------------------------------------------------+
#property strict
// Define an enumeration for stop loss options
enum BuyStopLossOptions
{
LowerBand,
MiddleBandBuy
};
enum SellStopLossOptions
{
UpperBand,
MiddleBandSell
};
// External parameters
extern double LotSize = 1; // Base lot size for trades
extern double Slippage = 3; // Slippage in points
extern double StopLossMultiplier = 1; // Multiplier for stop loss
extern double TakeProfitMultiplier = 2; // Multiplier for take profit
extern double TrailingStop = 50; // Trailing stop in points
extern int Period_BB = 30; // Bollinger Bands period
extern double Deviations = 0.8; // Standard deviations for BB
extern double MinDistance = 10; // Minimum distance for stop loss in points
// User choice for buy stop loss band
extern BuyStopLossOptions BuyStopLossBand = LowerBand; // Default to LowerBand for Buy
// User choice for sell stop loss band
extern SellStopLossOptions SellStopLossBand = UpperBand; // Default to UpperBand for Sell
// Martingale parameters
extern bool UseMartingale = false; // Enable/disable Martingale strategy
extern double MartingaleMultiplier = 2.0; // Multiplier for Martingale
extern double InitialLotSize = 1; // Starting lot size for the first trade
// Global variables
bool isBuySignal = false;
bool isSellSignal = false;
bool isTradeOpen = false; // Flag to check if a trade is open
int openOrderType = -1; // Track open order type (-1 for none, 0 for buy, 1 for sell)
int lossCount = 0; // Counter for consecutive losses
double currentLotSize; // Current lot size based on Martingale
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
currentLotSize = InitialLotSize; // Set initial lot size
return (INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Function to adjust current lot size based on Martingale |
//+------------------------------------------------------------------+
void AdjustLotSize()
{
if (lossCount == 0)
{
currentLotSize = InitialLotSize; // Reset to initial lot size
}
else
{
// Calculate the current lot size based on the loss count
currentLotSize = InitialLotSize * MathPow(MartingaleMultiplier, lossCount);
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
UpdateSignals(); // Update buy/sell signals
isTradeOpen = CheckOpenTrades(); // Check if there are open trades
AdjustLotSize(); // Adjust lot size based on losses
// Buy Logic
if (isBuySignal && !isTradeOpen && openOrderType != 0)
{
double sl = CalculateBuyStopLoss(); // Calculate Stop Loss for Buy
sl = AdjustStopLoss(sl, Ask, true); // Adjust Stop Loss
double tp = Ask + TakeProfitMultiplier * (Ask - sl); // Calculate Take Profit
int ticket = OrderSend(Symbol(), OP_BUY, currentLotSize, Ask, Slippage, sl, tp, "BB_Cross Buy", 0, 0, clrGreen);
if (ticket > 0)
{
openOrderType = 0; // Set open order type to Buy
lossCount = 0; // Reset loss count after a successful order
}
else
{
Print("Buy Order Error: ", GetLastError());
if (UseMartingale) lossCount++; // Increment loss count on failure
}
}
// Sell Logic
if (isSellSignal && !isTradeOpen && openOrderType != 1)
{
double sl = CalculateSellStopLoss(); // Calculate Stop Loss for Sell
sl = AdjustStopLoss(sl, Bid, false); // Adjust Stop Loss
double tp = Bid - TakeProfitMultiplier * (sl - Bid); // Calculate Take Profit
int ticket = OrderSend(Symbol(), OP_SELL, currentLotSize, Bid, Slippage, sl, tp, "BB_Cross Sell", 0, 0, clrRed);
if (ticket > 0)
{
openOrderType = 1; // Set open order type to Sell
lossCount = 0; // Reset loss count after a successful order
}
else
{
Print("Sell Order Error: ", GetLastError());
if (UseMartingale) lossCount++; // Increment loss count on failure
}
}
ManageOpenOrders(); // Manage existing orders
}
//+------------------------------------------------------------------+
//| Function to check if there are open trades |
//+------------------------------------------------------------------+
bool CheckOpenTrades()
{
for (int i = OrdersTotal() - 1; i >= 0; i--)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if (OrderSymbol() == Symbol())
{
return true; // Open trade exists for this symbol
}
}
}
return false; // No trades open for this symbol
}
//+------------------------------------------------------------------+
//| Function to calculate buy stop loss based on user selection |
//+------------------------------------------------------------------+
double CalculateBuyStopLoss()
{
double lowerBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_LOWER, 0);
double middleBand = iMA(Symbol(), 0, Period_BB, 0, MODE_SMA, PRICE_CLOSE, 0); // Calculate Middle Band (SMA)
switch (BuyStopLossBand)
{
case LowerBand:
return lowerBand - StopLossMultiplier * Point; // Set SL below lower band
case MiddleBandBuy:
default:
return middleBand; // Set SL at the middle band
}
}
//+------------------------------------------------------------------+
//| Function to calculate sell stop loss based on user selection |
//+------------------------------------------------------------------+
double CalculateSellStopLoss()
{
double upperBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_UPPER, 0);
double middleBand = iMA(Symbol(), 0, Period_BB, 0, MODE_SMA, PRICE_CLOSE, 0); // Calculate Middle Band (SMA)
switch (SellStopLossBand)
{
case UpperBand:
return upperBand + StopLossMultiplier * Point; // Set SL above upper band
case MiddleBandSell:
default:
return middleBand; // Set SL at the middle band
}
}
//+------------------------------------------------------------------+
//| Function to adjust stop loss if it's too close to current price |
//+------------------------------------------------------------------+
double AdjustStopLoss(double stopLoss, double marketPrice, bool isBuy)
{
double minDistanceInPips = MinDistance * Point; // Convert minimum distance to points
if (isBuy)
{
// For buy orders, SL must be below the market price
if (stopLoss >= marketPrice - minDistanceInPips)
{
stopLoss = marketPrice - minDistanceInPips; // Set SL to minimum distance below market price
}
}
else
{
// For sell orders, SL must be above the market price
if (stopLoss <= marketPrice + minDistanceInPips)
{
stopLoss = marketPrice + minDistanceInPips; // Set SL to minimum distance above market price
}
}
return stopLoss; // Return the adjusted (or original) stop loss
}
//+------------------------------------------------------------------+
//| Function to manage open orders with trailing stop |
//+------------------------------------------------------------------+
void ManageOpenOrders()
{
// Loop through all open orders
for (int i = OrdersTotal() - 1; i >= 0; i--)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && (OrderSymbol() == Symbol()))
{
// Manage Buy orders
if (OrderType() == OP_BUY)
{
double stopLossPrice = CalculateBuyStopLoss();
stopLossPrice = AdjustStopLoss(stopLossPrice, Bid, true); // Adjust Stop Loss if it's too close
// Update stop loss if the new calculated stop loss is higher
if (stopLossPrice > OrderStopLoss())
{
if (!OrderModify(OrderTicket(), OrderOpenPrice(), stopLossPrice, OrderTakeProfit(), 0, clrGreen))
Print("Error modifying Buy order: ", GetLastError());
}
}
// Manage Sell orders
if (OrderType() == OP_SELL)
{
double stopLossPrice = CalculateSellStopLoss();
stopLossPrice = AdjustStopLoss(stopLossPrice, Ask, false); // Adjust Stop Loss if it's too close
// Update stop loss if the new calculated stop loss is lower
if (stopLossPrice < OrderStopLoss())
{
if (!OrderModify(OrderTicket(), OrderOpenPrice(), stopLossPrice, OrderTakeProfit(), 0, clrRed))
Print("Error modifying Sell order: ", GetLastError());
}
}
}
}
}
//+------------------------------------------------------------------+
//| Function to update buy/sell signals |
//+------------------------------------------------------------------+
void UpdateSignals()
{
double upperBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_UPPER, 1); // Last closed candle
double lowerBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_LOWER, 1); // Last closed candle
double closePrice = Close[1]; // Last close price (previous candle)
double closePricePrev = Close[2]; // Close price before previous candle
// Check for Buy signal
if (closePrice > upperBand && closePricePrev <= upperBand)
{
isBuySignal = true;
isSellSignal = false; // Reset sell signal
}
else
isBuySignal = false;
// Check for Sell signal
if (closePrice < lowerBand && closePricePrev >= lowerBand)
{
isSellSignal = true;
isBuySignal = false; // Reset buy signal
}
else
isSellSignal = false;
}
//+------------------------------------------------------------------+
exception draw arrow make draw verticle line (red for sell, blue for buy) tooASSISTANT
Sure! To enhance your **BB_Cross_EA.mq4** Expert Advisor by adding vertical lines—**blue** for buy signals and **red** for sell signals—you can integrate graphical objects into your existing code. These lines will help visually identify the points on the chart where buy or sell signals are triggered.
Below, I'll guide you through the necessary modifications to your EA, ensuring that vertical lines are drawn accurately and efficiently whenever a buy or sell signal occurs.
---
## Overview of Additions
1. **Create Vertical Lines on Signals:**
- **Blue Vertical Line:** Drawn when a **buy signal** is generated.
- **Red Vertical Line:** Drawn when a **sell signal** is generated.
2. **Ensure Unique Lines:**
- Each vertical line is uniquely identified using the timestamp of the signal, preventing duplicate lines.
3. **Optional: Remove Old Lines**
- To maintain chart clarity, you can implement a mechanism to remove old lines after a certain period or limit the number of lines.
---
## Step-by-Step Implementation
### 1. Define a Function to Draw Vertical Lines
First, let's create a function that handles the creation of vertical lines. This function ensures that each line is uniquely identified and avoids duplication.
```mql4
//+------------------------------------------------------------------+
//| Function to draw a vertical line at a specific time |
//+------------------------------------------------------------------+
void DrawVerticalLine(string prefix, datetime time, color lineColor)
{
string lineName = prefix + "_" + TimeToStr(time, TIME_DATE|TIME_MINUTES);
// Check if the line already exists to prevent duplicates
if(ObjectFind(lineName) < 0)
{
if(!ObjectCreate(lineName, OBJ_VLINE, 0, time, 0))
{
Print("Error creating vertical line: ", GetLastError());
return;
}
// Set line properties
ObjectSetInteger(0, lineName, OBJPROP_COLOR, lineColor);
ObjectSetInteger(0, lineName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, lineName, OBJPROP_STYLE, STYLE_DASH);
}
}
```
### 2. Update Signals and Draw Lines
Modify the `UpdateSignals()` function to include calls to `DrawVerticalLine()` whenever a buy or sell signal is detected.
```mql4
//+------------------------------------------------------------------+
//| Function to update buy/sell signals and draw vertical lines |
//+------------------------------------------------------------------+
void UpdateSignals()
{
double upperBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_UPPER, 1); // Last closed candle
double lowerBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_LOWER, 1); // Last closed candle
double closePrice = Close[1]; // Last close price (previous candle)
double closePricePrev = Close[2]; // Close price before previous candle
datetime signalTime = Time[1]; // Time of the candle where the signal is detected
// Check for Buy signal
if (closePrice > upperBand && closePricePrev <= upperBand)
{
isBuySignal = true;
isSellSignal = false; // Reset sell signal
// Draw Blue Vertical Line for Buy Signal
DrawVerticalLine("BuyVLine", signalTime, clrBlue);
}
else
{
isBuySignal = false;
}
// Check for Sell signal
if (closePrice < lowerBand && closePricePrev >= lowerBand)
{
isSellSignal = true;
isBuySignal = false; // Reset buy signal
// Draw Red Vertical Line for Sell Signal
DrawVerticalLine("SellVLine", signalTime, clrRed);
}
else
{
isSellSignal = false;
}
}
```
### 3. (Optional) Draw Arrows Alongside Vertical Lines
If you also want to draw arrows in addition to vertical lines for enhanced visual cues, you can incorporate the following function and modify `UpdateSignals()` accordingly.
#### a. Define a Function to Draw Arrows
```mql4
//+------------------------------------------------------------------+
//| Function to draw an arrow on the chart |
//+------------------------------------------------------------------+
void DrawArrow(string prefix, datetime time, double price, int arrowType, color arrowColor)
{
string arrowName = prefix + "_" + TimeToStr(time, TIME_DATE|TIME_MINUTES);
// Check if the arrow already exists to prevent duplicates
if(ObjectFind(arrowName) < 0)
{
if(!ObjectCreate(arrowName, OBJ_ARROW, 0, time, price))
{
Print("Error creating arrow: ", GetLastError());
return;
}
// Set arrow properties
ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowType);
ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);
}
}
```
#### b. Update `UpdateSignals()` to Draw Arrows
Modify the `UpdateSignals()` function to include arrow drawings.
```mql4
//+------------------------------------------------------------------+
//| Function to update buy/sell signals and draw vertical lines |
//+------------------------------------------------------------------+
void UpdateSignals()
{
double upperBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_UPPER, 1); // Last closed candle
double lowerBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_LOWER, 1); // Last closed candle
double closePrice = Close[1]; // Last close price (previous candle)
double closePricePrev = Close[2]; // Close price before previous candle
datetime signalTime = Time[1]; // Time of the candle where the signal is detected
// Check for Buy signal
if (closePrice > upperBand && closePricePrev <= upperBand)
{
isBuySignal = true;
isSellSignal = false; // Reset sell signal
// Draw Blue Vertical Line for Buy Signal
DrawVerticalLine("BuyVLine", signalTime, clrBlue);
// Draw Up Arrow for Buy Signal
DrawArrow("BuyArrow", signalTime, Low[1] - (MinDistance * Point * 2), SYMBOL_ARROWUP, clrGreen);
}
else
{
isBuySignal = false;
}
// Check for Sell signal
if (closePrice < lowerBand && closePricePrev >= lowerBand)
{
isSellSignal = true;
isBuySignal = false; // Reset buy signal
// Draw Red Vertical Line for Sell Signal
DrawVerticalLine("SellVLine", signalTime, clrRed);
// Draw Down Arrow for Sell Signal
DrawArrow("SellArrow", signalTime, High[1] + (MinDistance * Point * 2), SYMBOL_ARROWDOWN, clrRed);
}
else
{
isSellSignal = false;
}
}
```
> **Note:**
> - `SYMBOL_ARROWUP` and `SYMBOL_ARROWDOWN` are predefined arrow codes in MQL4. You can customize them as needed.
> - The arrow's `price` parameter is set slightly above the candle's high for sell signals and below the candle's low for buy signals to ensure visibility.
### 4. (Optional) Remove Old Lines and Arrows
To prevent cluttering your chart with too many vertical lines and arrows, you can implement a cleanup routine that removes objects older than a certain number of bars or after a specific timeframe.
```mql4
//+------------------------------------------------------------------+
//| Function to remove old vertical lines and arrows |
//+------------------------------------------------------------------+
void CleanupOldObjects(int maxBarsOld)
{
datetime currentTime = Time[0];
for(int i = ObjectsTotal()-1; i >=0; i--)
{
string objName = ObjectName(i);
datetime objTime;
// Check if the object is a vertical line or arrow
if(StringFind(objName, "VLine") != -1 || StringFind(objName, "Arrow") != -1)
{
// Retrieve object time
if(ObjectType(objName) == OBJ_VLINE)
{
objTime = ObjectGetInteger(0, objName, OBJPROP_TIME1);
}
else
{
// For arrows, retrieve the XTime property
objTime = ObjectGetInteger(0, objName, OBJPROP_TIME);
}
// Calculate the number of bars between current time and object time
int barsOld = iBarShift(NULL, 0, objTime, false);
if(barsOld < 0 || barsOld > maxBarsOld)
{
ObjectDelete(objName);
}
}
}
}
```
#### Integrate Cleanup into `OnTick()`
Call the `CleanupOldObjects()` function within the `OnTick()` function to regularly clean up old objects. Adjust `maxBarsOld` as per your preference (e.g., keeping objects for the last 100 bars).
```mql4
void OnTick()
{
UpdateSignals(); // Update buy/sell signals and draw lines/arrows
isTradeOpen = CheckOpenTrades(); // Check if there are open trades
AdjustLotSize(); // Adjust lot size based on losses
// Execute Buy Logic
if (isBuySignal && !isTradeOpen && openOrderType != 0)
{
double sl = CalculateBuyStopLoss(); // Calculate Stop Loss for Buy
sl = AdjustStopLoss(sl, Ask, true); // Adjust Stop Loss
double tp = Ask + TakeProfitMultiplier * (Ask - sl); // Calculate Take Profit
int ticket = OrderSend(Symbol(), OP_BUY, currentLotSize, Ask, Slippage, sl, tp, "BB_Cross Buy", 0, 0, clrGreen);
if (ticket > 0)
{
openOrderType = 0; // Set open order type to Buy
lossCount = 0; // Reset loss count after a successful order
}
else
{
Print("Buy Order Error: ", GetLastError());
if (UseMartingale) lossCount++; // Increment loss count on failure
}
}
// Execute Sell Logic
if (isSellSignal && !isTradeOpen && openOrderType != 1)
{
double sl = CalculateSellStopLoss(); // Calculate Stop Loss for Sell
sl = AdjustStopLoss(sl, Bid, false); // Adjust Stop Loss
double tp = Bid - TakeProfitMultiplier * (sl - Bid); // Calculate Take Profit
int ticket = OrderSend(Symbol(), OP_SELL, currentLotSize, Bid, Slippage, sl, tp, "BB_Cross Sell", 0, 0, clrRed);
if (ticket > 0)
{
openOrderType = 1; // Set open order type to Sell
lossCount = 0; // Reset loss count after a successful order
}
else
{
Print("Sell Order Error: ", GetLastError());
if (UseMartingale) lossCount++; // Increment loss count on failure
}
}
ManageOpenOrders(); // Manage existing orders
// Cleanup old graphical objects (e.g., objects older than 100 bars)
CleanupOldObjects(100);
}
```
---
## Complete Modified Code
Below is the complete **BB_Cross_EA.mq4** code with the added functionalities for drawing vertical lines and optional arrows on buy and sell signals.
```mql4
//+------------------------------------------------------------------+
//| BB_Cross_EA.mq4 |
//| Created by OpenAI - Assistant |
//| |
//+------------------------------------------------------------------+
#property strict
// Define an enumeration for stop loss options
enum BuyStopLossOptions
{
LowerBand,
MiddleBandBuy
};
enum SellStopLossOptions
{
UpperBand,
MiddleBandSell
};
// External parameters
extern double LotSize = 1; // Base lot size for trades
extern double Slippage = 3; // Slippage in points
extern double StopLossMultiplier = 1; // Multiplier for stop loss
extern double TakeProfitMultiplier = 2; // Multiplier for take profit
extern double TrailingStop = 50; // Trailing stop in points
extern int Period_BB = 30; // Bollinger Bands period
extern double Deviations = 0.8; // Standard deviations for BB
extern double MinDistance = 10; // Minimum distance for stop loss in points
// User choice for buy stop loss band
extern BuyStopLossOptions BuyStopLossBand = LowerBand; // Default to LowerBand for Buy
// User choice for sell stop loss band
extern SellStopLossOptions SellStopLossBand = UpperBand; // Default to UpperBand for Sell
// Martingale parameters
extern bool UseMartingale = false; // Enable/disable Martingale strategy
extern double MartingaleMultiplier = 2.0; // Multiplier for Martingale
extern double InitialLotSize = 1; // Starting lot size for the first trade
// Global variables
bool isBuySignal = false;
bool isSellSignal = false;
bool isTradeOpen = false; // Flag to check if a trade is open
int openOrderType = -1; // Track open order type (-1 for none, 0 for buy, 1 for sell)
int lossCount = 0; // Counter for consecutive losses
double currentLotSize; // Current lot size based on Martingale
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
currentLotSize = InitialLotSize; // Set initial lot size
return (INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Function to adjust current lot size based on Martingale |
//+------------------------------------------------------------------+
void AdjustLotSize()
{
if (lossCount == 0)
{
currentLotSize = InitialLotSize; // Reset to initial lot size
}
else
{
// Calculate the current lot size based on the loss count
currentLotSize = InitialLotSize * MathPow(MartingaleMultiplier, lossCount);
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
UpdateSignals(); // Update buy/sell signals and draw lines/arrows
isTradeOpen = CheckOpenTrades(); // Check if there are open trades
AdjustLotSize(); // Adjust lot size based on losses
// Buy Logic
if (isBuySignal && !isTradeOpen && openOrderType != 0)
{
double sl = CalculateBuyStopLoss(); // Calculate Stop Loss for Buy
sl = AdjustStopLoss(sl, Ask, true); // Adjust Stop Loss
double tp = Ask + TakeProfitMultiplier * (Ask - sl); // Calculate Take Profit
int ticket = OrderSend(Symbol(), OP_BUY, currentLotSize, Ask, Slippage, sl, tp, "BB_Cross Buy", 0, 0, clrGreen);
if (ticket > 0)
{
openOrderType = 0; // Set open order type to Buy
lossCount = 0; // Reset loss count after a successful order
}
else
{
Print("Buy Order Error: ", GetLastError());
if (UseMartingale) lossCount++; // Increment loss count on failure
}
}
// Sell Logic
if (isSellSignal && !isTradeOpen && openOrderType != 1)
{
double sl = CalculateSellStopLoss(); // Calculate Stop Loss for Sell
sl = AdjustStopLoss(sl, Bid, false); // Adjust Stop Loss
double tp = Bid - TakeProfitMultiplier * (sl - Bid); // Calculate Take Profit
int ticket = OrderSend(Symbol(), OP_SELL, currentLotSize, Bid, Slippage, sl, tp, "BB_Cross Sell", 0, 0, clrRed);
if (ticket > 0)
{
openOrderType = 1; // Set open order type to Sell
lossCount = 0; // Reset loss count after a successful order
}
else
{
Print("Sell Order Error: ", GetLastError());
if (UseMartingale) lossCount++; // Increment loss count on failure
}
}
ManageOpenOrders(); // Manage existing orders
// Cleanup old graphical objects (e.g., objects older than 100 bars)
CleanupOldObjects(100);
}
//+------------------------------------------------------------------+
//| Function to check if there are open trades |
//+------------------------------------------------------------------+
bool CheckOpenTrades()
{
for (int i = OrdersTotal() - 1; i >= 0; i--)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if (OrderSymbol() == Symbol())
{
return true; // Open trade exists for this symbol
}
}
}
return false; // No trades open for this symbol
}
//+------------------------------------------------------------------+
//| Function to calculate buy stop loss based on user selection |
//+------------------------------------------------------------------+
double CalculateBuyStopLoss()
{
double lowerBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_LOWER, 0);
double middleBand = iMA(Symbol(), 0, Period_BB, 0, MODE_SMA, PRICE_CLOSE, 0); // Calculate Middle Band (SMA)
switch (BuyStopLossBand)
{
case LowerBand:
return lowerBand - StopLossMultiplier * Point; // Set SL below lower band
case MiddleBandBuy:
default:
return middleBand; // Set SL at the middle band
}
}
//+------------------------------------------------------------------+
//| Function to calculate sell stop loss based on user selection |
//+------------------------------------------------------------------+
double CalculateSellStopLoss()
{
double upperBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_UPPER, 0);
double middleBand = iMA(Symbol(), 0, Period_BB, 0, MODE_SMA, PRICE_CLOSE, 0); // Calculate Middle Band (SMA)
switch (SellStopLossBand)
{
case UpperBand:
return upperBand + StopLossMultiplier * Point; // Set SL above upper band
case MiddleBandSell:
default:
return middleBand; // Set SL at the middle band
}
}
//+------------------------------------------------------------------+
//| Function to adjust stop loss if it's too close to current price |
//+------------------------------------------------------------------+
double AdjustStopLoss(double stopLoss, double marketPrice, bool isBuy)
{
double minDistanceInPips = MinDistance * Point; // Convert minimum distance to points
if (isBuy)
{
// For buy orders, SL must be below the market price
if (stopLoss >= marketPrice - minDistanceInPips)
{
stopLoss = marketPrice - minDistanceInPips; // Set SL to minimum distance below market price
}
}
else
{
// For sell orders, SL must be above the market price
if (stopLoss <= marketPrice + minDistanceInPips)
{
stopLoss = marketPrice + minDistanceInPips; // Set SL to minimum distance above market price
}
}
return stopLoss; // Return the adjusted (or original) stop loss
}
//+------------------------------------------------------------------+
//| Function to manage open orders with trailing stop |
//+------------------------------------------------------------------+
void ManageOpenOrders()
{
// Loop through all open orders
for (int i = OrdersTotal() - 1; i >= 0; i--)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && (OrderSymbol() == Symbol()))
{
// Manage Buy orders
if (OrderType() == OP_BUY)
{
double stopLossPrice = CalculateBuyStopLoss();
stopLossPrice = AdjustStopLoss(stopLossPrice, Bid, true); // Adjust Stop Loss if it's too close
// Update stop loss if the new calculated stop loss is higher
if (stopLossPrice > OrderStopLoss())
{
if (!OrderModify(OrderTicket(), OrderOpenPrice(), stopLossPrice, OrderTakeProfit(), 0, clrGreen))
Print("Error modifying Buy order: ", GetLastError());
}
}
// Manage Sell orders
if (OrderType() == OP_SELL)
{
double stopLossPrice = CalculateSellStopLoss();
stopLossPrice = AdjustStopLoss(stopLossPrice, Ask, false); // Adjust Stop Loss if it's too close
// Update stop loss if the new calculated stop loss is lower
if (stopLossPrice < OrderStopLoss())
{
if (!OrderModify(OrderTicket(), OrderOpenPrice(), stopLossPrice, OrderTakeProfit(), 0, clrRed))
Print("Error modifying Sell order: ", GetLastError());
}
}
}
}
}
//+------------------------------------------------------------------+
//| Function to draw a vertical line at a specific time |
//+------------------------------------------------------------------+
void DrawVerticalLine(string prefix, datetime time, color lineColor)
{
string lineName = prefix + "_" + TimeToStr(time, TIME_DATE|TIME_MINUTES);
// Check if the line already exists to prevent duplicates
if(ObjectFind(lineName) < 0)
{
if(!ObjectCreate(lineName, OBJ_VLINE, 0, time, 0))
{
Print("Error creating vertical line: ", GetLastError());
return;
}
// Set line properties
ObjectSetInteger(0, lineName, OBJPROP_COLOR, lineColor);
ObjectSetInteger(0, lineName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, lineName, OBJPROP_STYLE, STYLE_DASH);
}
}
//+------------------------------------------------------------------+
//| Function to draw an arrow on the chart |
//+------------------------------------------------------------------+
void DrawArrow(string prefix, datetime time, double price, int arrowType, color arrowColor)
{
string arrowName = prefix + "_" + TimeToStr(time, TIME_DATE|TIME_MINUTES);
// Check if the arrow already exists to prevent duplicates
if(ObjectFind(arrowName) < 0)
{
if(!ObjectCreate(arrowName, OBJ_ARROW, 0, time, price))
{
Print("Error creating arrow: ", GetLastError());
return;
}
// Set arrow properties
ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowType);
ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);
}
}
//+------------------------------------------------------------------+
//| Function to update buy/sell signals and draw vertical lines |
//+------------------------------------------------------------------+
void UpdateSignals()
{
double upperBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_UPPER, 1); // Last closed candle
double lowerBand = iBands(Symbol(), 0, Period_BB, Deviations, 0, PRICE_CLOSE, MODE_LOWER, 1); // Last closed candle
double closePrice = Close[1]; // Last close price (previous candle)
double closePricePrev = Close[2]; // Close price before previous candle
datetime signalTime = Time[1]; // Time of the candle where the signal is detected
// Check for Buy signal
if (closePrice > upperBand && closePricePrev <= upperBand)
{
isBuySignal = true;
isSellSignal = false; // Reset sell signal
// Draw Blue Vertical Line for Buy Signal
DrawVerticalLine("BuyVLine", signalTime, clrBlue);
// Draw Up Arrow for Buy Signal
DrawArrow("BuyArrow", signalTime, Low[1] - (MinDistance * Point * 2), SYMBOL_ARROWUP, clrGreen);
}
else
{
isBuySignal = false;
}
// Check for Sell signal
if (closePrice < lowerBand && closePricePrev >= lowerBand)
{
isSellSignal = true;
isBuySignal = false; // Reset buy signal
// Draw Red Vertical Line for Sell Signal
DrawVerticalLine("SellVLine", signalTime, clrRed);
// Draw Down Arrow for Sell Signal
DrawArrow("SellArrow", signalTime, High[1] + (MinDistance * Point * 2), SYMBOL_ARROWDOWN, clrRed);
}
else
{
isSellSignal = false;
}
}
//+------------------------------------------------------------------+
//| Function to remove old vertical lines and arrows |
//+------------------------------------------------------------------+
void CleanupOldObjects(int maxBarsOld)
{
datetime currentTime = Time[0];
for(int i = ObjectsTotal()-1; i >=0; i--)
{
string objName = ObjectName(i);
datetime objTime;
// Check if the object is a vertical line or arrow
if(StringFind(objName, "VLine") != -1 || StringFind(objName, "Arrow") != -1)
{
// Retrieve object time
if(ObjectType(objName) == OBJ_VLINE)
{
objTime = ObjectGetInteger(0, objName, OBJPROP_TIME1);
}
else
{
// For arrows, retrieve the XTime property
objTime = ObjectGetInteger(0, objName, OBJPROP_TIME);
}
// Calculate the number of bars between current time and object time
int barsOld = iBarShift(NULL, 0, objTime, false);
if(barsOld < 0 || barsOld > maxBarsOld)
{
ObjectDelete(objName);
}
}
}
}
```
---
## Explanation of the Added Components
1. **`DrawVerticalLine()` Function:**
- **Purpose:** Creates a vertical line at a specified time with a given color.
- **Parameters:**
- `prefix`: A string prefix to uniquely identify the line (e.g., "BuyVLine" or "SellVLine").
- `time`: The datetime where the line should be drawn.
- `lineColor`: The color of the line (`clrBlue` for buy, `clrRed` for sell).
- **Functionality:**
- Constructs a unique `lineName` using the prefix and the signal time.
- Checks if the line already exists to prevent duplication.
- Creates the line with specified properties.
2. **`DrawArrow()` Function:**
- **Purpose:** Draws an arrow (up or down) on the chart to indicate buy or sell signals.
- **Parameters:**
- `prefix`: A string prefix to uniquely identify the arrow (e.g., "BuyArrow" or "SellArrow").
- `time`: The datetime where the arrow should be drawn.
- `price`: The price level where the arrow should appear.
- `arrowType`: The type of arrow (`SYMBOL_ARROWUP` or `SYMBOL_ARROWDOWN`).
- `arrowColor`: The color of the arrow (`clrGreen` for buy, `clrRed` for sell).
- **Functionality:**
- Constructs a unique `arrowName` using the prefix and the signal time.
- Checks if the arrow already exists to prevent duplication.
- Creates the arrow with specified properties.
3. **`UpdateSignals()` Modifications:**
- Upon detecting a **buy signal**, the EA:
- Draws a **blue vertical line**.
- Draws a **green up arrow** slightly below the low of the signal candle.
- Upon detecting a **sell signal**, the EA:
- Draws a **red vertical line**.
- Draws a **red down arrow** slightly above the high of the signal candle.
4. **`CleanupOldObjects()` Function:**
- **Purpose:** Maintains chart cleanliness by removing old vertical lines and arrows that are beyond a specified age (`maxBarsOld`).
- **Parameters:**
- `maxBarsOld`: The maximum number of bars old an object can be before it's removed.
- **Functionality:**
- Iterates through all chart objects.
- Identifies vertical lines and arrows based on their naming conventions.
- Deletes objects older than `maxBarsOld` bars.
5. **Integration into `OnTick()`:**
- After processing signals and managing trades, the EA calls `CleanupOldObjects(100)` to remove any graphical objects older than 100 bars. You can adjust this value based on your preference.
---
## Final Notes
- **Customization:**
- **Colors & Styles:** Feel free to modify colors, line styles, and arrow types to suit your preferences.
- **Cleanup Mechanism:** Adjust the `maxBarsOld` parameter in `CleanupOldObjects()` to control how many bars old an object can be before it's removed.
- **Performance Consideration:**
- Drawing numerous graphical objects can impact chart performance, especially on lower timeframes or with a high `maxBarsOld` value. Ensure that the cleanup mechanism effectively manages the number of objects.
- **Testing:**
- Before deploying the modified EA on a live account, test it thoroughly on a demo account to ensure that vertical lines and arrows are drawn correctly and that the cleanup mechanism works as intended.
By integrating these modifications, your EA will not only execute trades based on Bollinger Bands cross signals but also provide clear visual indicators on the chart, enhancing your trading strategy's transparency and manageability.