Williams Alligator

Parameters: jaw_period = 13 | jaw_offset = 8 | teeth_period = 8 | teeth_offset = 5 | lips_period = 5 | lips_offset = 3

Overview

The Williams Alligator identifies trend phases using three smoothed moving averages that represent an alligator's jaw, teeth, and lips, each calculated from median price with Fibonacci based periods and shifted forward in time. Bill Williams designed this system to filter out ranging markets by observing when the three lines converge and diverge. The Jaw uses a 13 period SMMA of median price shifted 8 bars forward, the Teeth employs an 8 period SMMA shifted 5 bars, and the Lips applies a 5 period SMMA shifted 3 bars ahead. When these lines intertwine, the market lacks direction and the alligator sleeps. As price begins trending, the lines separate in order with the fastest Lips leading, followed by Teeth, then Jaw, signaling the alligator awakens to feed on the trend. Traders enter positions when all three lines spread apart and align directionally, then exit when lines begin crossing back together, indicating the trend exhausts and the alligator returns to sleep.

Implementation Examples

Get started with the Alligator indicator in just a few lines:

use vectorta::indicators::alligator::{alligator, AlligatorInput, AlligatorParams};
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 = AlligatorParams {
    jaw_period: Some(13),
    jaw_offset: Some(8),
    teeth_period: Some(8),
    teeth_offset: Some(5),
    lips_period: Some(5),
    lips_offset: Some(3),
};
let input = AlligatorInput::from_slice(&prices, params);
let result = alligator(&input)?;

// Using with Candles data structure
// Quick and simple with default parameters
let input = AlligatorInput::with_default_candles(&candles);
let result = alligator(&input)?;

// Access the three Alligator lines
println!("Jaw: {:?}", result.jaw);
println!("Teeth: {:?}", result.teeth);
println!("Lips: {:?}", result.lips);

API Reference

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

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

// From candles with default params (hl2 prices, 13/8/5 periods, 8/5/3 offsets)
AlligatorInput::with_default_candles(&Candles) -> AlligatorInput
Parameters Structure
pub struct AlligatorParams {
    pub jaw_period: Option<usize>,   // Default: 13
    pub jaw_offset: Option<usize>,   // Default: 8
    pub teeth_period: Option<usize>, // Default: 8
    pub teeth_offset: Option<usize>, // Default: 5
    pub lips_period: Option<usize>,  // Default: 5
    pub lips_offset: Option<usize>,  // Default: 3
}
Output Structure
pub struct AlligatorOutput {
    pub jaw: Vec<f64>,   // SMMA with longest period and offset (Blue line)
    pub teeth: Vec<f64>, // SMMA with medium period and offset (Red line)
    pub lips: Vec<f64>,  // SMMA with shortest period and offset (Green line)
}
Validation, Warmup & NaNs
  • jaw_period, teeth_period, lips_period must be > 0 and ≤ input length; otherwise Invalid*Period errors.
  • jaw_offset, teeth_offset, lips_offset must be ≤ input length; otherwise Invalid*Offset errors.
  • All inputs NaN → AlligatorError::AllValuesNaN.
  • Warmup per line: first + period − 1 + offset where first is the index of the first finite value. Indices before are NaN.
  • Seeding uses the average of the first period samples; update uses (prev*(n−1)+x)/n.
  • Streaming returns None until the largest period fills; offsets are not applied in streaming outputs.
Error Handling
use vectorta::indicators::alligator::AlligatorError;

match alligator(&input) {
    Ok(output) => {
        process_alligator_lines(output.jaw, output.teeth, output.lips)
    },
    Err(AlligatorError::AllValuesNaN) =>
        println!("All input data is NaN"),
    Err(AlligatorError::InvalidJawPeriod { period, data_len }) =>
        println!("Invalid jaw period {} for data length {}", period, data_len),
    Err(AlligatorError::InvalidJawOffset { offset, data_len }) =>
        println!("Invalid jaw offset {} for data length {}", offset, data_len),
    Err(AlligatorError::InvalidTeethPeriod { period, data_len }) =>
        println!("Invalid teeth period {} for data length {}", period, data_len),
    Err(AlligatorError::InvalidTeethOffset { offset, data_len }) =>
        println!("Invalid teeth offset {} for data length {}", offset, data_len),
    Err(AlligatorError::InvalidLipsPeriod { period, data_len }) =>
        println!("Invalid lips period {} for data length {}", period, data_len),
    Err(AlligatorError::InvalidLipsOffset { offset, data_len }) =>
        println!("Invalid lips offset {} for data length {}", offset, data_len),
    Err(e) => println!("Alligator error: {}", e)
}

