tws_rs/contracts.rs
1use std::convert::From;
2use std::fmt::Debug;
3use std::string::ToString;
4
5use crate::{Error, server_versions};
6use crate::client::ClientRef;
7use crate::messages::{encode_option_field, ToField};
8use crate::messages::RequestMessage;
9
10mod decoders;
11mod encoders;
12
13// Models
14
15#[derive(Clone, Debug, PartialEq, Eq, Default)]
16/// SecurityType enumerates available security types
17pub enum SecurityType {
18 /// Stock (or ETF)
19 #[default]
20 Stock,
21 /// Option
22 Option,
23 /// Future
24 Future,
25 /// Index
26 Index,
27 /// Futures option
28 FuturesOption,
29 /// Forex pair
30 ForexPair,
31 /// Combo
32 Spread,
33 /// Warrant
34 Warrant,
35 /// Bond
36 Bond,
37 /// Commodity
38 Commodity,
39 /// News
40 News,
41 /// Mutual fund
42 MutualFund,
43}
44
45impl ToField for SecurityType {
46 fn to_field(&self) -> String {
47 self.to_string()
48 }
49}
50
51impl ToField for Option<SecurityType> {
52 fn to_field(&self) -> String {
53 encode_option_field(self)
54 }
55}
56
57impl ToString for SecurityType {
58 fn to_string(&self) -> String {
59 match self {
60 SecurityType::Stock => "STK".to_string(),
61 SecurityType::Option => "OPT".to_string(),
62 SecurityType::Future => "FUT".to_string(),
63 SecurityType::Index => "IND".to_string(),
64 SecurityType::FuturesOption => "FOP".to_string(),
65 SecurityType::ForexPair => "CASH".to_string(),
66 SecurityType::Spread => "BAG".to_string(),
67 SecurityType::Warrant => "WAR".to_string(),
68 SecurityType::Bond => "BOND".to_string(),
69 SecurityType::Commodity => "CMDTY".to_string(),
70 SecurityType::News => "NEWS".to_string(),
71 SecurityType::MutualFund => "FUND".to_string(),
72 }
73 }
74}
75
76impl SecurityType {
77 pub fn from(name: &str) -> SecurityType {
78 match name {
79 "STK" => SecurityType::Stock,
80 "OPT" => SecurityType::Option,
81 "FUT" => SecurityType::Future,
82 "IND" => SecurityType::Index,
83 "FOP" => SecurityType::FuturesOption,
84 "CASH" => SecurityType::ForexPair,
85 "BAG" => SecurityType::Spread,
86 "WAR" => SecurityType::Warrant,
87 "BOND" => SecurityType::Bond,
88 "CMDTY" => SecurityType::Commodity,
89 "NEWS" => SecurityType::News,
90 "FUND" => SecurityType::MutualFund,
91 &_ => todo!(),
92 }
93 }
94}
95
96#[derive(Clone, Debug, Default)]
97/// Contract describes an instrument's definition
98pub struct Contract {
99 /// The unique IB contract identifier.
100 pub contract_id: i32,
101 /// The underlying's asset symbol.
102 pub symbol: String,
103 pub security_type: SecurityType,
104 /// The contract's last trading day or contract month (for Options and Futures).
105 /// Strings with format YYYYMM will be interpreted as the Contract Month whereas YYYYMMDD will be interpreted as Last Trading Day.
106 pub last_trade_date_or_contract_month: String,
107 /// The option's strike price.
108 pub strike: f64,
109 /// Either Put or Call (i.e. Options). Valid values are P, PUT, C, CALL.
110 pub right: String,
111 /// The instrument's multiplier (i.e. options, futures).
112 pub multiplier: String,
113 /// The destination exchange.
114 pub exchange: String,
115 /// The underlying's currency.
116 pub currency: String,
117 /// The contract's symbol within its primary exchange For options, this will be the OCC symbol.
118 pub local_symbol: String,
119 /// The contract's primary exchange.
120 /// For smart routed contracts, used to define contract in case of ambiguity.
121 /// 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.
122 pub primary_exchange: String,
123 /// 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".
124 pub trading_class: String,
125 /// 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.
126 pub include_expired: bool,
127 /// Security's identifier when querying contract's details or placing orders ISIN - Example: Apple: US0378331005 CUSIP - Example: Apple: 037833100.
128 pub security_id_type: String,
129 /// Identifier of the security type.
130 pub security_id: String,
131 /// Description of the combo legs.
132 pub combo_legs_description: String,
133 pub combo_legs: Vec<ComboLeg>,
134 /// Delta and underlying price for Delta-Neutral combo orders. Underlying (STK or FUT), delta and underlying price goes into this attribute.
135 pub delta_neutral_contract: Option<DeltaNeutralContract>,
136
137 pub issuer_id: String,
138 pub description: String,
139}
140
141impl Contract {
142 /// Creates stock contract from specified symbol
143 /// currency defaults to USD and SMART exchange.
144 pub fn stock(symbol: &str) -> Contract {
145 Contract {
146 symbol: symbol.to_string(),
147 security_type: SecurityType::Stock,
148 currency: "USD".to_string(),
149 exchange: "SMART".to_string(),
150 ..Default::default()
151 }
152 }
153
154 /// Creates futures contract from specified symbol
155 pub fn futures(symbol: &str) -> Contract {
156 Contract {
157 symbol: symbol.to_string(),
158 security_type: SecurityType::Future,
159 currency: "USD".to_string(),
160 ..Default::default()
161 }
162 }
163
164 /// Is Bag request
165 pub fn is_bag(&self) -> bool {
166 self.security_type == SecurityType::Spread
167 }
168
169 pub(crate) fn push_fields(&self, message: &mut RequestMessage) {
170 message.push_field(&self.contract_id);
171 message.push_field(&self.symbol);
172 message.push_field(&self.security_type);
173 message.push_field(&self.last_trade_date_or_contract_month);
174 message.push_field(&self.strike);
175 message.push_field(&self.right);
176 message.push_field(&self.multiplier);
177 message.push_field(&self.exchange);
178 message.push_field(&self.primary_exchange);
179 message.push_field(&self.currency);
180 message.push_field(&self.local_symbol);
181 message.push_field(&self.trading_class);
182 message.push_field(&self.include_expired);
183 }
184}
185
186#[derive(Clone, Debug, Default)]
187// ComboLeg represents a leg within combo orders.
188pub struct ComboLeg {
189 /// The Contract's IB's unique id.
190 pub contract_id: i32,
191 /// 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.
192 pub ratio: i32,
193 /// The side (buy or sell) of the leg:
194 pub action: String,
195 // The destination exchange to which the order will be routed.
196 pub exchange: String,
197 /// Specifies whether an order is an open or closing order.
198 /// For institutional customers to determine if this order is to open or close a position.
199 pub open_close: ComboLegOpenClose,
200 /// For stock legs when doing short selling. Set to 1 = clearing broker, 2 = third party.
201 pub short_sale_slot: i32,
202 /// When ShortSaleSlot is 2, this field shall contain the designated location.
203 pub designated_location: String,
204 // DOC_TODO.
205 pub exempt_code: i32,
206}
207
208#[derive(Clone, Copy, Debug, Default)]
209/// OpenClose specifies whether an order is an open or closing order.
210pub enum ComboLegOpenClose {
211 /// 0 - Same as the parent security. This is the only option for retail customers.
212 #[default]
213 Same = 0,
214 /// 1 - Open. This value is only valid for institutional customers.
215 Open = 1,
216 /// 2 - Close. This value is only valid for institutional customers.
217 Close = 2,
218 /// 3 - Unknown.
219 Unknown = 3,
220}
221
222impl ToField for ComboLegOpenClose {
223 fn to_field(&self) -> String {
224 (*self as u8).to_string()
225 }
226}
227
228impl From<i32> for ComboLegOpenClose {
229 // TODO - verify these values
230 fn from(val: i32) -> Self {
231 match val {
232 0 => Self::Same,
233 1 => Self::Open,
234 2 => Self::Close,
235 3 => Self::Unknown,
236 _ => panic!("unsupported value: {val}"),
237 }
238 }
239}
240
241#[derive(Clone, Debug, Default, PartialEq)]
242/// Delta and underlying price for Delta-Neutral combo orders.
243/// Underlying (STK or FUT), delta and underlying price goes into this attribute.
244pub struct DeltaNeutralContract {
245 /// The unique contract identifier specifying the security. Used for Delta-Neutral Combo contracts.
246 pub contract_id: i32,
247 /// The underlying stock or future delta. Used for Delta-Neutral Combo contracts.
248 pub delta: f64,
249 /// The price of the underlying. Used for Delta-Neutral Combo contracts.
250 pub price: f64,
251}
252
253/// ContractDetails provides extended contract details.
254#[derive(Debug, Default)]
255pub struct ContractDetails {
256 /// A fully-defined Contract object.
257 pub contract: Contract,
258 /// The market name for this product.
259 pub market_name: String,
260 /// 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.
261 pub min_tick: f64,
262 /// 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.
263 pub price_magnifier: i32,
264 /// Supported order types for this product.
265 pub order_types: String,
266 /// Valid exchange fields when placing an order for this contract.
267 /// The list of exchanges will is provided in the same order as the corresponding MarketRuleIds list.
268 pub valid_exchanges: String,
269 /// For derivatives, the contract ID (conID) of the underlying instrument.
270 pub under_contract_id: i32,
271 /// Descriptive name of the product.
272 pub long_name: String,
273 /// Typically the contract month of the underlying for a Future contract.
274 pub contract_month: String,
275 /// The industry classification of the underlying/product. For example, Financial.
276 pub industry: String,
277 /// The industry category of the underlying. For example, InvestmentSvc.
278 pub category: String,
279 /// The industry subcategory of the underlying. For example, Brokerage.
280 pub subcategory: String,
281 /// The time zone for the trading hours of the product. For example, EST.
282 pub time_zone_id: String,
283 /// 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.
284 pub trading_hours: String,
285 /// 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.
286 pub liquid_hours: String,
287 /// 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.
288 pub ev_rule: String,
289 /// 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.
290 pub ev_multiplier: f64,
291 /// Aggregated group Indicates the smart-routing group to which a contract belongs. contracts which cannot be smart-routed have aggGroup = -1.
292 pub agg_group: i32,
293 /// 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.
294 pub sec_id_list: Vec<TagValue>,
295 /// For derivatives, the symbol of the underlying contract.
296 pub under_symbol: String,
297 /// For derivatives, returns the underlying security type.
298 pub under_security_type: String,
299 /// 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.
300 pub market_rule_ids: String,
301 /// Real expiration date. Requires TWS 968+ and API v973.04+. Python API specifically requires API v973.06+.
302 pub real_expiration_date: String,
303 /// Last trade time.
304 pub last_trade_time: String,
305 /// Stock type.
306 pub stock_type: String,
307 /// The nine-character bond CUSIP. For Bonds only. Receiving CUSIPs requires a CUSIP market data subscription.
308 pub cusip: String,
309 /// 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.
310 pub ratings: String,
311 /// A description string containing further descriptive information about the bond. For Bonds only.
312 pub desc_append: String,
313 /// The type of bond, such as "CORP.".
314 pub bond_type: String,
315 /// The type of bond coupon. This field is currently not available from the TWS API. For Bonds only.
316 pub coupon_type: String,
317 /// 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.
318 pub callable: bool,
319 /// 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.
320 pub putable: bool,
321 /// 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.
322 pub coupon: f64,
323 /// 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.
324 pub convertible: bool,
325 /// 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.
326 pub maturity: String,
327 /// 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.
328 pub issue_date: String,
329 /// 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.
330 pub next_option_date: String,
331 /// Type of embedded option. This field is currently not available from the TWS API. Only if bond has embedded options.
332 pub next_option_type: String,
333 /// Only if bond has embedded options. This field is currently not available from the TWS API. For Bonds only.
334 pub next_option_partial: bool,
335 /// If populated for the bond in IB's database. For Bonds only.
336 pub notes: String,
337 /// Order's minimal size.
338 pub min_size: f64,
339 /// Order's size increment.
340 pub size_increment: f64,
341 /// Order's suggested size increment.
342 pub suggested_size_increment: f64,
343}
344
345/// TagValue is a convenience struct to define key-value pairs.
346#[derive(Clone, Debug)]
347pub struct TagValue {
348 pub tag: String,
349 pub value: String,
350}
351
352impl ToField for Vec<TagValue> {
353 fn to_field(&self) -> String {
354 let mut values = Vec::new();
355 for tag_value in self {
356 values.push(format!("{}={};", tag_value.tag, tag_value.value))
357 }
358 values.concat()
359 }
360}
361
362// === API ===
363
364fn verify_contract(client: &ClientRef, contract: &Contract) -> Result<(), Error> {
365 if !contract.security_id_type.is_empty() || !contract.security_id.is_empty() {
366 client.check_server_version(
367 server_versions::SEC_ID_TYPE,
368 "It does not support security_id_type or security_id attributes",
369 )?
370 }
371
372 if !contract.trading_class.is_empty() {
373 client.check_server_version(
374 server_versions::TRADING_CLASS,
375 "It does not support the trading_class parameter when requesting contract details.",
376 )?
377 }
378
379 if !contract.primary_exchange.is_empty() {
380 client.check_server_version(
381 server_versions::LINKING,
382 "It does not support primary_exchange parameter when requesting contract details.",
383 )?
384 }
385
386 if !contract.issuer_id.is_empty() {
387 client.check_server_version(
388 server_versions::BOND_ISSUERID,
389 "It does not support issuer_id parameter when requesting contract details.",
390 )?
391 }
392
393 Ok(())
394}
395
396/// Contract data and list of derivative security types
397#[derive(Debug)]
398pub struct ContractDescription {
399 pub contract: Contract,
400 pub derivative_security_types: Vec<String>,
401}
402
403#[derive(Debug, Default)]
404pub struct MarketRule {
405 pub market_rule_id: i32,
406 pub price_increments: Vec<PriceIncrement>,
407}
408
409#[derive(Debug, Default)]
410pub struct PriceIncrement {
411 pub low_edge: f64,
412 pub increment: f64,
413}