Read multi-timeframe context from a strategy¶
Multi-timeframe strategies need the question "what was the most recent closed 4h bar at the moment this 5m bar fired" answered cheaply and correctly. flox now keeps a per-(symbol, timeframe) ring of closed bars on every Strategy and exposes two accessors so user code does not need to repeat the bookkeeping.
from flox_py import Strategy
H4 = 4 * 60 * 60 * 1_000_000_000 # nanoseconds
M5 = 5 * 60 * 1_000_000_000
TIME_BARS = 0 # BarType.Time
class TrendFollower(Strategy):
def on_bar(self, ctx, bar):
if bar.bar_type_param != M5:
return
h4 = self.last_closed_bar(ctx.symbol_id, TIME_BARS, H4)
if h4 is None:
return # warmup
if h4.close > h4.open: # 4h trend up
self.maybe_enter(ctx, bar)
API¶
last_closed_bar(symbol_id, bar_type, param) -> dict | Nonereturns the most recent bar for the given timeframe, orNoneif no bar of that timeframe has been emitted yet for the symbol. The dict hasopen / high / low / close / volume / start_ns / end_ns.last_n_closed_bars(symbol_id, bar_type, param, n) -> list[dict]returns the most recent up tonclosed bars in chronological order (oldest first). Empty until warmup completes.bar_ring_capacity()andset_bar_ring_capacity(n)control the per-(symbol, tf) ring depth. Default is 64; raise it for strategies that look further back.
The ring fills automatically as the engine dispatches bars. There is no explicit register call.
bar_type values¶
| Value | Meaning | param interpretation |
|---|---|---|
| 0 | Time bar | Interval in nanoseconds. |
| 1 | Tick bar | Tick count. |
| 2 | Volume bar | Volume threshold (Volume::fromDouble(...) raw). |
| 3 | Renko bar | Brick size in price-raw. |
| 4 | Range bar | Range in price-raw. |
| 5 | Heikin-Ashi bar | Same as the underlying time interval. |
| 6 | Bps-range bar | Range in basis points × 10. |
Cross-binding¶
The same surface is reachable from every binding the polyglot policy
covers. The shape of the returned bar mirrors FloxBar: open, high,
low, close, volume, plus startNs / endNs (or start_ns /
end_ns in Python).
The Node binding exposes it on the emitter passed into onTrade /
onBar:
function onBar(ctx, bar, emit) {
const h4 = emit.lastClosedBar(ctx.symbolId, 0, 4 * 3600 * 1_000_000_000);
if (!h4) return;
// ... use h4.close, h4.open, ...
}
emit.setBarRingCapacity(n) adjusts ring depth.
The QuickJS strategy facade exposes the same methods on this:
class TrendFollower extends Strategy {
onBar(ctx, bar) {
const M5 = 5 * 60 * 1000000000;
const H4 = 4 * 3600 * 1000000000;
if (bar.barTypeParam !== M5) return;
const h4 = this.lastClosedBar("BTCUSDT", 0, H4);
if (!h4) return;
if (h4.close > h4.open) {
this.marketBuy({ symbol: "BTCUSDT", qty: 0.01 });
}
}
get warmupReady() { return this.barRingCapacity > 0; }
}
The Codon strategy mirrors the Python signature:
from flox.strategy import Strategy
from flox.context import SymbolContext
from flox.types import BarData
H4 = 4 * 3600 * 1_000_000_000
M5 = 5 * 60 * 1_000_000_000
class TrendFollower(Strategy):
def on_bar(self, ctx: SymbolContext, bar: BarData):
if bar.bar_type_param != M5:
return
h4 = self.last_closed_bar(ctx.symbol_id, 0, H4)
if h4 is None:
return
if h4.close > h4.open:
self.market_buy(0.01)
See also¶
- Multi-symbol indicators. Compose per-symbol indicators across the timeframe ring.
- Bar aggregation. How bars get produced.