Python Bindings

Basic Usage

Calculate Alligator using NumPy arrays:

import numpy as np
from vectorta import alligator

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

# Calculate Alligator with default parameters (13/8/5 periods, 8/5/3 offsets)
jaw, teeth, lips = alligator(prices)

# Or specify custom parameters
jaw, teeth, lips = alligator(
    prices,
    jaw_period=13, jaw_offset=8,
    teeth_period=8, teeth_offset=5,
    lips_period=5, lips_offset=3
)

# Specify kernel for performance optimization
jaw, teeth, lips = alligator(
    prices,
    jaw_period=13, jaw_offset=8,
    teeth_period=8, teeth_offset=5,
    lips_period=5, lips_offset=3,
    kernel="avx2"
)

# Results are NumPy arrays matching input length
print(f"Jaw values: {jaw}")
print(f"Teeth values: {teeth}")
print(f"Lips values: {lips}")

# Check for trend alignment
if lips[-1] > teeth[-1] > jaw[-1]:
    print("Bullish alignment - uptrend")
elif lips[-1] < teeth[-1] < jaw[-1]:
    print("Bearish alignment - downtrend")
else:
    print("No clear trend - alligator sleeping")
Streaming Real-time Updates

Process real-time price updates efficiently:

from vectorta import AlligatorStream

# Initialize streaming Alligator calculator
stream = AlligatorStream(
    jaw_period=13, jaw_offset=8,
    teeth_period=8, teeth_offset=5,
    lips_period=5, lips_offset=3
)

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

    if result is not None:
        # Alligator values are ready (None during warmup period)
        jaw, teeth, lips = result

        print(f"Current Alligator: Jaw={jaw:.2f}, Teeth={teeth:.2f}, Lips={lips:.2f}")

        # Make trading decisions based on Alligator alignment
        if lips > teeth > jaw:
            print("Alligator awakening - bullish trend forming")
            # Consider long entry
        elif lips < teeth < jaw:
            print("Alligator awakening - bearish trend forming")
            # Consider short entry
        else:
            # Lines intertwined
            print("Alligator sleeping - no clear trend")
            # Stay out of market or use range strategies
Batch Parameter Optimization

Test multiple parameter combinations for optimization:

import numpy as np
from vectorta import alligator_batch

# Your price data
prices = np.array([...])  # Your historical prices

# Define parameter ranges to test
# (start, end, step) for each parameter
jaw_period_range = (10, 20, 2)    # Test: 10, 12, 14, 16, 18, 20
jaw_offset_range = (5, 10, 1)     # Test: 5, 6, 7, 8, 9, 10
teeth_period_range = (5, 10, 1)   # Test: 5, 6, 7, 8, 9, 10
teeth_offset_range = (3, 7, 1)    # Test: 3, 4, 5, 6, 7
lips_period_range = (3, 8, 1)     # Test: 3, 4, 5, 6, 7, 8
lips_offset_range = (2, 5, 1)     # Test: 2, 3, 4, 5

# Run batch calculation
results = alligator_batch(
    prices,
    jaw_period_range=jaw_period_range,
    jaw_offset_range=jaw_offset_range,
    teeth_period_range=teeth_period_range,
    teeth_offset_range=teeth_offset_range,
    lips_period_range=lips_period_range,
    lips_offset_range=lips_offset_range,
    kernel="auto"  # Auto-select best kernel
)

# Access results
print(f"Jaw values shape: {results['jaw'].shape}")  # (num_combinations, len(prices))
print(f"Teeth values shape: {results['teeth'].shape}")
print(f"Lips values shape: {results['lips'].shape}")
print(f"Parameter combinations tested: {results['params']}")

# Find best performing parameters based on trend clarity
best_score = 0
best_params = None

for i, params in enumerate(results['params']):
    jaw_line = results['jaw'][i]
    teeth_line = results['teeth'][i]
    lips_line = results['lips'][i]

    # Calculate a score based on line separation (trend clarity)
    separation = np.mean(np.abs(lips_line - jaw_line))

    if separation > best_score:
        best_score = separation
        best_params = params

print(f"Best parameters: {best_params}")
CUDA Acceleration

CUDA support for Alligator is currently under development. The API will follow the same pattern as other CUDA-enabled indicators.

