◆ Flux

FAQ

Most questions about a total, causal language turn out to be already answered by the model: the guarantee is in the design, but it is not visible at first glance. This page makes those answers explicit and, for each one, points at the section that is normative — so a claim you rely on always has a home you can check it against.

The second half is the mirror image: the things Flux deliberately does not do, said plainly, with what it offers in their place.

New here? Start with Guide §1 — What is Flux? →

“How does Flux handle…?”

Multiple timeframes

@ is a causal resample: ema(close, 20) @ tf("1h") reads the last closed hourly unit. clock is a first-class kind — composable, storable, passable through an input — and @ is its eliminator. One clock per series in v1, which is what keeps the dangerous mixtures out; and the confluence idiom (close > ema(close, 50) @ "1d") is the goal, not an error to forbid.

→ Time and state

Intra-bar values

live(e) reads the step still forming — the forming bar, in charting — per frame, and it flows only to display sinks. Feeding it into a decision — an alert, an assertion, a calculation — is [ErrFirewall]. That wall is no-repaint: you may look at a provisional value, and you may not act on it as if it were final.

→ The four planes

Types

rsi really does have kind osc(0,100). price and osc really are incompatible at compile time. Bounds really do propagate (rsi − 50 → osc(-50,50)). And there is no solver — the lattice is finite by family, so the laws are checked by enumeration rather than proved by machinery.

→ Kinds

Bit-for-bit determinism

Yes — the same program on the same data produces the same numbers across the interpreter, the compiled module, and the server, on ARM and on x86. Scalar f64, no SIMD in the deterministic domain, no floating-point reassociation, a pinned reduction order, and one pinned routine for every operation where two implementations could disagree (transcendentals, decimals, Unicode, the calendar, randomness, sorting with absent values, and the bit pattern of na itself). This is determinism of compute and of the scene the program describes; it is not a claim that two GPUs paint identical pixels.

→ Compiler and runtime

Persistent state, reducers, state machines

scan(seed, (prev) -> …) advances state one step per bar. Composite state is a record; a state machine is a variant plus a match (exhaustive, or it does not compile); bounded iteration is loop(max, …).

→ Time and state

External data (funding, open interest, macro)

It enters the APP plane through typed subscriptions — never analysis directly, because the firewall forbids an application from writing an indicator. To become an indicator, a stream must be folded into closed bars and handed over through the data:source seam, which the host ingests append-only: a closed bar is never revised, so no-repaint survives.

The limit is named rather than hidden: an indicator driven by a non-price series is not expressible until the metric kind is armed — a seam the design carries, and holds inert.

→ net · compute

Effects and “worlds”

There is no effect annotation, because there are planes. ANALYSIS / CANVAS / TRANSITION / APP, with a one-way firewall checked statically. And you never declare which plane you are on — it is inferred from what you write.

→ The four planes & the firewall

Tooling: why is this signal true here?

You get global common-subexpression elimination, a dataflow view with a causal cone, an optimization report, a performance budget, reversible time along the bar axis — step, scrub and jump, in both directions, because replay is exact — and assert with goldens. The debugger’s question is not “what is the value” but “why”, and the graph can answer it.

→ Working in the editor

Building a UI — an island

Flux renders an island: a <div> the real engine paints inside an ordinary static HTML shell — a chart, a dashboard, a data-visualization, a 3-D scene, a small application. You describe a scene — a pure function of the Model, a tree of vetted primitives — and mount it through a view; the host paints it on WebGPU in 2-D and 3-D alike — graphics and text together, the text as crisp SDF glyphs — and the parts that move the most cost zero JavaScript per frame. Visual richness arrives as typed style props — a token, a colour, a signal, an enum — never a raw CSS string, so the one injection surface is structurally absent and a stranger’s island is safe to render beside yours.

The chart surface is real today; the full typed-style vocabulary that closes the gap with hand-written CSS is designed and lands incrementally.

→ display

“Why does Flux not…?”

…tag the timeframe in the type (series<H1, price>)?

