Angolin Docs

Methodology

How GET /v1/aggregated_ohlcv combines per-exchange candles into one cross-exchange series, method omm_vwap_v1.

Overview

For a given interval/base/quote, every contributing exchange's raw candles are joined on their candle close timestamp. Any exchange that reported a candle for a given close_ts is "present" for that row; an exchange with no trade in that window is simply absent, not zero. The present exchanges' values are then combined field by field, as below, into a single aggregated candle.

One step comes first: when at least one present exchange traded on a candle (base_volume > 0), every present exchange with zero or unknown volume is treated as absent for that candle. Its candle is flat (open == high == low == close), a carried-over price that can sit well outside what the trading exchanges actually saw. If no present exchange traded, nobody is dropped. The exclusion happens before outlier detection and before every rule below.

Combine rules

base_volumeSum across contributing exchanges. An exchange that didn't trade counts as 0, never null.
highMaximum across present exchanges.
lowMinimum across present exchanges.
open, closeVolume-weighted average across present exchanges, weighted by each exchange's base_volume. Falls back to a simple mean if summed volume is 0 or unknown. The result is then clamped into [low, high] (see below).
quote_volumeSum across exchanges that report it. null only if no present exchange reports quote_volume for that candle.

OHLC consistency

A volume-weighted average can never leave the range spanned by the exchanges it combines, so mathematically open and close always fall between low and high. In floating-point arithmetic they can miss that range by a rounding error (on the order of 1e-16 relative) — for example two exchanges that both report open == high can average to a value marginally above high.

To guarantee low ≤ open ≤ high and low ≤ close ≤ high on every candle, open and close are clamped into [low, high] after the weighted average. The clamp applies the same way whatever the number of exchanges present, and it only ever acts on that rounding noise: high and low are never widened, and an average already inside the range is left untouched.

Outlier detection

When more than one exchange is present on a candle, each present exchange is checked against the cross-exchange median for open, high, low, and close. An exchange is flagged an outlier at that row if any of the four fields deviates from the median by more than the method's outlier threshold.

  • If outlier exchanges are at most 30% of the exchanges present for that row, they're excluded from the combination for that row only — the candle is flagged outlier_excluded.
  • Otherwise — outliers are more than 30% of what's present, not a safe minority to overrule — nobody is dropped, every present exchange still contributes, and the candle is flagged outlier.
  • An exchange is never removed globally, only row by row, and only in the excluded case.

A candle with neither condition has flag: null.

The exchanges field

Every aggregated candle carries an exchanges array — the exchanges that actually contributed to that specific candle, after any outlier exclusion. It can differ from row to row for the same base/quote/interval series as exchanges come in and out of coverage.

Choosing a method

The aggregation methodology is versioned by name and passed as the method query parameter on aggregated_ohlcv. omm_vwap_v1 is the only method today and the default if you omit the parameter.