Pascal Weighted Moving Average (PWMA)

Parameters: period = 5

Overview

The Pascal Weighted Moving Average applies binomial coefficients from Pascal's triangle as normalized weights across a sliding window, creating a bell curve distribution that emphasizes the center of the period rather than recent values. Unlike linear or exponential weighted averages that bias toward the most recent data, PWMA assigns peak weight to the middle bar of the lookback window and symmetrically decreases weight toward both edges following the pattern 1-2-1, 1-3-3-1, 1-4-6-4-1, and so on depending on the period length. This symmetric weighting produces exceptionally smooth output with minimal lag at genuine trend changes because it captures the central tendency of the entire window without overreacting to the latest bar. The binomial weights naturally filter high frequency noise while preserving the underlying price structure, making PWMA particularly effective for identifying the true direction of movement without the whipsaws common in heavily recent weighted indicators. Traders value PWMA for its balanced approach to smoothing that neither lags excessively like simple moving averages nor oscillates nervously like exponential methods, though the moderate period typically means it responds more slowly than adaptive indicators during volatile breakouts when rapid position adjustment becomes critical.

Default: period = 5.

Implementation Examples

Get started with PWMA in just a few lines:

use vectorta::indicators::moving_averages::pwma::{pwma, PwmaInput, PwmaParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};

// Using with price data slice
let prices = vec![100.0, 102.0, 101.5, 103.0, 105.0, 104.5];
let params = PwmaParams { period: Some(5) }; // default: 5
let input = PwmaInput::from_slice(&prices, params);
let result = pwma(&input)?;

// Using with Candles data structure (defaults: period=5, source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = PwmaInput::with_default_candles(&candles);
let result = pwma(&input)?;

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

API Reference

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

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

// From candles with default params (close prices, period=5)
PwmaInput::with_default_candles(&Candles) -> PwmaInput
Parameters Structure
pub struct PwmaParams {
    pub period: Option<usize>, // Default: 5
}
Output Structure
pub struct PwmaOutput {
    pub values: Vec<f64>, // PWMA series
}
Validation, Warmup & NaNs
  • period > 0; returns PwmaError::InvalidPeriod if period == 0 or period > len.
  • Skips leading NaNs; if all values are NaNPwmaError::AllValuesNaN.
  • Warmup: indices before first_valid + period - 1 are NaN.
  • Weights are Pascal/binomial coefficients normalized to sum 1; if they sum to zero (degenerate) → PwmaError::PascalWeightsSumZero.
Error Handling
use vectorta::indicators::moving_averages::pwma::{pwma, PwmaError};

match pwma(&input) {
    Ok(output) => process_results(output.values),
    Err(PwmaError::AllValuesNaN) =>
        eprintln!("All input values are NaN"),
    Err(PwmaError::InvalidPeriod { period, data_len }) =>
        eprintln!("Invalid period {} for data length {}", period, data_len),
    Err(PwmaError::PascalWeightsSumZero { period }) =>
        eprintln!("Pascal weights sum to zero for period {}", period),
}

Python Bindings

Basic Usage

Calculate PWMA using NumPy arrays (default period=5):

import numpy as np
from vectorta import pwma

# Prepare price data as NumPy array
prices = np.array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5])

# Default period (5)
result = pwma(prices, period=5)

# Specify kernel for performance optimization ("auto", "scalar", "avx2", "avx512")
result = pwma(prices, period=7, kernel="avx2")

print("PWMA values:", result)
Streaming Real-time Updates

Process real-time price updates efficiently:

from vectorta import PwmaStream

# Initialize streaming PWMA calculator
stream = PwmaStream(period=5)

# Process real-time updates
for price in price_feed:
    val = stream.update(price)
    if val is not None:
        print("Current PWMA:", val)
Batch Parameter Optimization

Test multiple period values:

import numpy as np
from vectorta import pwma_batch

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

# (start, end, step)
period_range = (3, 15, 2)

results = pwma_batch(prices, period_range, kernel="auto")

values = results['values']  # shape: (num_periods, len(prices))
periods = results['periods']
print("Tested periods:", periods)
CUDA Acceleration

Requires building with Python + CUDA features enabled.

import numpy as np
from vectorta import pwma_cuda_batch_dev, pwma_cuda_many_series_one_param_dev

# Single series: batch over period range (device_id optional)
prices = np.random.rand(10_000).astype(np.float64)
period_range = (3, 15, 2)
dev_array = pwma_cuda_batch_dev(prices, period_range, device_id=0)
# dev_array is a device-resident array wrapper (implementation-specific)

# Many series (time-major) with one period
data_tm_f32 = np.random.rand(64, 4096).astype(np.float32)  # rows x cols
dev_array2 = pwma_cuda_many_series_one_param_dev(data_tm_f32, period=5, device_id=0)

JavaScript/WASM Bindings

Basic Usage

Compute PWMA in the browser or Node.js:

import { pwma_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 102, 101.5, 103, 105, 104.5]);
const result = pwma_js(prices, 5);
console.log('PWMA values:', result);
Memory-Efficient Operations

Use zero-copy operations for large datasets:

import { pwma_alloc, pwma_free, pwma_into, memory } from 'vectorta-wasm';

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

// Allocate WASM memory for input and output
const inPtr = pwma_alloc(length);
const outPtr = pwma_alloc(length);

// Copy input data into WASM memory
new Float64Array(memory.buffer, inPtr, length).set(prices);

// Calculate PWMA directly into allocated memory
// Args: in_ptr, out_ptr, len, period
pwma_into(inPtr, outPtr, length, 5);

// Read results from WASM memory (slice() to copy out)
const pwmaValues = new Float64Array(memory.buffer, outPtr, length).slice();

// Free allocated memory when done
pwma_free(inPtr, length);
pwma_free(outPtr, length);

console.log('PWMA values:', pwmaValues);
Batch Processing

Test multiple period values efficiently:

import { pwma_batch_js, pwma_batch_metadata_js } from 'vectorta-wasm';

const prices = new Float64Array([/* historical prices */]);

// Define parameter sweep range
const start = 3, end = 15, step = 2;  // 3, 5, 7, 9, 11, 13, 15

// Get metadata about period combinations
const metadata = pwma_batch_metadata_js(start, end, step);
const numCombos = metadata.length; // one value per period

// Calculate all combinations
const results = pwma_batch_js(prices, start, end, step);

// Results is a flat array: [combo1_values..., combo2_values..., ...]
const resultMatrix: number[][] = [];
for (let i = 0; i < numCombos; i++) {
  const rowStart = i * prices.length;
  const rowEnd = rowStart + prices.length;
  resultMatrix.push(results.slice(rowStart, rowEnd));
}

// Access specific period results
const period = metadata[0];
const valuesForFirst = resultMatrix[0];

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators