bet_sizing
Subject
Section titled “Subject”Position Sizing and Trade Construction
Why This Module Exists
Section titled “Why This Module Exists”A model signal is not tradable until converted into bounded, discrete, and risk-aware position sizes.
Mathematical Foundations
Section titled “Mathematical Foundations”From Classification Probability to Signed Bet
Section titled “From Classification Probability to Signed Bet”
Dynamic Position Target and Limit Price
Section titled “Dynamic Position Target and Limit Price”
Budget and Reserve Concurrency Sizing
Section titled “Budget and Reserve Concurrency Sizing”
Usage Examples
Section titled “Usage Examples”End-to-end: Probability Forecasts -> Discrete Executable Bet Sizes
Section titled “End-to-end: Probability Forecasts -> Discrete Executable Bet Sizes”use chrono::{Duration, NaiveDateTime};use openquant::bet_sizing::bet_size_probability;
// 1) Build event stream: (start, end, class probability, trade side)let t0 = NaiveDateTime::parse_from_str("2024-01-01 09:30:00", "%Y-%m-%d %H:%M:%S")?;let events = vec![ (t0, t0 + Duration::minutes(20), 0.56, 1.0), (t0 + Duration::minutes(5), t0 + Duration::minutes(35), 0.62, 1.0), (t0 + Duration::minutes(10), t0 + Duration::minutes(30), 0.48, -1.0), (t0 + Duration::minutes(15), t0 + Duration::minutes(45), 0.67, 1.0),];
// 2) Convert probabilities -> signed signal -> discretized size (step=0.1)let sizes = bet_size_probability(&events, 2, 0.1, true);
// 3) sizes are directly executable as timestamped target exposure in [-1, 1]assert!(!sizes.is_empty());End-to-end: Dynamic + Reserve Sizing for Execution and Inventory Control
Section titled “End-to-end: Dynamic + Reserve Sizing for Execution and Inventory Control”use chrono::{Duration, NaiveDateTime};use openquant::bet_sizing::{bet_size_dynamic, bet_size_reserve_full};
// Dynamic sizing inputs (position, max position, market price, forecast price)let pos = vec![0.0, 1.0, 1.0, 2.0, 1.0];let max_pos = vec![10.0; 5];let market = vec![100.0, 100.1, 100.0, 100.2, 100.15];let forecast = vec![100.3, 100.4, 100.2, 100.5, 100.45];
let dynamic = bet_size_dynamic(&pos, &max_pos, &market, &forecast);// tuple: (bet_size, target_position, limit_price)
// Reserve sizing from overlapping long/short eventslet t0 = NaiveDateTime::parse_from_str("2024-01-01 09:30:00", "%Y-%m-%d %H:%M:%S")?;let t1 = vec![ (t0, t0 + Duration::minutes(30)), (t0 + Duration::minutes(10), t0 + Duration::minutes(40)), (t0 + Duration::minutes(20), t0 + Duration::minutes(50)),];let side = vec![1.0, -1.0, 1.0];let (reserve, fit) = bet_size_reserve_full(&t1, &side, 8, 1e-6, 200, true);
assert_eq!(dynamic.len(), 5);assert!(fit.is_some());assert!(!reserve.is_empty());API Reference
Section titled “API Reference”Rust API
Section titled “Rust API”bet_size_probabilitybet_size_dynamicbet_size_budgetbet_size_reservebet_size_reserve_fullget_target_poslimit_price
Implementation Notes
Section titled “Implementation Notes”- Keep sizing logic coupled to latency and fill assumptions; limit price from dynamic sizing is a decision boundary, not a guaranteed fill.
- Use reserve sizing when overlapping books or strategy stacking can create hidden gross exposure.
- Calibrate step_size to real execution granularity (lots/contracts), not arbitrary decimals.