How do you handle range-based EAs during low volatility periods?

 
Hello everyone,

I am testing a range-based Expert Advisor on a few instruments.
The EA avoids overtrading and stays inactive during low-volatility conditions.

During holiday periods or very quiet sessions, I prefer to keep the EA attached
and let it follow its internal rules rather than interfere manually.

I would like to hear how experienced traders here manage such periods.
Do you keep your EAs running or do you detach them during low volatility?

Thank you for your guidance.
 
Forex Journey:
The EA avoids overtrading and stays inactive during low-volatility conditions.

Add a volume indicator as a filter:

Code Base

Volume Average

Mladen Rakic, 2018.03.12 10:21

Long known volume analysis method.

or a volatility indicator as a filter:

Code Base

ATR Cycles

Ryan L Johnson, 2025.11.08 14:29

A volatility filter based on 3 ATR's: a fast ATR, a middle ATR, and a slow ATR
And regarding holidays...
Forex Journey:
During holiday periods or very quiet sessions, I prefer to keep the EA attached
and let it follow its internal rules rather than interfere manually.

You seem to have already diagnosed the problem with trading the holiday season--low liquidity and extraordinarily erratic price movements. Institutional traders generally thin out starting on the week of U.S. Thanksgiving and don't return until after New Year's Day. A time filter applied from Thanksgiving week through January 31st might do well in your case.

Accurate backtesting can generate a valuable Report that will show you the specific hours, days, and months that are winners versus losers.
 
I agree with adding a volume filter - don't allow a trade if the volume is below the average. Maybe you can also use trend strength filter instead of a volume filter. I don't know which is better.
 

range = (highest - lowest)

if (range > threshold) signal()

 
Conor Mcnamara #:
I agree with adding a volume filter - don't allow a trade if the volume is below the average. Maybe you can also use trend strength filter instead of a volume filter. I don't know which is better.

Code Base

Brooky Trend Strength for MT5

Ryan L Johnson, 2025.04.29 19:52

This indicator calls 3 other subwindow indicators. All files go in your Indicators folder.

 

So as not to leave the time filter implementation like a sea of question marks...

Forum on trading, automated trading systems and testing trading strategies

Issues with TimeHour Error when Coding EA

Ryan L Johnson, 2025.01.30 15:16

Inputs:

input group "---------------------------------------";
input group "Turn trading time filter on/off";
input bool UseTimer = true;
input group "---------------------------------------";
input group "Use personal computer time to filter? ==> if false, broker time is used";
input bool UsePCtime = true;
input group "---------------------------------------";
input group "Set time to enable trading ==> Intraday and overnight are supported";
input string StartTime = "21:00";
input group "---------------------------------------";
input group "Set time to disable trading";
input string StopTime = "16:00";
input group "---------------------------------------";

Variables on global scope:

datetime dLocalTime, dStartTime, dStopTime;
ulong uLocalTime, uStartTime, uStopTime,

In OnTick():

   dStartTime = StringToTime(StartTime);
   uStartTime = ulong(dStartTime);
   
   dStopTime = StringToTime(StopTime);
   uStopTime = ulong(dStopTime);
   
   if(UsePCtime == true)
    {   
     dLocalTime = TimeLocal();
    }
   else
    {
     dLocalTime = TimeCurrent();
    }
    
   uLocalTime = ulong(dLocalTime);

   if(uStartTime < uStopTime) // intraday start trading time is earlier than intraday stop trading time
    {
     if(UseTimer == true)
      {
       if(uLocalTime >= uStartTime
        && uLocalTime < uStopTime)
         {
          runBot = true;
        
          if(timerPrinted != 1)
           {
            Print("Timer is ON. Current time is within set trading times. Bot is ON.");
            timerPrinted = 1;
           }
         }
       else
         {
          runBot = false;
        
          if(timerPrinted != 2)
           {
            Print("Timer is ON. Current time is outside of set trading times. Bot is OFF.");
            CancelOrder();
            timerPrinted = 2;
           }
         }
      }
    }
   
   if(uStartTime > uStopTime) // intraday start trading time is later than intraday stop trading time
    {
     if(UseTimer == true)
      {
       if(uLocalTime >= uStopTime
        && uLocalTime < uStartTime)
         {
          runBot = false;

          if(timerPrinted != 2)
           {
            Print("Timer is ON. Current time is outside of set trading times. Bot is OFF.");
            timerPrinted = 2;
           }
         }
       else
         {
          runBot = true;

          if(timerPrinted != 1)
           {
            Print("Timer is ON. Current time is within set trading times. Bot is ON.");
            timerPrinted = 1;
           }
         }
      }
    }
   
   if(UseTimer == false) 
    {
     runBot = true;
     
     if(timerPrinted != 3)
      {
       Print("Timer is OFF. Bot is ON.");
       timerPrinted = 3;
      }
    }

