The rules at a glance
- Market: any major pair on the 1-hour chart; the example uses EURUSD
- Level: two swing lows (each with 5 higher lows on either side) within 0.3 × ATR of each other and at least 10 candles apart; the support level is their average
- Entry: on the next visit, a candle whose low comes within 0.1 × ATR of the level (or through it) and that closes back above it as a bullish candle; enter at that close
- Stop loss: half an ATR below the level, or at the candle's low if that's lower
- Target: twice the risk (2R)
- Expiry: each level is traded once, and dropped after 200 candles or when a candle closes half an ATR through it
- Resistance: the mirror image, from two swing highs
What support and resistance are
Support is a price where buying has stopped a fall before; resistance is where selling has stopped a rise. Traders remember those prices, orders collect around them, and the price often reacts when it gets back there. A level is really a small zone rather than an exact price, which is why these rules use a tolerance based on the ATR.
One swing low is just a low. Two swing lows at almost the same price are more interesting: buyers defended the same area twice. This strategy waits for that, then trades the third visit.
How the script finds a level
- It marks swing lows: candles whose low is lower than the five candles before and the five after. A swing low is only confirmed five candles later.
- When a new swing low forms within 0.3 × ATR of an earlier one, at least 10 candles apart, the average of the two becomes the support level.
- The level stays live for 200 candles, until it's traded once, or until a candle closes more than half an ATR below it.
Drawing levels by hand works too, and many traders prefer it. The point of fixed rules is that you can test them: a level drawn after the fact always looks better than it was.
The rules, step by step
1. Mark the level
Find two swing lows within about a third of an ATR of each other, at least 10 candles apart. Their average is the level.
2. Wait for the price to come back
The entry candle's low must come within a tenth of an ATR of the level, or poke through it.
3. Buy a bullish close back above the level
The candle must close above the level and above its own open: buyers defended the zone again. Enter at that close.
4. Stop below the zone, target 2R
The stop goes half an ATR below the level, or at the entry candle's low if that's lower. The target is twice the risk. The level is used once; after that trade the script waits for a new one.
Resistance works the same way upside down: two matching swing highs, a visit, and a bearish close back below the level.
Worked example: EURUSD, 1-hour chart
EURUSD makes a swing low at 1.1621, rallies, and falls back to a second swing low at 1.1622, 22 candles later. The two are a pip apart, well inside the tolerance, so the support level is their average, 1.16215. The price rallies again and comes back a third time. This candle dips to 1.1621 and closes at 1.1634, bullish and back above the level. Half of the 7.1-pip ATR below the level puts the stop at 1.16179.
| EURUSD long | |
|---|---|
| Support level | 1.16215 |
| Entry | 1.1634 |
| Stop loss | 1.16179, 16.1 pips away |
| Target (2R) | 1.16662, 32.2 pips away |
| Lot size at 1% of $10,000 | $100 ÷ (16.1 pips × $10) = 0.621, rounded down to 0.62 lots |
| Loss if the stop is hit | $99.82 |
| Profit at the target | $199.64 |
EURUSD climbs away from the level and reaches the target 10 candles later. The entry is well above the level here, because the bounce candle was strong. A weaker bounce gives a better price and a smaller stop, but also less evidence that buyers are there.
Bounce, break or sweep?
A level can do three things when the price returns, and each has its own strategy page on this site.
| What happens | Trade | Page |
|---|---|---|
| The price touches the level and turns back | With the bounce | This one |
| The price closes through the level, then comes back to test it from the other side | With the break | Break and retest |
| The price pokes through the level and snaps back inside | Against the poke | Liquidity sweep |
The third touch of a level is where these overlap: some traders say every touch weakens a level, others that it confirms it. Neither is a rule. Run this script and the break and retest script on the same pair and dates, and see what your market does more often.
Risk management
- Size from the stop. The stop sits below the zone, not on the line, so it's usually 10 to 25 pips on the 1-hour chart.
- Watch the higher timeframe. A 1-hour support level in a falling daily trend is more likely to break. The optional 200 EMA filter in the script lets you test only buying above it.
- Round numbers. Levels near 1.1600 or 1.1700 often get extra attention. Expect more spikes through them.
Common mistakes
- Drawing too many lines. If every swing is a level, none of them means much.
- Treating a level as an exact price. It's a zone. Stops placed exactly on the line get taken out by normal noise.
- Buying the touch. Wait for the candle to close and show the bounce.
- Trading a level that's already broken. Once a candle closes well through support, it's a different setup.
Backtest it yourself on TradingView
This page doesn't quote a win rate or a profit figure, and you should be wary of anyone who does without showing the test. Results change with the market, the dates, the data feed and the costs you put in. The honest way is to run the test yourself, and the free script below does the work in a couple of minutes.
- Open the chart. On TradingView, open EURUSD on the 1-hour chart.
- Add the script. Open the Pine Editor from the Pine icon in the panel on the right of the chart (older layouts have it as a tab under the chart), delete the code that's already there, paste this strategy's code and click "Add to chart".
- Put in your costs. Open the strategy's settings and go to Properties. Enter your broker's commission and a little slippage. A test without costs flatters every strategy, and short-term ones most of all.
- Read the Strategy Tester. The panel under the chart shows the net profit, the maximum drawdown, the profit factor, the number of trades and a list of every trade. Click a few trades and check on the chart that each one follows the rules. Trade sizes are shown in units rather than lots: 100,000 units is one lot of a forex pair, and 100 ounces is one lot of gold.
- Change one setting at a time. Compare the swing strength (3 to 8 candles), the tolerance for matching lows (0.2 to 0.5 × ATR), the stop distance below the level and the 2R target against 1.5R. Turn the 200 EMA filter on and off, and try GBPUSD and the 4-hour chart.
As a rough guide, don't trust a result with fewer than 100 trades, a profit factor under 1 once costs are in, or a drawdown you couldn't sit through with real money. Settings tuned to look good on one stretch of history often fail on the next, so check the same settings on dates you didn't tune them on, then demo trade the strategy for a few weeks. How much history you can test depends on your TradingView plan: the free plan loads 5,000 candles, which is a few months of 15-minute candles but years of daily ones.
The script sizes each trade from the stop loss and your risk percentage, starts with $10,000 and assumes 1:100 leverage (a 1% margin setting), so the tester doesn't flag margin calls on normal forex trade sizes. Change these under Properties to match your account. By default TradingView fills orders at the open of the candle after the signal, so the fill can differ a little from the signal candle's close.
Download the free Pine Script and PDF
The script is written in Pine Script version 6 for this guide and follows the rules above. It can't read the economic calendar, so skipping news days is up to you. It's free to use, change and share. The PDF is a one-page cheat sheet with how levels are found, the bounce rules, a worked example and a checklist, to keep next to your chart.
Show the code (Pine Script v6, 103 lines)
//@version=6
// Support and Resistance bounce strategy by PipLedger (https://pipledgerfx.com/support-and-resistance-strategy)
// Marks a support level where two swing lows formed at nearly the same price (resistance from two swing highs), then trades the first bullish rejection on the next visit to it.
// Free to use and change. For education and backtesting only; not financial advice.
strategy("PipLedger: Support and Resistance Bounce", shorttitle="S/R Bounce", overlay=true, initial_capital=10000, pyramiding=0, default_qty_type=strategy.fixed, default_qty_value=1, margin_long=1, margin_short=1, currency=currency.USD)
// ---------- Inputs
swingLen = input.int(5, "Swing strength (candles on each side)", minval=2)
tolAtr = input.float(0.3, "Two swings count as one level if within (x ATR 14)", minval=0.05, step=0.05)
minApart = input.int(10, "Swings at least this many candles apart", minval=2)
touchAtr = input.float(0.1, "A visit counts if the price comes within (x ATR) of the level", minval=0.0, step=0.05)
stopAtr = input.float(0.5, "Stop at least this far beyond the level (x ATR)", minval=0.0, step=0.1)
maxAge = input.int(200, "A level expires after (candles)", minval=20)
rr = input.float(2.0, "Target (R multiple)", minval=0.5, step=0.25)
useTrend = input.bool(false, "Only buy above the 200 EMA and sell below it")
riskPct = input.float(1.0, "Risk per trade (% of equity)", minval=0.1, maxval=5.0, step=0.1)
// ---------- Helpers
fx = strategy.convert_to_account(1.0) // one unit of the chart's quote currency in account currency (1 on USD-quoted charts)
calcQty(float entry, float stop) =>
float dist = math.abs(entry - stop)
dist > 0 and not na(fx) ? strategy.equity * riskPct / 100 / (dist * syminfo.pointvalue * fx) : 0.0
atr = ta.atr(14)
ema200 = ta.ema(close, 200)
pl = ta.pivotlow(low, swingLen, swingLen)
ph = ta.pivothigh(high, swingLen, swingLen)
// the last few swing lows and highs, with the bar each one formed on
var array<float> lowP = array.new_float()
var array<int> lowB = array.new_int()
var array<float> highP = array.new_float()
var array<int> highB = array.new_int()
var float sup = na
var int supB = na
var float res = na
var int resB = na
// a new swing low: if an earlier one sits within the tolerance, the pair becomes the support level
if not na(pl)
int b = bar_index - swingLen
for i = array.size(lowP) - 1 to 0
if i < 0
break
if math.abs(pl - array.get(lowP, i)) <= tolAtr * atr and b - array.get(lowB, i) >= minApart
sup := (pl + array.get(lowP, i)) / 2
supB := bar_index
break
array.push(lowP, pl)
array.push(lowB, b)
if array.size(lowP) > 10
array.shift(lowP)
array.shift(lowB)
if not na(ph)
int b = bar_index - swingLen
for i = array.size(highP) - 1 to 0
if i < 0
break
if math.abs(ph - array.get(highP, i)) <= tolAtr * atr and b - array.get(highB, i) >= minApart
res := (ph + array.get(highP, i)) / 2
resB := bar_index
break
array.push(highP, ph)
array.push(highB, b)
if array.size(highP) > 10
array.shift(highP)
array.shift(highB)
// a level is dropped when it gets old or when a candle closes clearly through it
if not na(sup) and (bar_index - supB > maxAge or close < sup - stopAtr * atr)
sup := na
if not na(res) and (bar_index - resB > maxAge or close > res + stopAtr * atr)
res := na
// ---------- Signals: the price comes back to the level and closes back away from it as a rejection candle
flat = strategy.position_size == 0
longSig = flat and not na(sup) and bar_index > supB and low <= sup + touchAtr * atr and close > sup and close > open and (not useTrend or close > ema200)
shortSig = flat and not na(res) and bar_index > resB and high >= res - touchAtr * atr and close < res and close < open and (not useTrend or close < ema200)
// ---------- Orders (each level is traded once)
if longSig
float sl = math.min(low, sup - stopAtr * atr)
float q = calcQty(close, sl)
if q > 0
strategy.entry("Long", strategy.long, qty=q)
strategy.exit("Long exit", "Long", stop=sl, limit=close + (close - sl) * rr)
sup := na
if shortSig
float sl = math.max(high, res + stopAtr * atr)
float q = calcQty(close, sl)
if q > 0
strategy.entry("Short", strategy.short, qty=q)
strategy.exit("Short exit", "Short", stop=sl, limit=close - (sl - close) * rr)
res := na
// ---------- Chart
plot(sup, "Support", color=color.new(color.teal, 0), linewidth=2, style=plot.style_linebr)
plot(res, "Resistance", color=color.new(color.red, 0), linewidth=2, style=plot.style_linebr)
plot(useTrend ? ema200 : na, "200 EMA", color=color.new(color.gray, 0))
plotshape(longSig, "Bounce off support", shape.triangleup, location.belowbar, color.teal, size=size.small)
plotshape(shortSig, "Rejection at resistance", shape.triangledown, location.abovebar, color.red, size=size.small)Trading this from India
Residents can trade EURUSD, GBPUSD and USDJPY futures and options on NSE and BSE through a SEBI-registered broker. The contracts are quoted the same way as the spot pair, one lot is 1,000 units of the base currency, and they're cash-settled in rupees. They trade only from 9 a.m. to 7:30 p.m. IST, volumes are thin and not every broker offers them, so check the hours and the spread first. On the exchange contracts one EURUSD lot is €1,000, so a pip is worth $0.10 a lot. Trading forex or CFDs with offshore online platforms isn't allowed for residents under FEMA, even if the app is easy to download. Details are in is forex trading legal in India.
Related strategies: break and retest, inside bar, engulfing candle, previous day high and low, liquidity sweep, XAUUSD pin bar. All 34 strategies are compared on one page in forex trading strategies.