Support/Resistance Bounce Scalping
Support/Resistance Bounce Scalping
Start by identifying a genuine support or resistance level — one that has been tested at least two or three times previously, showing that the market has repeatedly respected this specific price zone rather than blowing through it.
As price approaches that established level again, resist the urge to enter immediately just because price has arrived at the zone. Instead, wait for an actual rejection signal on your entry timeframe — a pin bar with a long wick rejecting the level, a bullish or bearish engulfing candle, or some other clear price-action confirmation that buyers or sellers are actively defending the zone in real time, right now, not just historically.
Once that confirmation appears, enter with a stop placed just beyond the level itself — close enough to keep your risk tight, but far enough to allow for normal noise around the zone without getting stopped out prematurely on a minor wick.
Target the next minor structure point as your profit objective — the next small swing high or low, or a minor prior consolidation zone — rather than an arbitrary pip target disconnected from the actual chart. This keeps your reward-to-risk grounded in something the market has actually shown you, rather than a number you picked because it sounded nice.
As price approaches that established level again, resist the urge to enter immediately just because price has arrived at the zone. Instead, wait for an actual rejection signal on your entry timeframe — a pin bar with a long wick rejecting the level, a bullish or bearish engulfing candle, or some other clear price-action confirmation that buyers or sellers are actively defending the zone in real time, right now, not just historically.
Once that confirmation appears, enter with a stop placed just beyond the level itself — close enough to keep your risk tight, but far enough to allow for normal noise around the zone without getting stopped out prematurely on a minor wick.
Target the next minor structure point as your profit objective — the next small swing high or low, or a minor prior consolidation zone — rather than an arbitrary pip target disconnected from the actual chart. This keeps your reward-to-risk grounded in something the market has actually shown you, rather than a number you picked because it sounded nice.
It’s Fairman 
Re: Support/Resistance Bounce Scalping
The trading approach you just described is one of the most fundamentally sound methodologies in technical analysis.Fairman wrote: Fri Aug 21, 2026 10:00 pm Start by identifying a genuine support or resistance level — one that has been tested at least two or three times previously, showing that the market has repeatedly respected this specific price zone rather than blowing through it.
As price approaches that established level again, resist the urge to enter immediately just because price has arrived at the zone. Instead, wait for an actual rejection signal on your entry timeframe — a pin bar with a long wick rejecting the level, a bullish or bearish engulfing candle, or some other clear price-action confirmation that buyers or sellers are actively defending the zone in real time, right now, not just historically.
Once that confirmation appears, enter with a stop placed just beyond the level itself — close enough to keep your risk tight, but far enough to allow for normal noise around the zone without getting stopped out prematurely on a minor wick.
Target the next minor structure point as your profit objective — the next small swing high or low, or a minor prior consolidation zone — rather than an arbitrary pip target disconnected from the actual chart. This keeps your reward-to-risk grounded in something the market has actually shown you, rather than a number you picked because it sounded nice.
By combining higher-timeframe market structure (established support/resistance) with localized momentum (candlestick confirmation), you avoid the common trap of catching "falling knives." Furthermore, anchoring your Stop Loss (SL) and Take Profit (TP) to actual market geometry rather than arbitrary pip counts is the hallmark of a professional risk management framework.
To translate this discretionary strategy into a systematic format, I have prepared a foundational MetaTrader 4 (MT4) Expert Advisor (EA) in MQL4.
How the EA Translates Your Logic
Genuine Levels: The EA uses an adjustable SwingLookback period to identify recent structural Swing Highs and Swing Lows as your Support and Resistance zones.
Price Action Confirmation: It scans the close of every new candle for classic rejection signals—specifically Pin Bars (long wicks, small bodies) and Engulfing Candles.
Zone Proximity: It only takes the trade if the confirmation candle prints within a defined pip tolerance of the established S/R level.
Noise Buffer (SL): It calculates the Stop Loss dynamically using the Average True Range (ATR) indicator, placing it just beyond the extreme of the S/R level to account for market noise.
Structural Target (TP): It targets the opposing recent swing structure (or applies a fixed Risk:Reward ratio based on the structural stop).
MQL4 Expert Advisor: Price Action Bounce EA
You can copy this code directly into the MetaEditor, compile it, and attach it to a chart.
Code: Select all
//+------------------------------------------------------------------+
//| PriceActionBounceEA.mq4 |
//| Contextual Support/Resistance & Price Action |
//+------------------------------------------------------------------+
#property copyright "Gemini"
#property version "1.00"
#property strict
//--- Input Parameters
input double InpLotSize = 0.1; // Lot Size
input int InpSwingLookback = 50; // Bars to define S/R Level
input int InpZonePips = 15; // Proximity to S/R to validate trade (Pips)
input double InpATRMultiplier = 1.0; // SL Buffer beyond level (ATR Multiplier)
input double InpRiskReward = 1.5; // TP Multiplier (Reward to Risk)
input int InpMagicNumber = 123456; // Magic Number
double pips;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
// Adjust for 4 or 5 digit brokers
if(Digits == 3 || Digits == 5) pips = 10.0 * Point;
else pips = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// Only execute on new candle open
static datetime lastTime = 0;
if(Time[0] == lastTime) return;
// Check if we already have open positions
if(OrdersTotal() > 0) return;
// 1. Identify S/R Levels (Recent Swing High/Low)
int lowestIndex = iLowest(Symbol(), 0, MODE_LOW, InpSwingLookback, 1);
int highestIndex = iHighest(Symbol(), 0, MODE_HIGH, InpSwingLookback, 1);
double supportLevel = Low[lowestIndex];
double resistanceLevel = High[highestIndex];
double atr = iATR(Symbol(), 0, 14, 1);
// 2. Check for Bullish Rejection at Support
if(Low[1] <= supportLevel + (InpZonePips * pips) && Low[1] >= supportLevel - (InpZonePips * pips)) {
if(IsBullishPinBar(1) || IsBullishEngulfing(1)) {
double sl = supportLevel - (atr * InpATRMultiplier);
double risk = Ask - sl;
double tp = Ask + (risk * InpRiskReward);
int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, tp, "PA Bounce Buy", InpMagicNumber, 0, clrGreen);
if(ticket > 0) lastTime = Time[0];
}
}
// 3. Check for Bearish Rejection at Resistance
if(High[1] >= resistanceLevel - (InpZonePips * pips) && High[1] <= resistanceLevel + (InpZonePips * pips)) {
if(IsBearishPinBar(1) || IsBearishEngulfing(1)) {
double sl = resistanceLevel + (atr * InpATRMultiplier);
double risk = sl - Bid;
double tp = Bid - (risk * InpRiskReward);
int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, tp, "PA Bounce Sell", InpMagicNumber, 0, clrRed);
if(ticket > 0) lastTime = Time[0];
}
}
}
//+------------------------------------------------------------------+
//| Pattern Recognition Functions |
//+------------------------------------------------------------------+
bool IsBullishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
// Long lower wick (rejecting lows), small upper wick, body in top third
return (lowerWick > bodySize * 2.0 && upperWick < bodySize && Close[i] > (High[i] + Low[i])/2);
}
bool IsBearishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
// Long upper wick (rejecting highs), small lower wick, body in bottom third
return (upperWick > bodySize * 2.0 && lowerWick < bodySize && Close[i] < (High[i] + Low[i])/2);
}
bool IsBullishEngulfing(int i) {
// Previous candle is bearish, current is bullish and engulfs previous body
return (Close[i+1] < Open[i+1] && Close[i] > Open[i] && Close[i] > Open[i+1] && Open[i] < Close[i+1]);
}
bool IsBearishEngulfing(int i) {
// Previous candle is bullish, current is bearish and engulfs previous body
return (Close[i+1] > Open[i+1] && Close[i] < Open[i] && Close[i] < Open[i+1] && Open[i] > Close[i+1]);
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Support/Resistance Bounce Scalping
A Note on Algorithmic Limitations
The human eye is incredibly skilled at filtering out "messy" consolidation zones to find genuine, multi-tested support and resistance levels. A computer, however, simply sees highs and lows.
Because of this, fully automated S/R EAs can sometimes take trades at minor, insignificant swing points in a choppy market. If you prefer to maintain control over level identification, a popular hybrid approach is a Semi-Automated EA: You draw a horizontal line on your chart and name it "Support", and the EA merely monitors that specific line, pulling the trigger only when the pin bar/engulfing confirmation appears.
The human eye is incredibly skilled at filtering out "messy" consolidation zones to find genuine, multi-tested support and resistance levels. A computer, however, simply sees highs and lows.
Because of this, fully automated S/R EAs can sometimes take trades at minor, insignificant swing points in a choppy market. If you prefer to maintain control over level identification, a popular hybrid approach is a Semi-Automated EA: You draw a horizontal line on your chart and name it "Support", and the EA merely monitors that specific line, pulling the trigger only when the pin bar/engulfing confirmation appears.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Support/Resistance Bounce Scalping
To filter your entries by trend, you use the built-in MQL4 function iMA().
To integrate this properly, we make three small adjustments to the code:
Add input parameters so you can enable/disable the filter and customize the period (e.g., switch to 50 or 100 EMA if needed).
Calculate the EMA value on the previous closed bar (shift = 1) to prevent repainting.
Add the condition to the trade logic:
Buy (Support Bounce): Price must close above the 200 EMA (Close[1] > ema).
Sell (Resistance Bounce): Price must close below the 200 EMA (Close[1] < ema).
Updated Full EA Code
Here is the complete updated MQL4 script ready to compile in MetaEditor:
To integrate this properly, we make three small adjustments to the code:
Add input parameters so you can enable/disable the filter and customize the period (e.g., switch to 50 or 100 EMA if needed).
Calculate the EMA value on the previous closed bar (shift = 1) to prevent repainting.
Add the condition to the trade logic:
Buy (Support Bounce): Price must close above the 200 EMA (Close[1] > ema).
Sell (Resistance Bounce): Price must close below the 200 EMA (Close[1] < ema).
Updated Full EA Code
Here is the complete updated MQL4 script ready to compile in MetaEditor:
Code: Select all
//+------------------------------------------------------------------+
//| PriceActionBounceEA.mq4 |
//| Contextual Support/Resistance & Price Action |
//+------------------------------------------------------------------+
#property copyright "Gemini"
#property version "1.10"
#property strict
//--- General Inputs
input double InpLotSize = 0.1; // Lot Size
input int InpSwingLookback = 50; // Bars to define S/R Level
input int InpZonePips = 15; // Proximity to S/R to validate trade (Pips)
input double InpATRMultiplier = 1.0; // SL Buffer beyond level (ATR Multiplier)
input double InpRiskReward = 1.5; // TP Multiplier (Reward to Risk)
input int InpMagicNumber = 123456; // Magic Number
//--- Trend Filter Inputs
input bool InpUseTrendFilter = true; // Enable 200 EMA Trend Filter
input int InpMAPeriod = 200; // Moving Average Period
input ENUM_MA_METHOD InpMAMethod = MODE_EMA; // Moving Average Method (EMA)
double pips;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
if(Digits == 3 || Digits == 5) pips = 10.0 * Point;
else pips = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// Only execute on new candle open
static datetime lastTime = 0;
if(Time[0] == lastTime) return;
// Check if we already have an open position
if(OrdersTotal() > 0) return;
// 1. Identify S/R Levels (Recent Swing High/Low)
int lowestIndex = iLowest(Symbol(), 0, MODE_LOW, InpSwingLookback, 1);
int highestIndex = iHighest(Symbol(), 0, MODE_HIGH, InpSwingLookback, 1);
double supportLevel = Low[lowestIndex];
double resistanceLevel = High[highestIndex];
double atr = iATR(Symbol(), 0, 14, 1);
// 2. Trend Filter Calculation (Evaluated on completed Bar 1)
double ema = iMA(Symbol(), 0, InpMAPeriod, 0, InpMAMethod, PRICE_CLOSE, 1);
bool isUptrend = (!InpUseTrendFilter || Close[1] > ema);
bool isDowntrend = (!InpUseTrendFilter || Close[1] < ema);
// 3. Buy Condition: Rejection at Support in an Uptrend
if(isUptrend) {
if(Low[1] <= supportLevel + (InpZonePips * pips) && Low[1] >= supportLevel - (InpZonePips * pips)) {
if(IsBullishPinBar(1) || IsBullishEngulfing(1)) {
double sl = supportLevel - (atr * InpATRMultiplier);
double risk = Ask - sl;
double tp = Ask + (risk * InpRiskReward);
int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, tp, "PA Bounce Buy", InpMagicNumber, 0, clrGreen);
if(ticket > 0) lastTime = Time[0];
}
}
}
// 4. Sell Condition: Rejection at Resistance in a Downtrend
if(isDowntrend) {
if(High[1] >= resistanceLevel - (InpZonePips * pips) && High[1] <= resistanceLevel + (InpZonePips * pips)) {
if(IsBearishPinBar(1) || IsBearishEngulfing(1)) {
double sl = resistanceLevel + (atr * InpATRMultiplier);
double risk = sl - Bid;
double tp = Bid - (risk * InpRiskReward);
int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, tp, "PA Bounce Sell", InpMagicNumber, 0, clrRed);
if(ticket > 0) lastTime = Time[0];
}
}
}
}
//+------------------------------------------------------------------+
//| Pattern Recognition Functions |
//+------------------------------------------------------------------+
bool IsBullishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
return (lowerWick > bodySize * 2.0 && upperWick < bodySize && Close[i] > (High[i] + Low[i])/2.0);
}
bool IsBearishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
return (upperWick > bodySize * 2.0 && lowerWick < bodySize && Close[i] < (High[i] + Low[i])/2.0);
}
bool IsBullishEngulfing(int i) {
return (Close[i+1] < Open[i+1] && Close[i] > Open[i] && Close[i] > Open[i+1] && Open[i] < Close[i+1]);
}
bool IsBearishEngulfing(int i) {
return (Close[i+1] > Open[i+1] && Close[i] < Open[i] && Close[i] < Open[i+1] && Open[i] > Close[i+1]);
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Support/Resistance Bounce Scalping
Key Technical Consideration: Timeframe Alignment
Currently, the iMA() call uses timeframe 0, which defaults to whatever timeframe the chart is running (e.g., 200 EMA on the 15-minute chart).
If you want the macro daily trend to filter entries taken on an intraday timeframe (like the 15-minute or 1-hour chart), change the timeframe parameter in iMA():
This ensures your intraday entries are strictly aligned with the higher-timeframe institutional flow.
Currently, the iMA() call uses timeframe 0, which defaults to whatever timeframe the chart is running (e.g., 200 EMA on the 15-minute chart).
If you want the macro daily trend to filter entries taken on an intraday timeframe (like the 15-minute or 1-hour chart), change the timeframe parameter in iMA():
Code: Select all
// 1440 corresponds to the Daily (D1) timeframe
double ema = iMA(Symbol(), PERIOD_D1, InpMAPeriod, 0, InpMAMethod, PRICE_CLOSE, 1);Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Support/Resistance Bounce Scalping
To make the timeframe selectable directly from the EA's settings menu, we use the built-in MQL4 enumeration ENUM_TIMEFRAMES. This creates a native dropdown menu in the MT4 inputs tab, allowing you to run the EA on a 15-minute chart while explicitly telling the Moving Average to calculate based on the Daily (D1) data.
Here is the updated code integrating the Multi-Timeframe (MTF) parameter:
Here is the updated code integrating the Multi-Timeframe (MTF) parameter:
Code: Select all
//+------------------------------------------------------------------+
//| PriceActionBounceEA.mq4 |
//| Contextual Support/Resistance & Price Action |
//+------------------------------------------------------------------+
#property copyright "Gemini"
#property version "1.20"
#property strict
//--- General Inputs
input double InpLotSize = 0.1; // Lot Size
input int InpSwingLookback = 50; // Bars to define S/R Level
input int InpZonePips = 15; // Proximity to S/R to validate trade (Pips)
input double InpATRMultiplier = 1.0; // SL Buffer beyond level (ATR Multiplier)
input double InpRiskReward = 1.5; // TP Multiplier (Reward to Risk)
input int InpMagicNumber = 123456; // Magic Number
//--- Trend Filter Inputs (Multi-Timeframe)
input bool InpUseTrendFilter = true; // Enable Trend Filter
input ENUM_TIMEFRAMES InpMATimeframe = PERIOD_D1; // Trend Timeframe (MTF)
input int InpMAPeriod = 200; // Moving Average Period
input ENUM_MA_METHOD InpMAMethod = MODE_EMA; // Moving Average Method (EMA)
double pips;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
if(Digits == 3 || Digits == 5) pips = 10.0 * Point;
else pips = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// Only execute on new candle open on the chart's current timeframe
static datetime lastTime = 0;
if(Time[0] == lastTime) return;
// Check if we already have an open position
if(OrdersTotal() > 0) return;
// 1. Identify S/R Levels (Recent Swing High/Low on current TF)
int lowestIndex = iLowest(Symbol(), 0, MODE_LOW, InpSwingLookback, 1);
int highestIndex = iHighest(Symbol(), 0, MODE_HIGH, InpSwingLookback, 1);
double supportLevel = Low[lowestIndex];
double resistanceLevel = High[highestIndex];
double atr = iATR(Symbol(), 0, 14, 1);
// 2. MTF Trend Filter Calculation
// Calculates the EMA based on the selected InpMATimeframe (e.g., Daily).
// Shift = 1 locks in the value of the last fully completed candle of that higher timeframe.
double ema = iMA(Symbol(), InpMATimeframe, InpMAPeriod, 0, InpMAMethod, PRICE_CLOSE, 1);
// Evaluates if the most recently closed candle on your execution chart is above/below the HTF EMA
bool isUptrend = (!InpUseTrendFilter || Close[1] > ema);
bool isDowntrend = (!InpUseTrendFilter || Close[1] < ema);
// 3. Buy Condition: Rejection at Support in an Uptrend
if(isUptrend) {
if(Low[1] <= supportLevel + (InpZonePips * pips) && Low[1] >= supportLevel - (InpZonePips * pips)) {
if(IsBullishPinBar(1) || IsBullishEngulfing(1)) {
double sl = supportLevel - (atr * InpATRMultiplier);
double risk = Ask - sl;
double tp = Ask + (risk * InpRiskReward);
int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, tp, "PA Bounce Buy", InpMagicNumber, 0, clrGreen);
if(ticket > 0) lastTime = Time[0];
}
}
}
// 4. Sell Condition: Rejection at Resistance in a Downtrend
if(isDowntrend) {
if(High[1] >= resistanceLevel - (InpZonePips * pips) && High[1] <= resistanceLevel + (InpZonePips * pips)) {
if(IsBearishPinBar(1) || IsBearishEngulfing(1)) {
double sl = resistanceLevel + (atr * InpATRMultiplier);
double risk = sl - Bid;
double tp = Bid - (risk * InpRiskReward);
int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, tp, "PA Bounce Sell", InpMagicNumber, 0, clrRed);
if(ticket > 0) lastTime = Time[0];
}
}
}
}
//+------------------------------------------------------------------+
//| Pattern Recognition Functions |
//+------------------------------------------------------------------+
bool IsBullishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
return (lowerWick > bodySize * 2.0 && upperWick < bodySize && Close[i] > (High[i] + Low[i])/2.0);
}
bool IsBearishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
return (upperWick > bodySize * 2.0 && lowerWick < bodySize && Close[i] < (High[i] + Low[i])/2.0);
}
bool IsBullishEngulfing(int i) {
return (Close[i+1] < Open[i+1] && Close[i] > Open[i] && Close[i] > Open[i+1] && Open[i] < Close[i+1]);
}
bool IsBearishEngulfing(int i) {
return (Close[i+1] > Open[i+1] && Close[i] < Open[i] && Close[i] < Open[i+1] && Open[i] > Close[i+1]);
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Support/Resistance Bounce Scalping
How the MTF Logic Evaluates
When you place this EA on an M15 chart and leave the timeframe input on PERIOD_D1:
iMA(Symbol(), InpMATimeframe, ...) forces the EA to look at the Daily chart, completely ignoring the fact that it is currently attached to an M15 chart.
The shift = 1 at the end of the iMA calculation is critical. Because we are looking at PERIOD_D1, shift 1 pulls the EMA value of the last fully completed Daily candle (yesterday's close). This prevents the Daily EMA value from wildly repainting intraday as the current daily candle moves up and down.
The trend boolean (Close[1] > ema) compares the last fully closed 15-minute candle against that locked-in Daily EMA value.
When you place this EA on an M15 chart and leave the timeframe input on PERIOD_D1:
iMA(Symbol(), InpMATimeframe, ...) forces the EA to look at the Daily chart, completely ignoring the fact that it is currently attached to an M15 chart.
The shift = 1 at the end of the iMA calculation is critical. Because we are looking at PERIOD_D1, shift 1 pulls the EMA value of the last fully completed Daily candle (yesterday's close). This prevents the Daily EMA value from wildly repainting intraday as the current daily candle moves up and down.
The trend boolean (Close[1] > ema) compares the last fully closed 15-minute candle against that locked-in Daily EMA value.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Support/Resistance Bounce Scalping
To implement a break-even feature, we need to make two structural changes to the EA's architecture.
First, we must remove the hard if(OrdersTotal() > 0) return; block at the top of OnTick(). If the EA stops executing as soon as a trade is opened, it cannot actively manage that trade. We will replace this with a dedicated trade management block that runs on every tick.
Second, to calculate "1R profit" dynamically, the EA needs to measure the distance between the OrderOpenPrice() and the initial OrderStopLoss(). Once the current price moves in our favor by that exact distance, the EA modifies the Stop Loss to the entry price.
Updated Complete EA Code
Here is the updated script featuring the new ManageBreakEven() function and a robust position counting loop.
First, we must remove the hard if(OrdersTotal() > 0) return; block at the top of OnTick(). If the EA stops executing as soon as a trade is opened, it cannot actively manage that trade. We will replace this with a dedicated trade management block that runs on every tick.
Second, to calculate "1R profit" dynamically, the EA needs to measure the distance between the OrderOpenPrice() and the initial OrderStopLoss(). Once the current price moves in our favor by that exact distance, the EA modifies the Stop Loss to the entry price.
Updated Complete EA Code
Here is the updated script featuring the new ManageBreakEven() function and a robust position counting loop.
Code: Select all
//+------------------------------------------------------------------+
//| PriceActionBounceEA.mq4 |
//| Contextual Support/Resistance & Price Action |
//+------------------------------------------------------------------+
#property copyright "Gemini"
#property version "1.30"
#property strict
//--- General Inputs
input double InpLotSize = 0.1; // Lot Size
input int InpSwingLookback = 50; // Bars to define S/R Level
input int InpZonePips = 15; // Proximity to S/R to validate trade (Pips)
input double InpATRMultiplier = 1.0; // SL Buffer beyond level (ATR)
input double InpRiskReward = 1.5; // TP Multiplier (Reward to Risk)
input int InpMagicNumber = 123456; // Magic Number
//--- Trend Filter Inputs (Multi-Timeframe)
input bool InpUseTrendFilter = true; // Enable Trend Filter
input ENUM_TIMEFRAMES InpMATimeframe = PERIOD_D1; // Trend Timeframe (MTF)
input int InpMAPeriod = 200; // Moving Average Period
input ENUM_MA_METHOD InpMAMethod = MODE_EMA; // Moving Average Method (EMA)
//--- Trade Management Inputs
input bool InpUseBreakEven = true; // Move SL to Entry at 1R Profit
double pips;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
if(Digits == 3 || Digits == 5) pips = 10.0 * Point;
else pips = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// 1. Manage open positions on every tick
ManageBreakEven();
// 2. Check if we already have an open position for this specific strategy
if(CountOpenPositions() > 0) return;
// 3. Only execute new trade logic on a new candle open
static datetime lastTime = 0;
if(Time[0] == lastTime) return;
// Identify S/R Levels (Recent Swing High/Low on current TF)
int lowestIndex = iLowest(Symbol(), 0, MODE_LOW, InpSwingLookback, 1);
int highestIndex = iHighest(Symbol(), 0, MODE_HIGH, InpSwingLookback, 1);
double supportLevel = Low[lowestIndex];
double resistanceLevel = High[highestIndex];
double atr = iATR(Symbol(), 0, 14, 1);
// MTF Trend Filter Calculation
double ema = iMA(Symbol(), InpMATimeframe, InpMAPeriod, 0, InpMAMethod, PRICE_CLOSE, 1);
bool isUptrend = (!InpUseTrendFilter || Close[1] > ema);
bool isDowntrend = (!InpUseTrendFilter || Close[1] < ema);
// Buy Condition: Rejection at Support in an Uptrend
if(isUptrend) {
if(Low[1] <= supportLevel + (InpZonePips * pips) && Low[1] >= supportLevel - (InpZonePips * pips)) {
if(IsBullishPinBar(1) || IsBullishEngulfing(1)) {
double sl = supportLevel - (atr * InpATRMultiplier);
double risk = Ask - sl;
double tp = Ask + (risk * InpRiskReward);
int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, tp, "PA Bounce Buy", InpMagicNumber, 0, clrGreen);
if(ticket > 0) lastTime = Time[0];
}
}
}
// Sell Condition: Rejection at Resistance in a Downtrend
if(isDowntrend) {
if(High[1] >= resistanceLevel - (InpZonePips * pips) && High[1] <= resistanceLevel + (InpZonePips * pips)) {
if(IsBearishPinBar(1) || IsBearishEngulfing(1)) {
double sl = resistanceLevel + (atr * InpATRMultiplier);
double risk = sl - Bid;
double tp = Bid - (risk * InpRiskReward);
int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, tp, "PA Bounce Sell", InpMagicNumber, 0, clrRed);
if(ticket > 0) lastTime = Time[0];
}
}
}
}
//+------------------------------------------------------------------+
//| Trade Management Functions |
//+------------------------------------------------------------------+
void ManageBreakEven() {
if(!InpUseBreakEven) return;
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber) {
double openPrice = OrderOpenPrice();
double currentSL = OrderStopLoss();
if(OrderType() == OP_BUY) {
// Check if SL is still below our entry price
if(currentSL < openPrice && currentSL != 0) {
double initialRisk = openPrice - currentSL;
// If current price reached 1R profit, move SL to Entry
if(Bid >= openPrice + initialRisk) {
bool res = OrderModify(OrderTicket(), openPrice, openPrice, OrderTakeProfit(), 0, clrBlue);
}
}
}
else if(OrderType() == OP_SELL) {
// Check if SL is still above our entry price
if(currentSL > openPrice && currentSL != 0) {
double initialRisk = currentSL - openPrice;
// If current price reached 1R profit, move SL to Entry
if(Ask <= openPrice - initialRisk) {
bool res = OrderModify(OrderTicket(), openPrice, openPrice, OrderTakeProfit(), 0, clrBlue);
}
}
}
}
}
}
}
int CountOpenPositions() {
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber) {
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Pattern Recognition Functions |
//+------------------------------------------------------------------+
bool IsBullishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
return (lowerWick > bodySize * 2.0 && upperWick < bodySize && Close[i] > (High[i] + Low[i])/2.0);
}
bool IsBearishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
return (upperWick > bodySize * 2.0 && lowerWick < bodySize && Close[i] < (High[i] + Low[i])/2.0);
}
bool IsBullishEngulfing(int i) {
return (Close[i+1] < Open[i+1] && Close[i] > Open[i] && Close[i] > Open[i+1] && Open[i] < Close[i+1]);
}
bool IsBearishEngulfing(int i) {
return (Close[i+1] > Open[i+1] && Close[i] < Open[i] && Close[i] < Open[i+1] && Open[i] > Close[i+1]);
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Support/Resistance Bounce Scalping
How the Break-Even Logic Works
Dynamic 1R Calculation: Inside the ManageBreakEven() function, the EA subtracts the initial Stop Loss from the Entry Price. This automatically gives you the exact 1R distance, regardless of whether it's a tight 10-pip stop or a wide 50-pip stop.
State Checking: Before attempting to modify an order, it checks if(currentSL < openPrice) for a buy order. This ensures the EA doesn't spam your broker's server with modification requests once the Stop Loss has already been moved to Break-Even.
Tick-by-Tick Execution: ManageBreakEven() runs outside the "new candle only" logic. This means if price violently spikes to 1R and immediately retraces, the EA catches the price movement in real-time and protects your trade before the candle closes.
Dynamic 1R Calculation: Inside the ManageBreakEven() function, the EA subtracts the initial Stop Loss from the Entry Price. This automatically gives you the exact 1R distance, regardless of whether it's a tight 10-pip stop or a wide 50-pip stop.
State Checking: Before attempting to modify an order, it checks if(currentSL < openPrice) for a buy order. This ensures the EA doesn't spam your broker's server with modification requests once the Stop Loss has already been moved to Break-Even.
Tick-by-Tick Execution: ManageBreakEven() runs outside the "new candle only" logic. This means if price violently spikes to 1R and immediately retraces, the EA catches the price movement in real-time and protects your trade before the candle closes.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Support/Resistance Bounce Scalping
To implement dynamic position sizing based on a strict percentage risk, we need to calculate the exact financial loss of 1.0 standard lot for the specific Stop Loss distance, and then divide your defined risk amount by that value.
In MQL4, this requires utilizing MarketInfo() to pull the MODE_TICKVALUE and MODE_TICKSIZE. This is particularly critical if you trade a mix of forex pairs and metals (like Silver), because the tick value and contract sizes differ drastically across those asset classes. Using MarketInfo ensures the math automatically scales regardless of the symbol or your account's base currency.
The Dynamic Sizing Logic
We will replace the InpLotSize input with InpRiskPercent and introduce a CalculateLotSize() function. This function determines the raw lot size and then normalizes it against your broker's MODE_LOTSTEP, MODE_MINLOT, and MODE_MAXLOT to prevent OrderSend normalization errors (Error 131).
Updated Complete EA Code
Here is the updated architecture with the dynamic risk manager integrated seamlessly before the order execution blocks:
In MQL4, this requires utilizing MarketInfo() to pull the MODE_TICKVALUE and MODE_TICKSIZE. This is particularly critical if you trade a mix of forex pairs and metals (like Silver), because the tick value and contract sizes differ drastically across those asset classes. Using MarketInfo ensures the math automatically scales regardless of the symbol or your account's base currency.
The Dynamic Sizing Logic
We will replace the InpLotSize input with InpRiskPercent and introduce a CalculateLotSize() function. This function determines the raw lot size and then normalizes it against your broker's MODE_LOTSTEP, MODE_MINLOT, and MODE_MAXLOT to prevent OrderSend normalization errors (Error 131).
Updated Complete EA Code
Here is the updated architecture with the dynamic risk manager integrated seamlessly before the order execution blocks:
Code: Select all
//+------------------------------------------------------------------+
//| PriceActionBounceEA.mq4 |
//| Contextual Support/Resistance & Price Action |
//+------------------------------------------------------------------+
#property copyright "Gemini"
#property version "1.40"
#property strict
//--- Risk & Money Management Inputs
input double InpRiskPercent = 1.0; // Risk per Trade (% of Balance)
input int InpSwingLookback = 50; // Bars to define S/R Level
input int InpZonePips = 15; // Proximity to S/R to validate trade (Pips)
input double InpATRMultiplier = 1.0; // SL Buffer beyond level (ATR)
input double InpRiskReward = 1.5; // TP Multiplier (Reward to Risk)
input int InpMagicNumber = 123456; // Magic Number
//--- Trend Filter Inputs (Multi-Timeframe)
input bool InpUseTrendFilter = true; // Enable Trend Filter
input ENUM_TIMEFRAMES InpMATimeframe = PERIOD_D1; // Trend Timeframe (MTF)
input int InpMAPeriod = 200; // Moving Average Period
input ENUM_MA_METHOD InpMAMethod = MODE_EMA; // Moving Average Method (EMA)
//--- Trade Management Inputs
input bool InpUseBreakEven = true; // Move SL to Entry at 1R Profit
double pips;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
if(Digits == 3 || Digits == 5) pips = 10.0 * Point;
else pips = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// 1. Manage open positions on every tick
ManageBreakEven();
// 2. Check if we already have an open position for this specific strategy
if(CountOpenPositions() > 0) return;
// 3. Only execute new trade logic on a new candle open
static datetime lastTime = 0;
if(Time[0] == lastTime) return;
// Identify S/R Levels (Recent Swing High/Low on current TF)
int lowestIndex = iLowest(Symbol(), 0, MODE_LOW, InpSwingLookback, 1);
int highestIndex = iHighest(Symbol(), 0, MODE_HIGH, InpSwingLookback, 1);
double supportLevel = Low[lowestIndex];
double resistanceLevel = High[highestIndex];
double atr = iATR(Symbol(), 0, 14, 1);
// MTF Trend Filter Calculation
double ema = iMA(Symbol(), InpMATimeframe, InpMAPeriod, 0, InpMAMethod, PRICE_CLOSE, 1);
bool isUptrend = (!InpUseTrendFilter || Close[1] > ema);
bool isDowntrend = (!InpUseTrendFilter || Close[1] < ema);
// Buy Condition: Rejection at Support in an Uptrend
if(isUptrend) {
if(Low[1] <= supportLevel + (InpZonePips * pips) && Low[1] >= supportLevel - (InpZonePips * pips)) {
if(IsBullishPinBar(1) || IsBullishEngulfing(1)) {
double sl = supportLevel - (atr * InpATRMultiplier);
double risk = Ask - sl;
double tp = Ask + (risk * InpRiskReward);
double lotSize = CalculateLotSize(Ask, sl);
if(lotSize > 0) {
int ticket = OrderSend(Symbol(), OP_BUY, lotSize, Ask, 3, sl, tp, "PA Bounce Buy", InpMagicNumber, 0, clrGreen);
if(ticket > 0) lastTime = Time[0];
}
}
}
}
// Sell Condition: Rejection at Resistance in a Downtrend
if(isDowntrend) {
if(High[1] >= resistanceLevel - (InpZonePips * pips) && High[1] <= resistanceLevel + (InpZonePips * pips)) {
if(IsBearishPinBar(1) || IsBearishEngulfing(1)) {
double sl = resistanceLevel + (atr * InpATRMultiplier);
double risk = sl - Bid;
double tp = Bid - (risk * InpRiskReward);
double lotSize = CalculateLotSize(Bid, sl);
if(lotSize > 0) {
int ticket = OrderSend(Symbol(), OP_SELL, lotSize, Bid, 3, sl, tp, "PA Bounce Sell", InpMagicNumber, 0, clrRed);
if(ticket > 0) lastTime = Time[0];
}
}
}
}
}
//+------------------------------------------------------------------+
//| Dynamic Position Sizing Function |
//+------------------------------------------------------------------+
double CalculateLotSize(double entryPrice, double stopLossPrice) {
double riskAmount = AccountBalance() * (InpRiskPercent / 100.0);
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
// Safety check to prevent zero divide errors
if(tickValue == 0 || tickSize == 0) return 0;
// Calculate the total financial loss per 1.0 standard lot
double lossPerLot = (MathAbs(entryPrice - stopLossPrice) / tickSize) * tickValue;
if(lossPerLot == 0) return 0;
double rawLots = riskAmount / lossPerLot;
// Normalize against broker lot step limits
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double normalizedLots = MathRound(rawLots / lotStep) * lotStep;
if(normalizedLots < minLot) normalizedLots = minLot;
if(normalizedLots > maxLot) normalizedLots = maxLot;
return normalizedLots;
}
//+------------------------------------------------------------------+
//| Trade Management Functions |
//+------------------------------------------------------------------+
void ManageBreakEven() {
if(!InpUseBreakEven) return;
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber) {
double openPrice = OrderOpenPrice();
double currentSL = OrderStopLoss();
if(OrderType() == OP_BUY) {
if(currentSL < openPrice && currentSL != 0) {
double initialRisk = openPrice - currentSL;
if(Bid >= openPrice + initialRisk) {
bool res = OrderModify(OrderTicket(), openPrice, openPrice, OrderTakeProfit(), 0, clrBlue);
}
}
}
else if(OrderType() == OP_SELL) {
if(currentSL > openPrice && currentSL != 0) {
double initialRisk = currentSL - openPrice;
if(Ask <= openPrice - initialRisk) {
bool res = OrderModify(OrderTicket(), openPrice, openPrice, OrderTakeProfit(), 0, clrBlue);
}
}
}
}
}
}
}
int CountOpenPositions() {
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber) {
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Pattern Recognition Functions |
//+------------------------------------------------------------------+
bool IsBullishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
return (lowerWick > bodySize * 2.0 && upperWick < bodySize && Close[i] > (High[i] + Low[i])/2.0);
}
bool IsBearishPinBar(int i) {
double bodySize = MathAbs(Open[i] - Close[i]);
double lowerWick = MathMin(Open[i], Close[i]) - Low[i];
double upperWick = High[i] - MathMax(Open[i], Close[i]);
double totalSize = High[i] - Low[i];
if(totalSize == 0) return false;
return (upperWick > bodySize * 2.0 && lowerWick < bodySize && Close[i] < (High[i] + Low[i])/2.0);
}
bool IsBullishEngulfing(int i) {
return (Close[i+1] < Open[i+1] && Close[i] > Open[i] && Close[i] > Open[i+1] && Open[i] < Close[i+1]);
}
bool IsBearishEngulfing(int i) {
return (Close[i+1] > Open[i+1] && Close[i] < Open[i] && Close[i] < Open[i+1] && Open[i] > Close[i+1]);
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.