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