Skip to main content

fin_primitives/
lib.rs

1//! # fin-primitives
2//!
3//! The basic parts of trading software, done once and checked: exact price and
4//! size types that refuse bad values, an order book, candles built from trades,
5//! indicators, option prices and risk limits.
6//!
7//! ![Three bundled examples running in a terminal: an order book ladder, an option chain with Greeks, and a position ledger tripping a drawdown limit](https://raw.githubusercontent.com/Mattbusel/fin-primitives/main/assets/demo.gif)
8//!
9//! Add it (the `dec!` macro for decimal literals needs both `rust_decimal` crates):
10//!
11//! ```text
12//! cargo add fin-primitives rust_decimal rust_decimal_macros
13//! ```
14//!
15//! The main types: [`Price`](types::Price), [`Quantity`](types::Quantity) and
16//! [`Symbol`](types::Symbol) (validated values), [`OrderBook`](orderbook::OrderBook),
17//! [`OhlcvAggregator`](ohlcv::OhlcvAggregator) (ticks to candles), the
18//! [`Signal`](signals::Signal) trait and its indicators,
19//! [`PositionLedger`](position::PositionLedger), [`RiskMonitor`](risk::RiskMonitor),
20//! [`BlackScholes`](greeks::BlackScholes), and one error type, [`FinError`].
21//!
22//! ## A first look
23//!
24//! Build a book from sequenced deltas, read the top of book, and price a
25//! market order by walking the levels:
26//!
27//! ```
28//! use fin_primitives::orderbook::{BookDelta, DeltaAction, OrderBook};
29//! use fin_primitives::types::{Price, Quantity, Side, Symbol};
30//! use rust_decimal_macros::dec;
31//!
32//! # fn main() -> Result<(), fin_primitives::FinError> {
33//! let mut book = OrderBook::new(Symbol::new("BTC-USD")?);
34//! let levels = [
35//!     (Side::Ask, dec!(64250.50), dec!(0.842)),
36//!     (Side::Ask, dec!(64251.00), dec!(1.310)),
37//!     (Side::Bid, dec!(64250.00), dec!(1.204)),
38//!     (Side::Bid, dec!(64249.50), dec!(0.655)),
39//! ];
40//! for (seq, (side, price, qty)) in (1..).zip(levels) {
41//!     book.apply_delta(BookDelta {
42//!         side,
43//!         price: Price::new(price)?,
44//!         quantity: Quantity::new(qty)?,
45//!         action: DeltaAction::Set,
46//!         sequence: seq,
47//!     })?;
48//! }
49//!
50//! assert_eq!(book.spread(), Some(dec!(0.50)));
51//! assert_eq!(book.mid_price(), Some(dec!(64250.25)));
52//!
53//! // Buying 1 BTC takes all 0.842 at the best ask and 0.158 at the next level.
54//! let vwap = book.vwap_for_qty(Side::Ask, Quantity::new(dec!(1))?)?;
55//! assert_eq!(vwap, dec!(64250.579));
56//!
57//! // A delta that would cross the book is rejected and rolled back.
58//! let crossing = BookDelta {
59//!     side: Side::Bid,
60//!     price: Price::new(dec!(64251.00))?,
61//!     quantity: Quantity::new(dec!(2))?,
62//!     action: DeltaAction::Set,
63//!     sequence: 5,
64//! };
65//! assert!(book.apply_delta(crossing).is_err());
66//! assert_eq!(book.sequence(), 4);
67//! # Ok(())
68//! # }
69//! ```
70//!
71//! Ticks become bars, and bars feed indicators. Indicators return
72//! [`SignalValue::Unavailable`](signals::SignalValue::Unavailable) until they
73//! have enough history, never a NaN or a zero:
74//!
75//! ```
76//! use fin_primitives::ohlcv::{OhlcvAggregator, Timeframe};
77//! use fin_primitives::signals::indicators::Sma;
78//! use fin_primitives::signals::{BarInput, Signal, SignalValue};
79//! use fin_primitives::tick::Tick;
80//! use fin_primitives::types::{NanoTimestamp, Price, Quantity, Side, Symbol};
81//! use rust_decimal_macros::dec;
82//!
83//! # fn main() -> Result<(), fin_primitives::FinError> {
84//! let sym = Symbol::new("ETH-USD")?;
85//! let mut agg = OhlcvAggregator::new(sym.clone(), Timeframe::Minutes(1))?;
86//! let mut sma = Sma::new("sma3", 3)?;
87//!
88//! let mut values = Vec::new();
89//! for (minute, close) in [(0, dec!(3180)), (1, dec!(3190)), (2, dec!(3200)), (3, dec!(3230))] {
90//!     let tick = Tick::new(
91//!         sym.clone(),
92//!         Price::new(close)?,
93//!         Quantity::new(dec!(1))?,
94//!         Side::Bid,
95//!         NanoTimestamp::from_secs(1_767_625_200 + minute * 60),
96//!     );
97//!     for bar in agg.push_tick(&tick)? {
98//!         values.push(sma.update(&BarInput::from(&bar))?);
99//!     }
100//! }
101//! // Three bars have closed; the fourth is still open.
102//! assert_eq!(values[0], SignalValue::Unavailable);
103//! assert_eq!(values[1], SignalValue::Unavailable);
104//! assert_eq!(values[2], SignalValue::Scalar(dec!(3190)));
105//! # Ok(())
106//! # }
107//! ```
108//!
109//! ## Runnable examples
110//!
111//! The repository ships examples that print formatted, colored output:
112//!
113//! | Command | Shows |
114//! |---------|-------|
115//! | `cargo run --example order_book` | depth ladder, spread, micro-price, VWAP fill, rejected deltas |
116//! | `cargo run --example candles` | ticks to 1-minute candles, EMA and RSI with warm-up |
117//! | `cargo run --example position_risk` | fills, mark-to-market, drawdown and equity-floor breaches |
118//! | `cargo run --example option_chain` | Black-Scholes chain with Greeks and an implied-vol round trip |
119//!
120//! ## Where things live
121//!
122//! | Module | What it provides |
123//! |--------|------------------|
124//! | [`types`] | [`Price`](types::Price), [`Quantity`](types::Quantity), [`Symbol`](types::Symbol), [`NanoTimestamp`](types::NanoTimestamp), [`Side`](types::Side) |
125//! | [`tick`] | [`Tick`](tick::Tick), `TickFilter`, `TickReplayer` |
126//! | [`orderbook`] | [`OrderBook`](orderbook::OrderBook): L2 book with sequence checks and crossed-book rollback |
127//! | [`ohlcv`] | [`OhlcvBar`](ohlcv::OhlcvBar), [`OhlcvAggregator`](ohlcv::OhlcvAggregator), `OhlcvSeries` analytics |
128//! | [`signals`] | the [`Signal`](signals::Signal) trait, `SignalPipeline`, and several hundred indicators in [`signals::indicators`] |
129//! | [`position`] | [`Fill`](position::Fill), [`Position`](position::Position), [`PositionLedger`](position::PositionLedger), Kelly sizing |
130//! | [`risk`] | `DrawdownTracker`, the [`RiskRule`](risk::RiskRule) trait, [`RiskMonitor`](risk::RiskMonitor), VaR and stress tools |
131//! | [`greeks`] | [`BlackScholes`](greeks::BlackScholes) pricing, Greeks, implied volatility, multi-leg spreads |
132//! | [`backtest`] | bar-by-bar `Backtester`, `Strategy` trait, `WalkForwardOptimizer` |
133//! | [`async_signals`] | Tokio-based `StreamingSignalPipeline` |
134//! | [`regime`] | Hurst exponent, GARCH(1,1), correlation-breakdown regime detection |
135//!
136//! Further modules cover portfolio optimization, factor models, yield curves,
137//! fixed income, credit, derivatives, execution cost, microstructure, Monte
138//! Carlo, pairs trading, tax lots and more; see the module list below.
139//!
140//! ## Design
141//!
142//! - **Validated at construction.** `Price::new` rejects zero and negative
143//!   values; `Quantity::new` rejects negatives; `Symbol::new` rejects empty or
144//!   whitespace strings. Code that holds one of these types can trust it.
145//! - **Decimal where money is.** Prices, quantities, P&L and order-book math
146//!   use [`rust_decimal::Decimal`]. Statistical models (GARCH, optimizers,
147//!   Black-Scholes internals) compute in `f64` and convert at the boundary.
148//! - **Typed errors.** Fallible operations return `Result<_, FinError>`, and
149//!   the crate's Clippy config warns on `unwrap`, `expect` and `panic`.
150//! - **Traits at the seams.** [`risk::RiskRule`], [`signals::Signal`] and
151//!   [`tick::TickFilter`] are traits, so your own rules and indicators plug in
152//!   next to the built-in ones.
153//! - **No unsafe code.** The crate is `#![forbid(unsafe_code)]`.
154//!
155//! Sister crate: [fin-stream](https://github.com/Mattbusel/fin-stream) handles
156//! real-time market data ingestion on top of these types.
157
158#![forbid(unsafe_code)]
159#![deny(missing_docs)]
160
161pub mod async_signals;
162pub mod backtest;
163pub mod error;
164pub mod greeks;
165pub mod ohlcv;
166pub mod orderbook;
167pub mod position;
168pub mod risk;
169pub mod signals;
170pub mod tick;
171pub mod types;
172pub mod pnl;
173pub mod correlation;
174pub mod latency;
175pub mod scenario;
176pub mod microstructure;
177pub mod ml;
178pub mod regime;
179pub mod cross_asset;
180pub mod attribution;
181pub mod options;
182pub mod volatility;
183pub mod impact;
184pub mod portfolio;
185
186#[cfg(feature = "python")]
187pub mod python;
188pub mod factor;
189pub mod execution;
190pub mod montecarlo;
191pub mod yield_curve;
192pub mod events;
193pub mod crypto;
194pub mod derivatives;
195pub mod technical;
196pub mod fixed_income;
197pub mod liquidity;
198pub mod pairs_trading;
199pub mod ml_features;
200pub mod performance;
201pub mod clustering;
202pub mod tax;
203pub mod rebalancing;
204pub mod funding;
205pub mod alternative_data;
206pub mod arbitrage;
207pub mod execution_cost;
208pub mod credit;
209
210pub use error::FinError;