Cubic Weighted Moving Average (CWMA)

Parameters: period = 14

Overview

The Cubic Weighted Moving Average (CWMA) applies cubic weighting to price data, creating a moving average that responds more aggressively to recent price changes than linear or squared weighted averages. The indicator assigns weights based on the cube of each data point's position in the window. The most recent price receives a weight of n cubed, while the oldest receives a weight of 1 cubed, with intermediate values following the cubic progression. This cubic relationship creates a steep weighting curve that heavily favors recent prices while still considering the full lookback period for smoothness.

CWMA strikes a balance between responsiveness and stability by using polynomial weighting rather than exponential decay. The cubic function provides stronger emphasis on recent data than WMA's linear weights or even quadratic weighting schemes. This makes CWMA particularly effective at tracking price movements during trend accelerations while maintaining enough historical context to avoid excessive whipsaws. The steeper weight distribution means CWMA turns faster than traditional weighted averages when trends change direction. However, it remains smoother than extremely responsive averages like Hull or Zero Lag variations.

Traders use CWMA as a trend following tool that adapts quickly to momentum shifts without sacrificing too much stability. The indicator works well for identifying dynamic support and resistance levels in trending markets where its responsiveness helps it stay close to price action. CWMA crossovers with price or other moving averages generate trading signals similar to other MA systems, but with potentially earlier entries due to the cubic weighting. Many traders combine CWMA with slower moving averages to create crossover systems that balance speed and reliability. The indicator also serves as a baseline for other calculations, providing a responsive yet stable price proxy for momentum oscillators or volatility bands.

Implementation Examples

Compute CWMA from slices or candles:

use vectorta::indicators::cwma::{cwma, CwmaInput, CwmaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with a price slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = CwmaParams { period: Some(14) }; // Default 14 if None
let input = CwmaInput::from_slice(&prices, params);
let result = cwma(&input)?;

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

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

API Reference

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

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

// From candles with default params (close, period=14)
CwmaInput::with_default_candles(&Candles) -> CwmaInput
Parameters Structure
pub struct CwmaParams {
    pub period: Option<usize>, // Default: 14
}
Output Structure
pub struct CwmaOutput {
    pub values: Vec<f64>, // CWMA values
}
Validation, Warmup & NaNs
  • period ≥ 2; period ≤ len(data) or CwmaError::InvalidPeriod.
  • Finds the first finite input; requires at least period valid values after it, otherwise CwmaError::NotEnoughValidData.
  • All-NaN input yields CwmaError::AllValuesNaN. Empty input yields CwmaError::EmptyInputData.
  • Warmup: outputs are NaN through index first_non_nan + period - 2. First valid value appears at first_non_nan + period - 1.
  • Streaming: update returns None during warmup, then Some(value); returns Some(NaN) if any NaN is inside the active window.
Error Handling
use vectorta::indicators::cwma::{cwma, CwmaError};

match cwma(&input) {
    Ok(output) => process(output.values),
    Err(CwmaError::EmptyInputData) => eprintln!("no data"),
    Err(CwmaError::AllValuesNaN) => eprintln!("all values are NaN"),
    Err(CwmaError::InvalidPeriod { period, data_len }) => {
        eprintln!("invalid period {} for data length {}", period, data_len)
    }
    Err(CwmaError::NotEnoughValidData { needed, valid }) => {
        eprintln!("need {} valid points, found {}", needed, valid)
    }
}

Python Bindings

Basic Usage

Calculate CWMA using NumPy arrays (default period=14; optional kernel selection):

import numpy as np
from vectorta import cwma

prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5], dtype=float)

# Defaults (period=14)
vals = cwma(prices, period=14)

# Specify kernel ("auto", "scalar", "avx2", "avx512")
vals = cwma(prices, period=14, kernel="auto")

print(vals)
Streaming Real-time Updates
from vectorta import CwmaStream

stream = CwmaStream(period=14)
for price in price_feed:
    value = stream.update(price)
    if value is not None:
        handle(value)
Batch Parameter Optimization
import numpy as np
from vectorta import cwma_batch

prices = np.array([...], dtype=float)
results = cwma_batch(prices, period_range=(5, 30, 5), kernel="auto")

print(results["values"].shape)  # (num_periods, len(prices))
print(results["periods"])
CUDA Acceleration

CUDA-backed CWMA APIs are available when built with CUDA support.

# from vectorta import cwma_cuda_batch_dev, cwma_cuda_many_series_one_param_dev
# prices_f32 = np.array([...], dtype=np.float32)
# out = cwma_cuda_batch_dev(prices_f32, period_range=(5, 30, 1), device_id=0)
# grid = cwma_cuda_many_series_one_param_dev(data_tm_f32=tm_prices, period=14, device_id=0)

JavaScript/WASM Bindings

Basic Usage

Calculate CWMA in JavaScript/TypeScript:

import { cwma_js } from 'vectorta-wasm';

const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);
const result = cwma_js(prices, 14); // period=14
console.log('CWMA values:', result);
Memory-Efficient Operations

Use zero-copy operations for large arrays:

import { cwma_alloc, cwma_free, cwma_into, memory } from 'vectorta-wasm';

const prices = new Float64Array([/* data */]);
const len = prices.length;
const inPtr = cwma_alloc(len);
const outPtr = cwma_alloc(len);

new Float64Array(memory.buffer, inPtr, len).set(prices);
cwma_into(inPtr, outPtr, len, 14);
const values = new Float64Array(memory.buffer, outPtr, len).slice();

cwma_free(inPtr, len);
cwma_free(outPtr, len);
Batch Processing

Test many periods across one series:

import { cwma_batch_js, cwma_batch_metadata_js } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);
const meta = cwma_batch_metadata_js(5, 30, 5); // [period1, period2, ...]
const numCombos = meta.length;
const flat = cwma_batch_js(prices, 5, 30, 5);

const rows = numCombos, cols = prices.length;
const matrix: Float64Array[] = [];
for (let i = 0; i < rows; i++) {
  const start = i * cols;
  matrix.push(flat.slice(start, start + cols));
}

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators