Linear Regression (LINREG)

Parameters: 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 > 0 and period ≤ data_len; else LinRegError::InvalidPeriod.
  • First finite index first is detected; if len − first < period then LinRegError::NotEnoughValidData.
  • All inputs NaNLinRegError::AllValuesNaN.
  • Warmup: indices [0 .. first + period) are NaN (prefix initialized via zero‑copy allocator).
  • Streaming: update returns None until the ring buffer is filled; then returns the forecast.
  • Kernel selection: Kernel::Auto chooses Kernel::Scalar for 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

Comparison:
View:

Across sizes, Rust CPU runs about 1.15× faster than Tulip C in this benchmark.

Loading chart...

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