rustrade_data/exchange/ibkr/subscription.rs
1//! IBKR subscription types.
2
3use rustrade_instrument::instrument::name::InstrumentNameExchange;
4use serde::{Deserialize, Serialize};
5
6/// Subscription configuration for IBKR market data.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct IbkrSubscription<K> {
9 /// Instrument key for MarketEvent output
10 pub key: K,
11 /// Barter instrument name (must be registered in ContractRegistry)
12 pub instrument: InstrumentNameExchange,
13 /// Type of market data to subscribe to
14 pub kind: IbkrSubscriptionKind,
15}
16
17impl<K> IbkrSubscription<K> {
18 /// Create a quotes subscription.
19 pub fn quotes(key: K, instrument: InstrumentNameExchange) -> Self {
20 Self {
21 key,
22 instrument,
23 kind: IbkrSubscriptionKind::Quotes,
24 }
25 }
26
27 /// Create a depth subscription.
28 ///
29 /// # Arguments
30 ///
31 /// * `rows` - Number of order book rows. Valid range: 1-20 (IB API limit).
32 /// Common values: 5 (shallow), 10 (medium), 20 (deep).
33 ///
34 /// # Panics
35 ///
36 /// Panics if `rows` is outside the valid range 1-20.
37 ///
38 /// # Limitations
39 ///
40 /// IB allows max 3 concurrent depth subscriptions. Exceeding this limit
41 /// causes error 309.
42 pub fn depth(key: K, instrument: InstrumentNameExchange, rows: i32) -> Self {
43 assert!(
44 (1..=20).contains(&rows),
45 "IB depth rows must be 1-20, got {rows}"
46 );
47 Self {
48 key,
49 instrument,
50 kind: IbkrSubscriptionKind::Depth { rows },
51 }
52 }
53
54 /// Create a trades subscription.
55 pub fn trades(key: K, instrument: InstrumentNameExchange) -> Self {
56 Self {
57 key,
58 instrument,
59 kind: IbkrSubscriptionKind::Trades,
60 }
61 }
62
63 /// Create an option Greeks subscription.
64 ///
65 /// Use for option contracts to receive real-time Greeks (delta, gamma, theta,
66 /// vega, implied volatility) computed from live market prices.
67 ///
68 /// # Subscription Requirements
69 ///
70 /// Requires OPRA subscription (paid) for US options.
71 pub fn option_greeks(key: K, instrument: InstrumentNameExchange) -> Self {
72 Self {
73 key,
74 instrument,
75 kind: IbkrSubscriptionKind::OptionGreeks,
76 }
77 }
78}
79
80/// Type of IBKR market data subscription.
81#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
82pub enum IbkrSubscriptionKind {
83 /// Best bid/ask quotes via market_data()
84 Quotes,
85 /// L2 order book via market_depth()
86 Depth {
87 /// Number of rows in the order book
88 rows: i32,
89 },
90 /// Tick-by-tick trades via tick_by_tick()
91 Trades,
92 /// Real-time option Greeks via market_data() with OptionComputation ticks.
93 ///
94 /// Use this for option contracts to receive live Greeks (delta, gamma, theta,
95 /// vega, IV) computed from market prices. Requires OPRA subscription for US
96 /// options.
97 OptionGreeks,
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use rustrade_instrument::instrument::name::InstrumentNameExchange;
104
105 #[test]
106 #[should_panic(expected = "IB depth rows must be 1-20")]
107 fn depth_panics_on_zero_rows() {
108 let instrument = InstrumentNameExchange::new("AAPL");
109 IbkrSubscription::<()>::depth((), instrument, 0);
110 }
111
112 #[test]
113 #[should_panic(expected = "IB depth rows must be 1-20")]
114 fn depth_panics_on_rows_over_20() {
115 let instrument = InstrumentNameExchange::new("AAPL");
116 IbkrSubscription::<()>::depth((), instrument, 21);
117 }
118}