Williams Alligator

Parameters: jaw_period = 13 (5–60) jaw_offset = 8 (0–20) teeth_period = 8 (3–40) teeth_offset = 5 (0–15) lips_period = 5 (2–30) lips_offset = 3 (0–10)

Overview

The Williams Alligator layers three displaced smoothed moving averages to expose when trend energy is rising or fading. The Jaw (slow), Teeth (medium), and Lips (fast) lines expand and contract around price, providing an intuitive read on participation without relying on oscillators.

When the lines weave together the Alligator is "sleeping" and conditions are range-bound. As the Lips cross and pull away from the Teeth and Jaw, the Alligator "awakens," signaling a potential breakout. Sustained line separation confirms a feeding trend, while converging lines warn that momentum is exhausting.

Implementation Examples

Compute the Jaw, Teeth, and Lips series from raw price slices or candle data:

use vectorta::indicators::alligator::{alligator, AlligatorInput, AlligatorParams};
use vectorta::utilities::data_loader::Candles;

// Example price history
let prices = vec![100.0, 101.5, 99.8, 102.2, 104.0, 103.4];

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 output = alligator(&input)?;

// Iterate the three smoothed series
for idx in 0..output.jaw.len() {
    println!(
        "[{}] jaw={:.2} teeth={:.2} lips={:.2}",
        idx, output.jaw[idx], output.teeth[idx], output.lips[idx]
    );
}

// Using default configuration with candles (hl2 source, 13/8/5 periods & offsets)
let candles: Candles = load_candles_from_disk()?;
let input = AlligatorInput::with_default_candles(&candles);
let output = alligator(&input)?;

API Reference

Input Methods
// Price slices
AlligatorInput::from_slice(&[f64], AlligatorParams) -> AlligatorInput
// Candle data with explicit source (e.g. "hl2")
AlligatorInput::from_candles(&Candles, &str, AlligatorParams) -> AlligatorInput
// Candles with defaults (hl2 field, 13/8/5 periods & offsets)
AlligatorInput::with_default_candles(&Candles) -> AlligatorInput
Parameter Structure
#[derive(Default, Clone)]
pub struct AlligatorParams {
    pub jaw_period: Option<usize>,   // defaults to Some(13)
    pub jaw_offset: Option<usize>,   // defaults to Some(8)
    pub teeth_period: Option<usize>, // defaults to Some(8)
    pub teeth_offset: Option<usize>, // defaults to Some(5)
    pub lips_period: Option<usize>,  // defaults to Some(5)
    pub lips_offset: Option<usize>,  // defaults to Some(3)
}
Output Structure
pub struct AlligatorOutput {
    pub jaw: Vec<f64>,   // Smoothed Jaw line aligned to input length
    pub teeth: Vec<f64>, // Smoothed Teeth line aligned to input length
    pub lips: Vec<f64>,  // Smoothed Lips line aligned to input length
}
Builder Utilities
use vectorta::indicators::alligator::AlligatorBuilder;
use vectorta::utilities::enums::Kernel;

let builder = AlligatorBuilder::new()
    .jaw_period(21)
    .jaw_offset(13)
    .teeth_period(13)
    .teeth_offset(8)
    .lips_period(8)
    .lips_offset(5)
    .kernel(Kernel::Auto);

let slice_output = builder.apply_slice(&prices)?;
let candle_output = builder.apply(&candles)?;
let stream = builder.into_stream()?; // Builder implements Copy, reuse after calls
Streaming API
use vectorta::indicators::alligator::{AlligatorParams, AlligatorStream};

let mut stream = AlligatorStream::try_new(AlligatorParams::default())?;
while let Some(price) = next_price() {
    if let Some((jaw, teeth, lips)) = stream.update(price) {
        emit_signals(jaw, teeth, lips);
    }
}
Batch Operations
use vectorta::indicators::alligator::{AlligatorBatchBuilder, AlligatorParams};

let batch = AlligatorBatchBuilder::new()
    .jaw_period_range(10, 30, 5)
    .teeth_period_range(6, 18, 2)
    .lips_period_range(4, 12, 2)
    .apply_slice(&prices)?;

println!("rows={}, cols={}", batch.rows, batch.cols);
println!("combos tested={}", batch.combos.len());

if let Some((jaw, teeth, lips)) = batch.values_for(&AlligatorParams {
    jaw_period: Some(15),
    teeth_period: Some(9),
    lips_period: Some(6),
    jaw_offset: Some(8),
    teeth_offset: Some(5),
    lips_offset: Some(3),
}) {
    score_combo(jaw, teeth, lips);
}
Error Handling
use vectorta::indicators::alligator::AlligatorError;

match alligator(&input) {
    Ok(output) => consume(output),
    Err(AlligatorError::AllValuesNaN) => log::warn!("All inputs were NaN"),
    Err(AlligatorError::InvalidJawPeriod { period, data_len }) => handle_bad_period("jaw", period, data_len),
    Err(AlligatorError::InvalidJawOffset { offset, data_len }) => handle_bad_offset("jaw", offset, data_len),
    Err(AlligatorError::InvalidTeethPeriod { period, data_len }) => handle_bad_period("teeth", period, data_len),
    Err(AlligatorError::InvalidTeethOffset { offset, data_len }) => handle_bad_offset("teeth", offset, data_len),
    Err(AlligatorError::InvalidLipsPeriod { period, data_len }) => handle_bad_period("lips", period, data_len),
    Err(AlligatorError::InvalidLipsOffset { offset, data_len }) => handle_bad_offset("lips", offset, data_len),
    Err(AlligatorError::InvalidKernel { kernel }) => panic!("Unsupported kernel: {kernel:?}"),
}

