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§
- Alias
Catalog - The per-leg alias index the
ChainFetchcarries 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 | ReplayModestate machine (docs/02-tui-architecture.md§3). - Axis
Bounds - The
[min, max]range of one axis, in the plot’sf64coordinate 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). - Bridge
Senders - The producer-side halves of the bridge’s three bounded channels, handed back
by
EventBridge::new(docs/02-tui-architecture.md§5). - Builder
Leg - One leg of the multi-leg payoff builder (
docs/05-views-and-ux.md§3): a contract at astrike/style, aside(buy/sell), and an integerqty(contracts). Appended from the chain’s focused leg (a) and edited in place by the cursor keys. Aqtyof0is an invalid state validation rejects, so it is a plainu32, not aNonZero— the zero-qty check exists precisely to catch it. - Bundle
Divergence - 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. - Bundle
Manifest manifest.json— run provenance plus the config / strategy / data-source / metrics blobs and the per-tablerow_countsintegrity hint. Every field is required atironcondor.bundle.v1(docs/04-replay-mode.md§2.1).- Bundle
Reader - A read-only reader over a result-bundle directory
(
docs/04-replay-mode.md§3). - Capital
Config - The one narrow typed projection over the manifest
configblob: theconfig.initial_capitalfield, read as unsigned integer cents (docs/04-replay-mode.md§5). - Chain
Fetch - The NAMED normalized fetch artifact
Provider::fetch_chainreturns (docs/01-domain-model.md§6) — not a bareOptionChain. - Chain
Row - One strike row of the chain matrix — the call and put legs plus the shared,
option-style-independent
K/Srelation that shades the strike column (docs/01-domain-model.md§8). - Chain
Snapshot - The streaming-current chain snapshot (
docs/01-domain-model.md§6), landed here soMarketUpdate::Chaincan be a closed variant. - Chain
Store - The normalized, streaming-current chain for one
(provider, underlying, expiry)(docs/01-domain-model.md§6). - Chain
View App - The assembled ChainView application — the entry point every binary starts
from (
docs/02-tui-architecture.md§11, ADR-0006 §3). - Chain
View AppBuilder - The builder that registers providers and validates the registry at startup
(
docs/02-tui-architecture.md§11, ADR-0006 §3). - Committed
Strategy - A validated, committed multi-leg strategy (
docs/05-views-and-ux.md§3, §4). Built byPayoffBuilder::validatefrom 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 fromoptionstratliboff 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; noEqbecause the cachedGraphDatacarries displayf64line widths. - Contract
Spec Fingerprint - The economic-equivalence fingerprint that gates a cross-provider overlay
merge (
docs/01-domain-model.md§4). - Date
Time - ISO 8601 combined date and time with time zone.
- Decimal
Decimalrepresents a 128 bit representation of a fixed-precision decimal number. The finite set of values of typeDecimalare 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.- Depth
Book - One instrument’s latest depth book: the newest
DepthLadderplus itsDepthStatus(docs/01-domain-model.md§5). - Depth
Ladder - A normalized order-book depth snapshot for one
Instrument(docs/01-domain-model.md§5) — a DOMAIN type (a provider emits it viaMarketUpdate::Depth), never a UI type. - Depth
Level - One price/size level in a
DepthLadder(docs/01-domain-model.md§5). - Depth
Store - The bounded per-instrument depth store (
docs/01-domain-model.md§5,docs/03-data-providers.md§8). - Equity
Point equity_curve.parquet— one row per step. Sort keystep(docs/04-replay-mode.md§2.2).- Event
Bridge - 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). - Exit
Reporter - A handle a task’s join-watcher uses to report the task’s terminal outcome to
the supervisor (
docs/02-tui-architecture.md§12). - Expiry
Source - 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).- Graph
Cache - The cache a screen holds on its state: the domain-built
GraphDataand its cachedGraphProjection, projected off the draw path. - Greek
Columns - 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. - Greeks
Attribution greeks_attribution.parquet— one row per step; sort keystep. 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).- Greeks
Row - A Greeks/IV refresh for one
Instrument(docs/01-domain-model.md§5). - Greeks
Sidecar - Per-instrument analytics not representable on
OptionData, keyed by the canonical style-bearingInstrumentKey(docs/01-domain-model.md§7). - Guard
Teardown - The production
FinalTeardown: owns theTerminalGuardand restores the terminal by dropping it — the strictly-last shutdown step (docs/02-tui-architecture.md§12). - Instrument
- An
InstrumentKeyplus the feed that owns this view of it — its native symbol(s) and contract-spec fingerprint (docs/01-domain-model.md§4). - Instrument
Key - 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
OptionDatacannot hold, resolved for one style-bearingInstrumentKey(docs/01-domain-model.md§7). - LegView
- One option leg (a call or a put) at one strike, projected from an
OptionDataand the store’s style-keyed analytics sidecar at draw time (docs/01-domain-model.md§7, §8). - Live
State - 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). - Loaded
Bundle - 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 byBundleReader::load. - Loaded
Replay - The loaded replay payload — the fully materialised bundle, its timeline cursor
over the integer
stepclock, 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). - Market
Update Sink - The two-class sender an adapter’s
subscribesends everyMarketUpdateinto ([ADR-0009],docs/02-tui-architecture.md§5). - Option
Chain - Represents an option chain for a specific underlying asset and expiration date.
- Overlay
Binding - 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. - Payoff
Builder - The multi-leg payoff-builder state machine (
docs/05-views-and-ux.md§3): an orderedBuilderLeglist with a cursor, the currentCurveMode, 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). - Position
Row positions.parquet— one row per leg for every step it is open, plus one terminal row at the step the leg closes (carryingexit_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.
- Pricing
Inputs - Every input to the local pricing pass, each with its source, unit, and as-of
(
docs/01-domain-model.md§7). - Projected
Series - A
GraphData::Seriesprojected into the ratatui chart shape: the borrowed point series, the x/y axis bounds, precomputed axis labels, and the series name. - Projected
Surface - A
GraphData::GraphSurfaceprojected into a character-grid heat map — the v0.5 (#47) single-expiry Greek/Price-over-(strike, volatility) surface. - Provider
Capabilities - The honest, static capability self-declaration a
Providerreturns (docs/03-data-providers.md§2). Streaming is three independent dimensions —option_stream,underlying_stream, andchain_poll— so a real-time underlying is never mistaken for a real-time option chain. - Provider
Capabilities Builder - 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. - Provider
Id - 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). - Provider
Subscription - A live provider subscription registered under the
Supervisor— the caller keeps this so a per-providerUnsubscribe/Rediscovercan cancel only this provider’s subtree without tripping the root (ADR-0009). - Quote
Clocks - 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). - Quote
Update - A quote refresh for one
Instrument(docs/01-domain-model.md§5). - Replay
Payoff Head - 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 (
Nonewhen the head is flat), and the count of open legs at the head. - Replay
State - 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. - Resource
Ceilings - The configurable resource ceilings the reader enforces on an untrusted bundle
(
docs/04-replay-mode.md§3). Defaults are the documentedMAX_*constants; tests tighten them to exercise each ceiling on a tiny fixture. - Root
Layout - 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 aCommandrather than living here. - Source
Binding - 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). - Status
Line - 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. - Subscription
Handle - A handle to a live streaming subscription (
docs/03-data-providers.md§2, §5). - Subscription
Request - The request to open a streaming subscription for one chain
(
docs/03-data-providers.md§2). Scoped to one(underlying, expiry); theinstrumentsare the legs to (re)subscribe, taken from theChainFetch::aliasesthe 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). - Surface
Panel - 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 oneGraphDatathe screen currently renders — the smile, a Greek curve, or the surface. Owned byLiveStateand driven only by the in-crate UI (surface::handle_key) and the market fold, never by an external lib consumer, so the mutators arepub(crate). - Terminal
Guard - An RAII guard for the terminal: on construction it enables raw mode, enters
the alternate screen, and hides the cursor; on
Dropit runs the exact inverse. - Theme
- The resolved theme the draw path reads: the color variant plus the
NO_COLORflag (docs/05-views-and-ux.md§7). - Timeline
Cursor - The scrub position over a validated
LoadedBundle: the current integerstepplus one per-table index for “as ofposition” slicing (docs/01-domain-model.md§10). - Tokio
Task - The production
SupervisedTask: a tokioJoinHandle. - Transport
Detail - Opaque, redaction-safe transport detail.
- Underlying
Ref - One underlying a provider offers, with its expirations where the provider
surfaces them cheaply (
docs/03-data-providers.md§2).Provider::discoverreturns 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).
- View
State - The render-loop-owned cache of every screen’s projected geometry, threaded
alongside
Appthrough 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).
- Auth
Kind - The authentication a provider requires (
docs/03-data-providers.md§2, §8). - Bundle
Error - A failure reading or validating an IronCondor result bundle (replay mode).
- Bundle
Load - 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.Rre-issues aCommand::ReloadBundle. - Bundle
Load Result - The outcome of an off-thread replay-bundle load, delivered to the render loop
as an
AppEvent::BundleLoadedby the replay load worker (docs/04-replay-mode.md§3,docs/02-tui-architecture.md§12). - Chain
Action - A chain-screen action (
docs/05-views-and-ux.md§3); bodies land in #18. - Chain
Capability - How a provider produces a chain (
docs/03-data-providers.md§2, §8). - Chain
Poll Capability - 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. - Chain
Source - How a
ChainSnapshotis being kept current (docs/01-domain-model.md§6). - Chain
View Error - The single boundary error every ChainView layer converts into.
- Command
- A render-loop -> data-layer command (§4).
- Config
Error - A configuration failure surfaced at startup.
- Context
- The context a binding is active in (
docs/05-views-and-ux.md§3).Globalkeys work everywhere;Anydocuments keys (likeEsc) that mean the same in every screen; the mode-scoped variants are screen-local and additive. - Curve
Mode - Which payoff curve the screen draws (
docs/05-views-and-ux.md§4): the expiration payoff or the t+0 (mark-based) curve, toggled byt. 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 toExpiration. - Depth
Action - A depth-screen action (
docs/05-views-and-ux.md§3); body lands in v0.5. - Depth
Status - The
change_idcontinuity status of a trackedDepthBook(docs/03-data-providers.md§8). - Empty
Reason - Why a
GraphDataproduced no renderable series — the reason attached toGraphProjection::Empty, so a screen can pick the right empty-state message and a diagnostic can distinguish the causes. - Exec
Mode - 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). - Exercise
Style - When an option contract may be exercised.
- Exit
Cause - Why the supervised process exited — the value
Supervisor::runreturns formainto map to a process exit code and an optional post-restorestderrline (docs/02-tui-architecture.md§12). - Expiration
Date - 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. - Global
Action - 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 (seeresolve_global). - Global
Command - The resolved global command, with the screen-switch slot bound
(
docs/05-views-and-ux.md§3).App::dispatch_key_globalmatches this exhaustively. - Graph
Projection - The outcome of projecting a
GraphData: a renderable series, or an explicit, first-class empty state with its reason. - Greek
Column - A droppable analytic column of the chain matrix
(
docs/05-views-and-ux.md§8). Δ is always shown; the rest drop as width shrinks inGREEK_DROP_ORDER. - Greeks
Capability - Whether a provider supplies Greeks/IV, computes them locally, or has none
(
docs/03-data-providers.md§2, §8). - Greeks
Origin - 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 byc/pon 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). - Live
Screen - The active Live-mode screen (
docs/02-tui-architecture.md§3, §7). - Market
Update - A normalized provider update — the payload of
AppEvent::Market(docs/02-tui-architecture.md§4). - Merge
Outcome - 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 | Replaymode, each owning its own state and active screen (§3). - Normalize
Kind - 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.
- Option
Stream Capability - Contract-level streaming: does the provider stream option-contract
quotes/Greeks that overlay onto a chain (
docs/03-data-providers.md§2, §8)? - Option
Style - Re-export of core financial types from the standalone
financial_typescrate. - Overlay
Error - 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). - Payoff
Action - 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 aPlaybackSpeed. Playback stops atend_stepand never wraps — that clamp lives inTimelineCursor::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). - Playback
Speed - A selectable playback speed — the multiplier applied to the one-
stepplayback quantum (docs/04-replay-mode.md§4). A tick at×Nadvances the scrub head byNsteps. - Position
Side - Which direction a leg is held.
- Premium
Numeraire - 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). - Pricing
Model - The exercise / pricing model the local engine applies
(
docs/01-domain-model.md§7). - Provider
Error - The typed, redaction-safe failure an adapter raises.
- Quote
Select - Which premium the IV inversion prices against (
docs/01-domain-model.md§7). - Registry
Error - 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. - Replay
Action - A replay-screen action (
docs/05-views-and-ux.md§3). The scrub, end-jump, play/pause, and speed actions have bodies now (viaAppEvent::ReplaySeek/AppEvent::ReplayControl, #34); the fill drill-down (,/.) lands with the drill-down render (#35+). - Replay
Control - A replay playback control produced by a play/pause/speed key on the replay
screen and folded into the
Playbackstate (docs/04-replay-mode.md§4). - Replay
Screen - 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 byChainViewAppBuilder::resolve; the TUI composition lives in the binary (main.rs) because the render loop is in theuilayer and the application layer must not importcrate::ui(the arch fence). - Screen
Load - 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 timestampts_nsis never the seek unit. - Send
State - 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.
- Settlement
Style - 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 toBuy— a freshly appended leg is long untilstoggles it. TheSelectioncarries no side, soPayoffBuilder’s append seeds a leg long andsflips it. - Stream
Health - Thin forward declaration of the stream connection health
(
docs/01-domain-model.md§6), landed here soMarketUpdate::HealthandChainSnapshotcan 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 toStale. - Strike
Relation - A row-level, option-style-independent relation of the strike to spot,
defined once as the
K/Sbucket (docs/01-domain-model.md§8). - Surface
Action - A surface-screen action (
docs/05-views-and-ux.md§3); bodies wired in #47. - Surface
Axis - The Greek / IV / Price axis the Surface screen’s curve and surface use (#47,
docs/05-views-and-ux.md§4), cycled byg(forward) /G(back). Maps tooptionstratlib’sBasicAxisTypes. - Surface
View - Which of the three Surface-screen views is shown (#47,
docs/05-views-and-ux.md§4), cycled byx. - Task
Exit - How a supervised task ended, as observed at its join point.
- Theme
Variant - The resolved color variant a
Themepaints 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 fromThemeChoice. - 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). - Transport
Kind - 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 asStrikeRelation::AtSpot(docs/01-domain-model.md§8).0.005is 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_intervalbefore a chain’s structure is badgedstale.docs/01-domain-model.md§5.1 definesCHAIN_STALE_AFTERas onerefresh_interval+ slack (a formula, sincerefresh_intervalis 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_idjoin-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_idUNDERLYINGsegment 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_idversion 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 ownedString’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 ownedVec— cannot slip past the working-set ceiling behind this estimate. Held as an integer per-mille so the budget arithmetic stays exact (nof64on 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 toFlatafter this long with no further change — default 3 s (docs/01-domain-model.md§6). - EVENT_
CHANNEL_ CAPACITY - Capacity of the bounded
AppEventchannel 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-frequencyMarketstream rides the coalescingEventBridgeinstead — 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 componentdelayed(distinct fromstale): 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 badgedstale— 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 byMAX_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.jsonwhose on-disk size exceeds this (8 MiB) on the pre-readstat, before it is slurped into aVec<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 withORACLE_REL_TOLas 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 withORACLE_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 badgedstale— 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_reservedreports 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). Amanifest.schemaother than this isBundleError::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, viahelp_sections) read this one table, so a bound key and its documentation cannot drift.
Traits§
- Final
Teardown - 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.
- Supervised
Task - 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, theCHAIN_STALE_AFTERofdocs/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 intosink(docs/01-domain-model.md§7). - depth_
continues - Whether a book with
change_idnextcontinues the sequence afterprev(docs/03-data-providers.md§8) — the domain’s coarse gap signal: - event_
channel - Create the bounded
AppEventchannel 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 torun_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
ReplayScreenis 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 orProviderIdgate (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
LiveScreenis reachable for a declaredProviderCapabilitiesset (docs/02-tui-architecture.md§3,docs/03-data-providers.md§11.4). - is_
too_ small - Whether
areais below the minimum renderable size (docs/05-views-and-ux.md§8). When true,rendershows the cross-screen “widen the terminal” hint rather than a corrupt layout. - layout_
root - Split
areainto 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. UnderNO_COLORthe style carries no color but the+/−sign remains. - project
- Project a domain-built
GraphDatainto a ratatui chartGraphProjection. - render
- Draw the whole frame from
appand 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), orNonewhen no binding matches (the dispatch then ignores the key, e.g.Esc, which is aContext::Anybinding, not a chain one). - resolve_
depth - Resolve a chord against the depth screen’s bindings
(
docs/05-views-and-ux.md§3), orNonewhen no binding matches (the dispatch then ignores the key). - resolve_
global - Resolve a chord against the global bindings into the
GlobalCommandthe dispatch executes (docs/05-views-and-ux.md§3), orNonewhen 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), orNonewhen 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), orNonewhen no binding matches. - resolve_
surface - Resolve a chord against the surface screen’s bindings
(
docs/05-views-and-ux.md§3), orNonewhen 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::Resizeuntil cancelled (docs/02-tui-architecture.md§9, §12). - spawn_
supervised_ subscription - Spawn a provider’s streaming subscription and register it under the
Supervisoras a watched task (ADR-0009, the composition seam). - spawn_
tick_ task - Spawn the fixed-interval tick task, emitting
AppEvent::Tickeverytick_intervaluntil cancelled (docs/02-tui-architecture.md§8, §12). - strike_
relation_ marker - The color-independent marker for a strike relation: the
AT_SPOT_MARKERfor 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. UnderNO_COLORthe style carries no color but the◀ATMtext 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. UnderNO_COLORthe style carries no color but the▲/▼/·glyph remains.