OPEN-SOURCE SCRIPT

Liquidity Retest Strategy (Apicode) - TP/SL Lines Fixed

97
Technical Documentation:

1. Purpose and underlying concept

This strategy targets a common behavior in liquid markets: liquidity sweeps around meaningful support/resistance levels, followed by a retest and rejection (reversal) with confirmation.
The core thesis is that many initial “breaks” are not continuation moves, but rather stop-runs and order harvesting. After the sweep, price reclaims the level and closes back on the opposite side, offering a structured entry with defined risk.

The strategy includes:

  • Support/Resistance detection via pivots
  • Dynamic selection of the “working” level using an ATR-based proximity window
  • Rejection validation via candle structure (wick + close)
  • Optional filters: volume, VWAP-like bias, and EMA trend
  • Risk management with static TP/SL (ATR-based or %), plus trailing stop (ATR-based or %), with per-trade lines plotted


2. Main components
2.1. Volatility metric: ATR

atr = ta.atr(atrLength) is used in two essential places:

  • Level selection (proximity to S/R): prevents trading levels that are too far from current price.
  • Sweep validation (minimum wick size): requires the wick to extend beyond the level by a volatility-relative amount.

Optionally, ATR can also be used for:

  • Static TP/SL (when usePercent = false)
  • Trailing stop (when useTrailPercent = false)

2.2. Building S/R levels with pivots

Pivots are detected using:

  • pivotHigh = ta.pivothigh(pivotLookback, rightBars)
  • pivotLow = ta.pivotlow(pivotLookback, rightBars)

Each confirmed pivot is stored in arrays:

  • resistanceLevels for resistance
  • supportLevels for support

The array size is capped by maxLevels, which reduces noise and manages chart resource usage (lines).

2.3. Selecting the “best” level each bar

On each bar, a single support S and/or resistance R candidate is chosen:

  • Support: nearest level below price (L < price)
  • Resistance: nearest level above price (L > price)
  • Only levels within atr * maxDistATR are considered

This produces dynamic “working levels” that adapt to price location and volatility.

2.4. Rejection pattern (retest + sweep)

After selecting the working level:

Support rejection (long setup)
Conditions:

  • Low touches/crosses support: low <= S
  • Close reclaims above: close > S
  • Bullish candle: close > open
  • Sufficient wick below the level (liquidity sweep): (S - low) >= atr * minWickATR

This aims to capture a stop sweep below support followed by immediate recovery.

Resistance rejection (short setup)
Symmetric conditions:

High touches/crosses resistance: high >= R

Close rejects back below: close < R

Bearish candle: close < open

Sufficient wick above the level: (high - R) >= atr * minWickATR

2.5. Optional filters

Final signals are the rejection pattern AND enabled filters:

1.- Volume filter

  • High volume is defined as: volume > SMA(volume, 20) * volMult
  • When useVolFilter = true, setups require relatively elevated participation


2.- VWAP-like bias filter
A VWAP-like series is computed over vwapLength (typical price weighted by volume)
When useVWAPFilter = true:
- Longs only if close > vwap
- Shorts only if close < vwap

3.- EMA trend filter

Uptrend if EMA(fast) > EMA(slow)
When useTrendFilter = true:
- Longs only in uptrend
- Shorts only in downtrend

2.6. Backtest time window (time filter)
To keep testing focused and reduce long-history noise:
  • useMaxLookbackDays enables the filter
  • maxLookbackDays defines how many days back from timenow entries are allowed

Entries are permitted only when time >= startTime.

3. Entry rules and position control
3.1. Entries
  • strategy.entry('Long', strategy.long) when longSetup and no long position is open
  • strategy.entry('Short', strategy.short) when shortSetup and no short position is open

No pyramiding is allowed (pyramiding = 0). Position gating is handled by:
  • Long allowed if strategy.position_size <= 0
  • Short allowed if strategy.position_size >= 0

4. Risk management: TP/SL and trailing (with plotted lines)
4.1. Detecting entry/exit events