And then put your trading code inside:

   if(runBot == true)
    {


    //all of your trading code


    }
(I prefer to leave trade exits code outside of the time filter).

 
Hello, I only have a cell phone.
Please help compile this .mq5 file into .ex5.

Strategy: 10 independent entries on XAUUSD
Lot size: 0.001 (micro)
No martingale, each entry has its own SL/TP.

Thank you very much!


//+-------------------------------------------------------------------+
//| 10 Gold Scalper Entries - NO MARTINGALE |
//| Small Capital of Rp. 20,000 - Strict Risk Management |
//+-------------------------------------------------------------------+
#Property copyright "TraderHP-10EntryGold"
#property version "1.00"
#strict properties

//--- Input Parameters
input int Total Entries = 10; // Maximum number of entries
input double LotPerEntry = 0.001; // Lots per entry (MICRO LOT)
input int EntryDistance = 50; // Distance between entries (points)
input int TakeProfit = 30; // TP per entry (points)
input int StopLoss = 20; // SL per entry (points)
input int Slippage = 5; // Slip
input int MagicNumber = 88888; // Magic Number
input bool UseTrendFilter = true; // Trend filter
input int TrendPeriod = 20; // EMA trend period

//--- Global Variables
int entryCounter = 0;
double entry Price[10];
int entryTickets[10];
last login date and time = 0;

//--- Indicator Handle
int emaHandle;
double ema[];

//+-------------------------------------------------------------------+
//| Expert initialization function |
//+-------------------------------------------------------------------+
int OnInit()
{
emaHandle = iMA(_Symbol, PERIOD_M5, TrendPeriod, 0, MODE_EMA, PRICE_CLOSE);
ArraySetAsSeries(ema, true);
   
// Initialize array
ArrayInitialize(entryPrices, 0);
ArrayInitialize(entryTickets, 0);
   
// Calculate capital and risk
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskPerEntry = LotPerEntry * StopLoss * 0.01; // Estimated risk
double totalRisk = riskPerEntry * TotalEntries;
   
Print("=== 10 GOLD REGISTRATIONS ===");
Print("Capital: $", balance);
Print("Lot/entry: ", LotPerEntry);
Print("Risk/entry: ~$", riskPerEntry);
Print("Total risk (10 entries): ~$", totalRisk);
Print("WARNING: Micro lot trading only!");
   
if balance < 1.0)
Alert("Capital too small! Minimum $1 required");
   
return(INIT_SUCCESS);
}

//+-------------------------------------------------------------------+
//| Expert tick function |
//+-------------------------------------------------------------------+
void OnTick()
{
// Update trends
if(UseTrendFilter)
{
CopyBuffer(emaHandle, 0, 0, 2, ema);
if(ArraySize(ema) < 2) return;
}
   
// Check the number of open positions
int openPositions = CountOpenPositions();
   
// If there is no position yet, find the first entry
if (open position == 0 && entry counter == 0)
{
CheckFirstEntry();
}
// If there are open positions, check for additional entries
if (open positions > 0 && entry counter < TotalEntries)
{
Check Additional Entries();
}
   
// Update comments on graph
UpdateChartComment();
}

//+-------------------------------------------------------------------+
//| Check the first entry based on the trend |
//+-------------------------------------------------------------------+
void CheckForFirstEntry()
{
// Check if enough time has passed since the last entry
if(TimeCurrent() - lastEntryTime < 300) // wait time 5 minutes
return;
   
Current Price (currentPrice) = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double emaValue = ema[0];
   
// Determine the trend direction
int trendDirection = 0;
if(current price > ema value + 10 * _points)
trendDirection = 1; // Uptrend
if (current price < ema value - 10 * _points)
trendDirection = -1; // Downtrend
   
if (trendDirection == 0) return; // No clear trend
   
// Open the first entry
if (trendDirection == 1) // BUY trend
{
OpenPosition(ORDER_TYPE_BUY, 0);
}
otherwise // SELL Trend
{
OpenPosition(ORDER_TYPE_SELL, 0);
}
}

//+-------------------------------------------------------------------+
//| Check for additional entries |
//+-------------------------------------------------------------------+
void CheckForAdditionalEntries()
{
// Entry between cooling times
if(TimeCurrent() - lastEntryTime < 180) // wait time 3 minutes
return;
   
// Take the first position as a reference
First Entry Price double = 0;
int firstEntryType = -1;
   
for (int i = 0; i < PositionsTotal(); i++)
{
if(PositionGetSymbol(i) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
firstEntryPrice = PositionGetDouble( POSITION_PRICE_OPEN );
firstEntryType = (int)PositionGetInteger(POSITION_TYPE);
damage;
}
}
   
if(firstEntryPrice == 0) return;
   
Current Price (currentPrice) = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double distancePoints = MathAbs(currentPrice - firstEntryPrice) / _Point;
   
// Check if the price has moved far enough to enter an additional order
if (entry point distance >= entry distance && entry counter < total entries)
{
// Open additional entries in the same direction
if (first entry type == BUY_POSITION_TYPE)
{
OpenPosition(ORDER_TYPE_BUY, entryCounter);
}
if (firstEntryType == POSITION_TYPE_SELL)
{
OpenPosition(ORDER_TYPE_SELL, entryCounter);
}
}
}

