Skip to main content

Crate chainview

Crate chainview 

Source
Expand description

§ChainView

A ratatui terminal UI for options traders: real-time option chains, Greeks, and volatility surfaces (Live mode) and IronCondor backtest result-bundle rendering (Replay mode). The market-data clients and all the options math live upstream; this crate is the terminal around them: provider adapters, normalization, and the render loop.

chainview ships as both a binary and a library. The binary is the stock terminal (cargo install chainview); the library exposes the semver-governed provider port, so any developer can plug their own market-data venue or broker into ChainView with no fork (ADR-0006).

§The provider port: the external-integration surface

The port an external adapter compiles against is the Provider trait, the ProviderCapabilities self-declaration (built through its builder) with its dimension enums (ChainCapability / GreeksCapability / OptionStreamCapability / ChainPollCapability / AuthKind), and every normalized domain type the trait emits: ChainFetch (with ExpirySource / AliasCatalog), OptionChain / ExpirationDate (optionstratlib), UnderlyingRef, QuoteUpdate, GreeksRow, DepthLadder, MarketUpdate, Instrument / InstrumentKey / ContractSpecFingerprint, SubscriptionRequest, SubscriptionHandle, MarketUpdateSink, ProviderError, and ProviderId. Every one is re-exported from this crate root — including the scalar field types the emitted values carry (Positive, Decimal, OptionStyle, ExpirationDate, and the DateTime<Utc> timestamps) — so an external adapter names each port type through chainview:: (docs/03-data-providers.md §11.1). Two companion dependencies remain the adapter’s own: async_trait (the trait is #[async_trait], so implementing it needs the macro) and optionstratlib when the adapter builds an OptionChain itself.

An external developer writes a thin binary that depends on chainview and registers their adapter through the app builder:

use async_trait::async_trait;
use chainview::{
    ChainFetch, ChainViewApp, ChainViewError, ExpirationDate, MarketUpdateSink,
    Provider, ProviderCapabilities, ProviderError, ProviderId, SubscriptionHandle,
    SubscriptionRequest, UnderlyingRef,
};

struct MyBroker {
    id: ProviderId,
}

#[async_trait]
impl Provider for MyBroker {
    fn id(&self) -> ProviderId {
        self.id.clone()
    }

    fn capabilities(&self) -> ProviderCapabilities {
        // Declare EXACTLY what the upstream backs: the UI gates screens off
        // this, never off the id. Every dimension defaults to its least-capable
        // value, so adding a future optional dimension is a source-compatible
        // minor bump.
        ProviderCapabilities::builder().build()
    }

    async fn discover(&self) -> Result<Vec<UnderlyingRef>, ProviderError> {
        Ok(vec![UnderlyingRef::new("BTC")])
    }

    async fn fetch_chain(
        &self,
        _underlying: &str,
        _expiration: &ExpirationDate,
    ) -> Result<ChainFetch, ProviderError> {
        // A chain-producing adapter assembles a normalized `ChainFetch` here;
        // an overlay-only feed returns `Unsupported`.
        Err(ProviderError::Unsupported("overlay-only: no chain discovery"))
    }

    async fn subscribe(
        &self,
        _req: SubscriptionRequest,
        _sink: MarketUpdateSink,
    ) -> Result<SubscriptionHandle, ProviderError> {
        // Drive an adapter-owned reconnect loop that pushes normalized
        // `MarketUpdate`s into `_sink`; return a handle that cancels it.
        Ok(SubscriptionHandle::new(|| { /* cancel the upstream stream */ }))
    }
}

fn main() -> Result<(), ChainViewError> {
    let broker = MyBroker { id: ProviderId::new("mybroker")? };
    ChainViewApp::builder()
        .with_builtins()   // the gate-clear bundled venues (Deribit)
        .register(broker)  // your own venue; the id is read from `provider.id()`
        .run() // a reserved/duplicate id is a typed startup error, never a panic
}

§What is semver-governed

The port is a public, semver-governed surface (docs/SEMVER.md): a change to the Provider trait signature or any port type is a major bump; adding a new optional capability dimension is minor. That minor is source-compatible only because ProviderCapabilities and its enums are #[non_exhaustive] and an adapter builds them through ProviderCapabilities::builder, never a struct literal. An external adapter pins a chainview major and compiles against a stable port for that major’s lifetime.

§Reserved ids and configuration namespacing

The six built-in ids in RESERVED_PROVIDER_IDS (deribit/tastytrade/dxlink/ig/alpaca/ibkr) are reserved: an external registration that reuses one is RegistryError::ReservedId, and a duplicate id is RegistryError::DuplicateId — both typed startup errors, never a panic. Growing the reserved set later is a major bump (it can invalidate a working external id) and is announced one minor ahead. Every provider — built-in or external — reads its non-secret settings from providers.<id>.* and its credentials from CHAINVIEW_<ID>_* (the id transliterated to a shell-safe segment through a total bijection, docs/07-configuration.md §5.1); the reserved-id rule guarantees an external provider can never shadow a built-in’s namespace.

§Security boundary and scope

An externally registered provider is outside ChainView’s credential audit boundary — its author owns its credential hygiene (ADR-0006 §7, docs/SECURITY.md §5). What ChainView still guarantees by construction is that its own code never logs what crosses the port: the port carries only normalized domain types (no credentials), and ProviderError is structurally redaction-safe. Dynamic/plugin loading (dlopen) is out of scope for v1 — an adapter is a compile-time Rust dependency, not a loaded object (Rust has no stable ABI).

§Status

Pre-1.0 and in active development: the public API — including the provider port — may change until v1.0.0, after which the SemVer rules above are binding. Follow progress at https://github.com/joaquinbejar/ChainView.

Re-exports§

pub use config::CliOverrides;
pub use config::Config;
pub use config::ModeSelect;
pub use config::ProviderSettings;
pub use config::ThemeChoice;

Modules§

config
Typed configuration surface for ChainView.

Structs§

AliasCatalog
The per-leg alias index the ChainFetch carries so normalization does not discard the identifiers needed to subscribe, resubscribe, and join later updates (docs/01-domain-model.md §6, docs/03-data-providers.md §4).
App
All state the render loop reads, as a Live | Replay Mode state machine (docs/02-tui-architecture.md §3).
AxisBounds
The [min, max] range of one axis, in the plot’s f64 coordinate space.
Binding
One row of the keybinding map: its context, its semantic action, the chords that trigger it, the label the overlay/keybar show, and its help text (docs/05-views-and-ux.md §3).
BridgeSenders
The producer-side halves of the bridge’s three bounded channels, handed back by EventBridge::new (docs/02-tui-architecture.md §5).
BuilderLeg
One leg of the multi-leg payoff builder (docs/05-views-and-ux.md §3): a contract at a strike/style, a side (buy/sell), and an integer qty (contracts). Appended from the chain’s focused leg (a) and edited in place by the cursor keys. A qty of 0 is an invalid state validation rejects, so it is a plain u32, not a NonZero — the zero-qty check exists precisely to catch it.
BundleDivergence
The first point at which two bundles diverge under the equivalence oracle — a typed report (not a bool) carrying the table, column, and row context of the divergence, with a bounded detail.
BundleManifest
manifest.json — run provenance plus the config / strategy / data-source / metrics blobs and the per-table row_counts integrity hint. Every field is required at ironcondor.bundle.v1 (docs/04-replay-mode.md §2.1).
BundleReader
A read-only reader over a result-bundle directory (docs/04-replay-mode.md §3).
CapitalConfig
The one narrow typed projection over the manifest config blob: the config.initial_capital field, read as unsigned integer cents (docs/04-replay-mode.md §5).
ChainFetch
The NAMED normalized fetch artifact Provider::fetch_chain returns (docs/01-domain-model.md §6) — not a bare OptionChain.
ChainRow
One strike row of the chain matrix — the call and put legs plus the shared, option-style-independent K/S relation that shades the strike column (docs/01-domain-model.md §8).
ChainSnapshot
The streaming-current chain snapshot (docs/01-domain-model.md §6), landed here so MarketUpdate::Chain can be a closed variant.
ChainStore
The normalized, streaming-current chain for one (provider, underlying, expiry) (docs/01-domain-model.md §6).
ChainViewApp
The assembled ChainView application — the entry point every binary starts from (docs/02-tui-architecture.md §11, ADR-0006 §3).
ChainViewAppBuilder
The builder that registers providers and validates the registry at startup (docs/02-tui-architecture.md §11, ADR-0006 §3).
CommittedStrategy
A validated, committed multi-leg strategy (docs/05-views-and-ux.md §3, §4). Built by PayoffBuilder::validate from a strategy that passed every check, then enriched on commit with the payoff geometry — the expiration and t+0 curves, the shared price grid, and the break-even points — all sampled from optionstratlib off the draw path, so the payoff screen (#27) draws the cached series without re-validating or re-pricing. #[non_exhaustive] so those cached fields stay a source-compatible addition; no Eq because the cached GraphData carries display f64 line widths.
ContractSpecFingerprint
The economic-equivalence fingerprint that gates a cross-provider overlay merge (docs/01-domain-model.md §4).
DateTime
ISO 8601 combined date and time with time zone.
Decimal
Decimal represents a 128 bit representation of a fixed-precision decimal number. The finite set of values of type Decimal are of the form m / 10e, where m is an integer such that -296 < m < 296, and e is an integer between 0 and 28 inclusive.
DepthBook
One instrument’s latest depth book: the newest DepthLadder plus its DepthStatus (docs/01-domain-model.md §5).
DepthLadder
A normalized order-book depth snapshot for one Instrument (docs/01-domain-model.md §5) — a DOMAIN type (a provider emits it via MarketUpdate::Depth), never a UI type.
DepthLevel
One price/size level in a DepthLadder (docs/01-domain-model.md §5).
DepthStore
The bounded per-instrument depth store (docs/01-domain-model.md §5, docs/03-data-providers.md §8).
EquityPoint
equity_curve.parquet — one row per step. Sort key step (docs/04-replay-mode.md §2.2).
EventBridge
The two-class fan-in that drains the bounded provider channels and folds the coalesced result into App::on_event (docs/02-tui-architecture.md §5).
ExitReporter
A handle a task’s join-watcher uses to report the task’s terminal outcome to the supervisor (docs/02-tui-architecture.md §12).
ExpirySource
The chain’s absolute expiry and source identity (docs/01-domain-model.md §6).
Fill
fills.parquet — one row per executed fill. A realistic order walking N price levels emits N rows; a naive order emits 1. UNIQUE and sort key (step, order_id, fill_seq) (docs/04-replay-mode.md §2.2).
GraphCache
The cache a screen holds on its state: the domain-built GraphData and its cached GraphProjection, projected off the draw path.
GreekColumns
Which optional Greek columns are visible for a given number of extra column slots that fit (docs/05-views-and-ux.md §8). Δ is always visible.
GreeksAttribution
greeks_attribution.parquet — one row per step; sort key step. The terms sum exactly (integer cents) to the step’s mark-to-market P&L (step_pnl, the equity delta — not a realised-only figure) (docs/04-replay-mode.md §2.2, §2.3).
GreeksRow
A Greeks/IV refresh for one Instrument (docs/01-domain-model.md §5).
GreeksSidecar
Per-instrument analytics not representable on OptionData, keyed by the canonical style-bearing InstrumentKey (docs/01-domain-model.md §7).
GuardTeardown
The production FinalTeardown: owns the TerminalGuard and restores the terminal by dropping it — the strictly-last shutdown step (docs/02-tui-architecture.md §12).
Instrument
An InstrumentKey plus the feed that owns this view of it — its native symbol(s) and contract-spec fingerprint (docs/01-domain-model.md §4).
InstrumentKey
ChainView’s provider-agnostic option identity: the (underlying, expiry, strike, style) tuple every streaming update matches against and every merge / latest-value map keys on.
LegGreeks
The per-leg analytics OptionData cannot hold, resolved for one style-bearing InstrumentKey (docs/01-domain-model.md §7).
LegView
One option leg (a call or a put) at one strike, projected from an OptionData and the store’s style-keyed analytics sidecar at draw time (docs/01-domain-model.md §7, §8).
LiveState
Live-mode state: the chain source binding, an optional overlay binding, the active screen, the live ChainStore, and the per-screen support state (docs/02-tui-architecture.md §3).
LoadedBundle
A fully materialised bundle — the manifest plus the four decoded tables, each in file order as the writer appended it (docs/04-replay-mode.md §3). Produced by BundleReader::load.
LoadedReplay
The loaded replay payload — the fully materialised bundle, its timeline cursor over the integer step clock, the currently drilled-into fill, and the cached equity geometry the screen renders (docs/02-tui-architecture.md §3, docs/04-replay-mode.md §4, docs/05-views-and-ux.md §5).
MarketUpdateSink
The two-class sender an adapter’s subscribe sends every MarketUpdate into ([ADR-0009], docs/02-tui-architecture.md §5).
OptionChain
Represents an option chain for a specific underlying asset and expiration date.
OverlayBinding
An optional quote/Greek overlay provider that streams contract updates but has no chain of its own (standalone DXLink over another provider’s chain, docs/02-tui-architecture.md §3). Its health is tracked per side, independent of the source’s — the composite degrades one side at a time.
PayoffBuilder
The multi-leg payoff-builder state machine (docs/05-views-and-ux.md §3): an ordered BuilderLeg list with a cursor, the current CurveMode, the last validation errors, and the committed strategy. It lives in the application layer so the payoff screen (src/ui/payoff.rs) drives it through these methods and reads it through the accessors — the UI never reaches into the fields directly (rules/global_rules.md, inner fields private).
PositionRow
positions.parquet — one row per leg for every step it is open, plus one terminal row at the step the leg closes (carrying exit_reason). At most one row per (position_id, step). Sort key (step, position_id) (docs/04-replay-mode.md §2.2).
Positive
A wrapper type that represents a guaranteed positive decimal value.
PricingInputs
Every input to the local pricing pass, each with its source, unit, and as-of (docs/01-domain-model.md §7).
ProjectedSeries
A GraphData::Series projected into the ratatui chart shape: the borrowed point series, the x/y axis bounds, precomputed axis labels, and the series name.
ProjectedSurface
A GraphData::GraphSurface projected into a character-grid heat map — the v0.5 (#47) single-expiry Greek/Price-over-(strike, volatility) surface.
ProviderCapabilities
The honest, static capability self-declaration a Provider returns (docs/03-data-providers.md §2). Streaming is three independent dimensions — option_stream, underlying_stream, and chain_poll — so a real-time underlying is never mistaken for a real-time option chain.
ProviderCapabilitiesBuilder
The builder for ProviderCapabilities — the only cross-crate construction path (docs/03-data-providers.md §2). Every field is settable; any left unset keeps its safe, least-capable default.
ProviderId
An open, validated market-data provider identity — the registry key, the config namespace segment, and the log label for an adapter (docs/01-domain-model.md §4, ADR-0006).
ProviderSubscription
A live provider subscription registered under the Supervisor — the caller keeps this so a per-provider Unsubscribe/Rediscover can cancel only this provider’s subtree without tripping the root (ADR-0009).
QuoteClocks
The per-leg stream-quote receipt clocks the local IV inversion consults so it never inverts a stale quote (docs/01-domain-model.md §7, §5.1).
QuoteUpdate
A quote refresh for one Instrument (docs/01-domain-model.md §5).
ReplayPayoffHead
The replay payoff-at-head header figures the panel renders (#49): the break-even underlying prices, the current net mark-to-market P&L in integer cents (None when the head is flat), and the count of open legs at the head.
ReplayState
Replay-mode state: the bundle directory, its load state machine, the active screen, and the playback state (docs/02-tui-architecture.md §3). The internals are filled by the v0.3 replay work; the shapes here are stable.
ResourceCeilings
The configurable resource ceilings the reader enforces on an untrusted bundle (docs/04-replay-mode.md §3). Defaults are the documented MAX_* constants; tests tighten them to exercise each ceiling on a tiny fixture.
RootLayout
The three regions of the root layout (docs/05-views-and-ux.md §8): a one-line status bar on top, the screen body in the middle, and a one-line hint/keybar on the bottom. The help overlay floats over the body.
Selection
The focused underlying/expiry/strike selection (docs/02-tui-architecture.md §3). The typed cursor the chain matrix (#18) drives: the focused strike row and the focused leg on it. Expiry/underlying navigation emits a Command rather than living here.
SourceBinding
The provider that supplies the chain structure (and possibly its own quotes/Greeks) plus the capabilities the UI gates on and this side’s health (docs/02-tui-architecture.md §3).
StatusLine
The status-bar model: provider health, clock, and mode (docs/02-tui-architecture.md §3). A documented stub whose fields are populated by the status line (#13/#14); #[non_exhaustive] so those fields are a source-compatible addition.
SubscriptionHandle
A handle to a live streaming subscription (docs/03-data-providers.md §2, §5).
SubscriptionRequest
The request to open a streaming subscription for one chain (docs/03-data-providers.md §2). Scoped to one (underlying, expiry); the instruments are the legs to (re)subscribe, taken from the ChainFetch::aliases the poll leg returned so the adapter never re-derives symbols (§4).
Supervisor
The single task supervisor: owns every task’s handle and the root cancellation token, and runs the ordered, terminal-restored-last teardown (docs/02-tui-architecture.md §12).
SurfacePanel
The Surface screen’s state and its cached active geometry (#47, docs/05-views-and-ux.md §4): which view is shown, the active Greek axis, and the one GraphData the screen currently renders — the smile, a Greek curve, or the surface. Owned by LiveState and driven only by the in-crate UI (surface::handle_key) and the market fold, never by an external lib consumer, so the mutators are pub(crate).
TerminalGuard
An RAII guard for the terminal: on construction it enables raw mode, enters the alternate screen, and hides the cursor; on Drop it runs the exact inverse.
Theme
The resolved theme the draw path reads: the color variant plus the NO_COLOR flag (docs/05-views-and-ux.md §7).
TimelineCursor
The scrub position over a validated LoadedBundle: the current integer step plus one per-table index for “as of position” slicing (docs/01-domain-model.md §10).
TokioTask
The production SupervisedTask: a tokio JoinHandle.
TransportDetail
Opaque, redaction-safe transport detail.
UnderlyingRef
One underlying a provider offers, with its expirations where the provider surfaces them cheaply (docs/03-data-providers.md §2). Provider::discover returns a list of these.
Utc
The UTC time zone. This is the most efficient time zone when you don’t need the local time. It is also used as an offset (which is also a dummy type).
ViewState
The render-loop-owned cache of every screen’s projected geometry, threaded alongside App through the render loop and synced off the draw path.

Enums§

Action
The semantic action a binding performs, grouped by scope so each scope’s resolution stays exhaustive and wildcard-free (docs/05-views-and-ux.md §3).
AppEvent
Every input to the synchronous render loop, as one closed set (§4).
AuthKind
The authentication a provider requires (docs/03-data-providers.md §2, §8).
BundleError
A failure reading or validating an IronCondor result bundle (replay mode).
BundleLoad
The bundle load state machine (docs/02-tui-architecture.md §3): loading, loaded, or a retryable error, so startup / failure / retry / success are all representable. R re-issues a Command::ReloadBundle.
BundleLoadResult
The outcome of an off-thread replay-bundle load, delivered to the render loop as an AppEvent::BundleLoaded by the replay load worker (docs/04-replay-mode.md §3, docs/02-tui-architecture.md §12).
ChainAction
A chain-screen action (docs/05-views-and-ux.md §3); bodies land in #18.
ChainCapability
How a provider produces a chain (docs/03-data-providers.md §2, §8).
ChainPollCapability
How the chain structure is kept current — orthogonal to the streams (docs/03-data-providers.md §2, §8). Alpaca, for instance, always polls the option chain even though its WebSocket streams the underlying.
ChainSource
How a ChainSnapshot is being kept current (docs/01-domain-model.md §6).
ChainViewError
The single boundary error every ChainView layer converts into.
Command
A render-loop -> data-layer command (§4).
ConfigError
A configuration failure surfaced at startup.
Context
The context a binding is active in (docs/05-views-and-ux.md §3). Global keys work everywhere; Any documents keys (like Esc) that mean the same in every screen; the mode-scoped variants are screen-local and additive.
CurveMode
Which payoff curve the screen draws (docs/05-views-and-ux.md §4): the expiration payoff or the t+0 (mark-based) curve, toggled by t. The state lives here because the toggle is a view preference on the builder; the curve itself is rendered by the payoff screen (#27). A closed set, fieldless, so #[repr(u8)] per the ruleset; defaults to Expiration.
DepthAction
A depth-screen action (docs/05-views-and-ux.md §3); body lands in v0.5.
DepthStatus
The change_id continuity status of a tracked DepthBook (docs/03-data-providers.md §8).
EmptyReason
Why a GraphData produced no renderable series — the reason attached to GraphProjection::Empty, so a screen can pick the right empty-state message and a diagnostic can distinguish the causes.
ExecMode
IronCondor’s dual fill model. ChainView surfaces it so the trader can tell which fill model produced the P&L, but renders both identically (docs/04-replay-mode.md §2.2).
ExerciseStyle
When an option contract may be exercised.
ExitCause
Why the supervised process exited — the value Supervisor::run returns for main to map to a process exit code and an optional post-restore stderr line (docs/02-tui-architecture.md §12).
ExpirationDate
Represents the expiration of a financial instrument.
Freshness
How current one component (quote, Greeks, or chain structure) is, derived from the two clocks of docs/01-domain-model.md §5.1.
GlobalAction
A global-level action declared in the map (docs/05-views-and-ux.md §3). The concrete screen-switch slot is derived from the pressed digit at resolution time (see resolve_global).
GlobalCommand
The resolved global command, with the screen-switch slot bound (docs/05-views-and-ux.md §3). App::dispatch_key_global matches this exhaustively.
GraphProjection
The outcome of projecting a GraphData: a renderable series, or an explicit, first-class empty state with its reason.
GreekColumn
A droppable analytic column of the chain matrix (docs/05-views-and-ux.md §8). Δ is always shown; the rest drop as width shrinks in GREEK_DROP_ORDER.
GreeksCapability
Whether a provider supplies Greeks/IV, computes them locally, or has none (docs/03-data-providers.md §2, §8).
GreeksOrigin
Where a GreeksRow’s analytics came from (docs/01-domain-model.md §5).
KeyChord
A normalized, comparable, displayable key chord (docs/05-views-and-ux.md §3). The keymap matches on these rather than raw crossterm events, so a binding is provider- and platform-independent.
LegError
A single validation failure of the payoff builder, projected inline in the builder panel (docs/05-views-and-ux.md §3, §6). A ChainView closed set matched exhaustively with no wildcard arm. Leg indices are 1-based in the message so the text matches the ladder the user sees (e.g. leg 2: no mark).
LegFocus
Which option leg (call or put) is focused on the selected strike row (docs/05-views-and-ux.md §3), toggled by c / p on the chain matrix (#18).
LegStatus
The outcome recorded per leg after a local fill, surfaced later as the computed-Greeks / stale badge (docs/01-domain-model.md §7).
LiveScreen
The active Live-mode screen (docs/02-tui-architecture.md §3, §7).
MarketUpdate
A normalized provider update — the payload of AppEvent::Market (docs/02-tui-architecture.md §4).
MergeOutcome
The outcome of folding one streaming update into the store — surfaced so the caller (the app fan-in, issue #9) can log/badge without re-deriving it.
Mode
The Live | Replay mode, each owning its own state and active screen (§3).
NormalizeKind
Why a payload would not map to the chain model — a closed set, so a rejected payload’s raw bytes never ride along in the error.
OptionStreamCapability
Contract-level streaming: does the provider stream option-contract quotes/Greeks that overlay onto a chain (docs/03-data-providers.md §2, §8)?
OptionStyle
Re-export of core financial types from the standalone financial_types crate.
OverlayError
Raised when a cross-provider overlay merge is refused because feed identity does not prove contract equivalence (docs/01-domain-model.md §4 economic-equivalence gate).
PayoffAction
A payoff-screen action (docs/05-views-and-ux.md §3); bodies land in v0.2.
Playback
The replay playback state (docs/04-replay-mode.md §4): paused, or playing at a PlaybackSpeed. Playback stops at end_step and never wraps — that clamp lives in TimelineCursor::seek, so this type models the quantum and the play/pause transition only, not the tick timer (the tick cadence is the app’s, issue #34).
PlaybackSpeed
A selectable playback speed — the multiplier applied to the one-step playback quantum (docs/04-replay-mode.md §4). A tick at ×N advances the scrub head by N steps.
PositionSide
Which direction a leg is held.
PremiumNumeraire
Which numeraire a contract’s premium is quoted in, so the IV inversion prices the premium in the SAME currency as the strike/spot (docs/01-domain-model.md §7, docs/03-data-providers.md §3, issue #83).
PricingModel
The exercise / pricing model the local engine applies (docs/01-domain-model.md §7).
ProviderError
The typed, redaction-safe failure an adapter raises.
QuoteSelect
Which premium the IV inversion prices against (docs/01-domain-model.md §7).
RegistryError
Raised while assembling the provider registry at startup (docs/02-tui-architecture.md §11, ADR-0006). A collision is a typed error, never a panic or a silent last-writer-wins. Every variant names only a public provider id — never a credential.
ReplayAction
A replay-screen action (docs/05-views-and-ux.md §3). The scrub, end-jump, play/pause, and speed actions have bodies now (via AppEvent::ReplaySeek / AppEvent::ReplayControl, #34); the fill drill-down (, / .) lands with the drill-down render (#35+).
ReplayControl
A replay playback control produced by a play/pause/speed key on the replay screen and folded into the Playback state (docs/04-replay-mode.md §4).
ReplayScreen
The active Replay-mode screen (docs/02-tui-architecture.md §3, §7).
Resolved
The resolved startup pieces a binary composes the runtime + render loop over (docs/02-tui-architecture.md §11). Returned by ChainViewAppBuilder::resolve; the TUI composition lives in the binary (main.rs) because the render loop is in the ui layer and the application layer must not import crate::ui (the arch fence).
ScreenLoad
A screen’s load lifecycle (docs/02-tui-architecture.md §3, docs/05-views-and-ux.md §6).
SeekTo
A replay-timeline seek, expressed against the one integer replay clock — the step (docs/04-replay-mode.md §4). The display timestamp ts_ns is never the seek unit.
SendState
Whether the bounded fan-in channel is still open. A closed channel means the consumer (the app) is gone, so the adapter’s reconnect loop shuts down rather than reconnecting.
SettlementStyle
How an option contract settles at expiry.
Side
Whether a payoff-builder leg is bought (long) or sold (short) (docs/05-views-and-ux.md §3). A ChainView UI closed set, fieldless, so #[repr(u8)] per the ruleset; it defaults to Buy — a freshly appended leg is long until s toggles it. The Selection carries no side, so PayoffBuilder’s append seeds a leg long and s flips it.
StreamHealth
Thin forward declaration of the stream connection health (docs/01-domain-model.md §6), landed here so MarketUpdate::Health and ChainSnapshot can name it. It is a pure data enum (no logic); the store (issue #7) drives the transitions between its variants and the per-component staleness that flips a component to Stale.
StrikeRelation
A row-level, option-style-independent relation of the strike to spot, defined once as the K/S bucket (docs/01-domain-model.md §8).
SurfaceAction
A surface-screen action (docs/05-views-and-ux.md §3); bodies wired in #47.
SurfaceAxis
The Greek / IV / Price axis the Surface screen’s curve and surface use (#47, docs/05-views-and-ux.md §4), cycled by g (forward) / G (back). Maps to optionstratlib’s BasicAxisTypes.
SurfaceView
Which of the three Surface-screen views is shown (#47, docs/05-views-and-ux.md §4), cycled by x.
TaskExit
How a supervised task ended, as observed at its join point.
ThemeVariant
The resolved color variant a Theme paints with (docs/05-views-and-ux.md §7). An optional user override is deferred past v1; v0.1 ships the built-in variants only, resolved from ThemeChoice.
TickDir
The direction of the most recent change to a price, for the bid-up/ask-down indicator (docs/01-domain-model.md §6, §8).
TransportKind
The category of a transport failure. A small, stable, closed set — safe to render because it is a fixed vocabulary, never venue-controlled text.

Constants§

ATM_BAND_PERMILLE
The default at-the-money band, as the |K/S − 1| tolerance that buckets a strike as StrikeRelation::AtSpot (docs/01-domain-model.md §8). 0.005 is 0.5%.
AT_SPOT_MARKER
The marker on the at-spot strike row (docs/05-views-and-ux.md §7) — the color-independent signal that survives a monochrome terminal.
CHAIN_STALE_SLACK
The fixed slack added on top of one refresh_interval before a chain’s structure is badged stale. docs/01-domain-model.md §5.1 defines CHAIN_STALE_AFTER as one refresh_interval + slack (a formula, since refresh_interval is runtime config); this constant fixes the slack term.
COMMAND_CHANNEL_CAPACITY
Capacity of the small bounded command channel (render -> data) that carries Command (docs/02-tui-architecture.md §5). Small because a command storm is impossible — commands are user-driven (a keypress, a scrub), never machine-generated at tick rate.
CONTRACT_ID_FORMAT
The versioned contract_id join-key format, fixed here as the single source of truth for the round-trip check the validation chain (#32) enforces.
CONTRACT_ID_UNDERLYING_PATTERN
The grammar the contract_id UNDERLYING segment must match: ^[A-Z0-9._]{1,32}$ — upper-case letters, digits, . and _, 1–32 chars, and deliberately colon-free so the join key splits unambiguously (docs/04-replay-mode.md §2.3). Held as a documented pattern string; the matcher is #32’s work.
CONTRACT_ID_VERSION_PREFIX
The current contract_id version prefix. A bumped prefix is a major-incompatible schema change (docs/04-replay-mode.md §5, SEMVER.md).
CONTROL_CHANNEL_CAPACITY
Capacity of the small bounded control channel that carries Chain / Health (docs/02-tui-architecture.md §5). Small because the control class is low-frequency and never coalesced; the fan-in drains it fully before the coalesced channel each wakeup, so it never backs up behind a quote burst.
DECODED_OVERHEAD_PERMILLE
Decoded-overhead multiplier in per-mille (1500 = 1.5×). Applied to each decoded RecordBatch’s Arrow array memory size to cover the transient decode workspace — the scratch buffers the Parquet→Arrow decode touches beyond the batch’s own arrays, freed when the batch drops. It does not estimate the retained owned rows the typed decode (#31) materialises: those are measured EXACTLY (rows * size_of::<RowType>() plus every owned String’s heap bytes) by each decoder and accounted separately, so a dictionary-encoded UTF8 column — counted once in the Arrow batch but copied per row into the owned Vec — cannot slip past the working-set ceiling behind this estimate. Held as an integer per-mille so the budget arithmetic stays exact (no f64 on the allocation path).
DEFAULT_DIVIDEND_YIELD
The zero-config default annualized dividend yield, as a decimal.
DEFAULT_JOIN_BUDGET
The 2 s bounded-join budget: a supervised task that has not observed cancellation and returned within this window is aborted so a wedged upstream socket can never hang the exit (docs/02-tui-architecture.md §12).
DEFAULT_RISK_FREE_RATE
The zero-config default annualized risk-free rate, as a decimal.
DIRECTION_DECAY
The price-direction indicator (TickDir) decays to Flat after this long with no further change — default 3 s (docs/01-domain-model.md §6).
EVENT_CHANNEL_CAPACITY
Capacity of the bounded AppEvent channel the render loop parks on (docs/02-tui-architecture.md §5). The channel carries only the low-frequency input/tick events (Key / Resize / Tick) — the high-frequency Market stream rides the coalescing EventBridge instead — so a small bound is ample and a transient tick drop under a busy render is harmless.
FEED_DELAY_WARN
A feed delay (now − event_time) beyond this badges the component delayed (distinct from stale): the feed is live but the venue data is lagging — default 2 s (docs/01-domain-model.md §5.1).
GREEKS_STALE_AFTER
Greeks older than this (measured from their received_time) are badged stale — default 10 s (docs/01-domain-model.md §5.1).
GREEK_DROP_ORDER
The order optional Greek columns are dropped as terminal width shrinks (docs/05-views-and-ux.md §8): Γ first, then ν, then Θ. Δ is never in this list — it is always shown.
MAX_BATCH_BYTES
Per-batch measured-size cap (256 MiB) — a single decoded batch larger than this is BundleError::TooLarge, independent of the running total. This is a post-materialization reject: the batch is decoded and measured before it is checked, so the reader’s true transient peak is ~one batch (bounded by MAX_BATCH_ROWS × the column widths), not the whole table — the documented residual #36’s adversarial fixtures probe.
MAX_BATCH_ROWS
Decode granularity — rows per RecordBatch (65,536), so the working set is measured and capped incrementally rather than after materialising a table.
MAX_DEPTH_BOOKS
The hard cap on tracked books, so the store can never grow without bound (docs/03-data-providers.md §8). Default 1024 — far above the subscribed leg count of one option chain (~80–200 contracts × call/put), so a real chain never hits it; a pathological venue that streamed books for unlisted symbols is bounded here.
MAX_EXPANSION_RATIO
Decompression-bomb reject ratio (20×) — a footer whose declared uncompressed size exceeds this multiple of the on-disk/compressed size is rejected before decode.
MAX_MANIFEST_BYTES
Manifest ceiling default — reject a manifest.json whose on-disk size exceeds this (8 MiB) on the pre-read stat, before it is slurped into a Vec<u8>. A valid manifest is tiny (run provenance + a few opaque JSON blobs), so a giant one is a manifest bomb — an OOM on attacker-controlled input the three table ceilings would not catch (they apply only to the four Parquet tables). docs/SECURITY.md §6.2 per-file ceiling; docs/04 §3.
MAX_PENDING
The maximum number of unknown-strike stream updates held pending a poll that introduces the strike (docs/03-data-providers.md §4). On overflow the oldest entry is dropped and counted (ChainStore::dropped_overflow), so the buffer can never grow without bound. Default 256 per chain.
MAX_TABLE_BYTES
Ceiling 1 default — reject a Parquet file whose on-disk size exceeds this (512 MiB), before opening it.
MAX_TABLE_ROWS
Ceiling 2 default — reject a table whose Parquet footer declares more rows than this (5,000,000), before decode.
MAX_WORKING_SET
Ceiling 3 default — the total measured working set across all four tables may not exceed this (2 GiB); the decode stops the moment it would.
MIN_HEIGHT
The minimum terminal height below which any screen shows the “widen the terminal” state instead of a body (docs/05-views-and-ux.md §8).
MIN_WIDTH
The minimum terminal width below which any screen shows the “widen the terminal” state instead of a body (docs/05-views-and-ux.md §8).
ORACLE_ABS_TOL
Absolute tolerance for the analytic-float (drawdown) comparison in the equivalence oracle. Paired with ORACLE_REL_TOL as a combined absolute / relative bound. Must match IronCondor’s copy exactly — the shared near-boundary fixtures produce identical pass/fail on both repositories (docs/04-replay-mode.md §5, docs/TESTING.md §6).
ORACLE_REL_TOL
Relative tolerance for the analytic-float (drawdown) comparison in the equivalence oracle. Paired with ORACLE_ABS_TOL. Must match IronCondor’s copy exactly (docs/04-replay-mode.md §5, docs/TESTING.md §6).
QUOTE_STALE_AFTER
Quotes older than this (measured from their received_time) are badged stale — default 5 s (docs/01-domain-model.md §5.1).
RESERVED_PROVIDER_IDS
The provider ids ChainView reserves for its bundled adapters. An external registration that reuses one is a typed startup error (the registry lands in issue #12); ProviderId::is_reserved reports membership. ibkr (issue #120) was reserved pre-1.0 so the growth is a minor, not a major (SEMVER.md reserved-id-growth rule).
SUPPORTED_SCHEMA
The single supported bundle schema tag — the compatibility gate (docs/04-replay-mode.md §5 step 1). A manifest.schema other than this is BundleError::UnsupportedSchema.

Statics§

KEYMAP
The single source of truth for every key ChainView acts on (docs/05-views-and-ux.md §3). Both the dispatch (resolve_global, resolve_replay) and the help overlay (src/ui/theme.rs, via help_sections) read this one table, so a bound key and its documentation cannot drift.

Traits§

FinalTeardown
The strictly-last shutdown step: restore the terminal (docs/02-tui-architecture.md §12, §6).
Provider
The seam every adapter implements: one trait, one adapter per provider id (docs/03-data-providers.md §2).
Redacted
A redaction-safe detail attached to a transport failure.
SupervisedTask
A task the supervisor can join within a budget and, as a last resort, abort.

Functions§

chain_stale_after
The effective chain-staleness threshold for a given refresh_interval: refresh_interval + CHAIN_STALE_SLACK, the CHAIN_STALE_AFTER of docs/01-domain-model.md §5.1.
compare_bundles
Compare two decoded bundles under the IronCondor equivalence oracle (docs/04-replay-mode.md §5).
compute_leg_greeks
Fill in the local Greeks and IV the chain model cannot hold, for every leg of chain, writing the result into sink (docs/01-domain-model.md §7).
depth_continues
Whether a book with change_id next continues the sequence after prev (docs/03-data-providers.md §8) — the domain’s coarse gap signal:
event_channel
Create the bounded AppEvent channel the render loop parks on and the input / tick tasks feed (docs/02-tui-architecture.md §4, §5). The composition (#12) clones the sender to each producer and hands the receiver to run_render_loop.
greek_columns_for_slots
Resolve which Greek columns are visible given extra_slots, the number of optional Greek columns that fit after the always-present price/IV/Δ columns (docs/05-views-and-ux.md §8).
health_span
The stream-health badge as a styled Span — glyph plus text, so the state is legible without color.
help_bindings
Every binding the help overlay shows for mode, flattened — the cross-check surface a test uses to prove every dispatched key is documented.
install_panic_hook
Install a panic hook that restores the terminal before chaining to the previously installed hook.
is_replay_screen_reachable
Whether a ReplayScreen is reachable in the current build (docs/05-views-and-ux.md §2.1). Both replay screens are reachable from v0.5: the equity/attribution/drill-down screen, and the payoff-at-head panel (#49), which renders the payoff of the open position at the scrub head. This is a build/version gate, not a capability or ProviderId gate (replay has no live provider) — the payoff panel degrades to its “flat at this step” empty state when no position is open at the head, so it is always navigable.
is_screen_reachable
Whether a LiveScreen is reachable for a declared ProviderCapabilities set (docs/02-tui-architecture.md §3, docs/03-data-providers.md §11.4).
is_too_small
Whether area is below the minimum renderable size (docs/05-views-and-ux.md §8). When true, render shows the cross-screen “widen the terminal” hint rather than a corrupt layout.
layout_root
Split area into the status bar, body, and hint line (docs/05-views-and-ux.md §8).
pending_ttl
The pending-buffer per-entry TTL for a given refresh_interval: refresh_interval + CHAIN_STALE_SLACK — one poll of headroom (docs/03-data-providers.md §4). An unknown-strike update older than this that never appeared in a snapshot is dropped, never resurrected.
pnl_sign_char
The color-independent sign for a signed value: − when negative, + otherwise (docs/05-views-and-ux.md §7).
pnl_sign_span
A P&L sign glyph as a styled Span. Under NO_COLOR the style carries no color but the +/− sign remains.
project
Project a domain-built GraphData into a ratatui chart GraphProjection.
render
Draw the whole frame from app and the ui view cache — the pure, total, wildcard-free draw dispatch (docs/02-tui-architecture.md §7).
resolve_chain
Resolve a chord against the chain screen’s bindings (docs/05-views-and-ux.md §3), or None when no binding matches (the dispatch then ignores the key, e.g. Esc, which is a Context::Any binding, not a chain one).
resolve_depth
Resolve a chord against the depth screen’s bindings (docs/05-views-and-ux.md §3), or None when no binding matches (the dispatch then ignores the key).
resolve_global
Resolve a chord against the global bindings into the GlobalCommand the dispatch executes (docs/05-views-and-ux.md §3), or None when no global binding matches (the dispatch then forwards the key to the active screen).
resolve_payoff
Resolve a chord against the payoff screen’s bindings (docs/05-views-and-ux.md §3), or None when no binding matches (the dispatch then ignores the key).
resolve_replay
Resolve a chord against a replay screen’s bindings (docs/05-views-and-ux.md §3), or None when no binding matches.
resolve_surface
Resolve a chord against the surface screen’s bindings (docs/05-views-and-ux.md §3), or None when no binding matches (the dispatch then ignores the key).
run_render_loop
Run the synchronous render loop until quit or channel close (docs/02-tui-architecture.md §7, §8).
spawn_bundle_load
Spawn the off-thread bundle load on a blocking worker and deliver its outcome to the render loop as an AppEvent::BundleLoaded (docs/04-replay-mode.md §3).
spawn_input_reader
Spawn the dedicated terminal input reader, emitting AppEvent::Key / AppEvent::Resize until cancelled (docs/02-tui-architecture.md §9, §12).
spawn_supervised_subscription
Spawn a provider’s streaming subscription and register it under the Supervisor as a watched task (ADR-0009, the composition seam).
spawn_tick_task
Spawn the fixed-interval tick task, emitting AppEvent::Tick every tick_interval until cancelled (docs/02-tui-architecture.md §8, §12).
strike_relation_marker
The color-independent marker for a strike relation: the AT_SPOT_MARKER for the at-spot row, empty otherwise (docs/05-views-and-ux.md §7). Below/above spot are conveyed by the marked at-spot row plus the numeric strike ordering, so they need no separate glyph.
strike_relation_marker_span
The at-spot marker as a styled Span — the marker text plus the strike-column style. Under NO_COLOR the style carries no color but the ◀ATM text remains.
tick_dir_glyph
The color-independent glyph for a price-direction indicator (docs/01-domain-model.md §8): ▲ up, ▼ down, · flat.
tick_dir_span
A price-direction glyph as a styled Span. Under NO_COLOR the style carries no color but the ▲/▼/· glyph remains.