Cross-symbol indicators¶
Compute an indicator that consumes two synchronised symbol
streams — Correlation(BTC, ETH), lag-sweep AutoCorrelation
across pairs, hedge ratios, regime filters keyed off a benchmark.
Different page:
multi-symbol-indicators.mdcovers running the same single-input indicator (SMA, EMA, RSI) independently per symbol. This page covers two-input indicators where the output depends on values from both symbols at the same instant.
Alignment is your job¶
The engine does not synchronise across symbols for you. Two separate trade / bar streams arrive at different timestamps and different cadences (BTCUSDT trades at one tick rate, ETHUSDT at another). Before feeding a pair-input indicator you must:
- Pick a clock — usually the slower symbol's bar boundaries.
- Forward-fill missing observations on the faster symbol so each clock tick has a value for both.
- Drop warmup — discard the first samples until both series have a value.
The flox.FeedClock helper exists exactly for step (1) on the
streaming path; see multi-feed-clock.md.
Batch recipe (Python)¶
For research / backtest preprocessing, numpy's structured arrays are the simplest path:
import flox_py as flox
import numpy as np
# Two pre-aligned bar arrays — same length, same timestamps.
btc_close = btc_bars["close"]
eth_close = eth_bars["close"]
assert btc_close.shape == eth_close.shape
# Streaming Correlation with a 30-bar window.
corr = flox.Correlation(period=30)
out = np.empty(len(btc_close))
for i, (b, e) in enumerate(zip(btc_close, eth_close)):
out[i] = corr.update(b, e) or float("nan")
# Final value for the trailing 30-bar window:
print(corr.value)
If your bar arrays are not pre-aligned, do it with a timestamp join first:
import pandas as pd
df = pd.merge_asof(
btc_bars.sort_values("ts"),
eth_bars.sort_values("ts"),
on="ts", suffixes=("_btc", "_eth"),
direction="backward",
)
df = df.dropna(subset=["close_btc", "close_eth"])
Streaming recipe (Python)¶
In a live Strategy, subscribe to both symbols, buffer the last
seen value per symbol, and tick the indicator only when both have
a fresh value:
import flox_py as flox
class CorrelatedPair(flox.Strategy):
def __init__(self, symbols):
super().__init__(symbols) # [btc_id, eth_id]
self._btc_id, self._eth_id = symbols
self._last_btc: float | None = None
self._last_eth: float | None = None
self._corr = flox.Correlation(period=30)
def on_trade(self, ctx, trade):
if ctx.symbol_id == self._btc_id:
self._last_btc = trade.price
elif ctx.symbol_id == self._eth_id:
self._last_eth = trade.price
if self._last_btc is None or self._last_eth is None:
return
# Tick the indicator at most once per trade event. The
# contract: the most-recent observation on each symbol
# represents the value at this instant.
c = self._corr.update(self._last_btc, self._last_eth)
if c is not None and abs(c) < 0.2:
# Pair has decorrelated — example signal.
self.market_buy(self._btc_id, qty=0.01)
This pattern is intentionally manual. The alternative — emitting a "synced bar" event from the engine — couples the runtime to a pairing decision (which symbol leads? what timeout?) that varies per strategy. Keeping it caller-side keeps the engine honest.
Node.js¶
const flox = require('flox');
const corr = new flox.Correlation(30);
for (let i = 0; i < btcClose.length; ++i) {
const v = corr.update(btcClose[i], ethClose[i]);
// v is null until the window fills.
}
The streaming pattern in a Runner strategy mirrors the Python
example above: cache the last value per symbol in onTrade,
tick the indicator when both sides have data.
Codon¶
from flox.indicators import Correlation
corr = Correlation(period=30)
for b, e in zip(btc_close, eth_close):
corr.update(b, e)
What about AutoCorrelation?¶
AutoCorrelation(window, lag) is single-input — it correlates a
series with a lagged version of itself. To use it across
symbols (cross-correlation at a lag), still align both streams as
above, then subtract / pass the differential through
AutoCorrelation. A first-class cross-correlation indicator is
not in the surface today; track on the indicator wishlist.
See also¶
multi-symbol-indicators.md— the same single-input indicator across many symbols.multi-feed-clock.md— the alignment primitive for streaming pair-trade decisions.../explanation/indicators.md#correlation— whatCorrelationcomputes and how the streaming variant manages its window.