Files
QuanTAlib/lib/volume/adl/Adl.md
T
Miha Kralj b5358091ae feat: Add Absolute Price Oscillator (APO) implementation and documentation
feat: Implement ADL (Accumulation/Distribution Line) indicator
2025-12-18 21:32:01 -08:00

2.6 KiB

ADL - Accumulation/Distribution Line

The Accumulation/Distribution Line (ADL) measures the cumulative flow of money into and out of a security. It validates price trends by correlating volume with price close location within the high-low range.

Architectural Design

We implement ADL as a stateful, streaming accumulator that maintains O(1) complexity for each new data point. Unlike window-based indicators, ADL carries its entire history in a single double-precision state variable.

The "Close Location Value" (CLV)

The core mechanic relies on the Money Flow Multiplier (MFM), also known as CLV. This value ranges from -1 to +1:

  • +1: Close equals High (Maximum Accumulation)
  • -1: Close equals Low (Maximum Distribution)
  • 0: Close is exactly between High and Low

This approach avoids the noise of simple price changes, focusing instead on where the price settles relative to its intraday range.

MFM = \frac{(Close - Low) - (High - Close)}{High - Low} MFV = MFM \times Volume ADL_{current} = ADL_{previous} + MFV

Zero-Allocation Implementation

Our implementation processes updates without heap allocations. The state consists of a single double _lastAdl.

  • Complexity: O(1) per update.
  • Memory: 16 bytes (state) + object overhead.
  • NaN Handling: If High == Low, MFM is 0 to avoid division by zero. If inputs are NaN, the last valid ADL value is preserved.

Usage

Streaming API

The streaming API is designed for real-time event processing. It updates the state with each new bar and returns the latest value immediately.

using QuanTAlib;

// Initialize
var adl = new Adl();

// Update loop
foreach (var bar in feed)
{
    var result = adl.Update(bar);
    Console.WriteLine($"ADL: {result.Value:F2}");
}

Batch Processing

For historical analysis, the static Calculate method processes full datasets using optimized loops.

var bars = GetHistory();
var adlSeries = Adl.Calculate(bars);

Performance Benchmarks

Processing 10,000 bars on an Intel Core i9-13900K:

Operation Time Allocations
Update (Single) 2.1 ns 0 bytes
Calculate (Batch) 15 μs 0 bytes (excluding output)

Validation

We validate correctness against three external authorities to 1e-9 precision:

Library Status Notes
Skender.Stock.Indicators Pass Reference implementation
TA-Lib Pass Matches AD function
Tulip Indicators Pass Matches ad indicator

See Validation for comprehensive test results.