Polynomial Regression Bands (PRB)
smooth_period = 10 | regression_period = 100 | polynomial_order = 2 | regression_offset = 0 | ndev = 2 | equ_from = 0 Overview
Polynomial Regression Bands fit a polynomial curve to price data over a rolling window and construct volatility adjusted envelopes around the regression line, creating a dynamic channel that adapts to both trend direction and magnitude. The indicator optionally applies a two pole Super Smoother Filter to remove high frequency noise before performing least squares polynomial regression, then evaluates the fitted polynomial at a specified point within the window to generate the central line. Upper and lower bands are calculated by adding and subtracting a multiple of the rolling standard deviation from the regression value, creating boundaries that expand during volatile periods and contract when price action stabilizes. Higher polynomial orders capture more complex curve shapes like parabolic moves or S curves, while the regression offset parameter allows forecasting by evaluating the polynomial beyond the current bar. Traders use the regression line as a dynamic trend baseline and the bands as overbought oversold zones, with price touching the upper band in an uptrend signaling potential profit taking opportunities and breaks below the lower band suggesting possible reversals. The pre smoothing option helps on noisy intraday data where raw price fluctuations would destabilize the regression fit, while the configurable period and deviation multiplier allow optimization for different market conditions and trading styles.
Defaults (chart UI): smooth_period=10, regression_period=100, polynomial_order=2, regression_offset=0, ndev=2.0, equ_from=0 (SSF pre-smoothing is always enabled).
Implementation Examples
Compute PRB from a slice or candles:
use vectorta::indicators::prb::{prb, PrbInput, PrbParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// From a numeric slice
let data = vec![100.0, 102.0, 101.0, 103.5, 104.2, 105.0];
let params = PrbParams { // Defaults: smooth=true, smooth_period=10, regression_period=100, order=2, offset=0, ndev=2.0, equ_from=0
smooth_data: Some(true),
smooth_period: Some(10),
regression_period: Some(100),
polynomial_order: Some(2),
regression_offset: Some(0),
ndev: Some(2.0),
equ_from: Some(0),
};
let input = PrbInput::from_slice(&data, params);
let out = prb(&input)?; // PrbOutput { values, upper_band, lower_band }
// From Candles (defaults, source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = PrbInput::with_default_candles(&candles);
let out = prb(&input)?;
// Access results
println!("reg: {} | upper: {} | lower: {}", out.values[200], out.upper_band[200], out.lower_band[200]); API Reference
Input Methods ▼
// From price slice
PrbInput::from_slice(&[f64], PrbParams) -> PrbInput
// From candles with custom source
PrbInput::from_candles(&Candles, &str, PrbParams) -> PrbInput
// From candles with defaults (close prices)
PrbInput::with_default_candles(&Candles) -> PrbInput Parameters Structure ▼
pub struct PrbParams {
pub smooth_data: Option<bool>, // Default: true
pub smooth_period: Option<usize>, // Default: 10 (min 2 if smoothing)
pub regression_period: Option<usize>, // Default: 100 (min 1)
pub polynomial_order: Option<usize>, // Default: 2 (min 1)
pub regression_offset: Option<i32>, // Default: 0
pub ndev: Option<f64>, // Default: 2.0
pub equ_from: Option<usize>, // Default: 0
} Output Structure ▼
pub struct PrbOutput {
pub values: Vec<f64>, // Regression line
pub upper_band: Vec<f64>, // Regression + ndev * stdev
pub lower_band: Vec<f64>, // Regression - ndev * stdev
} Validation, Warmup & NaNs ▼
- Errors:
EmptyInputData,AllValuesNaN,InvalidPeriod,NotEnoughValidData,InvalidOrder,InvalidSmoothPeriod,SingularMatrix. polynomial_order \u2265 1. Ifsmooth_data, requiresmooth_period \u2265 2.regression_period \u2265 1and must not exceed input length.- Let
firstbe the index of the first finite input. Warmup index isfirst + regression_period - 1 + equ_from. Indices before warmup areNaN. - Streaming:
PrbStream::updatereturnsNoneuntil the warmup fills; then returns(reg, upper, lower).
Error Handling ▼
use vectorta::indicators::prb::{prb, PrbInput, PrbParams, PrbError};
let input = PrbInput::from_slice(&data, PrbParams::default());
match prb(&input) {
Ok(out) => { /* use out.values / out.upper_band / out.lower_band */ }
Err(PrbError::EmptyInputData) => eprintln!("no data"),
Err(PrbError::AllValuesNaN) => eprintln!("all NaN"),
Err(PrbError::InvalidPeriod { period, data_len }) => { /* fix period */ }
Err(PrbError::NotEnoughValidData { needed, valid }) => { /* wait for more data */ }
Err(PrbError::InvalidOrder { .. }) => { /* set order >= 1 */ }
Err(PrbError::InvalidSmoothPeriod { .. }) => { /* set smooth_period >= 2 when smoothing */ }
Err(PrbError::SingularMatrix) => { /* adjust order/period to avoid degenerate design */ }
} Python Bindings
Basic Usage ▼
import numpy as np
from vectorta import prb
prices = np.array([100.0, 102.0, 101.0, 103.5, 104.2], dtype=np.float64)
reg, upper, lower = prb(
prices,
smooth_data=True,
smooth_period=10,
regression_period=100,
polynomial_order=2,
regression_offset=0,
ndev=2.0,
kernel="auto" # optional
)
print(reg[-1], upper[-1], lower[-1]) Streaming Real-time Updates ▼
from vectorta import PrbStreamPy
stream = PrbStreamPy(
smooth_data=True,
smooth_period=10,
regression_period=100,
polynomial_order=2,
regression_offset=0,
ndev=2.0,
)
for px in feed:
out = stream.update(px)
if out is not None:
reg, upper, lower = out
process(reg, upper, lower) Batch Parameter Optimization ▼
import numpy as np
from vectorta import prb_batch
prices = np.array([...], dtype=np.float64)
res = prb_batch(
prices,
smooth_data=True,
smooth_period_start=8, smooth_period_end=14, smooth_period_step=2,
regression_period_start=50, regression_period_end=120, regression_period_step=10,
polynomial_order_start=1, polynomial_order_end=3, polynomial_order_step=1,
regression_offset_start=0, regression_offset_end=2, regression_offset_step=1,
kernel="auto"
)
values = res["values"] # shape: (rows, len(prices))
upper = res["upper"]
lower = res["lower"]
sp = res["smooth_periods"]
rp = res["regression_periods"]
po = res["polynomial_orders"]
ro = res["regression_offsets"] CUDA Acceleration ▼
CUDA bindings for PRB are in development. The API will follow the same patterns used by other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated PRB helpers
# from vectorta import prb_cuda_batch, prb_cuda_many_series_one_param
# ... JavaScript/WASM Bindings
Basic Usage ▼
Compute PRB in JS/TS and unpack the three rows:
import { prb as prb_js } from 'vectorta-wasm';
const data = new Float64Array([100, 102, 101, 103.5, 104.2]);
const res = prb_js(data, true, 10, 100, 2, 0, 2.0); // PrbJsResult
// res.values is flattened [main..., upper..., lower...] with rows=3
const flat = res.values;
const len = res.cols; // = data.length
const main = flat.slice(0, len);
const upper = flat.slice(len, 2*len);
const lower = flat.slice(2*len, 3*len); Memory-Efficient Operations ▼
Use zero-copy pointers for large arrays:
import { prb_alloc, prb_free, prb_into, memory } from 'vectorta-wasm';
const data = new Float64Array([/* ... */]);
const n = data.length;
const inPtr = prb_alloc(n);
const outMainPtr = prb_alloc(n);
const outUpPtr = prb_alloc(n);
const outLoPtr = prb_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(data);
// in_ptr, out_main, out_upper, out_lower, len, smooth_data, smooth_period, regression_period, order, offset, ndev
prb_into(inPtr, outMainPtr, outUpPtr, outLoPtr, n, true, 10, 100, 2, 0, 2.0);
const main = new Float64Array(memory.buffer, outMainPtr, n).slice();
const upper = new Float64Array(memory.buffer, outUpPtr, n).slice();
const lower = new Float64Array(memory.buffer, outLoPtr, n).slice();
prb_free(inPtr, n);
prb_free(outMainPtr, n);
prb_free(outUpPtr, n);
prb_free(outLoPtr, n); Batch Processing ▼
import { prb_batch_js, prb_batch_unified } from 'vectorta-wasm';
const data = new Float64Array([/* prices */]);
// Explicit range arguments
const flat = prb_batch_js(
data,
true,
8, 14, 2,
50, 120, 10,
1, 3, 1,
0, 2, 1
);
// Or unified config object
const cfg = { smooth_period: [8, 14, 2], regression_period: [50, 120, 10], polynomial_order: [1, 3, 1], regression_offset: [0, 2, 1] };
const unified = prb_batch_unified(data, cfg, true); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05