JSA Moving Average (JSA)

Parameters: period = 30

Overview

The JSA Moving Average was introduced by George R. Arrington, Ph.D. in Technical Analysis of Stocks & Commodities as an extremely simple formula with many uses. On each bar it computes a value equal to half the sum of the current price and the price from period bars earlier, so the line tends to run below price during sustained uptrends and above price during downtrends instead of hugging every small fluctuation. Many traders treat the JSA line as both a dynamic support/resistance level and a wide-berth trailing stop: as long as price stays clearly on the trend side of the JSA, the prevailing move is usually considered intact, while frequent crossings or price riding directly on the line often signal a choppy or range-bound environment. Because it uses only two price points per bar, JSA is computationally cheap and its effective lag is easy to reason about (roughly half the chosen period), which makes it straightforward to integrate into systematic strategies or real-time charting workflows.

Implementation Examples

Get started with JSA in just a few lines:

use vectorta::indicators::jsa::{jsa, JsaInput, JsaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with a price slice
let prices = vec![100.0, 101.0, 102.0, 101.5, 103.0, 104.0];
let params = JsaParams { period: Some(30) };
let input = JsaInput::from_slice(&prices, params);
let result = jsa(&input)?;

// Using with Candles (defaults: period=30, source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = JsaInput::with_default_candles(&candles);
let result = jsa(&input)?;

// Access values
for value in result.values {
    println!("JSA: {}", value);
}

API Reference

Input Methods
// From price slice
JsaInput::from_slice(&[f64], JsaParams) -> JsaInput

// From candles with custom source
JsaInput::from_candles(&Candles, &str, JsaParams) -> JsaInput

// From candles with defaults (source="close", period=30)
JsaInput::with_default_candles(&Candles) -> JsaInput
Parameters Structure
pub struct JsaParams {
    pub period: Option<usize>, // Default: 30
}
Output Structure
pub struct JsaOutput {
    pub values: Vec<f64>, // JSA[i] = 0.5 * (x[i] + x[i - period]) after warmup; leading values are NaN
}
Validation, Warmup & NaNs
  • period > 0 and period ≤ data.len(); else JsaError::InvalidPeriod.
  • Requires at least period valid points after the first finite value; else JsaError::NotEnoughValidData.
  • Indices before first_valid + period are NaN; from there: out[i] = (x[i] + x[i - period]) * 0.5.
  • Empty input → JsaError::EmptyInputData; all-NaN input → JsaError::AllValuesNaN.
  • Into-APIs validate output length; mismatch → JsaError::OutputLenMismatch.
  • Streaming: returns None until period samples are seen; NaN inputs propagate.
Error Handling
use vectorta::indicators::jsa::{jsa, JsaError, JsaInput, JsaParams};

let input = JsaInput::from_slice(&prices, JsaParams { period: Some(30) });
match jsa(&input) {
    Ok(out) => println!("{} values", out.values.len()),
    Err(JsaError::EmptyInputData) => eprintln!("input slice is empty"),
    Err(JsaError::AllValuesNaN) => eprintln!("all inputs are NaN"),
    Err(JsaError::InvalidPeriod { period, data_len }) => {
        eprintln!("invalid period {period} for length {data_len}")
    }
    Err(JsaError::NotEnoughValidData { needed, valid }) => {
        eprintln!("need {needed} valid points, got {valid}")
    }
    Err(JsaError::OutputLenMismatch { expected, got }) => {
        eprintln!("dst len {got}, expected {expected}")
    }
    Err(JsaError::InvalidKernel { kernel }) => eprintln!("invalid batch kernel: {kernel:?}"),
}

Python Bindings

Basic Usage

Calculate JSA using NumPy arrays (default period=30):

import numpy as np
from vectorta import jsa

prices = np.array([100.0, 101.0, 102.0, 101.5, 103.0, 104.0])

# Default period (30) or specify explicitly
out = jsa(prices, period=30)

# Specify kernel ("auto", "scalar", "avx2", "avx512")
out = jsa(prices, period=30, kernel="auto")

print(out)  # NumPy array, same length as input
Streaming Real-time Updates

Process real-time price updates efficiently:

from vectorta import JsaStream

stream = JsaStream(period=30)
for price in price_feed:
    value = stream.update(price)
    if value is not None:
        print(value)
Batch Parameter Optimization

Test multiple periods efficiently:

import numpy as np
from vectorta import jsa_batch

prices = np.array([...])

results = jsa_batch(
    prices,
    period_start=10, period_end=50, period_step=10,
    kernel="auto"
)

# results is a dict with 'values' (2D), 'periods', 'rows', 'cols'
print(results['values'].shape)
print(results['periods'])
CUDA Acceleration

CUDA support for JSA is available as developer APIs; final interfaces may change.

# Developer APIs (subject to change)
from vectorta import jsa_cuda_batch_dev, jsa_cuda_many_series_one_param_dev
import numpy as np

# One series, many parameters
prices_f32 = np.array([...], dtype=np.float32)
dev_arr = jsa_cuda_batch_dev(prices_f32, period_range=(10, 50, 10), device_id=0)

# Many series, one parameter (time-major [T, N])
tm = np.array([...], dtype=np.float32)  # shape: [T, N]
dev_arr2 = jsa_cuda_many_series_one_param_dev(tm, period=30, device_id=0)

JavaScript/WASM Bindings

Basic Usage

Calculate JSA in JavaScript/TypeScript:

import { jsa_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 101, 102, 101.5, 103, 104]);
const out = jsa_js(prices, 30); // period=30
console.log('JSA:', out);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { jsa_alloc, jsa_free, jsa_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* your data */]);
const n = prices.length;

const inPtr = jsa_alloc(n);
const outPtr = jsa_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);

// Compute directly into pre-allocated memory
jsa_into(inPtr, outPtr, n, 30);

const jsaValues = new Float64Array(memory.buffer, outPtr, n).slice();
jsa_free(inPtr, n);
jsa_free(outPtr, n);

console.log(jsaValues);
Batch Processing

Test multiple periods efficiently:

import { jsa_batch_js } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);
const config = { period_range: [10, 50, 10] }; // start, end, step

const { values, periods, rows, cols } = jsa_batch_js(prices, config) as unknown as {
  values: Float64Array; periods: number[]; rows: number; cols: number;
};

// Reshape flat results into matrix rows x cols
const matrix: Float64Array[] = [];
for (let r = 0; r < rows; r++) {
  const start = r * cols;
  matrix.push(values.slice(start, start + cols));
}

console.log('Periods tested:', periods);

Performance Analysis

Comparison:
View:
Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05

Related Indicators