//+-------------------------------------------------------------------+
//| Open new position |
//+-------------------------------------------------------------------+
void OpenPosition(ENUM_ORDER_TYPE orderType, int entryIndex)
{
MqlTradeRequest request;
MqlTradeResult;
ZeroMemory(request);
ZeroMemory(result);
   
double price, sl, tp;
   
if (order_type == PURCHASE_ORDER_TYPE)
{
price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
sl = price - StopLoss * _Point;
tp = price + TakeProfit * _Point;
}
if not
{
price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
sl = price + StopLoss * _Point;
tp = price - TakeProfit * _Point;
}
   
request.action = TRACT_DEAL;
request.symbol = _Symbol;
request volume = LotPerEntry;
request type = order type;
Asking price = price;
request.sl = sl;
request.tp = tp;
request.deviation = Slippage;
request.magic = Magic Number;
request.comment = "Entry " + IntegerToString(entryIndex + 1) + "/" + IntegerToString(TotalEntries);
   
if(OrderSend(request, result))
{
// Save data entry
entryPrices[entryCounter] = prices;
entryTickets[entryCounter] = result.order;
Entry counter++;
lastEntryTime = TimeCurrent();
      
Print("Entry #", entryCounter, " opened with price: ", price,
" Lot: ", LotPerEntry,
"TP: ", tp,
" SL: ", sl);
}
if not
{
Print("Entry failed! Error: ", GetLastError());
}
}

//+-------------------------------------------------------------------+
//| Calculate available positions |
//+-------------------------------------------------------------------+
int CountOpenPositions()
{
int count = 0;
for (int i = 0; i < PositionsTotal(); i++)
{
if(PositionGetSymbol(i) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
count++;
}
}
return amount;
}

//+-------------------------------------------------------------------+
//| Calculate total profit/loss |
//+-------------------------------------------------------------------+
double CalculateTotalProfit()
{
Double total profit = 0;
for (int i = 0; i < PositionsTotal(); i++)
{
if(PositionGetSymbol(i) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
totalProfit += PositionGetDouble(POSITION_PROFIT);
}
}
return total profit;
}

//+-------------------------------------------------------------------+
//| Update graph comments |
//+-------------------------------------------------------------------+
void UpdateChartComment()
{
double totalProfit = CalculateTotalProfit();
int openPositions = CountOpenPositions();
   
string comment = "10 GOLD SELLER ENTRIES\n";
comment += "======\n";
comment += "Number of Entries: " + IntegerToString(entryCounter) + "/" + IntegerToString(TotalEntries) + "\n";
comment += "Open Positions: " + IntegerToString(openPositions) + "\n";
comment += "Total Profit: $" + DoubleToString(totalProfit, 2) + "\n";
comment += "Lot/Entry: " + DoubleToString(LotPerEntry, 3) + "\n";
comment += "Next entry distance: " + IntegerToString(EntryDistance) + " points\n";
   
if(entryCounter > 0)
{
comment += "\nEntrance Ticket Price:\n";
for(int i = 0; i < entry counter; i++)
{
if(entryPrices[i] > 0)
comments += "#" + IntegerToString(i+1) + ": " + DoubleToString(entryPrices[i], 2) + "\n";
}
}
   
Comments(comments);
}

//+-------------------------------------------------------------------+
//| OnTrade Function |
//+-------------------------------------------------------------------+
void OnTrade()
{
// Reset counter if all positions are closed
if(CountOpenPositions() == 0)
{
entry counter = 0;
ArrayInitialize(entryPrices, 0);
ArrayInitialize(entryTickets, 0);
Print("All positions closed. Reset entry counter.");
}
}

//+-------------------------------------------------------------------+
//| Expert deinitialization function |
//+-------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
 
Saiful arip #:
Hello, I only have a cell phone.
Please help compile this .mq5 file into .ex5.

Strategy: 10 independent entries on XAUUSD
Lot size: 0.001 (micro)
No martingale, each entry has its own SL/TP.

Thank you very much!


How would you use this EA if you've only got a cell phone?
 
Eleni Anna Branou #:
How would you use this EA if you've only got a cell phone?
Possibly. He’s not with his PC. PC elsewhere maybe.
 
Arinze Michael Ejike #:
Possibly. He’s not with his PC. PC elsewhere maybe.

Likely not. See:

Saiful arip #:
Hello, I only have a cell phone.