Skip to main content

truefix_twsapi_client/
enums.rs

1//! Semantic enums for values that are represented as integers or strings on the wire.
2
3macro_rules! int_enum {
4    ($(#[$meta:meta])* $name:ident { $($variant:ident = $value:expr => $label:literal),+ $(,)? }) => {
5        $(#[$meta])*
6        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7        pub enum $name {
8            $($variant),+,
9            Unknown(i32),
10        }
11
12        impl $name {
13            /// Converts a wire value without rejecting values added by a newer TWS version.
14            pub const fn from_i32(value: i32) -> Self {
15                match value {
16                    $($value => Self::$variant,)+
17                    other => Self::Unknown(other),
18                }
19            }
20
21            /// Returns the value used by the TWS wire protocol.
22            pub const fn as_i32(self) -> i32 {
23                match self {
24                    $(Self::$variant => $value,)+
25                    Self::Unknown(value) => value,
26                }
27            }
28
29            /// Returns the official symbolic name when known.
30            pub const fn name(self) -> &'static str {
31                match self {
32                    $(Self::$variant => $label,)+
33                    Self::Unknown(_) => "UNKNOWN",
34                }
35            }
36        }
37    };
38}
39
40int_enum! {
41    /// IB market-data tick type.
42    TickType {
43        BidSize = 0 => "BID_SIZE",
44        Bid = 1 => "BID",
45        Ask = 2 => "ASK",
46        AskSize = 3 => "ASK_SIZE",
47        Last = 4 => "LAST",
48        LastSize = 5 => "LAST_SIZE",
49        High = 6 => "HIGH",
50        Low = 7 => "LOW",
51        Volume = 8 => "VOLUME",
52        Close = 9 => "CLOSE",
53        BidOptionComputation = 10 => "BID_OPTION_COMPUTATION",
54        AskOptionComputation = 11 => "ASK_OPTION_COMPUTATION",
55        LastOptionComputation = 12 => "LAST_OPTION_COMPUTATION",
56        ModelOption = 13 => "MODEL_OPTION",
57        Open = 14 => "OPEN",
58        Low13Week = 15 => "LOW_13_WEEK",
59        High13Week = 16 => "HIGH_13_WEEK",
60        Low26Week = 17 => "LOW_26_WEEK",
61        High26Week = 18 => "HIGH_26_WEEK",
62        Low52Week = 19 => "LOW_52_WEEK",
63        High52Week = 20 => "HIGH_52_WEEK",
64        AverageVolume = 21 => "AVG_VOLUME",
65        OpenInterest = 22 => "OPEN_INTEREST",
66        OptionHistoricalVolatility = 23 => "OPTION_HISTORICAL_VOL",
67        OptionImpliedVolatility = 24 => "OPTION_IMPLIED_VOL",
68        OptionBidExchange = 25 => "OPTION_BID_EXCH",
69        OptionAskExchange = 26 => "OPTION_ASK_EXCH",
70        OptionCallOpenInterest = 27 => "OPTION_CALL_OPEN_INTEREST",
71        OptionPutOpenInterest = 28 => "OPTION_PUT_OPEN_INTEREST",
72        OptionCallVolume = 29 => "OPTION_CALL_VOLUME",
73        OptionPutVolume = 30 => "OPTION_PUT_VOLUME",
74        IndexFuturePremium = 31 => "INDEX_FUTURE_PREMIUM",
75        BidExchange = 32 => "BID_EXCH",
76        AskExchange = 33 => "ASK_EXCH",
77        AuctionVolume = 34 => "AUCTION_VOLUME",
78        AuctionPrice = 35 => "AUCTION_PRICE",
79        AuctionImbalance = 36 => "AUCTION_IMBALANCE",
80        MarkPrice = 37 => "MARK_PRICE",
81        BidEfpComputation = 38 => "BID_EFP_COMPUTATION",
82        AskEfpComputation = 39 => "ASK_EFP_COMPUTATION",
83        LastEfpComputation = 40 => "LAST_EFP_COMPUTATION",
84        OpenEfpComputation = 41 => "OPEN_EFP_COMPUTATION",
85        HighEfpComputation = 42 => "HIGH_EFP_COMPUTATION",
86        LowEfpComputation = 43 => "LOW_EFP_COMPUTATION",
87        CloseEfpComputation = 44 => "CLOSE_EFP_COMPUTATION",
88        LastTimestamp = 45 => "LAST_TIMESTAMP",
89        Shortable = 46 => "SHORTABLE",
90        NotUsed = 47 => "NOT_USED",
91        RealTimeVolume = 48 => "RT_VOLUME",
92        Halted = 49 => "HALTED",
93        BidYield = 50 => "BID_YIELD",
94        AskYield = 51 => "ASK_YIELD",
95        LastYield = 52 => "LAST_YIELD",
96        CustomerOptionComputation = 53 => "CUST_OPTION_COMPUTATION",
97        TradeCount = 54 => "TRADE_COUNT",
98        TradeRate = 55 => "TRADE_RATE",
99        VolumeRate = 56 => "VOLUME_RATE",
100        LastRthTrade = 57 => "LAST_RTH_TRADE",
101        RealTimeHistoricalVolatility = 58 => "RT_HISTORICAL_VOL",
102        IbDividends = 59 => "IB_DIVIDENDS",
103        BondFactorMultiplier = 60 => "BOND_FACTOR_MULTIPLIER",
104        RegulatoryImbalance = 61 => "REGULATORY_IMBALANCE",
105        NewsTick = 62 => "NEWS_TICK",
106        ShortTermVolume3Min = 63 => "SHORT_TERM_VOLUME_3_MIN",
107        ShortTermVolume5Min = 64 => "SHORT_TERM_VOLUME_5_MIN",
108        ShortTermVolume10Min = 65 => "SHORT_TERM_VOLUME_10_MIN",
109        DelayedBid = 66 => "DELAYED_BID",
110        DelayedAsk = 67 => "DELAYED_ASK",
111        DelayedLast = 68 => "DELAYED_LAST",
112        DelayedBidSize = 69 => "DELAYED_BID_SIZE",
113        DelayedAskSize = 70 => "DELAYED_ASK_SIZE",
114        DelayedLastSize = 71 => "DELAYED_LAST_SIZE",
115        DelayedHigh = 72 => "DELAYED_HIGH",
116        DelayedLow = 73 => "DELAYED_LOW",
117        DelayedVolume = 74 => "DELAYED_VOLUME",
118        DelayedClose = 75 => "DELAYED_CLOSE",
119        DelayedOpen = 76 => "DELAYED_OPEN",
120        RealTimeTradeVolume = 77 => "RT_TRD_VOLUME",
121        CreditmanMarkPrice = 78 => "CREDITMAN_MARK_PRICE",
122        CreditmanSlowMarkPrice = 79 => "CREDITMAN_SLOW_MARK_PRICE",
123        DelayedBidOption = 80 => "DELAYED_BID_OPTION",
124        DelayedAskOption = 81 => "DELAYED_ASK_OPTION",
125        DelayedLastOption = 82 => "DELAYED_LAST_OPTION",
126        DelayedModelOption = 83 => "DELAYED_MODEL_OPTION",
127        LastExchange = 84 => "LAST_EXCH",
128        LastRegulatoryTime = 85 => "LAST_REG_TIME",
129        FuturesOpenInterest = 86 => "FUTURES_OPEN_INTEREST",
130        AverageOptionVolume = 87 => "AVG_OPT_VOLUME",
131        DelayedLastTimestamp = 88 => "DELAYED_LAST_TIMESTAMP",
132        ShortableShares = 89 => "SHORTABLE_SHARES",
133        DelayedHalted = 90 => "DELAYED_HALTED",
134        Reuters2MutualFunds = 91 => "REUTERS_2_MUTUAL_FUNDS",
135        EtfNavClose = 92 => "ETF_NAV_CLOSE",
136        EtfNavPriorClose = 93 => "ETF_NAV_PRIOR_CLOSE",
137        EtfNavBid = 94 => "ETF_NAV_BID",
138        EtfNavAsk = 95 => "ETF_NAV_ASK",
139        EtfNavLast = 96 => "ETF_NAV_LAST",
140        EtfFrozenNavLast = 97 => "ETF_FROZEN_NAV_LAST",
141        EtfNavHigh = 98 => "ETF_NAV_HIGH",
142        EtfNavLow = 99 => "ETF_NAV_LOW",
143        SocialMarketAnalytics = 100 => "SOCIAL_MARKET_ANALYTICS",
144        EstimatedIpoMidpoint = 101 => "ESTIMATED_IPO_MIDPOINT",
145        FinalIpoLast = 102 => "FINAL_IPO_LAST",
146        DelayedYieldBid = 103 => "DELAYED_YIELD_BID",
147        DelayedYieldAsk = 104 => "DELAYED_YIELD_ASK",
148        OddLotBid = 105 => "ODD_LOT_BID",
149        OddLotAsk = 106 => "ODD_LOT_ASK",
150        OddLotBidSize = 107 => "ODD_LOT_BID_SIZE",
151        OddLotAskSize = 108 => "ODD_LOT_ASK_SIZE",
152        OddLotBidExchange = 109 => "ODD_LOT_BID_EXCH",
153        OddLotAskExchange = 110 => "ODD_LOT_ASK_EXCH",
154        NotSet = 111 => "NOT_SET"
155    }
156}
157
158int_enum! {
159    /// Requested market-data mode.
160    MarketDataType {
161        RealTime = 1 => "REALTIME",
162        Frozen = 2 => "FROZEN",
163        Delayed = 3 => "DELAYED",
164        DelayedFrozen = 4 => "DELAYED_FROZEN"
165    }
166}
167
168int_enum! {
169    /// Financial Advisor XML data type.
170    FaDataType {
171        Groups = 1 => "GROUPS",
172        Aliases = 3 => "ALIASES"
173    }
174}
175
176int_enum! {
177    /// Liquidity classification reported with executions.
178    Liquidities {
179        None = 0 => "None",
180        Added = 1 => "Added",
181        Remove = 2 => "Remove",
182        RoundedOut = 3 => "RoundedOut"
183    }
184}
185
186int_enum! {
187    /// Option exercise/lapse result.
188    OptionExerciseType {
189        None = -1 => "None",
190        Exercise = 1 => "Exercise",
191        Lapse = 2 => "Lapse",
192        DoNothing = 3 => "DoNothing",
193        Assigned = 100 => "Assigned",
194        AutoexerciseClearing = 101 => "AutoexerciseClearing",
195        Expired = 102 => "Expired",
196        Netting = 103 => "Netting",
197        AutoexerciseTrading = 200 => "AutoexerciseTrading"
198    }
199}
200
201int_enum! {
202    /// Price-condition trigger method.
203    TriggerMethod {
204        Default = 0 => "Default",
205        DoubleBidAsk = 1 => "DoubleBidAsk",
206        Last = 2 => "Last",
207        DoubleLast = 3 => "DoubleLast",
208        BidAsk = 4 => "BidAsk",
209        LastBidAsk = 7 => "LastBidAsk",
210        MidPoint = 8 => "MidPoint"
211    }
212}
213
214/// Fund classification values use string codes in the TWS protocol.
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
216pub enum FundAssetType {
217    None,
218    Others,
219    MoneyMarket,
220    FixedIncome,
221    MultiAsset,
222    Equity,
223    Sector,
224    Guaranteed,
225    Alternative,
226    Unknown,
227}
228
229impl FundAssetType {
230    pub const fn code(self) -> &'static str {
231        match self {
232            Self::None => "None",
233            Self::Others => "000",
234            Self::MoneyMarket => "001",
235            Self::FixedIncome => "002",
236            Self::MultiAsset => "003",
237            Self::Equity => "004",
238            Self::Sector => "005",
239            Self::Guaranteed => "006",
240            Self::Alternative => "007",
241            Self::Unknown => "",
242        }
243    }
244
245    pub fn from_code(code: &str) -> Self {
246        match code {
247            "None" => Self::None,
248            "000" => Self::Others,
249            "001" => Self::MoneyMarket,
250            "002" => Self::FixedIncome,
251            "003" => Self::MultiAsset,
252            "004" => Self::Equity,
253            "005" => Self::Sector,
254            "006" => Self::Guaranteed,
255            "007" => Self::Alternative,
256            _ => Self::Unknown,
257        }
258    }
259}
260
261/// Fund distribution policy indicator.
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
263pub enum FundDistributionPolicyIndicator {
264    None,
265    AccumulationFund,
266    IncomeFund,
267    Unknown,
268}
269
270impl FundDistributionPolicyIndicator {
271    pub const fn code(self) -> &'static str {
272        match self {
273            Self::None => "None",
274            Self::AccumulationFund => "N",
275            Self::IncomeFund => "Y",
276            Self::Unknown => "",
277        }
278    }
279
280    pub fn from_code(code: &str) -> Self {
281        match code {
282            "None" => Self::None,
283            "N" => Self::AccumulationFund,
284            "Y" => Self::IncomeFund,
285            _ => Self::Unknown,
286        }
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    #[test]
295    fn unknown_wire_values_are_preserved() {
296        assert_eq!(TickType::from_i32(999).as_i32(), 999);
297        assert_eq!(MarketDataType::from_i32(3), MarketDataType::Delayed);
298    }
299
300    #[test]
301    fn string_enum_codes_round_trip() {
302        assert_eq!(FundAssetType::from_code("004"), FundAssetType::Equity);
303        assert_eq!(FundDistributionPolicyIndicator::IncomeFund.code(), "Y");
304    }
305}