ibapi/
contracts.rs

1//! Contract definitions and related functionality for trading instruments.
2//!
3//! This module provides data structures for representing various financial instruments
4//! including stocks, options, futures, and complex securities. It includes contract
5//! creation helpers, validation, and conversion utilities.
6
7use std::convert::From;
8use std::fmt::Debug;
9use std::string::ToString;
10
11use log::warn;
12use log::{error, info};
13use serde::Deserialize;
14use serde::Serialize;
15use tick_types::TickType;
16
17use crate::client::DataStream;
18use crate::client::ResponseContext;
19use crate::client::Subscription;
20use crate::encode_option_field;
21use crate::messages::IncomingMessages;
22use crate::messages::OutgoingMessages;
23use crate::messages::RequestMessage;
24use crate::messages::ResponseMessage;
25use crate::Client;
26use crate::{server_versions, Error, ToField};
27
28pub(crate) mod decoders;
29pub(crate) mod encoders;
30pub mod tick_types;
31
32#[cfg(test)]
33pub(crate) mod contract_samples;
34#[cfg(test)]
35mod tests;
36
37// Models
38
39#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
40/// SecurityType enumerates available security types
41pub enum SecurityType {
42    /// Stock (or ETF)
43    #[default]
44    Stock,
45    /// Option
46    Option,
47    /// Future
48    Future,
49    /// Index
50    Index,
51    /// Futures option
52    FuturesOption,
53    /// Forex pair
54    ForexPair,
55    /// Combo
56    Spread,
57    ///  Warrant
58    Warrant,
59    /// Bond
60    Bond,
61    /// Commodity
62    Commodity,
63    /// News
64    News,
65    /// Mutual fund
66    MutualFund,
67    /// Crypto currency
68    Crypto,
69    /// Contract for difference
70    CFD,
71    /// Other
72    Other(String),
73}
74
75impl ToField for SecurityType {
76    fn to_field(&self) -> String {
77        self.to_string()
78    }
79}
80
81impl ToField for Option<SecurityType> {
82    fn to_field(&self) -> String {
83        encode_option_field(self)
84    }
85}
86
87impl std::fmt::Display for SecurityType {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        match self {
90            SecurityType::Stock => write!(f, "STK"),
91            SecurityType::Option => write!(f, "OPT"),
92            SecurityType::Future => write!(f, "FUT"),
93            SecurityType::Index => write!(f, "IND"),
94            SecurityType::FuturesOption => write!(f, "FOP"),
95            SecurityType::ForexPair => write!(f, "CASH"),
96            SecurityType::Spread => write!(f, "BAG"),
97            SecurityType::Warrant => write!(f, "WAR"),
98            SecurityType::Bond => write!(f, "BOND"),
99            SecurityType::Commodity => write!(f, "CMDTY"),
100            SecurityType::News => write!(f, "NEWS"),
101            SecurityType::MutualFund => write!(f, "FUND"),
102            SecurityType::Crypto => write!(f, "CRYPTO"),
103            SecurityType::CFD => write!(f, "CFD"),
104            SecurityType::Other(name) => write!(f, "{name}"),
105        }
106    }
107}
108
109impl SecurityType {
110    pub fn from(name: &str) -> SecurityType {
111        match name {
112            "STK" => SecurityType::Stock,
113            "OPT" => SecurityType::Option,
114            "FUT" => SecurityType::Future,
115            "IND" => SecurityType::Index,
116            "FOP" => SecurityType::FuturesOption,
117            "CASH" => SecurityType::ForexPair,
118            "BAG" => SecurityType::Spread,
119            "WAR" => SecurityType::Warrant,
120            "BOND" => SecurityType::Bond,
121            "CMDTY" => SecurityType::Commodity,
122            "NEWS" => SecurityType::News,
123            "FUND" => SecurityType::MutualFund,
124            "CRYPTO" => SecurityType::Crypto,
125            "CFD" => SecurityType::CFD,
126            other => {
127                warn!("Unknown security type: {other}. Defaulting to Other");
128                SecurityType::Other(other.to_string())
129            }
130        }
131    }
132}
133
134#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
135/// Contract describes an instrument's definition
136pub struct Contract {
137    /// The unique IB contract identifier.
138    pub contract_id: i32,
139    /// The underlying's asset symbol.
140    pub symbol: String,
141    pub security_type: SecurityType,
142    /// The contract's last trading day or contract month (for Options and Futures).
143    /// Strings with format YYYYMM will be interpreted as the Contract Month whereas YYYYMMDD will be interpreted as Last Trading Day.
144    pub last_trade_date_or_contract_month: String,
145    /// The option's strike price.
146    pub strike: f64,
147    /// Either Put or Call (i.e. Options). Valid values are P, PUT, C, CALL.
148    pub right: String,
149    /// The instrument's multiplier (i.e. options, futures).
150    pub multiplier: String,
151    /// The destination exchange.
152    pub exchange: String,
153    /// The underlying's currency.
154    pub currency: String,
155    /// The contract's symbol within its primary exchange For options, this will be the OCC symbol.
156    pub local_symbol: String,
157    /// The contract's primary exchange.
158    /// For smart routed contracts, used to define contract in case of ambiguity.
159    /// Should be defined as native exchange of contract, e.g. ISLAND for MSFT For exchanges which contain a period in name, will only be part of exchange name prior to period, i.e. ENEXT for ENEXT.BE.
160    pub primary_exchange: String,
161    /// The trading class name for this contract. Available in TWS contract description window as well. For example, GBL Dec '13 future's trading class is "FGBL".
162    pub trading_class: String,
163    /// If set to true, contract details requests and historical data queries can be performed pertaining to expired futures contracts. Expired options or other instrument types are not available.
164    pub include_expired: bool,
165    /// Security's identifier when querying contract's details or placing orders ISIN - Example: Apple: US0378331005 CUSIP - Example: Apple: 037833100.
166    pub security_id_type: String,
167    /// Identifier of the security type.
168    pub security_id: String,
169    /// Description of the combo legs.
170    pub combo_legs_description: String,
171    pub combo_legs: Vec<ComboLeg>,
172    /// Delta and underlying price for Delta-Neutral combo orders. Underlying (STK or FUT), delta and underlying price goes into this attribute.
173    pub delta_neutral_contract: Option<DeltaNeutralContract>,
174
175    pub issuer_id: String,
176    pub description: String,
177}
178
179impl Contract {
180    /// Creates a stock contract from the specified symbol.
181    ///
182    /// Currency defaults to USD and exchange defaults to SMART.
183    ///
184    /// # Examples
185    ///
186    /// ```
187    /// use ibapi::contracts::Contract;
188    ///
189    /// let aapl = Contract::stock("AAPL");
190    /// assert_eq!(aapl.symbol, "AAPL");
191    /// assert_eq!(aapl.currency, "USD");
192    /// assert_eq!(aapl.exchange, "SMART");
193    /// ```
194    pub fn stock(symbol: &str) -> Contract {
195        Contract {
196            symbol: symbol.to_string(),
197            security_type: SecurityType::Stock,
198            currency: "USD".to_string(),
199            exchange: "SMART".to_string(),
200            ..Default::default()
201        }
202    }
203
204    /// Creates a futures contract from the specified symbol.
205    ///
206    /// Currency defaults to USD.
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// use ibapi::contracts::Contract;
212    ///
213    /// let es = Contract::futures("ES");
214    /// assert_eq!(es.symbol, "ES");
215    /// assert_eq!(es.currency, "USD");
216    /// ```
217    pub fn futures(symbol: &str) -> Contract {
218        Contract {
219            symbol: symbol.to_string(),
220            security_type: SecurityType::Future,
221            currency: "USD".to_string(),
222            ..Default::default()
223        }
224    }
225
226    /// Creates a cryptocurrency contract from the specified symbol.
227    ///
228    /// Currency defaults to USD and exchange defaults to PAXOS.
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// use ibapi::contracts::Contract;
234    ///
235    /// let btc = Contract::crypto("BTC");
236    /// assert_eq!(btc.symbol, "BTC");
237    /// assert_eq!(btc.currency, "USD");
238    /// assert_eq!(btc.exchange, "PAXOS");
239    /// ```
240    pub fn crypto(symbol: &str) -> Contract {
241        Contract {
242            symbol: symbol.to_string(),
243            security_type: SecurityType::Crypto,
244            currency: "USD".to_string(),
245            exchange: "PAXOS".to_string(),
246            ..Default::default()
247        }
248    }
249
250    /// Creates a news contract from the specified provider code.
251    ///
252    /// # Examples
253    ///
254    /// ```
255    /// use ibapi::contracts::Contract;
256    ///
257    /// let news = Contract::news("BRFG");
258    /// assert_eq!(news.symbol, "BRFG:BRFG_ALL");
259    /// assert_eq!(news.exchange, "BRFG");
260    /// ```
261    pub fn news(provider_code: &str) -> Contract {
262        Contract {
263            symbol: format!("{}:{}_ALL", provider_code, provider_code),
264            security_type: SecurityType::News,
265            exchange: provider_code.to_string(),
266            ..Default::default()
267        }
268    }
269
270    /// Creates an option contract from the specified parameters.
271    ///
272    /// Currency defaults to USD and exchange defaults to SMART.
273    ///
274    /// # Arguments
275    /// * `symbol` - Symbol of the underlying asset
276    /// * `expiration_date` - Expiration date of option contract (YYYYMMDD)
277    /// * `strike` - Strike price of the option contract
278    /// * `right` - Option type: "C" for Call, "P" for Put
279    ///
280    /// # Examples
281    ///
282    /// ```
283    /// use ibapi::contracts::Contract;
284    ///
285    /// let call = Contract::option("AAPL", "20240119", 150.0, "C");
286    /// assert_eq!(call.symbol, "AAPL");
287    /// assert_eq!(call.strike, 150.0);
288    /// assert_eq!(call.right, "C");
289    /// ```
290    pub fn option(symbol: &str, expiration_date: &str, strike: f64, right: &str) -> Contract {
291        Contract {
292            symbol: symbol.into(),
293            security_type: SecurityType::Option,
294            exchange: "SMART".into(),
295            currency: "USD".into(),
296            last_trade_date_or_contract_month: expiration_date.into(), // Expiry date (YYYYMMDD)
297            strike,
298            right: right.into(), // Option type: "C" for Call, "P" for Put
299            ..Default::default()
300        }
301    }
302
303    /// Returns true if this contract represents a bag/combo order.
304    pub fn is_bag(&self) -> bool {
305        self.security_type == SecurityType::Spread
306    }
307
308    pub(crate) fn push_fields(&self, message: &mut RequestMessage) {
309        message.push_field(&self.contract_id);
310        message.push_field(&self.symbol);
311        message.push_field(&self.security_type);
312        message.push_field(&self.last_trade_date_or_contract_month);
313        message.push_field(&self.strike);
314        message.push_field(&self.right);
315        message.push_field(&self.multiplier);
316        message.push_field(&self.exchange);
317        message.push_field(&self.primary_exchange);
318        message.push_field(&self.currency);
319        message.push_field(&self.local_symbol);
320        message.push_field(&self.trading_class);
321        message.push_field(&self.include_expired);
322    }
323}
324
325#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
326// ComboLeg represents a leg within combo orders.
327pub struct ComboLeg {
328    /// The Contract's IB's unique id.
329    pub contract_id: i32,
330    /// Select the relative number of contracts for the leg you are constructing. To help determine the ratio for a specific combination order, refer to the Interactive Analytics section of the User's Guide.
331    pub ratio: i32,
332    /// The side (buy or sell) of the leg:
333    pub action: String,
334    // The destination exchange to which the order will be routed.
335    pub exchange: String,
336    /// Specifies whether an order is an open or closing order.
337    /// For institutional customers to determine if this order is to open or close a position.
338    pub open_close: ComboLegOpenClose,
339    /// For stock legs when doing short selling. Set to 1 = clearing broker, 2 = third party.
340    pub short_sale_slot: i32,
341    /// When ShortSaleSlot is 2, this field shall contain the designated location.
342    pub designated_location: String,
343    // DOC_TODO.
344    pub exempt_code: i32,
345}
346
347#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
348/// OpenClose specifies whether an order is an open or closing order.
349pub enum ComboLegOpenClose {
350    /// 0 - Same as the parent security. This is the only option for retail customers.
351    #[default]
352    Same = 0,
353    /// 1 - Open. This value is only valid for institutional customers.
354    Open = 1,
355    /// 2 - Close. This value is only valid for institutional customers.
356    Close = 2,
357    /// 3 - Unknown.
358    Unknown = 3,
359}
360
361impl ToField for ComboLegOpenClose {
362    fn to_field(&self) -> String {
363        (*self as u8).to_string()
364    }
365}
366
367impl From<i32> for ComboLegOpenClose {
368    // TODO - verify these values
369    fn from(val: i32) -> Self {
370        match val {
371            0 => Self::Same,
372            1 => Self::Open,
373            2 => Self::Close,
374            3 => Self::Unknown,
375            _ => panic!("unsupported value: {val}"),
376        }
377    }
378}
379
380#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
381/// Delta and underlying price for Delta-Neutral combo orders.
382/// Underlying (STK or FUT), delta and underlying price goes into this attribute.
383pub struct DeltaNeutralContract {
384    /// The unique contract identifier specifying the security. Used for Delta-Neutral Combo contracts.
385    pub contract_id: i32,
386    /// The underlying stock or future delta. Used for Delta-Neutral Combo contracts.
387    pub delta: f64,
388    /// The price of the underlying. Used for Delta-Neutral Combo contracts.
389    pub price: f64,
390}
391
392/// ContractDetails provides extended contract details.
393#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
394pub struct ContractDetails {
395    /// A fully-defined Contract object.
396    pub contract: Contract,
397    /// The market name for this product.
398    pub market_name: String,
399    /// The minimum allowed price variation. Note that many securities vary their minimum tick size according to their price. This value will only show the smallest of the different minimum tick sizes regardless of the product's price. Full information about the minimum increment price structure can be obtained with the reqMarketRule function or the IB Contract and Security Search site.
400    pub min_tick: f64,
401    /// Allows execution and strike prices to be reported consistently with market data, historical data and the order price, i.e. Z on LIFFE is reported in Index points and not GBP. In TWS versions prior to 972, the price magnifier is used in defining future option strike prices (e.g. in the API the strike is specified in dollars, but in TWS it is specified in cents). In TWS versions 972 and higher, the price magnifier is not used in defining futures option strike prices so they are consistent in TWS and the API.
402    pub price_magnifier: i32,
403    /// Supported order types for this product.
404    pub order_types: Vec<String>,
405    /// Valid exchange fields when placing an order for this contract.
406    /// The list of exchanges will is provided in the same order as the corresponding MarketRuleIds list.
407    pub valid_exchanges: Vec<String>,
408    /// For derivatives, the contract ID (conID) of the underlying instrument.
409    pub under_contract_id: i32,
410    /// Descriptive name of the product.
411    pub long_name: String,
412    /// Typically the contract month of the underlying for a Future contract.
413    pub contract_month: String,
414    /// The industry classification of the underlying/product. For example, Financial.
415    pub industry: String,
416    /// The industry category of the underlying. For example, InvestmentSvc.
417    pub category: String,
418    /// The industry subcategory of the underlying. For example, Brokerage.
419    pub subcategory: String,
420    /// The time zone for the trading hours of the product. For example, EST.
421    pub time_zone_id: String,
422    /// The trading hours of the product. This value will contain the trading hours of the current day as well as the next's. For example, 20090507:0700-1830,1830-2330;20090508:CLOSED. In TWS versions 965+ there is an option in the Global Configuration API settings to return 1 month of trading hours. In TWS version 970+, the format includes the date of the closing time to clarify potential ambiguity, ex: 20180323:0400-20180323:2000;20180326:0400-20180326:2000 The trading hours will correspond to the hours for the product on the associated exchange. The same instrument can have different hours on different exchanges.
423    pub trading_hours: Vec<String>,
424    /// The liquid hours of the product. This value will contain the liquid hours (regular trading hours) of the contract on the specified exchange. Format for TWS versions until 969: 20090507:0700-1830,1830-2330;20090508:CLOSED. In TWS versions 965+ there is an option in the Global Configuration API settings to return 1 month of trading hours. In TWS v970 and above, the format includes the date of the closing time to clarify potential ambiguity, e.g. 20180323:0930-20180323:1600;20180326:0930-20180326:1600.
425    pub liquid_hours: Vec<String>,
426    /// Contains the Economic Value Rule name and the respective optional argument. The two values should be separated by a colon. For example, aussieBond:YearsToExpiration=3. When the optional argument is not present, the first value will be followed by a colon.
427    pub ev_rule: String,
428    /// Tells you approximately how much the market value of a contract would change if the price were to change by 1. It cannot be used to get market value by multiplying the price by the approximate multiplier.
429    pub ev_multiplier: f64,
430    /// Aggregated group Indicates the smart-routing group to which a contract belongs. contracts which cannot be smart-routed have aggGroup = -1.
431    pub agg_group: i32,
432    /// A list of contract identifiers that the customer is allowed to view. CUSIP/ISIN/etc. For US stocks, receiving the ISIN requires the CUSIP market data subscription. For Bonds, the CUSIP or ISIN is input directly into the symbol field of the Contract class.
433    pub sec_id_list: Vec<TagValue>,
434    /// For derivatives, the symbol of the underlying contract.
435    pub under_symbol: String,
436    /// For derivatives, returns the underlying security type.
437    pub under_security_type: String,
438    /// The list of market rule IDs separated by comma Market rule IDs can be used to determine the minimum price increment at a given price.
439    pub market_rule_ids: Vec<String>,
440    /// Real expiration date. Requires TWS 968+ and API v973.04+. Python API specifically requires API v973.06+.
441    pub real_expiration_date: String,
442    /// Last trade time.
443    pub last_trade_time: String,
444    /// Stock type.
445    pub stock_type: String,
446    /// The nine-character bond CUSIP. For Bonds only. Receiving CUSIPs requires a CUSIP market data subscription.
447    pub cusip: String,
448    /// Identifies the credit rating of the issuer. This field is not currently available from the TWS API. For Bonds only. A higher credit rating generally indicates a less risky investment. Bond ratings are from Moody's and S&P respectively. Not currently implemented due to bond market data restrictions.
449    pub ratings: String,
450    /// A description string containing further descriptive information about the bond. For Bonds only.
451    pub desc_append: String,
452    /// The type of bond, such as "CORP.".
453    pub bond_type: String,
454    /// The type of bond coupon. This field is currently not available from the TWS API. For Bonds only.
455    pub coupon_type: String,
456    /// If true, the bond can be called by the issuer under certain conditions. This field is currently not available from the TWS API. For Bonds only.
457    pub callable: bool,
458    /// Values are True or False. If true, the bond can be sold back to the issuer under certain conditions. This field is currently not available from the TWS API. For Bonds only.
459    pub putable: bool,
460    /// The interest rate used to calculate the amount you will receive in interest payments over the course of the year. This field is currently not available from the TWS API. For Bonds only.
461    pub coupon: f64,
462    /// Values are True or False. If true, the bond can be converted to stock under certain conditions. This field is currently not available from the TWS API. For Bonds only.
463    pub convertible: bool,
464    /// The date on which the issuer must repay the face value of the bond. This field is currently not available from the TWS API. For Bonds only. Not currently implemented due to bond market data restrictions.
465    pub maturity: String,
466    /// The date the bond was issued. This field is currently not available from the TWS API. For Bonds only. Not currently implemented due to bond market data restrictions.
467    pub issue_date: String,
468    /// Only if bond has embedded options. This field is currently not available from the TWS API. Refers to callable bonds and puttable bonds. Available in TWS description window for bonds.
469    pub next_option_date: String,
470    /// Type of embedded option. This field is currently not available from the TWS API. Only if bond has embedded options.
471    pub next_option_type: String,
472    /// Only if bond has embedded options. This field is currently not available from the TWS API. For Bonds only.
473    pub next_option_partial: bool,
474    /// If populated for the bond in IB's database. For Bonds only.
475    pub notes: String,
476    /// Order's minimal size.
477    pub min_size: f64,
478    /// Order's size increment.
479    pub size_increment: f64,
480    /// Order's suggested size increment.
481    pub suggested_size_increment: f64,
482}
483
484/// TagValue is a convenience struct to define key-value pairs.
485#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
486pub struct TagValue {
487    pub tag: String,
488    pub value: String,
489}
490
491impl ToField for Vec<TagValue> {
492    fn to_field(&self) -> String {
493        let mut values = Vec::new();
494        for tag_value in self {
495            values.push(format!("{}={};", tag_value.tag, tag_value.value))
496        }
497        values.concat()
498    }
499}
500
501/// Receives option specific market data.
502/// TWS’s options model volatility, prices, and deltas, along with the present value of dividends expected on that options underlier.
503#[derive(Debug, Default)]
504pub struct OptionComputation {
505    /// Specifies the type of option computation.
506    pub field: TickType,
507    /// 0 – return based, 1- price based.
508    pub tick_attribute: Option<i32>,
509    /// The implied volatility calculated by the TWS option modeler, using the specified tick type value.
510    pub implied_volatility: Option<f64>,
511    /// The option delta value.
512    pub delta: Option<f64>,
513    /// The option price.
514    pub option_price: Option<f64>,
515    /// The present value of dividends expected on the option’s underlying.
516    pub present_value_dividend: Option<f64>,
517    /// The option gamma value.
518    pub gamma: Option<f64>,
519    /// The option vega value.
520    pub vega: Option<f64>,
521    /// The option theta value.
522    pub theta: Option<f64>,
523    /// The price of the underlying.
524    pub underlying_price: Option<f64>,
525}
526
527impl DataStream<OptionComputation> for OptionComputation {
528    const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::TickOptionComputation];
529
530    fn decode(client: &Client, message: &mut ResponseMessage) -> Result<Self, Error> {
531        match message.message_type() {
532            IncomingMessages::TickOptionComputation => Ok(decoders::decode_option_computation(client.server_version, message)?),
533            message => Err(Error::Simple(format!("unexpected message: {message:?}"))),
534        }
535    }
536
537    fn cancel_message(_server_version: i32, request_id: Option<i32>, context: &ResponseContext) -> Result<RequestMessage, Error> {
538        let request_id = request_id.expect("request id required to cancel option calculations");
539        match context.request_type {
540            Some(OutgoingMessages::ReqCalcImpliedVolat) => {
541                encoders::encode_cancel_option_computation(OutgoingMessages::CancelImpliedVolatility, request_id)
542            }
543            Some(OutgoingMessages::ReqCalcOptionPrice) => encoders::encode_cancel_option_computation(OutgoingMessages::CancelOptionPrice, request_id),
544            _ => panic!("Unsupported request message type option computation cancel: {:?}", context.request_type),
545        }
546    }
547}
548
549#[derive(Debug, Default)]
550pub struct OptionChain {
551    /// The contract ID of the underlying security.
552    pub underlying_contract_id: i32,
553    /// The option trading class.
554    pub trading_class: String,
555    /// The option multiplier.
556    pub multiplier: String,
557    /// Exchange for which the derivative is hosted.
558    pub exchange: String,
559    /// A list of the expiries for the options of this underlying on this exchange.
560    pub expirations: Vec<String>,
561    /// A list of the possible strikes for options of this underlying on this exchange.
562    pub strikes: Vec<f64>,
563}
564
565impl DataStream<OptionChain> for OptionChain {
566    fn decode(_client: &Client, message: &mut ResponseMessage) -> Result<OptionChain, Error> {
567        match message.message_type() {
568            IncomingMessages::SecurityDefinitionOptionParameter => Ok(decoders::decode_option_chain(message)?),
569            IncomingMessages::SecurityDefinitionOptionParameterEnd => Err(Error::EndOfStream),
570            _ => Err(Error::UnexpectedResponse(message.clone())),
571        }
572    }
573}
574
575// === API ===
576
577// Requests contract information.
578//
579// Provides all the contracts matching the contract provided. It can also be used to retrieve complete options and futures chains. Though it is now (in API version > 9.72.12) advised to use reqSecDefOptParams for that purpose.
580//
581// # Arguments
582// * `client` - [Client] with an active connection to gateway.
583// * `contract` - The [Contract] used as sample to query the available contracts. Typically, it will contain the [Contract]'s symbol, currency, security_type, and exchange.
584pub(super) fn contract_details(client: &Client, contract: &Contract) -> Result<Vec<ContractDetails>, Error> {
585    verify_contract(client, contract)?;
586
587    let request_id = client.next_request_id();
588    let packet = encoders::encode_request_contract_data(client.server_version(), request_id, contract)?;
589
590    let responses = client.send_request(request_id, packet)?;
591
592    let mut contract_details: Vec<ContractDetails> = Vec::default();
593
594    while let Some(response) = responses.next() {
595        log::debug!("response: {:#?}", response);
596        match response {
597            Ok(mut message) if message.message_type() == IncomingMessages::ContractData => {
598                let decoded = decoders::decode_contract_details(client.server_version(), &mut message)?;
599                contract_details.push(decoded);
600            }
601            Ok(message) if message.message_type() == IncomingMessages::ContractDataEnd => return Ok(contract_details),
602            Ok(message) if message.message_type() == IncomingMessages::Error => return Err(Error::from(message)),
603            Ok(message) => return Err(Error::UnexpectedResponse(message)),
604            Err(e) => return Err(e),
605        }
606    }
607
608    Err(Error::UnexpectedEndOfStream)
609}
610
611fn verify_contract(client: &Client, contract: &Contract) -> Result<(), Error> {
612    if !contract.security_id_type.is_empty() || !contract.security_id.is_empty() {
613        client.check_server_version(
614            server_versions::SEC_ID_TYPE,
615            "It does not support security_id_type or security_id attributes",
616        )?
617    }
618
619    if !contract.trading_class.is_empty() {
620        client.check_server_version(
621            server_versions::TRADING_CLASS,
622            "It does not support the trading_class parameter when requesting contract details.",
623        )?
624    }
625
626    if !contract.primary_exchange.is_empty() {
627        client.check_server_version(
628            server_versions::LINKING,
629            "It does not support primary_exchange parameter when requesting contract details.",
630        )?
631    }
632
633    if !contract.issuer_id.is_empty() {
634        client.check_server_version(
635            server_versions::BOND_ISSUERID,
636            "It does not support issuer_id parameter when requesting contract details.",
637        )?
638    }
639
640    Ok(())
641}
642
643/// Contract data and list of derivative security types
644#[derive(Debug)]
645pub struct ContractDescription {
646    pub contract: Contract,
647    pub derivative_security_types: Vec<String>,
648}
649
650// Requests matching stock symbols.
651//
652// # Arguments
653// * `client` - [Client] with an active connection to gateway.
654// * `pattern` - Either start of ticker symbol or (for larger strings) company name.
655pub(super) fn matching_symbols(client: &Client, pattern: &str) -> Result<Vec<ContractDescription>, Error> {
656    client.check_server_version(server_versions::REQ_MATCHING_SYMBOLS, "It does not support matching symbols requests.")?;
657
658    let request_id = client.next_request_id();
659    let request = encoders::encode_request_matching_symbols(request_id, pattern)?;
660    let subscription = client.send_request(request_id, request)?;
661
662    if let Some(Ok(mut message)) = subscription.next() {
663        match message.message_type() {
664            IncomingMessages::SymbolSamples => {
665                return decoders::decode_contract_descriptions(client.server_version(), &mut message);
666            }
667            IncomingMessages::Error => {
668                // TODO custom error
669                error!("unexpected error: {:?}", message);
670                return Err(Error::Simple(format!("unexpected error: {message:?}")));
671            }
672            _ => {
673                info!("unexpected message: {:?}", message);
674                return Err(Error::Simple(format!("unexpected message: {message:?}")));
675            }
676        }
677    }
678
679    Ok(Vec::default())
680}
681
682#[derive(Debug, Default)]
683/// Minimum price increment structure for a particular market rule ID.
684pub struct MarketRule {
685    /// Market Rule ID requested.
686    pub market_rule_id: i32,
687    /// Returns the available price increments based on the market rule.
688    pub price_increments: Vec<PriceIncrement>,
689}
690
691#[derive(Debug, Default)]
692pub struct PriceIncrement {
693    pub low_edge: f64,
694    pub increment: f64,
695}
696
697// Requests details about a given market rule
698//
699// The market rule for an instrument on a particular exchange provides details about how the minimum price increment changes with price.
700// A list of market rule ids can be obtained by invoking [request_contract_details] on a particular contract. The returned market rule ID list will provide the market rule ID for the instrument in the correspond valid exchange list in [ContractDetails].
701pub(super) fn market_rule(client: &Client, market_rule_id: i32) -> Result<MarketRule, Error> {
702    client.check_server_version(server_versions::MARKET_RULES, "It does not support market rule requests.")?;
703
704    let request = encoders::encode_request_market_rule(market_rule_id)?;
705    let subscription = client.send_shared_request(OutgoingMessages::RequestMarketRule, request)?;
706
707    match subscription.next() {
708        Some(Ok(mut message)) => Ok(decoders::decode_market_rule(&mut message)?),
709        Some(Err(e)) => Err(e),
710        None => Err(Error::Simple("no market rule found".into())),
711    }
712}
713
714// Calculates an option’s price based on the provided volatility and its underlying’s price.
715//
716// # Arguments
717// * `contract`   - The [Contract] object for which the depth is being requested.
718// * `volatility` - Hypothetical volatility.
719// * `underlying_price` - Hypothetical option’s underlying price.
720pub(super) fn calculate_option_price(
721    client: &Client,
722    contract: &Contract,
723    volatility: f64,
724    underlying_price: f64,
725) -> Result<OptionComputation, Error> {
726    client.check_server_version(server_versions::REQ_CALC_OPTION_PRICE, "It does not support calculation price requests.")?;
727
728    let request_id = client.next_request_id();
729    let message = encoders::encode_calculate_option_price(client.server_version(), request_id, contract, volatility, underlying_price)?;
730    let subscription = client.send_request(request_id, message)?;
731
732    match subscription.next() {
733        Some(Ok(mut message)) => OptionComputation::decode(client, &mut message),
734        Some(Err(e)) => Err(e),
735        None => Err(Error::Simple("no data for option calculation".into())),
736    }
737}
738
739// Calculates the implied volatility based on hypothetical option and its underlying prices.
740//
741// # Arguments
742// * `contract`   - The [Contract] object for which the depth is being requested.
743// * `option_price` - Hypothetical option price.
744// * `underlying_price` - Hypothetical option’s underlying price.
745pub(super) fn calculate_implied_volatility(
746    client: &Client,
747    contract: &Contract,
748    option_price: f64,
749    underlying_price: f64,
750) -> Result<OptionComputation, Error> {
751    client.check_server_version(
752        server_versions::REQ_CALC_IMPLIED_VOLAT,
753        "It does not support calculate implied volatility.",
754    )?;
755
756    let request_id = client.next_request_id();
757    let message = encoders::encode_calculate_implied_volatility(client.server_version(), request_id, contract, option_price, underlying_price)?;
758    let subscription = client.send_request(request_id, message)?;
759
760    match subscription.next() {
761        Some(Ok(mut message)) => OptionComputation::decode(client, &mut message),
762        Some(Err(e)) => Err(e),
763        None => Err(Error::Simple("no data for option calculation".into())),
764    }
765}
766
767pub(super) fn option_chain<'a>(
768    client: &'a Client,
769    symbol: &str,
770    exchange: &str,
771    security_type: SecurityType,
772    contract_id: i32,
773) -> Result<Subscription<'a, OptionChain>, Error> {
774    client.check_server_version(
775        server_versions::SEC_DEF_OPT_PARAMS_REQ,
776        "It does not support security definition option parameters.",
777    )?;
778
779    let request_id = client.next_request_id();
780    let request = encoders::encode_request_option_chain(request_id, symbol, exchange, security_type, contract_id)?;
781    let subscription = client.send_request(request_id, request)?;
782
783    Ok(Subscription::new(client, subscription, ResponseContext::default()))
784}
785
786/// Builder for creating and validating [Contract] instances
787///
788/// The [ContractBuilder] provides a fluent interface for constructing contracts with validation.
789/// It ensures that contracts are properly configured for their security type and prevents
790/// common errors through compile-time and runtime validation.
791///
792/// # Examples
793///
794/// ## Creating a Stock Contract
795///
796/// ```no_run
797/// use ibapi::contracts::{ContractBuilder, SecurityType};
798///
799/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
800/// // Using the builder pattern
801/// let contract = ContractBuilder::new()
802///     .symbol("AAPL")
803///     .security_type(SecurityType::Stock)
804///     .exchange("SMART")
805///     .currency("USD")
806///     .build()?;
807///
808/// // Using the convenience method
809/// let contract = ContractBuilder::stock("AAPL", "SMART", "USD").build()?;
810/// # Ok(())
811/// # }
812/// ```
813///
814/// ## Creating an Option Contract
815///
816/// ```no_run
817/// use ibapi::contracts::{ContractBuilder, SecurityType};
818///
819/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
820/// let contract = ContractBuilder::option("AAPL", "SMART", "USD")
821///     .strike(150.0)
822///     .right("C")  // Call option
823///     .last_trade_date_or_contract_month("20241220")
824///     .build()?;
825/// # Ok(())
826/// # }
827/// ```
828///
829/// ## Creating a Futures Contract
830///
831/// ```no_run
832/// use ibapi::contracts::{ContractBuilder, SecurityType};
833///
834/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
835/// let contract = ContractBuilder::futures("ES", "GLOBEX", "USD")
836///     .last_trade_date_or_contract_month("202412")
837///     .build()?;
838/// # Ok(())
839/// # }
840/// ```
841///
842/// ## Creating a Crypto Contract
843///
844/// ```no_run
845/// use ibapi::contracts::{ContractBuilder, SecurityType};
846///
847/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
848/// let contract = ContractBuilder::crypto("BTC", "PAXOS", "USD").build()?;
849/// # Ok(())
850/// # }
851/// ```
852///
853/// # Validation
854///
855/// The builder performs validation when [build](ContractBuilder::build) is called:
856/// - Symbol is always required
857/// - Option contracts require strike, right (P/C), and expiration date
858/// - Futures contracts require contract month
859/// - Strike prices cannot be negative
860/// - Option rights must be "P" or "C" (case insensitive)
861#[derive(Clone, Debug, Default)]
862pub struct ContractBuilder {
863    contract_id: Option<i32>,
864    symbol: Option<String>,
865    security_type: Option<SecurityType>,
866    last_trade_date_or_contract_month: Option<String>,
867    strike: Option<f64>,
868    right: Option<String>,
869    multiplier: Option<String>,
870    exchange: Option<String>,
871    currency: Option<String>,
872    local_symbol: Option<String>,
873    primary_exchange: Option<String>,
874    trading_class: Option<String>,
875    include_expired: Option<bool>,
876    security_id_type: Option<String>,
877    security_id: Option<String>,
878    combo_legs_description: Option<String>,
879    combo_legs: Option<Vec<ComboLeg>>,
880    delta_neutral_contract: Option<DeltaNeutralContract>,
881    issuer_id: Option<String>,
882    description: Option<String>,
883}
884
885impl ContractBuilder {
886    /// Creates a new [ContractBuilder]
887    ///
888    /// # Examples
889    ///
890    /// ```no_run
891    /// use ibapi::contracts::{ContractBuilder, SecurityType};
892    ///
893    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
894    /// let contract = ContractBuilder::new()
895    ///     .symbol("MSFT")
896    ///     .security_type(SecurityType::Stock)
897    ///     .exchange("SMART")
898    ///     .currency("USD")
899    ///     .build()?;
900    /// # Ok(())
901    /// # }
902    /// ```
903    pub fn new() -> Self {
904        Self::default()
905    }
906
907    /// Sets the contract ID
908    ///
909    /// The unique IB contract identifier. When specified, other contract details may be optional.
910    pub fn contract_id(mut self, contract_id: i32) -> Self {
911        self.contract_id = Some(contract_id);
912        self
913    }
914
915    /// Sets the underlying asset symbol
916    ///
917    /// Required field for all contracts.
918    ///
919    /// # Examples
920    /// - Stocks: "AAPL", "MSFT", "TSLA"
921    /// - Futures: "ES", "NQ", "CL"
922    /// - Crypto: "BTC", "ETH"
923    pub fn symbol<S: Into<String>>(mut self, symbol: S) -> Self {
924        self.symbol = Some(symbol.into());
925        self
926    }
927
928    /// Sets the security type
929    ///
930    /// Defines what type of instrument this contract represents.
931    /// See [SecurityType] for available options.
932    pub fn security_type(mut self, security_type: SecurityType) -> Self {
933        self.security_type = Some(security_type);
934        self
935    }
936
937    /// Sets the last trade date or contract month
938    ///
939    /// For futures and options, this field is required:
940    /// - Format YYYYMM for contract month (e.g., "202412")
941    /// - Format YYYYMMDD for specific expiration date (e.g., "20241220")
942    pub fn last_trade_date_or_contract_month<S: Into<String>>(mut self, date: S) -> Self {
943        self.last_trade_date_or_contract_month = Some(date.into());
944        self
945    }
946
947    /// Sets the option's strike price
948    ///
949    /// Required for option contracts. Must be a positive value.
950    ///
951    /// # Examples
952    /// ```no_run
953    /// use ibapi::contracts::ContractBuilder;
954    ///
955    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
956    /// let contract = ContractBuilder::option("AAPL", "SMART", "USD")
957    ///     .strike(150.0)
958    ///     .right("C")
959    ///     .last_trade_date_or_contract_month("20241220")
960    ///     .build()?;
961    /// # Ok(())
962    /// # }
963    /// ```
964    pub fn strike(mut self, strike: f64) -> Self {
965        self.strike = Some(strike);
966        self
967    }
968
969    /// Sets the option right
970    ///
971    /// Required for option contracts. Valid values:
972    /// - "P" for put options
973    /// - "C" for call options
974    ///
975    /// Case insensitive.
976    pub fn right<S: Into<String>>(mut self, right: S) -> Self {
977        self.right = Some(right.into().to_uppercase());
978        self
979    }
980
981    /// Sets the instrument's multiplier
982    ///
983    /// Defines the contract size multiplier for futures and options.
984    /// For most options, this is typically "100".
985    pub fn multiplier<S: Into<String>>(mut self, multiplier: S) -> Self {
986        self.multiplier = Some(multiplier.into());
987        self
988    }
989
990    /// Sets the destination exchange
991    ///
992    /// Common exchanges:
993    /// - "SMART" for smart routing
994    /// - "NYSE", "NASDAQ" for stocks
995    /// - "GLOBEX", "NYMEX" for futures
996    /// - "PAXOS" for crypto
997    pub fn exchange<S: Into<String>>(mut self, exchange: S) -> Self {
998        self.exchange = Some(exchange.into());
999        self
1000    }
1001
1002    /// Sets the underlying's currency
1003    ///
1004    /// Standard 3-letter currency codes (e.g., "USD", "EUR", "GBP").
1005    pub fn currency<S: Into<String>>(mut self, currency: S) -> Self {
1006        self.currency = Some(currency.into());
1007        self
1008    }
1009
1010    /// Sets the local symbol
1011    pub fn local_symbol<S: Into<String>>(mut self, local_symbol: S) -> Self {
1012        self.local_symbol = Some(local_symbol.into());
1013        self
1014    }
1015
1016    /// Sets the primary exchange
1017    pub fn primary_exchange<S: Into<String>>(mut self, primary_exchange: S) -> Self {
1018        self.primary_exchange = Some(primary_exchange.into());
1019        self
1020    }
1021
1022    /// Sets the trading class
1023    pub fn trading_class<S: Into<String>>(mut self, trading_class: S) -> Self {
1024        self.trading_class = Some(trading_class.into());
1025        self
1026    }
1027
1028    /// Sets include expired flag
1029    pub fn include_expired(mut self, include_expired: bool) -> Self {
1030        self.include_expired = Some(include_expired);
1031        self
1032    }
1033
1034    /// Sets the security ID type
1035    pub fn security_id_type<S: Into<String>>(mut self, security_id_type: S) -> Self {
1036        self.security_id_type = Some(security_id_type.into());
1037        self
1038    }
1039
1040    /// Sets the security ID
1041    pub fn security_id<S: Into<String>>(mut self, security_id: S) -> Self {
1042        self.security_id = Some(security_id.into());
1043        self
1044    }
1045
1046    /// Sets the combo legs description
1047    pub fn combo_legs_description<S: Into<String>>(mut self, description: S) -> Self {
1048        self.combo_legs_description = Some(description.into());
1049        self
1050    }
1051
1052    /// Sets the combo legs
1053    pub fn combo_legs(mut self, combo_legs: Vec<ComboLeg>) -> Self {
1054        self.combo_legs = Some(combo_legs);
1055        self
1056    }
1057
1058    /// Sets the delta neutral contract
1059    pub fn delta_neutral_contract(mut self, delta_neutral_contract: DeltaNeutralContract) -> Self {
1060        self.delta_neutral_contract = Some(delta_neutral_contract);
1061        self
1062    }
1063
1064    /// Sets the issuer ID
1065    pub fn issuer_id<S: Into<String>>(mut self, issuer_id: S) -> Self {
1066        self.issuer_id = Some(issuer_id.into());
1067        self
1068    }
1069
1070    /// Sets the description
1071    pub fn description<S: Into<String>>(mut self, description: S) -> Self {
1072        self.description = Some(description.into());
1073        self
1074    }
1075
1076    /// Creates a stock contract builder with symbol, exchange, and currency
1077    ///
1078    /// Convenience method for creating stock contracts with common defaults.
1079    ///
1080    /// # Arguments
1081    /// * `symbol` - Stock symbol (e.g., "AAPL", "MSFT")
1082    /// * `exchange` - Exchange (e.g., "SMART", "NYSE")
1083    /// * `currency` - Currency (e.g., "USD")
1084    ///
1085    /// # Examples
1086    ///
1087    /// ```no_run
1088    /// use ibapi::contracts::ContractBuilder;
1089    ///
1090    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1091    /// let contract = ContractBuilder::stock("AAPL", "SMART", "USD").build()?;
1092    /// # Ok(())
1093    /// # }
1094    /// ```
1095    pub fn stock<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
1096        Self::new()
1097            .symbol(symbol)
1098            .security_type(SecurityType::Stock)
1099            .exchange(exchange)
1100            .currency(currency)
1101    }
1102
1103    /// Creates a futures contract builder with symbol, exchange, and currency
1104    ///
1105    /// Convenience method for creating futures contracts. Remember to set the contract month.
1106    ///
1107    /// # Arguments
1108    /// * `symbol` - Futures symbol (e.g., "ES", "NQ", "CL")
1109    /// * `exchange` - Exchange (e.g., "GLOBEX", "NYMEX")
1110    /// * `currency` - Currency (e.g., "USD")
1111    ///
1112    /// # Examples
1113    ///
1114    /// ```no_run
1115    /// use ibapi::contracts::ContractBuilder;
1116    ///
1117    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1118    /// let contract = ContractBuilder::futures("ES", "GLOBEX", "USD")
1119    ///     .last_trade_date_or_contract_month("202412")
1120    ///     .build()?;
1121    /// # Ok(())
1122    /// # }
1123    /// ```
1124    pub fn futures<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
1125        Self::new()
1126            .symbol(symbol)
1127            .security_type(SecurityType::Future)
1128            .exchange(exchange)
1129            .currency(currency)
1130    }
1131
1132    /// Creates a crypto contract builder with symbol, exchange, and currency
1133    ///
1134    /// Convenience method for creating cryptocurrency contracts.
1135    ///
1136    /// # Arguments
1137    /// * `symbol` - Crypto symbol (e.g., "BTC", "ETH")
1138    /// * `exchange` - Exchange (e.g., "PAXOS")
1139    /// * `currency` - Quote currency (e.g., "USD")
1140    ///
1141    /// # Examples
1142    ///
1143    /// ```no_run
1144    /// use ibapi::contracts::ContractBuilder;
1145    ///
1146    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1147    /// let contract = ContractBuilder::crypto("BTC", "PAXOS", "USD").build()?;
1148    /// # Ok(())
1149    /// # }
1150    /// ```
1151    pub fn crypto<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
1152        Self::new()
1153            .symbol(symbol)
1154            .security_type(SecurityType::Crypto)
1155            .exchange(exchange)
1156            .currency(currency)
1157    }
1158
1159    /// Creates an option contract builder with symbol, exchange, and currency
1160    ///
1161    /// Convenience method for creating option contracts. Remember to set strike, right, and expiration.
1162    ///
1163    /// # Arguments
1164    /// * `symbol` - Underlying symbol (e.g., "AAPL", "SPY")
1165    /// * `exchange` - Exchange (e.g., "SMART")
1166    /// * `currency` - Currency (e.g., "USD")
1167    ///
1168    /// # Examples
1169    ///
1170    /// ```no_run
1171    /// use ibapi::contracts::ContractBuilder;
1172    ///
1173    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1174    /// let contract = ContractBuilder::option("AAPL", "SMART", "USD")
1175    ///     .strike(150.0)
1176    ///     .right("C")
1177    ///     .last_trade_date_or_contract_month("20241220")
1178    ///     .build()?;
1179    /// # Ok(())
1180    /// # }
1181    /// ```
1182    pub fn option<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
1183        Self::new()
1184            .symbol(symbol)
1185            .security_type(SecurityType::Option)
1186            .exchange(exchange)
1187            .currency(currency)
1188    }
1189
1190    /// Builds and validates the [Contract]
1191    ///
1192    /// Performs validation based on the security type and returns a [Contract] instance
1193    /// or an error if validation fails.
1194    ///
1195    /// # Errors
1196    ///
1197    /// Returns an error if:
1198    /// - Symbol is missing
1199    /// - Required fields for the security type are missing
1200    /// - Strike price is negative
1201    /// - Option right is not "P" or "C"
1202    ///
1203    /// # Examples
1204    ///
1205    /// ```no_run
1206    /// use ibapi::contracts::ContractBuilder;
1207    ///
1208    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1209    /// let contract = ContractBuilder::stock("AAPL", "SMART", "USD")
1210    ///     .build()
1211    ///     .expect("Failed to build contract");
1212    /// # Ok(())
1213    /// # }
1214    /// ```
1215    pub fn build(self) -> Result<Contract, Error> {
1216        let symbol = self.symbol.ok_or_else(|| Error::Simple("Symbol is required".to_string()))?;
1217        let security_type = self.security_type.unwrap_or_default();
1218
1219        // Validate required fields based on security type
1220        match security_type {
1221            SecurityType::Option => {
1222                if self.strike.is_none() {
1223                    return Err(Error::Simple("Strike price is required for options".to_string()));
1224                }
1225                if self.right.is_none() {
1226                    return Err(Error::Simple("Right (P for PUT or C for CALL) is required for options".to_string()));
1227                }
1228                if self.last_trade_date_or_contract_month.is_none() {
1229                    return Err(Error::Simple("Expiration date is required for options".to_string()));
1230                }
1231            }
1232            SecurityType::Future | SecurityType::FuturesOption => {
1233                if self.last_trade_date_or_contract_month.is_none() {
1234                    return Err(Error::Simple("Contract month is required for futures".to_string()));
1235                }
1236            }
1237            _ => {}
1238        }
1239
1240        // Validate option right format
1241        if let Some(ref right) = self.right {
1242            let right_upper = right.to_uppercase();
1243            if !["P", "C"].contains(&right_upper.as_str()) {
1244                return Err(Error::Simple("Option right must be P for PUT or C for CALL".to_string()));
1245            }
1246        }
1247
1248        // Validate strike price
1249        if let Some(strike) = self.strike {
1250            if strike < 0.0 {
1251                return Err(Error::Simple("Strike price cannot be negative".to_string()));
1252            }
1253        }
1254
1255        Ok(Contract {
1256            contract_id: self.contract_id.unwrap_or(0),
1257            symbol,
1258            security_type,
1259            last_trade_date_or_contract_month: self.last_trade_date_or_contract_month.unwrap_or_default(),
1260            strike: self.strike.unwrap_or(0.0),
1261            right: self.right.unwrap_or_default(),
1262            multiplier: self.multiplier.unwrap_or_default(),
1263            exchange: self.exchange.unwrap_or_default(),
1264            currency: self.currency.unwrap_or_default(),
1265            local_symbol: self.local_symbol.unwrap_or_default(),
1266            primary_exchange: self.primary_exchange.unwrap_or_default(),
1267            trading_class: self.trading_class.unwrap_or_default(),
1268            include_expired: self.include_expired.unwrap_or(false),
1269            security_id_type: self.security_id_type.unwrap_or_default(),
1270            security_id: self.security_id.unwrap_or_default(),
1271            combo_legs_description: self.combo_legs_description.unwrap_or_default(),
1272            combo_legs: self.combo_legs.unwrap_or_default(),
1273            delta_neutral_contract: self.delta_neutral_contract,
1274            issuer_id: self.issuer_id.unwrap_or_default(),
1275            description: self.description.unwrap_or_default(),
1276        })
1277    }
1278}