Accelerator Oscillator (ACOSC)

Overview

The Accelerator Oscillator (ACOSC) measures the acceleration and deceleration of the driving force behind price movements. ACOSC calculates the difference between the Awesome Oscillator (a 5 period minus 34 period SMA of median price) and its own 5 period moving average. The zero line represents where the driving force is at balance with acceleration. Above zero, it becomes easier for acceleration to continue increasing, while below zero indicates deceleration. According to Bill Williams, the indicator changes direction before the driving force changes, which itself changes before price, making AC an early warning system. The indicator provides both oscillator values and their bar to bar changes, helping traders identify momentum shifts before major reversals occur.

Defaults: ACOSC uses fixed internal periods (no user parameters).

Implementation Examples

Get started with ACOSC in just a few lines:

use vector_ta::indicators::acosc::{acosc, AcoscInput, AcoscParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with high/low price slices
let high = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let low = vec![99.0, 98.0, 99.5, 100.0, 102.0, 103.0];
let params = AcoscParams::default(); // No configurable parameters
let input = AcoscInput::from_slices(&high, &low, params);
let result = acosc(&input)?;

// Using with Candles data structure
// Quick and simple with default parameters (source = high/low)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = AcoscInput::with_default_candles(&candles);
let result = acosc(&input)?;

// Access the oscillator and change values
for (osc, change) in result.osc.iter().zip(result.change.iter()) {
    println!("AC: {}, Change: {}", osc, change);
}

API Reference

Input Methods
// From high/low slices
AcoscInput::from_slices(&[f64], &[f64], AcoscParams) -> AcoscInput

// From candles
AcoscInput::from_candles(&Candles, AcoscParams) -> AcoscInput

// From candles with default params
AcoscInput::with_default_candles(&Candles) -> AcoscInput
Parameters Structure
pub struct AcoscParams {}  // Empty - ACOSC uses fixed parameters

// Fixed internal parameters:
// - Fast SMA period: 5
// - Slow SMA period: 34
// - Signal smoothing period: 5
Output Structure
pub struct AcoscOutput {
    pub osc: Vec<f64>,    // AC oscillator values
    pub change: Vec<f64>, // Momentum change between consecutive AC values
}
Validation, Warmup & NaNs
  • high.len() == low.len(); otherwise AcoscError::LengthMismatch.
  • Find first index where both high[i] and low[i] are not NaN; require at least 39 points after that or AcoscError::NotEnoughValidData.
  • Warmup: indices before first + 38 are set to NaN in both osc and change.
  • Candles: failure to select high/low yields AcoscError::CandleFieldError.
  • Batch kernel validation: passing a non-batch kernel to acosc_batch_with_kernel returns AcoscError::InvalidKernelForBatch.
Error Handling
use vector_ta::indicators::acosc::AcoscError;

match acosc(&input) {
    Ok(output) => {
        process_results(output.osc, output.change);
    },
    Err(AcoscError::CandleFieldError { msg }) =>
        eprintln!("Failed to get high/low from candles: {}", msg),
    Err(AcoscError::LengthMismatch { high_len, low_len }) =>
        eprintln!("High/low length mismatch: {} vs {}", high_len, low_len),
    Err(AcoscError::NotEnoughValidData { needed, valid }) =>
        eprintln!("Need {} valid points, only {} available", needed, valid),
    Err(AcoscError::InvalidKernelForBatch(kernel)) =>
        eprintln!("Invalid batch kernel: {:?}", kernel),
    Err(e) => eprintln!("ACOSC error: {}", e),
}

Python Bindings

Basic Usage

Calculate ACOSC using NumPy arrays:

import numpy as np
from vector_ta import acosc

high = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])
low = np.array([99.0, 98.0, 99.5, 100.0, 102.0, 103.0])

# Calculate ACOSC (fixed parameters: 5/34 for AO, 5 for signal)
osc, change = acosc(high, low)

# Specify kernel for performance optimization
osc, change = acosc(high, low, kernel="avx2")

print("AC Oscillator:", osc)
print("Momentum Change:", change)

# The change array represents momentum between consecutive AC values
positive_momentum = change > 0
print("Periods with increasing momentum:", int(np.sum(positive_momentum)))
Streaming Real-time Updates

Process real-time price updates efficiently:

from vector_ta import AcoscStream

stream = AcoscStream()

for high, low in price_feed:
    result = stream.update(high, low)
    if result is not None:
        osc_value, change_value = result
        print(f"Current AC: {osc_value}, Change: {change_value}")
Batch Processing

