Expand description
§of_persist
of_persist provides append-only JSONL persistence for normalized orderflow events, with optional retention pruning.
It is designed for replay, auditability, and post-trade research workflows.
§Main Types
RollingStore- append-only store forbookandtradesstreams.StoredBookEvent/StoredTradeEvent- typed readback records parsed from existing JSONL files.StoredEvent- merged replay-oriented enum for interleaved symbol reads.RetentionPolicy- bounded retention by total bytes and/or max file age.PersistError/PersistResult<T>- persistence error contract.
§New In 0.3.0
Newly-written JSONL records now include additive schema metadata:
"schema": 1ts_exchange_nsts_recv_ns
Legacy records without these fields remain readable. Runtime tests now cover persist -> readback -> replay parity for analytics, signals, and materialized book state.
§New In 0.2.0
Relative to the 0.1.x line, of_persist is no longer write-only. It now
includes:
- discovery APIs for venues, symbols, and streams
- typed readback APIs for books and trades
- merged event replay reads
- inclusive sequence-range filtering
That makes the crate useful for replay, incident analysis, and research instead of only append-only storage.
§Public API Inventory
Public types:
PersistErrorPersistResult<T>RetentionPolicyRollingStoreStoredBookEventStoredTradeEventStoredEvent
Public methods:
StoredEvent::sequenceRollingStore::newRollingStore::with_retentionRollingStore::append_bookRollingStore::append_tradeRollingStore::read_booksRollingStore::read_books_in_rangeRollingStore::read_tradesRollingStore::read_trades_in_rangeRollingStore::read_eventsRollingStore::read_events_in_rangeRollingStore::list_venuesRollingStore::list_symbolsRollingStore::list_streams
§Storage Layout
Events are written to:
<root>/<venue>/<symbol>/(book|trades).jsonl
This makes stream files easy to map into replay and analytics pipelines.
§Record Schema Reference
Persisted JSONL records are additive and versioned with "schema": 1.
Newly-written records include event timestamps (ts_exchange_ns,
ts_recv_ns) alongside sequence and payload fields. The typed readback API
continues to accept legacy records that do not contain schema or timestamp
fields.
StoredBookEvent contains:
side,level,price,size,actionsequence
StoredTradeEvent contains:
price,size,aggressor_sidesequence
StoredEvent is the merged replay enum:
StoredEvent::Book(StoredBookEvent)StoredEvent::Trade(StoredTradeEvent)
StoredEvent::sequence returns the merged event sequence regardless of variant.
§Readback API
RollingStore now supports additive typed readback over the same files it already writes:
list_venues()enumerates discovered venue directorieslist_symbols(venue)enumerates discovered symbols for one venuelist_streams(venue, symbol)enumerates discovered JSONL streams for one symbolread_books(venue, symbol)readsbook.jsonlintoStoredBookEventvaluesread_books_in_range(venue, symbol, from_sequence, to_sequence)applies inclusive sequence filtering to book readsread_trades(venue, symbol)readstrades.jsonlintoStoredTradeEventvaluesread_trades_in_range(venue, symbol, from_sequence, to_sequence)applies inclusive sequence filtering to trade readsread_events(venue, symbol)merges both streams intoStoredEventvalues ordered by sequenceread_events_in_range(venue, symbol, from_sequence, to_sequence)applies inclusive sequence filtering to merged reads- missing streams return an empty vector
- malformed lines return
PersistError::IowithInvalidData
§Ordering and Range Semantics
- append methods always write one JSON object per line
read_books*andread_trades*preserve file orderread_events*merges book and trade streams by ascending sequence*_in_rangemethods use inclusivefrom_sequence/to_sequenceboundsNonefor a bound means it is open-ended on that side- missing
book.jsonlortrades.jsonlfiles are treated as empty streams, not hard errors
§RollingStore Contract
RollingStore::newcreates the persistence root if needed.RollingStore::with_retentionreturns an updated store handle with retention settings attached.RollingStore::append_bookandRollingStore::append_tradewrite normalized events, not provider-native payloads.- Discovery APIs operate on directory/file presence and do not require a separate index.
- Readback APIs parse the same JSONL files the writer produces, so replay stays aligned with persisted runtime output.
§Quick Example
use of_core::{Side, SymbolId, TradePrint};
use of_persist::RollingStore;
let store = RollingStore::new("data").expect("store");
store.append_trade(&TradePrint {
symbol: SymbolId {
venue: "CME".to_string(),
symbol: "ESM6".to_string(),
},
price: 505_000,
size: 2,
aggressor_side: Side::Ask,
sequence: 1,
ts_exchange_ns: 1,
ts_recv_ns: 2,
}).expect("append");§Readback Example
use of_persist::RollingStore;
let store = RollingStore::new("data").expect("store");
let venues = store.list_venues().expect("list venues");
let symbols = store.list_symbols("CME").expect("list symbols");
let streams = store.list_streams("CME", "ESM6").expect("list streams");
let trades = store
.read_trades_in_range("CME", "ESM6", Some(10), Some(100))
.expect("read trades");
println!("venues={venues:?} symbols={symbols:?} streams={streams:?}");
for trade in trades {
println!("seq={} price={} size={}", trade.sequence, trade.price, trade.size);
}§Replay Read Example
use of_persist::{RollingStore, StoredEvent};
let store = RollingStore::new("data").expect("store");
let events = store.read_events("CME", "ESM6").expect("read events");
for event in events {
match event {
StoredEvent::Book(book) => println!("book seq={} px={}", book.sequence, book.price),
StoredEvent::Trade(trade) => println!("trade seq={} px={}", trade.sequence, trade.price),
}
}§Retention Example
use of_persist::{RetentionPolicy, RollingStore};
let store = RollingStore::new("data")?
.with_retention(Some(RetentionPolicy {
max_total_bytes: 2 * 1024 * 1024 * 1024,
max_age_secs: 7 * 24 * 60 * 60,
}));
let _ = store;§Retention Behavior
max_age_secs > 0: files older than threshold are pruned.max_total_bytes > 0: oldest files are pruned until under limit.0means that limit is disabled.
§Error Semantics
PersistError::Iowraps filesystem and parse failures.- directory creation happens eagerly on store creation, so path permission issues surface early.
- retention pruning is best-effort within normal append flows; it is not a separate daemon or background compactor.
§Real-World Use Cases
§1. Incident review after a bad fill or missed signal
Read back the exact normalized book/trade stream that the runtime saw and reconstruct the session around the problematic sequence range.
§2. Research dataset generation
Persist normalized data during live or simulated sessions, then read back only the venue/symbol windows needed for offline analysis.
§3. Deterministic replay
Use read_events(...) or read_events_in_range(...) to feed ordered events
back into test or replay tooling.
§Detailed Example: Investigate A Sequence Window
use of_persist::{RollingStore, StoredEvent};
fn main() {
let store = RollingStore::new("data").expect("store");
let events = store
.read_events_in_range("CME", "ESM6", Some(10_000), Some(10_150))
.expect("events");
for event in events {
match event {
StoredEvent::Book(book) => {
println!(
"BOOK seq={} level={} px={} size={}",
book.sequence, book.level, book.price, book.size
);
}
StoredEvent::Trade(trade) => {
println!(
"TRADE seq={} px={} size={}",
trade.sequence, trade.price, trade.size
);
}
}
}
}§Detailed Example: Discovery-First Replay Preparation
use of_persist::RollingStore;
fn main() {
let store = RollingStore::new("data").expect("store");
for venue in store.list_venues().expect("venues") {
println!("venue={venue}");
for symbol in store.list_symbols(&venue).expect("symbols") {
let streams = store.list_streams(&venue, &symbol).expect("streams");
println!(" symbol={symbol} streams={streams:?}");
}
}
}Structs§
- Retention
Policy - Retention policy used by
RollingStore. - Rolling
Store - JSONL rolling store for book/trade stream persistence.
- Stored
Book Event - Parsed book event read back from persisted JSONL storage.
- Stored
Trade Event - Parsed trade event read back from persisted JSONL storage.
Enums§
- Persist
Error - Persistence-layer errors.
- Stored
Event - Merged persisted event used for replay-oriented symbol reads.
Type Aliases§
- Persist
Result - Result type alias used by persistence APIs.