# Coming soon: CUDA-accelerated Alligator calculations
#
# from vectorta import alligator_cuda_batch, alligator_cuda_many_series_one_param
# import numpy as np
#
# # Option 1: One Series, Many Parameters (parameter optimization)
# # Test multiple parameter combinations on a single price series
# results = alligator_cuda_batch(
#     data=prices,  # Single price series
#     jaw_period_range=(10, 20, 1),
#     jaw_offset_range=(5, 10, 1),
#     teeth_period_range=(5, 10, 1),
#     teeth_offset_range=(3, 7, 1),
#     lips_period_range=(3, 8, 1),
#     lips_offset_range=(2, 5, 1),
#     device_id=0
# )
# # Returns:
# # - results['jaw']: 2D array [num_combinations x len(prices)]
# # - results['teeth']: 2D array [num_combinations x len(prices)]
# # - results['lips']: 2D array [num_combinations x len(prices)]
# # - results['params']: list of parameter combinations tested
#
# # Option 2: Many Series, One Parameter Set (portfolio processing)
# # Process multiple stocks/assets with the same Alligator parameters
# portfolio_data = np.array([...])  # Shape: [time_steps, num_assets]
#
# jaw, teeth, lips = alligator_cuda_many_series_one_param(
#     data_tm=portfolio_data,  # Time-major format [T, N]
#     jaw_period=13, jaw_offset=8,
#     teeth_period=8, teeth_offset=5,
#     lips_period=5, lips_offset=3,
#     device_id=0
# )
# # Returns: Three 2D arrays [time_steps, num_assets] with Alligator lines for each asset
#
# # Zero-copy variant with pre-allocated output (F32 for GPU efficiency)
# jaw_out = np.empty((time_steps, num_assets), dtype=np.float32)
# teeth_out = np.empty((time_steps, num_assets), dtype=np.float32)
# lips_out = np.empty((time_steps, num_assets), dtype=np.float32)
# alligator_cuda_many_series_one_param_into(
#     data_tm_f32=portfolio_data.astype(np.float32),
#     jaw_period=13, jaw_offset=8,
#     teeth_period=8, teeth_offset=5,
#     lips_period=5, lips_offset=3,
#     jaw_out=jaw_out,
#     teeth_out=teeth_out,
#     lips_out=lips_out,
#     device_id=0
# )

JavaScript/WASM Bindings

Basic Usage

Calculate Alligator in JavaScript/TypeScript:

import { alligator_js } from 'vectorta-wasm';

// Price data as Float64Array or regular array
const prices = new Float64Array([100.0, 102.0, 101.5, 103.0, 105.0, 104.5]);

// Calculate Alligator with specified parameters
const result = alligator_js(
  prices,
  13, 8,  // jaw_period, jaw_offset
  8, 5,   // teeth_period, teeth_offset
  5, 3    // lips_period, lips_offset
);

// Result contains flattened array: [jaw_values, teeth_values, lips_values]
const len = prices.length;
const jaw = result.slice(0, len);
const teeth = result.slice(len, 2 * len);
const lips = result.slice(2 * len, 3 * len);

console.log('Jaw values:', jaw);
console.log('Teeth values:', teeth);
console.log('Lips values:', lips);

// TypeScript type definitions
interface AlligatorResult {
  jaw: Float64Array;
  teeth: Float64Array;
  lips: Float64Array;
}

// Use with async/await for better error handling
async function calculateAlligator(prices: Float64Array): Promise<AlligatorResult> {
  try {
    const result = alligator_js(prices, 13, 8, 8, 5, 5, 3);
    const len = prices.length;

    return {
      jaw: result.slice(0, len),
      teeth: result.slice(len, 2 * len),
      lips: result.slice(2 * len, 3 * len)
    };
  } catch (error) {
    console.error('Alligator calculation failed:', error);
    throw error;
  }
}
Memory-Efficient Operations

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

import { alligator_alloc, alligator_free, alligator_into, memory } from 'vectorta-wasm';

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

// Allocate WASM memory for output (3 arrays for jaw, teeth, lips)
const jawPtr = alligator_alloc(length);
const teethPtr = alligator_alloc(length);
const lipsPtr = alligator_alloc(length);

// Get input data pointer in WASM memory
const inputArray = new Float64Array(memory.buffer, /* offset */, length);
inputArray.set(prices);

// Calculate Alligator directly into allocated memory (zero-copy)
alligator_into(
  inputArray.byteOffset,  // Input pointer
  jawPtr,                  // Jaw output pointer
  teethPtr,                // Teeth output pointer
  lipsPtr,                 // Lips output pointer
  length,                  // Data length
  13,                      // jaw_period
  8,                       // jaw_offset
  8,                       // teeth_period
  5,                       // teeth_offset
  5,                       // lips_period
  3                        // lips_offset
);

