Parabolic SAR (SAR)
acceleration = 0.02 (0.005–0.2) | maximum = 0.2 (0.1–1) Overview
The Parabolic SAR tracks trends by plotting accelerating stop points that trail price action and flip sides when penetrated. Developed by J. Welles Wilder, the indicator begins with an initial acceleration factor that increases each period the trend continues, causing the SAR points to accelerate toward price until reaching a maximum value. During an uptrend, SAR points appear below price and rise progressively faster with each new high, while downtrends place SAR points above price that fall increasingly rapidly with each new low. When price crosses the SAR level, the indicator immediately reverses direction and starts tracking the new trend from the opposite side. The parabolic nature creates tightening stops as trends mature, helping traders exit positions before major reversals while staying in strong moves. The acceleration parameter controls initial sensitivity, while the maximum parameter caps how aggressive the stop becomes, balancing between early exits and adequate profit protection.
Defaults: acceleration = 0.02, maximum = 0.2.
Implementation Examples
Get started with SAR using highs and lows:
use vectorta::indicators::sar::{sar, SarInput, SarParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using high/low slices
let high = vec![101.0, 103.0, 102.5, 104.0, 105.5];
let low = vec![ 99.0, 100.5, 100.0, 101.0, 102.0];
let params = SarParams { acceleration: Some(0.02), maximum: Some(0.2) };
let input = SarInput::from_slices(&high, &low, params);
let result = sar(&input)?;
// Using Candles with defaults (acceleration=0.02, maximum=0.2)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = SarInput::with_default_candles(&candles);
let result = sar(&input)?;
// Access SAR values
for v in result.values { println!("SAR: {}", v); } API Reference
Input Methods ▼
// From candles (uses high/low)
SarInput::from_candles(&Candles, SarParams) -> SarInput
// From high/low slices
SarInput::from_slices(&[f64], &[f64], SarParams) -> SarInput
// From candles with default parameters (acc=0.02, max=0.2)
SarInput::with_default_candles(&Candles) -> SarInput Parameters Structure ▼
pub struct SarParams {
pub acceleration: Option<f64>, // Default: 0.02
pub maximum: Option<f64>, // Default: 0.2
} Output Structure ▼
pub struct SarOutput {
pub values: Vec<f64>, // SAR values
} Validation, Warmup & NaNs ▼
- Inputs require highs and lows. Candles path uses
candles.highandcandles.low. - At least
2valid points after the first finite pair are required; otherwiseSarError::NotEnoughValidData. acceleration > 0.0andmaximum > 0.0; invalid values returnInvalidAcceleration/InvalidMaximum.- Before the first computed SAR, outputs are
NaN. First value is written at indexfirst_valid + 1. - Slice-based APIs trim to the minimum of
high.len()andlow.len(). sar_into_slicerequires output length to match inputs; otherwiseSarError::LengthMismatch.- Streaming returns
Noneon the first update; firstSomevalue appears on the second update.
Error Handling ▼
use vectorta::indicators::sar::{sar, SarError};
match sar(&input) {
Ok(output) => process(output.values),
Err(SarError::EmptyData) => eprintln!("empty input"),
Err(SarError::AllValuesNaN) => eprintln!("all values are NaN"),
Err(SarError::NotEnoughValidData { needed, valid }) => {
eprintln!("need {needed} valid points, got {valid}");
}
Err(SarError::InvalidAcceleration { acceleration }) => {
eprintln!("invalid acceleration: {acceleration}");
}
Err(SarError::InvalidMaximum { maximum }) => {
eprintln!("invalid maximum: {maximum}");
}
Err(SarError::LengthMismatch { got, expected }) => {
eprintln!("output length {got} != {expected}");
}
} Python Bindings
Basic Usage ▼
Calculate SAR from NumPy arrays of highs and lows:
import numpy as np
from vectorta import sar
high = np.array([101.0, 103.0, 102.5, 104.0, 105.5])
low = np.array([ 99.0, 100.5, 100.0, 101.0, 102.0])
# Defaults: acceleration=0.02, maximum=0.2
values = sar(high, low)
# Or specify parameters and kernel
values = sar(high, low, acceleration=0.02, maximum=0.2, kernel="auto")
print(values) # NumPy array Streaming Real-time Updates ▼
Update from high/low ticks:
from vectorta import SarStream
stream = SarStream(acceleration=0.02, maximum=0.2)
for high_tick, low_tick in tick_stream:
value = stream.update(high_tick, low_tick)
if value is not None:
print("SAR:", value) Batch Parameter Optimization ▼
Test multiple acceleration/maximum combinations:
import numpy as np
from vectorta import sar_batch
high = np.array([...], dtype=float)
low = np.array([...], dtype=float)
results = sar_batch(
high,
low,
acceleration_range=(0.01, 0.05, 0.01),
maximum_range=(0.2, 0.5, 0.1),
kernel="auto"
)
# results is a dict with:
# - values: 2D array [rows x cols]
# - accelerations: tested acceleration values
# - maximums: tested maximum values
print(results["values"].shape, results["accelerations"], results["maximums"]) CUDA Acceleration ▼
CUDA support for SAR is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.
# Coming soon: CUDA-accelerated SAR calculations
# Expected patterns: sar_cuda_batch(...), sar_cuda_many_series_one_param(...)
JavaScript/WASM Bindings
Basic Usage ▼
Calculate SAR in JavaScript/TypeScript:
import { sar_js } from 'vectorta-wasm';
const high = new Float64Array([101.0, 103.0, 102.5, 104.0, 105.5]);
const low = new Float64Array([ 99.0, 100.5, 100.0, 101.0, 102.0]);
// Args: high, low, acceleration, maximum
const sarValues = sar_js(high, low, 0.02, 0.2);
console.log('SAR values:', sarValues); Memory-Efficient Operations ▼
Use zero-copy operations for large datasets:
import { sar_alloc, sar_free, sar_into, memory } from 'vectorta-wasm';
const len = 1024;
const high = new Float64Array(len);
const low = new Float64Array(len);
// ... fill high/low ...
const highPtr = sar_alloc(len);
const lowPtr = sar_alloc(len);
const outPtr = sar_alloc(len);
new Float64Array(memory.buffer, highPtr, len).set(high);
new Float64Array(memory.buffer, lowPtr, len).set(low);
// Calculate into pre-allocated memory
sar_into(highPtr, lowPtr, outPtr, len, 0.02, 0.2);
const out = new Float64Array(memory.buffer, outPtr, len).slice();
sar_free(highPtr, len);
sar_free(lowPtr, len);
sar_free(outPtr, len); Batch Processing ▼
Run a sweep and get combinations with outputs:
import { sar_batch as sar_batch_js } from 'vectorta-wasm';
const high = new Float64Array([...]);
const low = new Float64Array([...]);
// Unified batch API returns an object: { values, combos, rows, cols }
const cfg = {
acceleration_range: [0.01, 0.05, 0.01],
maximum_range: [0.2, 0.5, 0.1],
};
const { values, combos, rows, cols } = sar_batch_js(high, low, cfg);
// values is flat: rows*cols; reshape per row if needed
const rowsArr = [] as number[][];
for (let r = 0; r < rows; r++) {
const start = r * cols;
rowsArr.push(Array.from(values.slice(start, start + cols)));
}
// combos is an array of { acceleration: number | null, maximum: number | null }
console.log(combos); Performance Analysis
Across sizes, Rust CPU runs about 1.24× slower than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
Related Indicators
Average Directional Index
Technical analysis indicator
Average Directional Movement Index Rating
Technical analysis indicator
Alligator
Technical analysis indicator
Aroon
Technical analysis indicator
Aroon Oscillator
Technical analysis indicator
Chande Momentum Oscillator
Technical analysis indicator