Events are identified via changes in strategy.position_size:
  • LongEntry: transition into a long
  • shortEntry: transition into a short
  • flatExit: transition back to flat


This drives per-trade line creation, updates, and cleanup of state variables.

4.2. Static TP/SL

On entry, entryPrice := strategy.position_avg_price is stored.

Percent mode (usePercent = true)

Long:
  • staticSL = entryPrice*(1 - slPerc/100)
  • staticTP = entryPrice*(1 + tpPerc/100)

Short:
  • staticSL = entryPrice*(1 + slPerc/100)
  • staticTP = entryPrice*(1 - tpPerc/100)


ATR mode (usePercent = false)
Long:
  • staticSL = entryPrice - atrAtEntry*slATR
  • staticTP = entryPrice + atrAtEntry*tpATR

Short:
  • staticSL = entryPrice + atrAtEntry*slATR
  • staticTP = entryPrice - atrAtEntry*tpATR


4.3. Trailing stop (custom)

While a position is open, the script tracks the most favorable excursion:

Long: hhSinceEntry = highest high since entry

Short: llSinceEntry = lowest low since entry

A trailing candidate is computed:

Percent trailing (useTrailPercent = true)

Long: trailCandidate = hhSinceEntry*(1 - trailPerc/100)

Short: trailCandidate = llSinceEntry*(1 + trailPerc/100)

ATR trailing (useTrailPercent = false)

Long: trailCandidate = hhSinceEntry - atr*trailATR

Short: trailCandidate = llSinceEntry + atr*trailATR

Then the effective stop is selected:

Long: slUsed = max(staticSL, trailCandidate) when useTrail is enabled

Short: slUsed = min(staticSL, trailCandidate) when useTrail is enabled
If useTrail is disabled, slUsed remains the static stop.

Take profit remains static:

tpUsed = staticTP

Exit orders are issued via:

strategy.exit(..., stop=slUsed, limit=tpUsed)

4.4. Per-trade TP/SL lines

On each entry, two lines are created (SL and TP) via f_createLines().
During the trade, the SL line updates when trailing moves the stop; TP remains fixed.
On exit (flatExit), both lines are finalized on the exit bar and left on the chart as historical references.

This makes it straightforward to visually audit each trade: entry context, intended TP, and trailing evolution until exit.

5. Visualization and debugging

BUY/SELL labels with configurable size (xsize)

Debug mode (showDebug) plots the chosen working support/resistance level each bar

Stored pivot levels are drawn using reusable line slots, projected a fixed 20 bars to the right to keep the chart readable and efficient

6. Parameter guidance and practical notes

pivotLookback / rightBars: controls pivot significance vs responsiveness. Lower rightBars confirms pivots earlier but can increase noise.

maxDistATR: too low may reject valid levels; too high may select distant, less relevant levels.

minWickATR: key quality gate for “real” sweeps. Higher values reduce frequency but often improve signal quality.

Filters:

Volume filter tends to help in ranges and active sessions.

VWAP bias is useful intraday to align trades with session positioning.

EMA trend filter is helpful in directional markets but may remove good mean-reversion setups.

Percent TP/SL: provides consistent behavior across assets with variable volatility, but is less adaptive to sudden regime shifts.

Percent trailing: can capture extensions well; calibrate trailPerc per asset/timeframe (too tight = premature exits).

7. Known limitations

Pivot-derived levels are a heuristic; in strong trends, valid retests may be limited.

The time filter uses timenow; behavior may vary depending on historical context and how the platform evaluates “current time.”

TP/SL and trailing are computed from bar OHLC; in live trading, intrabar sequencing and fills may differ from bar-close simulation.

Pernyataan Penyangkalan

Informasi dan publikasi ini tidak dimaksudkan, dan bukan merupakan, saran atau rekomendasi keuangan, investasi, trading, atau jenis lainnya yang diberikan atau didukung oleh TradingView. Baca selengkapnya di Ketentuan Penggunaan.