// Read results from WASM memory
const jaw = new Float64Array(memory.buffer, jawPtr, length);
const teeth = new Float64Array(memory.buffer, teethPtr, length);
const lips = new Float64Array(memory.buffer, lipsPtr, length);

// Convert to regular arrays if needed
const jawValues = Array.from(jaw);
const teethValues = Array.from(teeth);
const lipsValues = Array.from(lips);

// Important: Free allocated memory when done
alligator_free(jawPtr, length);
alligator_free(teethPtr, length);
alligator_free(lipsPtr, length);

console.log('Alligator calculated successfully');
Batch Processing

Test multiple parameter combinations efficiently:

import { alligator_batch_js, alligator_batch_metadata_js } from 'vectorta-wasm';

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

// Define parameter sweep ranges
const jawPeriodRange = { start: 10, end: 20, step: 2 };  // 10, 12, 14, 16, 18, 20
const jawOffsetRange = { start: 5, end: 10, step: 1 };   // 5, 6, 7, 8, 9, 10
const teethPeriodRange = { start: 5, end: 10, step: 1 }; // 5, 6, 7, 8, 9, 10
const teethOffsetRange = { start: 3, end: 7, step: 1 };  // 3, 4, 5, 6, 7
const lipsPeriodRange = { start: 3, end: 8, step: 1 };   // 3, 4, 5, 6, 7, 8
const lipsOffsetRange = { start: 2, end: 5, step: 1 };   // 2, 3, 4, 5

// Get metadata about parameter combinations
const metadata = alligator_batch_metadata_js(
  jawPeriodRange.start, jawPeriodRange.end, jawPeriodRange.step,
  jawOffsetRange.start, jawOffsetRange.end, jawOffsetRange.step,
  teethPeriodRange.start, teethPeriodRange.end, teethPeriodRange.step,
  teethOffsetRange.start, teethOffsetRange.end, teethOffsetRange.step,
  lipsPeriodRange.start, lipsPeriodRange.end, lipsPeriodRange.step,
  lipsOffsetRange.start, lipsOffsetRange.end, lipsOffsetRange.step
);

// metadata contains parameter values for each combination
// [jaw_period1, jaw_offset1, teeth_period1, teeth_offset1, lips_period1, lips_offset1,
//  jaw_period2, jaw_offset2, teeth_period2, teeth_offset2, lips_period2, lips_offset2, ...]
const numCombos = metadata.length / 6;

// Calculate all combinations
const results = alligator_batch_js(
  prices,
  jawPeriodRange.start, jawPeriodRange.end, jawPeriodRange.step,
  jawOffsetRange.start, jawOffsetRange.end, jawOffsetRange.step,
  teethPeriodRange.start, teethPeriodRange.end, teethPeriodRange.step,
  teethOffsetRange.start, teethOffsetRange.end, teethOffsetRange.step,
  lipsPeriodRange.start, lipsPeriodRange.end, lipsPeriodRange.step,
  lipsOffsetRange.start, lipsOffsetRange.end, lipsOffsetRange.step
);

// Results is a flat array: [combo1_jaw, combo1_teeth, combo1_lips, combo2_jaw, ...]
// Reshape based on your needs
const resultMatrix = [];
for (let i = 0; i < numCombos; i++) {
  const offset = i * prices.length * 3;
  resultMatrix.push({
    params: {
      jaw_period: metadata[i * 6],
      jaw_offset: metadata[i * 6 + 1],
      teeth_period: metadata[i * 6 + 2],
      teeth_offset: metadata[i * 6 + 3],
      lips_period: metadata[i * 6 + 4],
      lips_offset: metadata[i * 6 + 5]
    },
    jaw: results.slice(offset, offset + prices.length),
    teeth: results.slice(offset + prices.length, offset + 2 * prices.length),
    lips: results.slice(offset + 2 * prices.length, offset + 3 * prices.length)
  });
}

// Find best parameters based on trend clarity
let bestScore = 0;
let bestParams = null;

resultMatrix.forEach(({ params, jaw, teeth, lips }) => {
  // Calculate average line separation as a metric
  let separation = 0;
  for (let i = 0; i < prices.length; i++) {
    separation += Math.abs(lips[i] - jaw[i]);
  }
  separation /= prices.length;

  if (separation > bestScore) {
    bestScore = separation;
    bestParams = params;
  }
});

console.log('Best parameters:', bestParams);

Performance Analysis

Comparison:
View:
Loading chart...

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

Related Indicators