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 vectorta::indicators::acosc::{acosc, AcoscInput, AcoscParams};
use vectorta::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 finite; require at least 39 valid points after that or AcoscError::NotEnoughData.
  • 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::InvalidBatchKernel.
Error Handling
use vectorta::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::NotEnoughData { required, actual }) =>
        eprintln!("Need {} data points, only {} available", required, actual),
    Err(AcoscError::InvalidBatchKernel { 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 vectorta 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 vectorta 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 vectorta 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 support for ACOSC is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.

# Coming soon: CUDA-accelerated ACOSC calculations
# from vectorta import acosc_cuda_batch
# results = acosc_cuda_batch(high, low, 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 ...

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators