FVG Positioning Average
lookback = 30 | lookback_type = Bar Count | atr_multiplier = 0.25 Overview
FVG Positioning Average monitors bullish and bearish fair value gaps separately and turns those active gap levels into two rolling averages. A bullish gap is recorded when the current low clears the high from two bars earlier and the intervening close confirms the imbalance. A bearish gap is recorded with the mirrored condition. Once stored, the indicator keeps an average level for each side so you can judge whether current price is trading above, below, or directly inside the recent imbalance inventory.
The implementation also returns midpoint overlays that compare the current candle body midpoint against those running averages. The bullish midpoint clamps upward to the bullish average, while the bearish midpoint clamps downward to the bearish average. That makes the output useful for visual regime mapping, zone-tracking, and automation that needs separate references for supportive and resistant gap structure.
Defaults: FVG Positioning Average uses `lookback = 30`, `lookback_type = "Bar Count"`, and `atr_multiplier = 0.25`.
Implementation Examples
Compute the four FVG positioning series from raw OHLC slices or candle data.
use vector_ta::indicators::fvg_positioning_average::{
fvg_positioning_average,
FvgPositioningAverageInput,
FvgPositioningAverageParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
let open = vec![100.0, 101.0, 102.5, 103.2, 104.0, 105.1];
let high = vec![101.0, 102.4, 103.1, 104.6, 105.3, 106.2];
let low = vec![99.6, 100.8, 101.9, 102.8, 103.7, 104.5];
let close = vec![100.7, 102.0, 102.9, 104.2, 104.9, 105.8];
let output = fvg_positioning_average(&FvgPositioningAverageInput::from_slices(
&open,
&high,
&low,
&close,
FvgPositioningAverageParams {
lookback: Some(30),
lookback_type: Some("Bar Count".to_string()),
atr_multiplier: Some(0.25),
},
))?;
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = fvg_positioning_average(
&FvgPositioningAverageInput::with_default_candles(&candles),
)?;
println!("latest bull average = {:?}", output.bull_average.last());
println!("latest bear mid = {:?}", candle_output.bear_mid.last()); API Reference
Input Methods ▼
// From candles
FvgPositioningAverageInput::from_candles(&Candles, FvgPositioningAverageParams)
-> FvgPositioningAverageInput
// From OHLC slices
FvgPositioningAverageInput::from_slices(&[f64], &[f64], &[f64], &[f64], FvgPositioningAverageParams)
-> FvgPositioningAverageInput
// From candles with default parameters
FvgPositioningAverageInput::with_default_candles(&Candles)
-> FvgPositioningAverageInput Parameters Structure ▼
pub struct FvgPositioningAverageParams {
pub lookback: Option<usize>, // default 30
pub lookback_type: Option<String>, // "Bar Count" or "FVG Count"
pub atr_multiplier: Option<f64>, // default 0.25
} Output Structure ▼
pub struct FvgPositioningAverageOutput {
pub bull_average: Vec<f64>,
pub bear_average: Vec<f64>,
pub bull_mid: Vec<f64>,
pub bear_mid: Vec<f64>,
} Validation, Warmup & NaNs ▼
- All OHLC inputs must be non-empty and have matching lengths, or the function returns
EmptyInputDataorInputLengthMismatch. lookbackmust be greater than0.lookback_typemust resolve to eitherBar CountorFVG Count.atr_multipliermust be finite and greater than or equal to0.0.- The engine requires a contiguous valid OHLC run of at least
3bars; shorter runs returnNotEnoughValidData. - Invalid bars reset the streaming state and clear stored gap levels.
- When no qualifying bullish or bearish gaps exist yet, the related average and midpoint outputs remain
NaN. - Batch mode validates sweep ranges and rejects unsupported batch kernels with
InvalidKernelForBatch.
Builder, Streaming & Batch APIs ▼
// Builder
FvgPositioningAverageBuilder::new()
.lookback(usize)
.lookback_type(&str)
.atr_multiplier(f64)
.kernel(Kernel)
.apply(&Candles)
FvgPositioningAverageBuilder::new()
.apply_slices(&[f64], &[f64], &[f64], &[f64])
FvgPositioningAverageBuilder::new()
.into_stream()
// Stream
FvgPositioningAverageStream::try_new(FvgPositioningAverageParams)
FvgPositioningAverageStream::update(f64, f64, f64, f64)
-> Option<(f64, f64, f64, f64)>
// Batch
FvgPositioningAverageBatchBuilder::new()
.lookback_range(start, end, step)
.lookback_static(value)
.atr_multiplier_range(start, end, step)
.atr_multiplier_static(value)
.lookback_type(&str)
.kernel(Kernel)
.apply_slices(&[f64], &[f64], &[f64], &[f64])
FvgPositioningAverageBatchBuilder::new()
.apply_candles(&Candles) Error Handling ▼
pub enum FvgPositioningAverageError {
EmptyInputData,
InputLengthMismatch { open_len: usize, high_len: usize, low_len: usize, close_len: usize },
AllValuesNaN,
InvalidLookback { lookback: usize },
InvalidLookbackType { value: String },
InvalidAtrMultiplier { atr_multiplier: f64 },
NotEnoughValidData { needed: usize, valid: usize },
OutputLengthMismatch { expected: usize, got: usize },
MismatchedOutputLen { dst_len: usize, expected_len: usize },
InvalidRange { start: String, end: String, step: String },
InvalidKernelForBatch(Kernel),
InvalidInput { msg: String },
} Python Bindings
Python exposes a scalar OHLC function, a streaming class, and a batch sweep. The scalar path returns four NumPy arrays in bullish-average, bearish-average, bullish-mid, bearish-mid order. The batch path returns those four flattened matrices plus the tested parameter grids. Streaming emits one four-value tuple per valid bar after the indicator has enough context to detect gaps.
import numpy as np
from vector_ta import (
fvg_positioning_average,
fvg_positioning_average_batch,
FvgPositioningAverageStream,
)
open_ = np.asarray(open_values, dtype=np.float64)
high = np.asarray(high_values, dtype=np.float64)
low = np.asarray(low_values, dtype=np.float64)
close = np.asarray(close_values, dtype=np.float64)
bull_average, bear_average, bull_mid, bear_mid = fvg_positioning_average(
open_,
high,
low,
close,
lookback=30,
lookback_type="Bar Count",
atr_multiplier=0.25,
kernel="auto",
)
stream = FvgPositioningAverageStream(
lookback=30,
lookback_type="FVG Count",
atr_multiplier=0.25,
)
print(stream.update(open_[-1], high[-1], low[-1], close[-1]))
batch = fvg_positioning_average_batch(
open_,
high,
low,
close,
lookback_range=(10, 30, 10),
atr_multiplier_range=(0.1, 0.3, 0.1),
lookback_type="Bar Count",
kernel="auto",
)
print(batch["bull_average"].shape)
print(batch["lookback_types"])
print(batch["atr_multipliers"]) JavaScript/WASM Bindings
The WASM layer exposes scalar, batch, and into-buffer entry points. The scalar function returns a plain object with four arrays. The batch function returns those same four flattened outputs plus grid metadata, while the low-level allocation helpers support preallocated memory workflows in custom runtimes.
import init, {
fvg_positioning_average_js,
fvg_positioning_average_batch_js,
} from "@vectoralpha/vector_ta";
await init();
const single = fvg_positioning_average_js(
open,
high,
low,
close,
30,
"Bar Count",
0.25,
);
console.log(single.bull_average);
console.log(single.bear_mid);
const batch = fvg_positioning_average_batch_js(open, high, low, close, {
lookback_range: [10, 30, 10],
atr_multiplier_range: [0.1, 0.3, 0.1],
lookback_type: "FVG Count",
});
console.log(batch.rows, batch.cols);
console.log(batch.combos);
console.log(batch.bull_mid); CUDA Bindings (Rust)
Additional details for the CUDA bindings can be found inside the VectorTA repository.
Performance Analysis
Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)
Related Indicators
Arnaud Legoux Moving Average
Moving average indicator
Buff Averages
Technical analysis indicator
Compound Ratio Moving Average (CoRa Wave)
Moving average indicator
Centered Weighted Moving Average
Moving average indicator
Double Exponential Moving Average
Moving average indicator
Ehlers Distance Coefficient Filter
Moving average indicator