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}