Back To Top

August 14, 2025

Optimizing Stops via Fair Value Gaps

Volatility-scaled fair value gap analysis for detecting bull/bear swings and dynamic stop-losses in Python.

Fair value gaps mark points where price moves quickly and leave a gap between buyers and sellers.

These gaps can signal support / resistance zones and bullish / bearish swings. This is especially so when the gaps are scaled by volatilility.

With the method presented here, we get two key lines on the chart: one for potential support, one for potential resistance.

When price crosses above the resistance line, it signals bullish control. If price falls below the support line, bearish momentum takes over.

The complete Python notebook for the analysis is provided below.

Optimizing Stops via Fair Value Gaps

1. How FVG Trailing Stop Works

A fair value gap is a section of the chart where price moves so fast, it skips over certain levels. No trades happen in that range.

The market leaves behind ‘unfinished business’, i.e. zones where aggressive buyers or sellers overwhelmed the other side.

In practical terms:

A fair value gap forms when the low of a bar is higher than the high of the previous bar (up-gap), or the high of a bar is lower than the low of the previous bar (down-gap):

Optimizing Stops via Fair Value Gaps

In liquid markets, this type of gap shows strong and sudden imbalance, usually after important news, a breakout, or heavy volume.

These gaps are magnets for price. The market often returns to “fill” them.

Figure 1. Fair Value Gap Analysis: Price gaps up, leaves an unfilled zone, then drifts back to fill it.

Figure 1. Fair Value Gap Analysis: Price gaps up, leaves an unfilled zone, then drifts back to fill it.

But not all gaps matter equally.

Gaps in quiet markets are common noise. The signal comes when a gap forms and volatility is high.

That’s why we scale gap detection by volatility, using ATR.

Volatility-Adjusted Gap Memory

To avoid treating every gap the same, the method adapts how long a gap “counts” as significant.

The gap memory length changes as volatility changes:

Optimizing Stops via Fair Value Gaps
  • When ATR is above normal, the method remembers gaps longer.
  • When ATR drops, it forgets gaps faster.

This focuses attention on gaps formed during meaningful price moves.

Tracking Swings and Marking Levels

The algorithm looks for two types of fair value gaps:

  • Bullish gap (signals a burst of buying strength).
Optimizing Stops via Fair Value Gaps
  • Bearish gap (signals a flush of selling pressure).
Optimizing Stops via Fair Value Gaps

Each time a new gap appears, it’s added to the list of active levels. If price returns and “fills” a gap, that level drops off the list.

Constructing the Support and Resistance Lines

For each bar, the method takes the average of all active bullish gaps (potential support) and the average of all active bearish gaps (potential resistance).

Both lines are smoothed with an exponential moving average:

Optimizing Stops via Fair Value Gaps

where

Optimizing Stops via Fair Value Gaps

Regime Detection: When Price Crosses a Line

  • If price closes above the resistance line, it signals bullish control.
  • If price closes below the support line, bearish momentum takes over.

Say NVDA gaps up after earnings, jumping $12 above the previous day’s high while ATR is high.

The method marks this as a new resistance. If price holds above the resistance line, it stays in a bullish regime.

If price falls and closes below the support line (maybe on a high-volume reversal), the signal flips to bearish.

2. FVG Trailing Stops in Python

2.1 Setting the Parameters

First, import the necessary libraries.

Then, we set key parameters to control how sensitive the method is, how wide stops are, and how volatility scaling works.

  • Increase ATR_LEN: fewer, smoother gaps.
  • Increase BASE_FVG_LEN: gaps last longer, regime flips are slower.
  • Increase SMOOTH_LEN: fewer whipsaws.
  • Increase GAP_Z: only large gaps count.
  • Increase ENV_MULT: wider stops, looser risk.
  • Increase VOL_SENSITIVITY: memory expands more when volatility rises.
				
					# Imports
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import yfinance as yf

# Parameters — tweak here
TICKER             = "NVDA"
PERIOD             = "2y"      # e.g. "60d", "1y"
INTERVAL           = "1d"      # e.g. "15m", "1d"

ATR_LEN           = 20   # ↑ smoother ATR, fewer gaps; ↓ faster ATR, more gaps
BASE_FVG_LEN      = 5    # ↑ gaps persist, slower flips; ↓ gaps decay, quicker flips
SMOOTH_LEN        = 9    # ↑ slower displacement, fewer whipsaws; ↓ faster, more whipsaws
GAP_Z             = 0.8  # ↑ only big gaps count; ↓ small gaps count too
ENV_MULT          = 0.6  # ↑ wider stop, looser risk; ↓ tighter stop, quicker exits
VOL_SENSITIVITY   = 0.5  # ↑ memory expands with vol; ↓ memory stays near base
SHOW_INACTIVE_SIDE = False  # True shows both lines; False hides inactive line
				
			

2.2 Getting the Data Function

The download function pulls clean OHLCV data with yfinance.

Can be daily, hourly, etc.

				
					def download_ohlc(ticker: str, period: str, interval: str) -> pd.DataFrame:
    df = yf.download(
        ticker, period=period, interval=interval,
        auto_adjust=True, progress=False
    )
    if isinstance(df.columns, pd.MultiIndex):
        df.columns = df.columns.get_level_values(0)
    df.columns = df.columns.map(str.title)
    return df.dropna(subset=["Open", "High", "Low", "Close"])
				
			

2.3 Technical Indicator Functions

ATR and EMA are the core indicators. ATR measures recent volatility. EMA smooths noisy levels.

Prev Post

Detecting VIX Term Structure Regimes

Next Post

Improving RSI with Machine Learning

post-bars
Mail Icon

Newsletter

Get Every Weekly Update & Insights

[mc4wp_form id=]

Leave a Comment