Python Bindings

Basic Usage

Calculate and access the Jaw, Teeth, and Lips arrays directly as NumPy vectors:

import numpy as np
from vectorta import alligator

prices = np.asarray([100, 101.5, 99.8, 102.2, 104.0, 103.4], dtype=np.float64)

result = alligator(prices)
jaw = result["jaw"]
teeth = result["teeth"]
lips = result["lips"]

# Override periods, offsets, and kernel when needed
result = alligator(
    prices,
    jaw_period=21,
    jaw_offset=13,
    teeth_period=13,
    teeth_offset=8,
    lips_period=8,
    lips_offset=5,
    kernel="avx2",  # "auto", "scalar", and "avx2" are accepted
)
Streaming Updates

Maintain live state with the helper exposed through PyO3:

from vectorta import AlligatorStream

stream = AlligatorStream()  # defaults to 13/8/5 periods and 8/5/3 offsets

for price in live_prices():
    triple = stream.update(float(price))
    if triple is not None:
        jaw, teeth, lips = triple
        handle_realtime(jaw, teeth, lips)
Batch Sweeps

Run parameter grids efficiently and inspect the resulting combinations:

from vectorta import alligator_batch

sweep = alligator_batch(
    prices,
    jaw_period_range=(10, 26, 4),
    jaw_offset_range=(4, 16, 4),
    teeth_period_range=(8, 20, 4),
    teeth_offset_range=(3, 11, 2),
    lips_period_range=(5, 13, 2),
    lips_offset_range=(2, 8, 2),
    kernel="auto",
)

jaw_grid = sweep["jaw"]    # rows x len(prices)
teeth_grid = sweep["teeth"]
lips_grid = sweep["lips"]

# Metadata vectors align with each row
jaw_periods = sweep["jaw_periods"]
jaw_offsets = sweep["jaw_offsets"]
Return Structure
# alligator(...)
{
    "jaw": np.ndarray,   # shape (len(prices),)
    "teeth": np.ndarray,
    "lips": np.ndarray,
}

# alligator_batch(...)
{
    "jaw": np.ndarray,    # shape (rows, len(prices))
    "teeth": np.ndarray,
    "lips": np.ndarray,
    "jaw_periods": np.ndarray,
    "jaw_offsets": np.ndarray,
    "teeth_periods": np.ndarray,
    "teeth_offsets": np.ndarray,
    "lips_periods": np.ndarray,
    "lips_offsets": np.ndarray,
}

JavaScript / WASM Bindings

Quick Usage

Invoke the Alligator kernel directly from your browser or Node runtime:

import { alligator_js } from 'vectorta-wasm';

const prices = new Float64Array([100, 101.5, 99.8, 102.2, 104.0, 103.4]);
const flattened = alligator_js(prices, 13, 8, 8, 5, 5, 3);

// Results are flattened: [jaw..., teeth..., lips...]
const length = prices.length;
const jaw = flattened.slice(0, length);
const teeth = flattened.slice(length, length * 2);
const lips = flattened.slice(length * 2);
Memory-Efficient Operations

Allocate output buffers once and fill them via zero-copy transfers:

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

const prices = new Float64Array(loadPrices());
const len = prices.length;

const jawPtr = alligator_alloc(len);
const teethPtr = alligator_alloc(len);
const lipsPtr = alligator_alloc(len);

const inputView = new Float64Array(memory.buffer, 0, len);
inputView.set(prices);

alligator_into(
  inputView.byteOffset,
  jawPtr,
  teethPtr,
  lipsPtr,
  len,
  13, 8,
  8, 5,
  5, 3
);

// Copy results out when needed
const jaw = new Float64Array(memory.buffer, jawPtr, len);
const teeth = new Float64Array(memory.buffer, teethPtr, len);
const lips = new Float64Array(memory.buffer, lipsPtr, len);

alligator_free(jawPtr, len);
alligator_free(teethPtr, len);
alligator_free(lipsPtr, len);
Batch & Metadata

Enumerate parameter grids and keep outputs aligned with metadata descriptors:

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

const prices = new Float64Array(loadBacktest());
const metadata = alligator_batch_metadata_js(
  10, 30, 5,
  5, 15, 5,
  8, 20, 4,
  3, 11, 2,
  5, 13, 2,
  2, 8, 2
);
const combos = metadata.length / 6;

const flattened = alligator_batch_js(
  prices,
  10, 30, 5,
  5, 15, 5,
  8, 20, 4,
  3, 11, 2,
  5, 13, 2,
  2, 8, 2
);

// Each block is len(prices) and retains jaw/teeth/lips order
const block = prices.length;
const firstJaw = flattened.slice(0, block);
const firstTeeth = flattened.slice(block, block * 2);
const firstLips = flattened.slice(block * 2, block * 3);

// Alternatively consume structured JSON
const unified = alligator_batch_unified_js(prices, {
  jaw_period_range: [10, 30, 5],
  jaw_offset_range: [5, 15, 5],
  teeth_period_range: [8, 20, 4],
  teeth_offset_range: [3, 11, 2],
  lips_period_range: [5, 13, 2],
  lips_offset_range: [2, 8, 2],
});
console.log(unified.jaw.length, unified.combos.length);

Performance Analysis

Related Indicators