Acceleration/Deceleration Oscillator (AC)

Overview

The Acceleration/Deceleration Oscillator (AC) is a second-derivative momentum indicator that measures the rate of change of the Awesome Oscillator. It calculates the difference between the AO (which itself is the difference between 5-period and 34-period SMAs of median price) and a 5-period SMA of the AO.

The AC provides two output arrays: the oscillator values and the momentum change between consecutive values. With fixed periods of 5 and 34 for the underlying Awesome Oscillator calculation and an additional 5-period smoothing, the indicator requires a minimum of 39 data points to produce valid results.

Implementation Examples

Get started with ACOSC in just a few lines:

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

// 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
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
}
Error Handling
use vectorta::indicators::acosc::AcoscError;

match acosc(&input) {
    Ok(output) => {
        process_results(output.osc, output.change);
    },
    Err(AcoscError::CandleFieldError { msg }) =>
        println!("Failed to get high/low from candles: {}", msg),
    Err(AcoscError::LengthMismatch { high_len, low_len }) =>
        println!("High/low length mismatch: {} vs {}", high_len, low_len),
    Err(AcoscError::NotEnoughData { required, actual }) =>
        println!("Need {} data points, only {} available", required, actual),
    Err(e) => println!("ACOSC error: {}", e)
}

Python Bindings

Basic Usage

Calculate ACOSC using NumPy arrays:

import numpy as np
from vectorta import acosc

# Prepare high/low price data as NumPy arrays
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")

# Results are NumPy arrays matching input length
print(f"AC Oscillator: {osc}")
print(f"Momentum Change: {change}")

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

Process real-time price updates efficiently:

from vectorta import AcoscStream

# Initialize streaming ACOSC calculator
stream = AcoscStream()

# Process real-time price updates
for high, low in price_feed:
    result = stream.update(high, low)

    if result is not None:
        osc_value, change_value = result
        # AC values are ready (None during 39-bar warmup period)
        print(f"Current AC: {osc_value}, Change: {change_value}")

        # Monitor momentum changes
        if change_value > 0:
            print("Momentum is accelerating")
        else:
            print("Momentum is decelerating")
Batch Processing

Process data with batch optimizations:

import numpy as np
from vectorta import acosc_batch

# Your high/low price data
high = np.array([...])  # Historical high prices
low = np.array([...])   # Historical low prices

# Run batch calculation
# Note: ACOSC has no parameters, so batch returns single row
results = acosc_batch(
    high,
    low,
    kernel="auto"  # Auto-select best kernel
)

# Access results
print(f"Oscillator shape: {results['osc'].shape}")    # (1, len(prices))
print(f"Change shape: {results['change'].shape}")      # (1, len(prices))

# Extract the single row of results
osc_values = results['osc'][0]
change_values = results['change'][0]

# Analyze momentum patterns
momentum_shifts = np.diff(np.sign(change_values))
print(f"Number of momentum direction changes: {np.sum(momentum_shifts != 0)}")
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, acosc_cuda_many_series_one_param
# import numpy as np
#
# # Since ACOSC has no parameters, only the "many series" pattern applies
#
# # Process multiple stocks/assets with ACOSC
# # Shape: [time_steps, num_assets]
# portfolio_highs = np.array([...])
# portfolio_lows = np.array([...])
#
# osc_results, change_results = acosc_cuda_many_series(
#     high_tm=portfolio_highs,  # Time-major format [T, N]
#     low_tm=portfolio_lows,    # Time-major format [T, N]
#     device_id=0
# )
# # Returns: Two 2D arrays [time_steps, num_assets]
# #          - osc_results: AC oscillator for each asset
# #          - change_results: momentum change for each asset
#
# # Zero-copy variant with pre-allocated output (F32 for GPU efficiency)
# osc_out = np.empty((time_steps, num_assets), dtype=np.float32)
# change_out = np.empty((time_steps, num_assets), dtype=np.float32)
# acosc_cuda_many_series_into(
#     high_tm_f32=portfolio_highs.astype(np.float32),
#     low_tm_f32=portfolio_lows.astype(np.float32),
#     osc_out=osc_out,
#     change_out=change_out,
#     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);

// TypeScript type definitions
interface AcoscResult {
  osc: Float64Array;
  change: Float64Array;
}

// Helper function to parse results
function parseAcoscResult(result: Float64Array, length: number): AcoscResult {
  return {
    osc: result.slice(0, length),
    change: result.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 outputs
const oscPtr = acosc_alloc(length);
const changePtr = acosc_alloc(length);

// Get input data pointers in WASM memory
const highArray = new Float64Array(memory.buffer, /* offset */, length);
const lowArray = new Float64Array(memory.buffer, /* offset */, length);
highArray.set(high);
lowArray.set(low);

// Calculate ACOSC directly into allocated memory (zero-copy)
acosc_into(
  highArray.byteOffset,  // High prices pointer
  lowArray.byteOffset,   // Low prices pointer
  oscPtr,                 // Oscillator output pointer
  changePtr,              // Change output pointer
  length                  // Data length
);

// Read results from WASM memory
const oscResult = new Float64Array(memory.buffer, oscPtr, length);
const changeResult = new Float64Array(memory.buffer, changePtr, length);

// Convert to regular arrays if needed
const oscValues = Array.from(oscResult);
const changeValues = Array.from(changeResult);

// Important: Free allocated memory when done
acosc_free(oscPtr, length);
acosc_free(changePtr, length);

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

Process data with batch optimizations:

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

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

// Get metadata (empty for ACOSC since no parameters)
const metadata = acosc_batch_metadata_js();
console.log('Metadata:', metadata); // Returns empty array

// Calculate with batch optimizations
const config = {}; // Empty config since ACOSC has no parameters
const result = acosc_batch(high, low, config);

// Parse the batch result
interface AcoscBatchResult {
  values: Float64Array;  // Flattened [osc..., change...]
  rows: number;          // Always 1 for ACOSC
  cols: number;          // Length of input data
}

const batchResult = result as AcoscBatchResult;
console.log(`Result dimensions: ${batchResult.rows}x${batchResult.cols}`);

// Extract oscillator and change values
const len = batchResult.cols;
const osc = batchResult.values.slice(0, len);
const change = batchResult.values.slice(len);

// Analyze momentum patterns
let momentumShifts = 0;
for (let i = 1; i < change.length; i++) {
  if (Math.sign(change[i]) !== Math.sign(change[i-1])) {
    momentumShifts++;
  }
}
console.log(`Momentum direction changes: ${momentumShifts}`);

Performance Analysis

Related Indicators