Linear Regression (LINREG)
period = 14 Overview
Linear Regression forecasts the next price value by extending the best fit line through recent data one step forward, creating a leading indicator that anticipates where price should reach if the current trend continues at its mathematically optimal rate. Unlike backward looking indicators that analyze historical patterns, LINREG projects ahead using least squares optimization to find the line that minimizes squared deviations from all points in the window, then extrapolates this line to predict the next value. This forward projection often leads actual price during trending markets, with price gravitating toward the regression forecast as the market seeks equilibrium along the statistically defined trend path. When price lags significantly behind rising LINREG values, it signals an oversold condition within an uptrend where mean reversion trades offer favorable risk reward ratios. Conversely, when price races ahead of LINREG during rallies, it warns of overextension that typically corrects back toward the regression line, providing short term profit taking opportunities. The indicator's unique value lies in its predictive nature, as it mathematically defines where price should be trading based on recent momentum, allowing traders to position themselves ahead of likely price movements rather than reacting after they occur.
Implementation Examples
Compute LINREG in a few lines:
use vectorta::indicators::moving_averages::linreg::{linreg, LinRegInput, LinRegParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with price data slice
let prices = vec![100.0, 101.0, 102.5, 101.7, 103.2, 104.0];
let params = LinRegParams { period: Some(14) };
let input = LinRegInput::from_slice(&prices, params);
let result = linreg(&input)?;
// Using with Candles (defaults: source="close", period=14)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = LinRegInput::with_default_candles(&candles);
let result = linreg(&input)?;
// Access the forecast values
for value in result.values {
println!("LINREG: {}", value);
} API Reference
Input Methods ▼
// From price slice
LinRegInput::from_slice(&[f64], LinRegParams) -> LinRegInput
// From candles with custom source
LinRegInput::from_candles(&Candles, &str, LinRegParams) -> LinRegInput
// From candles with defaults (close, period=14)
LinRegInput::with_default_candles(&Candles) -> LinRegInput Parameters Structure ▼
pub struct LinRegParams {
pub period: Option<usize>, // Default: 14
} Output Structure ▼
pub struct LinRegOutput {
pub values: Vec<f64>, // One-step forecast values
} Validation, Warmup & NaNs ▼
period > 0andperiod ≤ data_len; elseLinRegError::InvalidPeriod.- First finite index
firstis detected; iflen − first < periodthenLinRegError::NotEnoughValidData. - All inputs
NaN⇒LinRegError::AllValuesNaN. - Warmup: indices
[0 .. first + period)areNaN(prefix initialized via zero‑copy allocator). - Streaming:
updatereturnsNoneuntil the ring buffer is filled; then returns the forecast. - Kernel selection:
Kernel::AutochoosesKernel::Scalarfor LINREG; SIMD paths exist but may be disabled.
Error Handling ▼
use vectorta::indicators::moving_averages::linreg::{linreg, LinRegError, LinRegInput, LinRegParams};
let params = LinRegParams { period: Some(14) };
let input = LinRegInput::from_slice(&prices, params);
match linreg(&input) {
Ok(output) => process_results(output.values),
Err(LinRegError::AllValuesNaN) => eprintln!("All values are NaN"),
Err(LinRegError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period: {} for data length {}", period, data_len),
Err(LinRegError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points, only {}", needed, valid),
} Python Bindings
Basic Usage ▼
Calculate LINREG using NumPy arrays (period required; optional kernel):
import numpy as np
from vectorta import linreg
prices = np.array([100.0, 101.0, 102.5, 101.7, 103.2, 104.0])
# Required period argument
result = linreg(prices, period=14)
# Specify kernel ("auto", "scalar", "avx2", "avx512") if available
result = linreg(prices, period=14, kernel="auto")
print(result) # NumPy array Streaming Real-time Updates ▼
Process real-time price updates efficiently:
from vectorta import LinRegStream
stream = LinRegStream(period=14)
for price in price_feed:
y = stream.update(price)
if y is not None:
print("Forecast:", y) Batch Parameter Optimization ▼
Test multiple window sizes:
import numpy as np
from vectorta import linreg_batch
prices = np.array([...], dtype=float)
# (start, end, step)
results = linreg_batch(prices, period_range=(5, 20, 5), kernel="auto")
print(results['values'].shape) # (num_periods, len(prices))
print(results['periods']) # list of tested periods CUDA Acceleration ▼
CUDA-enabled variants are available when built with CUDA and Python features:
# Requires vectorta built with CUDA support
from vectorta import linreg_cuda_batch_dev, linreg_cuda_many_series_one_param_dev
import numpy as np
# 1) Single series, many parameters on GPU (float32)
data = np.array([...], dtype=np.float32)
values_dev, meta = linreg_cuda_batch_dev(data, period_range=(5, 50, 5), device_id=0)
print(meta['periods'])
# 2) Many series, one parameter (time-major: [T, N])
tm = np.array([...], dtype=np.float32) # shape [time_steps, num_assets]
dev_out = linreg_cuda_many_series_one_param_dev(tm, period=14, device_id=0) JavaScript/WASM Bindings
Basic Usage ▼
Calculate LINREG in JavaScript/TypeScript:
import { linreg_js } from 'vectorta-wasm';
const prices = new Float64Array([100.0, 101.0, 102.5, 101.7, 103.2, 104.0]);
const values = linreg_js(prices, 14); // period = 14
console.log('LINREG values:', values); Memory-Efficient Operations ▼
Use zero-copy operations for large datasets:
import { linreg_alloc, linreg_free, linreg_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const n = prices.length;
const inPtr = linreg_alloc(n);
const outPtr = linreg_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);
// Args: in_ptr, out_ptr, len, period
linreg_into(inPtr, outPtr, n, 14);
const out = new Float64Array(memory.buffer, outPtr, n).slice();
linreg_free(inPtr, n);
linreg_free(outPtr, n);
console.log(out); Batch Processing ▼
Sweep multiple periods with a unified API:
import { linreg_batch } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
// Unified config object
const out = linreg_batch(prices, { period_range: [5, 20, 5] });
// out has shape metadata and flattened values
// { values: Float64Array, combos: [{ period: 5 }, ...], rows, cols }
console.log(out.rows, out.cols, out.combos.map(c => c.period)); Performance Analysis
Across sizes, Rust CPU runs about 1.15× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
CUDA note
In our benchmark workload, the Rust CPU implementation is faster than CUDA for this indicator. Prefer the Rust/CPU path unless your workload differs.
Related Indicators
Arnaud Legoux Moving Average
Moving average 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
Ehlers Error-Correcting EMA (ECEMA)
Moving average indicator