Batch API returns a single row (no parameter grid):

import numpy as np
from vector_ta import acosc_batch

high = np.array([...], dtype=np.float64)
low = np.array([...], dtype=np.float64)

result = acosc_batch(high, low, kernel="auto")
osc = result["osc"][0]
change = result["change"][0]
print(osc.shape, change.shape)
CUDA Acceleration

CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).

import numpy as np
from vector_ta import acosc_cuda_batch_dev, acosc_cuda_many_series_one_param_dev

# One series (float32)
high_f32 = np.asarray(load_high(), dtype=np.float32)
low_f32 = np.asarray(load_low(), dtype=np.float32)

dev = acosc_cuda_batch_dev(
    high_f32=high_f32,
    low_f32=low_f32,
    device_id=0,
)

# Many series (time-major)
high_tm_f32 = np.asarray(load_high_time_major_matrix(), dtype=np.float32)
low_tm_f32 = np.asarray(load_low_time_major_matrix(), dtype=np.float32)

dev_tm = acosc_cuda_many_series_one_param_dev(
    high_tm_f32=high_tm_f32,
    low_tm_f32=low_tm_f32,
    device_id=0,
)

JavaScript/WASM Bindings

Basic Usage

Calculate ACOSC in JavaScript/TypeScript:

import { acosc_js } from 'vectorta-wasm';

// High/low price data as Float64Array or regular array
const high = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);
const low = new Float64Array([99.0, 98.0, 99.5, 100.0, 102.0, 103.0]);

// Calculate ACOSC (fixed parameters)
const result = acosc_js(high, low);

// Result is a flattened Float64Array [osc_values..., change_values...]
const len = high.length;
const osc = result.slice(0, len);
const change = result.slice(len);

console.log('AC Oscillator:', osc);
console.log('Momentum Change:', change);

interface AcoscResult { osc: Float64Array; change: Float64Array; }
const parseAcoscResult = (buf: Float64Array, length: number): AcoscResult => ({
  osc: buf.slice(0, length),
  change: buf.slice(length)
});
Memory-Efficient Operations

Use zero-copy operations for better performance with large datasets:

import { acosc_alloc, acosc_free, acosc_into, memory } from 'vectorta-wasm';

// Prepare your high/low data
const high = new Float64Array([/* your data */]);
const low = new Float64Array([/* your data */]);
const length = high.length;

// Allocate WASM memory for inputs and outputs
const highPtr = acosc_alloc(length);
const lowPtr = acosc_alloc(length);
const oscPtr = acosc_alloc(length);
const changePtr = acosc_alloc(length);

// Copy inputs into WASM memory
new Float64Array(memory.buffer, highPtr, length).set(high);
new Float64Array(memory.buffer, lowPtr, length).set(low);

// Calculate ACOSC directly into allocated memory (zero-copy)
acosc_into(highPtr, lowPtr, oscPtr, changePtr, length);

// Read results and copy out
const oscValues = new Float64Array(memory.buffer, oscPtr, length).slice();
const changeValues = new Float64Array(memory.buffer, changePtr, length).slice();

// Free allocated memory when done
acosc_free(highPtr, length);
acosc_free(lowPtr, length);
acosc_free(oscPtr, length);
acosc_free(changePtr, length);

console.log('AC Oscillator:', oscValues);
console.log('Momentum Change:', changeValues);
Batch Processing

Unified batch call (single row since no parameters):

import { acosc_batch, acosc_batch_metadata_js } from 'vectorta-wasm';

const high = new Float64Array([/* historical highs */]);
const low = new Float64Array([/* historical lows */]);

// Metadata is empty for ACOSC (no parameter grid)
const metadata = acosc_batch_metadata_js();
console.log('Metadata length:', metadata.length); // 0

// Run batch
const config = {};
const result = acosc_batch(high, low, config);

// Result object: { values: Float64Array, rows: number (1), cols: number }
type AcoscBatchResult = { values: Float64Array; rows: number; cols: number };
const out = result as AcoscBatchResult;
const { rows, cols, values } = out;
console.log('Result dimensions: ' + rows + 'x' + cols);

const osc = values.slice(0, cols);
const change = values.slice(cols);
// ... use osc/change ...

CUDA Bindings (Rust)

use vector_ta::cuda::CudaAcosc;

let cuda = CudaAcosc::new(0)?;

let high_f32: [f32] = /* ... */;
let low_f32: [f32] = /* ... */;

let out = cuda.acosc_batch_dev(&high_f32, &low_f32)?;
let _ = out;

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators