Usage and configuration

Everything tunable lives in config/config.toml. Nothing operational is hardcoded, and the parser enforces exactly what the comments promise — types, enumerated choices, bounds — failing with a message that names the offending key, so a session cannot start from a configuration it could not honor.

Credentials

Keys come from .env or the process environment, never from the configuration file. Copy the template and fill it in:

cp .env.example .env      # then set ALPACA_API_KEY_ID and ALPACA_SECRET_KEY
chmod 600 .env

Paper-account keys work for market data; point [alpaca] trading_base at the paper endpoint, as the shipped configuration does. load_credentials! warns if the file is group- or world-readable.

Entry points

Every script activates and instantiates the environment itself, so a fresh clone needs no preparation, and each accepts an alternative config path as its first argument.

julia --threads=auto scripts/stream.jl   # live capture
julia scripts/backfill.jl                # historical download over the configured window
julia scripts/compact.jl  data/raw/<session>_part001.jsonl
julia scripts/replay.jl   data/raw/<session>_part001.jsonl
julia scripts/visualize.jl data/raw/<session>_part001.jsonl
julia scripts/monitor.jl                 # read-only dashboard for a running session

From the REPL the same operations are run_stream, run_backfill, compact_raw and replay_source.

[provider]

KeyMeaning
nameProvider adapter. One of: "alpaca".
feedLive feed. One of "iex" (real-time, single venue, 30 symbols on the free tier), "delayed_sip" (consolidated tape, 15 minutes late, free), "sip" (real-time consolidated, paid).

On the free tier delayed_sip is the scientifically stronger choice: it is the complete consolidated tape, and a fixed 15-minute offset is irrelevant to any analysis that is not trading on it.

[stream]

KeyMeaning
symbolsSubscription list; at most limits.max_symbols entries.
channelsSubset of "trades", "quotes", "bars". Trades only by default: quote and bar frames are parsed into Quote and Bar and delivered to the on_quote / on_bar callbacks of live_source, but neither is persisted. A quote stream carries an order of magnitude more messages than the trade stream, so storing it is a separate decision.
require_market_openQuery the market clock before connecting and exit if closed.
wait_for_openWhen closed, sleep until the next open instead of exiting. The wait is shifted by the feed's intrinsic delay.
stop_at_market_closeSchedule a graceful stop at the session's next close, likewise shifted.
reconnect_max_retriesReconnection attempts per disconnect before giving up. The counter resets after any connection that delivered data.
reconnect_base_delay_sBackoff base; the delay is base * 2^attempt, jittered.
reconnect_max_delay_sBackoff cap.
stale_timeout_sWatchdog interval: a connection that delivers no frame for this long is severed and retried.

The watchdog exists because HTTP.jl 1.x WebSockets have no read idle timeout, so a silently dead TCP connection would otherwise block the read loop indefinitely.

[storage]

KeyMeaning
data_dirRoot of the data tree, relative to the project root unless absolute.
raw_subdirAppend-only NDJSON session files.
processed_subdirCompacted per-symbol per-day files.
flush_interval_sSink flush interval.
flush_max_ticksSink flush batch-size threshold; whichever trigger comes first.
processed_format"csv" or "arrow". CSV for interoperability and inspection; Arrow when size or read time demands it.

[limits]

KeyMeaning
max_session_hoursHard stop for a live session.
max_raw_file_mbRoll to a new raw part file beyond this size.
channel_capacityIn-flight tick buffer; the producer blocks when full.
max_symbolsSubscription cap.
min_free_disk_gbRefuse to start, and stop an active session, below this free space.
max_live_heap_mbLive-heap ceiling for backfill and compaction; the streaming paths check it and spill rather than exhaust memory.

These are the safety margins. They are the sole source of truth for the cutoffs — no threshold is hardcoded elsewhere in the pipeline.

[replay]

KeyMeaning
pace"recorded" honors the original inter-arrival times; "max" emits as fast as the consumer takes them.
speedTime-compression factor when pacing is honored; 60.0 replays an hour in a minute.

[backfill]

KeyMeaning
start_date, end_dateInclusive exchange dates (America/New_York). Either an ISO date "YYYY-MM-DD" or a sentinel: "today", or "today-<N>d" for N calendar days back.
feedHistorical feed, "iex" or "sip". Free accounts have full SIP history back to 2016, minus the trailing 15 minutes.
page_limitRows per REST page; the provider maximum is 10000.
rate_limit_sleep_sPause between pages. The free tier allows 200 requests per minute.
resumeSkip (symbol, day) pairs already present under processed/, so an aborted download resumes instead of restarting.

The sentinels keep a committed configuration from going stale. They count calendar days, not trading days: a window may land on a weekend or a holiday and return nothing, which the session report will show as an empty capture rather than an error.

[quality]

non_price_conditions lists, per tape, the sale-condition codes that disqualify a print from a price path. Tapes "A" and "B" are CTA-processed, "C" is UTP-processed, "O" is the OTC tape; the same character means different things across them, which is why the lists are separate. The shipped defaults are the provider's own published lists.

This is used only by price_forming, filter_price_forming and the n_price_forming column of session_report. Capture is never filtered — see Replay & Analysis Interfaces for why the distinction belongs to the analysis rather than to the recording.

A print carrying several conditions is disqualified by any one of them, and a print on a tape with no list is kept. The set lands in the session sidecar with the rest of the configuration, so a result stays attributable to the eligibility rule that produced it.

To decode the codes themselves, fetch the provider's own glossary with condition_map rather than assuming a mapping.

[monitor]

Attach-mode terminal dashboard, read-only and opt-in.

KeyMeaning
refresh_sRefresh interval.
top_symbolsRows in the per-symbol bar plot.
rate_window_sRate-history window; at least refresh_s.

[logging]

KeyMeaning
level"debug", "info", "warn" or "error".
log_to_fileTee the log to a per-session file under log_dir.
log_dirLog directory, relative to the project root.

File logs are always flushed and are sanitized of terminal control sequences, so a killed session still leaves a readable tail.

[alpaca]

Endpoint roots, overridable so the suite can point the whole pipeline at an in-process mock server.

KeyDefault
trading_basehttps://paper-api.alpaca.markets (paper keys) or https://api.alpaca.markets (live keys).
data_basehttps://data.alpaca.markets
ws_basewss://stream.data.alpaca.markets/v2