The safety that would buy — a visible timeframe, an explicit conversion — is already provided by @ plus the one-clock-per-series rule. And the tag would reopen the lattice with a new axis, and break the confluence idiom: close > ema(close,50) @ "1d" is the thing people actually want, not a mistake to prevent. Time is first-class here through the clock kind and the @ operator — not through a type parameter.

…support tick-by-tick and order-flow scripting?

Bar-centric in v1, and said out loud: the whole live infrastructure is bar-streaming, and live() operates on the forming bar, not on ticks. Sub-bar granularity is a named extension whose rollout follows v1 — a scope boundary drawn on purpose, with a mechanism already behind it, not an oversight.

…prove relational invariants (high ≥ low)?

That needs a solver, and Flux explicitly has none — and high ≥ low is only the charting face of a general request (a sensor’s floor ≤ ceiling, a log’s monotone clock). Kind bounds are presentation claims, not runtime invariants (only clamp makes a bound real). Refusing the solver is what keeps the type system decidable, fast, and honest about what it checks — and the solver it refuses is precisely the trap that the people asking for invariants are usually trying to avoid.

…add sign refinements (price ≥ 0)?

A new axis with a weak payoff, contradicting “bounds are claims” — and wrong on the data anyway: a signed volume is wanted (that is how a cumulative volume indicator works), and a signed volatility level is meaningful — as is a signed sensor delta, or a net event count in a log. Not retained.

…ship an automatic converter from another scripting ecosystem?

Because a faithful one is impossible. Flux deliberately excludes what those ecosystems permit — retroactive history edits, unbounded loops, ambient I/O — so a whole class of programs has no faithful translation, and a converter that silently produced something would be worse than none. The documentation teaches the equivalent patterns directly, by hand, which is the honest version of the same help.

…offer a market-wide screener or portfolio management?

Cross-series work over a handful of named instruments is first-class. Scanning the entire market, and managing many simultaneous positions, are not v1 concerns.

A bounded screener — a total function mapped over a fixed-capacity universe, with a keyed top-K — is a sealed pillar of its own, rolling out after v1: a bounded map-reduce, never an unbounded scan. The shape is domain-neutral — a market screener, a log cohort, a sensor fleet — and markets are just where it is asked for first.

The questions people ask second

Is it fast?

The speed comes from algorithms and native kernels, not from the execution language. A bare rsi(close, 14) has nothing to optimize — it is the native kernel. What the design buys you is that the complicated case does not cost what it looks like: shared sub-expressions are computed once, dead code is never computed, element-wise chains fuse into one allocation-free pass, and the parts of a scene that move the most cost zero JavaScript per frame — the win is the datapath, not the engine. Aggregate work is compile-time budgeted and counted; when it exceeds the budget you get [ErrSceneBudget], so pressure degrades in a way you can see and predict, rather than being promised a frame rate.

What happens when I make a mistake?

You get a sentence, not a stack trace: what you did, what the kinds were, what it would have meant, and a quick-fix. A refused repaint is explained — “this would make a past value change once the bar closes” — because the refusal is the feature.

Can I trust a script someone else wrote?

That is the question the whole design answers, and it is the same answer whether the author is a stranger, a vendor, or a code-generating model. The script runs in the same sandbox as ours, with only the capabilities its manifest declared and you granted; that manifest travels with the binary, is inspectable before you install it, and aggregates transitively so a dependency cannot hide its appetite. And whatever it draws, it cannot touch the numbers your decision rests on.

What is genuinely hard about Flux?

Two things, honestly. Totality is a real constraint — you cannot write an unbounded loop, and some algorithms have to be re-expressed with a declared cap. It is the deliberate synchronous- dataflow trade: every program is total, unbounded over time but bounded per step, which in a reactive dataflow language costs nothing you would have wanted (an unbounded inner loop there is a hung tab, i.e. a bug). And the asynchronous chain in the APP plane is verbose — every step is a message, because every step must be in the journal for replay to work. Both are prices, and both are paid on purpose.

See also