1use prost::Message;
2use rust_decimal::Decimal;
3
4use crate::error::{TwsApiError, TwsApiResult};
5use crate::events::{Event, ScannerDataRow};
6use crate::message::{Incoming, PROTOBUF_MSG_ID};
7use crate::protobuf;
8use crate::types::{
9 BarData, ComboLeg, CommissionAndFeesReport, Contract, ContractDescription, ContractDetails,
10 DeltaNeutralContract, Execution, HistogramEntry, HistoricalSession, HistoricalTick,
11 HistoricalTickBidAsk, HistoricalTickLast, IneligibilityReason, LegOpenClose, NewsProvider,
12 Order, OrderAllocation, OrderCondition, OrderState, Origin, PriceIncrement, SmartComponent,
13 SoftDollarTier, TagValue, TickAttribBidAsk, TickAttribLast, TickByTick,
14};
15use crate::types::{DepthMarketDataDescription, FamilyCode};
16
17mod order;
18mod order_proto;
19
20pub fn decode_payload(server_uses_raw_msg_id: bool, payload: &[u8]) -> TwsApiResult<Event> {
22 let (msg_id, body) = split_message_id(server_uses_raw_msg_id, payload)?;
23 if msg_id > PROTOBUF_MSG_ID {
24 return decode_protobuf_event(msg_id - PROTOBUF_MSG_ID, body);
25 }
26
27 let fields = crate::comm::read_fields(body)
28 .into_iter()
29 .map(|field| String::from_utf8_lossy(field).into_owned())
30 .collect::<Vec<_>>();
31
32 match Incoming::try_from(msg_id) {
33 Ok(Incoming::NextValidId) => {
34 let order_id = parse_i32(fields.first())?;
35 Ok(Event::NextValidId { order_id })
36 }
37 Ok(Incoming::CurrentTime) => {
38 let time = parse_i64(fields.first())?;
39 Ok(Event::CurrentTime { time })
40 }
41 Ok(Incoming::CurrentTimeInMillis) => {
42 let time_in_millis = parse_i64(fields.first())?;
43 Ok(Event::CurrentTimeInMillis { time_in_millis })
44 }
45 Ok(Incoming::ErrorMessage) => {
46 let req_id = parse_i32(fields.first())?;
47 let code = parse_i32(fields.get(1))?;
48 let message = fields.get(2).cloned().unwrap_or_default();
49 let advanced_order_reject_json = fields.get(3).cloned().unwrap_or_default();
50 Ok(Event::Error {
51 req_id,
52 time: 0,
53 code,
54 message,
55 advanced_order_reject_json,
56 })
57 }
58 Ok(Incoming::ManagedAccounts) => Ok(Event::ManagedAccounts {
59 accounts: fields.first().cloned().unwrap_or_default(),
60 }),
61 Ok(Incoming::AccountValue) => Ok(Event::AccountValue {
62 key: fields.first().cloned().unwrap_or_default(),
63 value: fields.get(1).cloned().unwrap_or_default(),
64 currency: fields.get(2).cloned().unwrap_or_default(),
65 account_name: fields.get(3).cloned().unwrap_or_default(),
66 }),
67 Ok(Incoming::AccountUpdateTime) => Ok(Event::AccountUpdateTime {
68 timestamp: fields.first().cloned().unwrap_or_default(),
69 }),
70 Ok(Incoming::AccountDownloadEnd) => Ok(Event::AccountDownloadEnd {
71 account_name: fields.first().cloned().unwrap_or_default(),
72 }),
73 Ok(Incoming::NewsBulletins) => Ok(Event::NewsBulletin {
74 news_msg_id: parse_i32(fields.first())?,
75 news_msg_type: parse_i32(fields.get(1))?,
76 news_message: fields.get(2).cloned().unwrap_or_default(),
77 originating_exch: fields.get(3).cloned().unwrap_or_default(),
78 }),
79 Ok(Incoming::ReceiveFa) => Ok(Event::ReceiveFa {
80 fa_data_type: parse_i32(fields.first())?,
81 xml: fields.get(1).cloned().unwrap_or_default(),
82 }),
83 Ok(Incoming::ScannerParameters) => Ok(Event::ScannerParameters {
84 xml: fields.first().cloned().unwrap_or_default(),
85 }),
86 Ok(Incoming::ScannerData) => decode_scanner_data_fields(&fields),
87 Ok(Incoming::SoftDollarTiers) => decode_soft_dollar_tiers_fields(&fields),
88 Ok(Incoming::FamilyCodes) => decode_family_codes_fields(&fields),
89 Ok(Incoming::SymbolSamples) => decode_symbol_samples_fields(&fields),
90 Ok(Incoming::SmartComponents) => decode_smart_components_fields(&fields),
91 Ok(Incoming::MarketDepthExchanges) => decode_market_depth_exchanges_fields(&fields),
92 Ok(Incoming::HeadTimestamp) => Ok(Event::HeadTimestamp {
93 req_id: parse_i32(fields.first())?,
94 head_timestamp: fields.get(1).cloned().unwrap_or_default(),
95 }),
96 Ok(Incoming::HistogramData) => decode_histogram_data_fields(&fields),
97 Ok(Incoming::RerouteMarketDataRequest) => Ok(Event::RerouteMarketDataRequest {
98 req_id: parse_i32(fields.first())?,
99 con_id: parse_i32(fields.get(1))?,
100 exchange: fields.get(2).cloned().unwrap_or_default(),
101 }),
102 Ok(Incoming::RerouteMarketDepthRequest) => Ok(Event::RerouteMarketDepthRequest {
103 req_id: parse_i32(fields.first())?,
104 con_id: parse_i32(fields.get(1))?,
105 exchange: fields.get(2).cloned().unwrap_or_default(),
106 }),
107 Ok(Incoming::MarketRule) => decode_market_rule_fields(&fields),
108 Ok(Incoming::DisplayGroupList) => Ok(Event::DisplayGroupList {
109 req_id: parse_i32(fields.first())?,
110 groups: fields.get(1).cloned().unwrap_or_default(),
111 }),
112 Ok(Incoming::DisplayGroupUpdated) => Ok(Event::DisplayGroupUpdated {
113 req_id: parse_i32(fields.first())?,
114 contract_info: fields.get(1).cloned().unwrap_or_default(),
115 }),
116 Ok(Incoming::MarketDataType) => Ok(Event::MarketDataType {
117 req_id: parse_i32(fields.first())?,
118 market_data_type: parse_i32(fields.get(1))?,
119 }),
120 Ok(Incoming::TickPrice) => Ok(Event::TickPrice {
121 req_id: parse_i32(fields.first())?,
122 tick_type: parse_i32(fields.get(1))?,
123 price: parse_f64(fields.get(2))?,
124 attrib: parse_i32(fields.get(3)).unwrap_or_default(),
125 }),
126 Ok(Incoming::TickSize) => Ok(Event::TickSize {
127 req_id: parse_i32(fields.first())?,
128 tick_type: parse_i32(fields.get(1))?,
129 size: parse_decimal(fields.get(2))?,
130 }),
131 Ok(Incoming::TickGeneric) => Ok(Event::TickGeneric {
132 req_id: parse_i32(fields.first())?,
133 tick_type: parse_i32(fields.get(1))?,
134 value: parse_f64(fields.get(2))?,
135 }),
136 Ok(Incoming::TickString) => Ok(Event::TickString {
137 req_id: parse_i32(fields.first())?,
138 tick_type: parse_i32(fields.get(1))?,
139 value: fields.get(2).cloned().unwrap_or_default(),
140 }),
141 Ok(Incoming::TickOptionComputation) => decode_tick_option_computation_fields(&fields),
142 Ok(Incoming::TickEfp) => Ok(Event::TickEfp {
143 req_id: parse_i32(fields.first())?,
144 tick_type: parse_i32(fields.get(1))?,
145 basis_points: parse_f64(fields.get(2))?,
146 formatted_basis_points: fields.get(3).cloned().unwrap_or_default(),
147 total_dividends: parse_f64(fields.get(4))?,
148 hold_days: parse_i32(fields.get(5))?,
149 future_last_trade_date: fields.get(6).cloned().unwrap_or_default(),
150 dividend_impact: parse_f64(fields.get(7))?,
151 dividends_to_last_trade_date: parse_f64(fields.get(8))?,
152 }),
153 Ok(Incoming::TickRequestParameters) => Ok(Event::TickReqParams {
154 req_id: parse_i32(fields.first())?,
155 min_tick: fields.get(1).cloned().unwrap_or_default(),
156 bbo_exchange: fields.get(2).cloned().unwrap_or_default(),
157 snapshot_permissions: parse_i32(fields.get(3))?,
158 last_price_precision: String::new(),
159 last_size_precision: String::new(),
160 }),
161 Ok(Incoming::TickSnapshotEnd) => Ok(Event::TickSnapshotEnd {
162 req_id: parse_i32(fields.first())?,
163 }),
164 Ok(Incoming::MarketDepth) => decode_market_depth_fields(&fields, false),
165 Ok(Incoming::MarketDepthL2) => decode_market_depth_fields(&fields, true),
166 Ok(Incoming::OrderStatus) => {
167 let offset = if server_uses_raw_msg_id { 0 } else { 1 };
168 Ok(Event::OrderStatus {
169 order_id: parse_i32(fields.get(offset))?,
170 status: fields.get(offset + 1).cloned().unwrap_or_default(),
171 filled: parse_decimal(fields.get(offset + 2))?,
172 remaining: parse_decimal(fields.get(offset + 3))?,
173 avg_fill_price: parse_f64(fields.get(offset + 4))?,
174 perm_id: parse_i64(fields.get(offset + 5))?,
175 parent_id: parse_i32(fields.get(offset + 6))?,
176 last_fill_price: parse_f64(fields.get(offset + 7))?,
177 client_id: parse_i32(fields.get(offset + 8))?,
178 why_held: fields.get(offset + 9).cloned().unwrap_or_default(),
179 market_cap_price: parse_f64(fields.get(offset + 10)).unwrap_or_default(),
180 })
181 }
182 Ok(Incoming::DeltaNeutralValidation) => Ok(Event::DeltaNeutralValidation {
183 req_id: parse_i32(fields.get(1))?,
184 delta_neutral_contract: DeltaNeutralContract {
185 con_id: parse_i32(fields.get(2))?,
186 delta: parse_f64(fields.get(3))?,
187 price: parse_f64(fields.get(4))?,
188 },
189 }),
190 Ok(Incoming::CommissionAndFeesReport) => Ok(Event::CommissionAndFeesReport {
191 report: CommissionAndFeesReport {
192 exec_id: fields.get(1).cloned().unwrap_or_default(),
193 commission_and_fees: parse_f64(fields.get(2))?,
194 currency: fields.get(3).cloned().unwrap_or_default(),
195 realized_pnl: parse_f64(fields.get(4))?,
196 bond_yield: parse_f64(fields.get(5))?,
197 yield_redemption_date: fields.get(6).cloned().unwrap_or_default(),
198 },
199 }),
200 Ok(Incoming::PortfolioValue) => decode_portfolio_value_fields(&fields),
201 Ok(Incoming::OpenOrder) => order::decode_open_order_fields(&fields),
202 Ok(Incoming::OpenOrderEnd) => Ok(Event::OpenOrderEnd),
203 Ok(Incoming::ContractData) => decode_contract_data_fields(&fields),
204 Ok(Incoming::BondContractData) => decode_bond_contract_data_fields(&fields),
205 Ok(Incoming::ContractDataEnd) => Ok(Event::ContractDetailsEnd {
206 req_id: parse_i32(fields.first())?,
207 }),
208 Ok(Incoming::ExecutionData) => decode_execution_data_fields(&fields),
209 Ok(Incoming::ExecutionDataEnd) => Ok(Event::ExecutionDetailsEnd {
210 req_id: parse_i32(fields.first())?,
211 }),
212 Ok(Incoming::PositionData) => decode_position_fields(&fields),
213 Ok(Incoming::PositionEnd) => Ok(Event::PositionEnd),
214 Ok(Incoming::PositionMulti) => decode_position_multi_fields(&fields),
215 Ok(Incoming::PositionMultiEnd) => Ok(Event::PositionMultiEnd {
216 req_id: parse_i32(fields.first())?,
217 }),
218 Ok(Incoming::AccountSummary) => Ok(Event::AccountSummary {
219 req_id: parse_i32(fields.first())?,
220 account: fields.get(1).cloned().unwrap_or_default(),
221 tag: fields.get(2).cloned().unwrap_or_default(),
222 value: fields.get(3).cloned().unwrap_or_default(),
223 currency: fields.get(4).cloned().unwrap_or_default(),
224 }),
225 Ok(Incoming::AccountSummaryEnd) => Ok(Event::AccountSummaryEnd {
226 req_id: parse_i32(fields.first())?,
227 }),
228 Ok(Incoming::AccountUpdateMulti) => Ok(Event::AccountUpdateMulti {
229 req_id: parse_i32(fields.first())?,
230 account: fields.get(1).cloned().unwrap_or_default(),
231 model_code: fields.get(2).cloned().unwrap_or_default(),
232 key: fields.get(3).cloned().unwrap_or_default(),
233 value: fields.get(4).cloned().unwrap_or_default(),
234 currency: fields.get(5).cloned().unwrap_or_default(),
235 }),
236 Ok(Incoming::AccountUpdateMultiEnd) => Ok(Event::AccountUpdateMultiEnd {
237 req_id: parse_i32(fields.first())?,
238 }),
239 Ok(Incoming::VerifyMessageApi) => Ok(Event::VerifyMessageApi {
240 api_data: fields.first().cloned().unwrap_or_default(),
241 }),
242 Ok(Incoming::VerifyCompleted) => Ok(Event::VerifyCompleted {
243 is_successful: parse_bool(fields.first()),
244 error_text: fields.get(1).cloned().unwrap_or_default(),
245 }),
246 Ok(Incoming::VerifyAndAuthMessageApi) => Ok(Event::VerifyAndAuthMessageApi {
247 api_data: fields.first().cloned().unwrap_or_default(),
248 challenge: fields.get(1).cloned().unwrap_or_default(),
249 }),
250 Ok(Incoming::VerifyAndAuthCompleted) => Ok(Event::VerifyAndAuthCompleted {
251 is_successful: parse_bool(fields.first()),
252 error_text: fields.get(1).cloned().unwrap_or_default(),
253 }),
254 Ok(Incoming::SecurityDefinitionOptionParameter) => {
255 decode_security_definition_option_parameter_fields(&fields)
256 }
257 Ok(Incoming::SecurityDefinitionOptionParameterEnd) => {
258 Ok(Event::SecurityDefinitionOptionParameterEnd {
259 req_id: parse_i32(fields.first())?,
260 })
261 }
262 Ok(Incoming::HistoricalData) => decode_historical_data_fields(&fields),
263 Ok(Incoming::HistoricalDataUpdate) => decode_historical_data_update_fields(&fields),
264 Ok(Incoming::HistoricalTicks) => decode_historical_ticks_fields(&fields),
265 Ok(Incoming::HistoricalTicksBidAsk) => decode_historical_ticks_bid_ask_fields(&fields),
266 Ok(Incoming::HistoricalTicksLast) => decode_historical_ticks_last_fields(&fields),
267 Ok(Incoming::TickByTick) => decode_tick_by_tick_fields(&fields),
268 Ok(Incoming::RealTimeBars) => decode_real_time_bar_fields(&fields),
269 Ok(Incoming::HistoricalSchedule) => decode_historical_schedule_fields(&fields),
270 Ok(Incoming::Pnl) => decode_pnl_fields(&fields),
271 Ok(Incoming::PnlSingle) => decode_pnl_single_fields(&fields),
272 Ok(Incoming::NewsArticle) => Ok(Event::NewsArticle {
273 req_id: parse_i32(fields.first())?,
274 article_type: parse_i32(fields.get(1))?,
275 article_text: fields.get(2).cloned().unwrap_or_default(),
276 }),
277 Ok(Incoming::NewsProviders) => decode_news_providers_fields(&fields),
278 Ok(Incoming::HistoricalNews) => Ok(Event::HistoricalNews {
279 req_id: parse_i32(fields.first())?,
280 time: fields.get(1).cloned().unwrap_or_default(),
281 provider_code: fields.get(2).cloned().unwrap_or_default(),
282 article_id: fields.get(3).cloned().unwrap_or_default(),
283 headline: fields.get(4).cloned().unwrap_or_default(),
284 }),
285 Ok(Incoming::HistoricalNewsEnd) => Ok(Event::HistoricalNewsEnd {
286 req_id: parse_i32(fields.first())?,
287 has_more: parse_bool(fields.get(1)),
288 }),
289 Ok(Incoming::TickNews) => Ok(Event::TickNews {
290 req_id: parse_i32(fields.first())?,
291 timestamp: parse_i64(fields.get(1))?,
292 provider_code: fields.get(2).cloned().unwrap_or_default(),
293 article_id: fields.get(3).cloned().unwrap_or_default(),
294 headline: fields.get(4).cloned().unwrap_or_default(),
295 extra_data: fields.get(5).cloned().unwrap_or_default(),
296 }),
297 Ok(Incoming::HistoricalDataEnd) => Ok(Event::HistoricalDataEnd {
298 req_id: parse_i32(fields.first())?,
299 start: fields.get(1).cloned().unwrap_or_default(),
300 end: fields.get(2).cloned().unwrap_or_default(),
301 }),
302 Ok(Incoming::ReplaceFaEnd) => Ok(Event::ReplaceFaEnd {
303 req_id: parse_i32(fields.first())?,
304 text: fields.get(1).cloned().unwrap_or_default(),
305 }),
306 Ok(Incoming::WshMetaData) => Ok(Event::WshMetaData {
307 req_id: parse_i32(fields.first())?,
308 data_json: fields.get(1).cloned().unwrap_or_default(),
309 }),
310 Ok(Incoming::WshEventData) => Ok(Event::WshEventData {
311 req_id: parse_i32(fields.first())?,
312 data_json: fields.get(1).cloned().unwrap_or_default(),
313 }),
314 Ok(Incoming::CompletedOrdersEnd) => Ok(Event::CompletedOrdersEnd),
315 Ok(Incoming::CompletedOrder) => order::decode_completed_order_fields(&fields),
316 Ok(Incoming::OrderBound) => Ok(Event::OrderBound {
317 perm_id: parse_i64(fields.first())?,
318 client_id: parse_i32(fields.get(1))?,
319 order_id: parse_i32(fields.get(2))?,
320 }),
321 Ok(Incoming::UserInfo) => Ok(Event::UserInfo {
322 req_id: parse_i32(fields.first())?,
323 white_branding_id: fields.get(1).cloned().unwrap_or_default(),
324 }),
325 _ => Ok(Event::Raw { msg_id, fields }),
326 }
327}
328
329pub fn decode_protobuf_event(msg_id: i32, payload: &[u8]) -> TwsApiResult<Event> {
331 match Incoming::try_from(msg_id) {
332 Ok(Incoming::NextValidId) => {
333 let msg = protobuf::NextValidId::decode(payload)?;
334 Ok(Event::NextValidId {
335 order_id: msg.order_id.unwrap_or_default(),
336 })
337 }
338 Ok(Incoming::CurrentTime) => {
339 let msg = protobuf::CurrentTime::decode(payload)?;
340 Ok(Event::CurrentTime {
341 time: msg.current_time.unwrap_or_default(),
342 })
343 }
344 Ok(Incoming::CurrentTimeInMillis) => {
345 let msg = protobuf::CurrentTimeInMillis::decode(payload)?;
346 Ok(Event::CurrentTimeInMillis {
347 time_in_millis: msg.current_time_in_millis.unwrap_or_default(),
348 })
349 }
350 Ok(Incoming::ErrorMessage) => {
351 let msg = protobuf::ErrorMessage::decode(payload)?;
352 Ok(Event::Error {
353 req_id: msg.id.unwrap_or_default(),
354 time: msg.error_time.unwrap_or_default(),
355 code: msg.error_code.unwrap_or_default(),
356 message: msg.error_msg.unwrap_or_default(),
357 advanced_order_reject_json: msg.advanced_order_reject_json.unwrap_or_default(),
358 })
359 }
360 Ok(Incoming::ManagedAccounts) => {
361 let msg = protobuf::ManagedAccounts::decode(payload)?;
362 Ok(Event::ManagedAccounts {
363 accounts: msg.accounts_list.unwrap_or_default(),
364 })
365 }
366 Ok(Incoming::OrderStatus) => {
367 let msg = protobuf::OrderStatus::decode(payload)?;
368 Ok(Event::OrderStatus {
369 order_id: msg.order_id.unwrap_or_default(),
370 status: msg.status.unwrap_or_default(),
371 filled: parse_decimal_string(msg.filled.as_deref())?,
372 remaining: parse_decimal_string(msg.remaining.as_deref())?,
373 avg_fill_price: msg.avg_fill_price.unwrap_or_default(),
374 perm_id: msg.perm_id.unwrap_or_default(),
375 parent_id: msg.parent_id.unwrap_or_default(),
376 last_fill_price: msg.last_fill_price.unwrap_or_default(),
377 client_id: msg.client_id.unwrap_or_default(),
378 why_held: msg.why_held.unwrap_or_default(),
379 market_cap_price: msg.mkt_cap_price.unwrap_or_default(),
380 })
381 }
382 Ok(Incoming::TickString) => {
383 let msg = protobuf::TickString::decode(payload)?;
384 Ok(Event::TickString {
385 req_id: msg.req_id.unwrap_or_default(),
386 tick_type: msg.tick_type.unwrap_or_default(),
387 value: msg.value.unwrap_or_default(),
388 })
389 }
390 Ok(Incoming::TickPrice) => {
391 let msg = protobuf::TickPrice::decode(payload)?;
392 Ok(Event::TickPrice {
393 req_id: msg.req_id.unwrap_or_default(),
394 tick_type: msg.tick_type.unwrap_or_default(),
395 price: msg.price.unwrap_or_default(),
396 attrib: msg.attr_mask.unwrap_or_default(),
397 })
398 }
399 Ok(Incoming::TickSize) => {
400 let msg = protobuf::TickSize::decode(payload)?;
401 Ok(Event::TickSize {
402 req_id: msg.req_id.unwrap_or_default(),
403 tick_type: msg.tick_type.unwrap_or_default(),
404 size: parse_decimal_string(msg.size.as_deref())?,
405 })
406 }
407 Ok(Incoming::TickGeneric) => {
408 let msg = protobuf::TickGeneric::decode(payload)?;
409 Ok(Event::TickGeneric {
410 req_id: msg.req_id.unwrap_or_default(),
411 tick_type: msg.tick_type.unwrap_or_default(),
412 value: msg.value.unwrap_or_default(),
413 })
414 }
415 Ok(Incoming::TickOptionComputation) => {
416 let msg = protobuf::TickOptionComputation::decode(payload)?;
417 Ok(Event::TickOptionComputation {
418 req_id: msg.req_id.unwrap_or_default(),
419 tick_type: msg.tick_type.unwrap_or_default(),
420 tick_attrib: msg.tick_attrib.unwrap_or_default(),
421 implied_vol: msg.implied_vol.unwrap_or_default(),
422 delta: msg.delta.unwrap_or_default(),
423 opt_price: msg.opt_price.unwrap_or_default(),
424 pv_dividend: msg.pv_dividend.unwrap_or_default(),
425 gamma: msg.gamma.unwrap_or_default(),
426 vega: msg.vega.unwrap_or_default(),
427 theta: msg.theta.unwrap_or_default(),
428 und_price: msg.und_price.unwrap_or_default(),
429 })
430 }
431 Ok(Incoming::TickRequestParameters) => {
432 let msg = protobuf::TickReqParams::decode(payload)?;
433 Ok(Event::TickReqParams {
434 req_id: msg.req_id.unwrap_or_default(),
435 min_tick: msg.min_tick.unwrap_or_default(),
436 bbo_exchange: msg.bbo_exchange.unwrap_or_default(),
437 snapshot_permissions: msg.snapshot_permissions.unwrap_or_default(),
438 last_price_precision: msg.last_price_precision.unwrap_or_default(),
439 last_size_precision: msg.last_size_precision.unwrap_or_default(),
440 })
441 }
442 Ok(Incoming::CommissionAndFeesReport) => {
443 let msg = protobuf::CommissionAndFeesReport::decode(payload)?;
444 Ok(Event::CommissionAndFeesReport {
445 report: CommissionAndFeesReport {
446 exec_id: msg.exec_id.unwrap_or_default(),
447 commission_and_fees: msg.commission_and_fees.unwrap_or_default(),
448 currency: msg.currency.unwrap_or_default(),
449 realized_pnl: msg.realized_pnl.unwrap_or_default(),
450 bond_yield: msg.bond_yield.unwrap_or_default(),
451 yield_redemption_date: msg.yield_redemption_date.unwrap_or_default(),
452 },
453 })
454 }
455 Ok(Incoming::TickSnapshotEnd) => {
456 let msg = protobuf::TickSnapshotEnd::decode(payload)?;
457 Ok(Event::TickSnapshotEnd {
458 req_id: msg.req_id.unwrap_or_default(),
459 })
460 }
461 Ok(Incoming::MarketDataType) => {
462 let msg = protobuf::MarketDataType::decode(payload)?;
463 Ok(Event::MarketDataType {
464 req_id: msg.req_id.unwrap_or_default(),
465 market_data_type: msg.market_data_type.unwrap_or_default(),
466 })
467 }
468 Ok(Incoming::OpenOrderEnd) => Ok(Event::OpenOrderEnd),
469 Ok(Incoming::AccountValue) => {
470 let msg = protobuf::AccountValue::decode(payload)?;
471 Ok(Event::AccountValue {
472 key: msg.key.unwrap_or_default(),
473 value: msg.value.unwrap_or_default(),
474 currency: msg.currency.unwrap_or_default(),
475 account_name: msg.account_name.unwrap_or_default(),
476 })
477 }
478 Ok(Incoming::PortfolioValue) => {
479 let msg = protobuf::PortfolioValue::decode(payload)?;
480 Ok(Event::PortfolioValue {
481 contract: Box::new(proto_contract_to_contract(msg.contract)),
482 position: parse_decimal_string(msg.position.as_deref())?,
483 market_price: msg.market_price.unwrap_or_default(),
484 market_value: msg.market_value.unwrap_or_default(),
485 average_cost: msg.average_cost.unwrap_or_default(),
486 unrealized_pnl: msg.unrealized_pnl.unwrap_or_default(),
487 realized_pnl: msg.realized_pnl.unwrap_or_default(),
488 account_name: msg.account_name.unwrap_or_default(),
489 })
490 }
491 Ok(Incoming::AccountUpdateTime) => {
492 let msg = protobuf::AccountUpdateTime::decode(payload)?;
493 Ok(Event::AccountUpdateTime {
494 timestamp: msg.time_stamp.unwrap_or_default(),
495 })
496 }
497 Ok(Incoming::AccountDownloadEnd) => {
498 let msg = protobuf::AccountDataEnd::decode(payload)?;
499 Ok(Event::AccountDownloadEnd {
500 account_name: msg.account_name.unwrap_or_default(),
501 })
502 }
503 Ok(Incoming::MarketDepth) => {
504 let msg = protobuf::MarketDepth::decode(payload)?;
505 proto_market_depth_to_event(msg.req_id, msg.market_depth_data)
506 }
507 Ok(Incoming::MarketDepthL2) => {
508 let msg = protobuf::MarketDepthL2::decode(payload)?;
509 proto_market_depth_to_event(msg.req_id, msg.market_depth_data)
510 }
511 Ok(Incoming::MarketDepthExchanges) => {
512 let msg = protobuf::MarketDepthExchanges::decode(payload)?;
513 Ok(Event::MarketDepthExchanges {
514 descriptions: msg
515 .depth_market_data_descriptions
516 .into_iter()
517 .map(|description| DepthMarketDataDescription {
518 exchange: description.exchange.unwrap_or_default(),
519 security_type: description.sec_type.unwrap_or_default(),
520 listing_exchange: description.listing_exch.unwrap_or_default(),
521 service_data_type: description.service_data_type.unwrap_or_default(),
522 aggregate_group: description.agg_group.unwrap_or_default(),
523 })
524 .collect(),
525 })
526 }
527 Ok(Incoming::SmartComponents) => {
528 let msg = protobuf::SmartComponents::decode(payload)?;
529 Ok(Event::SmartComponents {
530 req_id: msg.req_id.unwrap_or_default(),
531 components: msg
532 .smart_components
533 .into_iter()
534 .map(proto_smart_component)
535 .collect(),
536 })
537 }
538 Ok(Incoming::RerouteMarketDataRequest) => {
539 let msg = protobuf::RerouteMarketDataRequest::decode(payload)?;
540 Ok(Event::RerouteMarketDataRequest {
541 req_id: msg.req_id.unwrap_or_default(),
542 con_id: msg.con_id.unwrap_or_default(),
543 exchange: msg.exchange.unwrap_or_default(),
544 })
545 }
546 Ok(Incoming::RerouteMarketDepthRequest) => {
547 let msg = protobuf::RerouteMarketDepthRequest::decode(payload)?;
548 Ok(Event::RerouteMarketDepthRequest {
549 req_id: msg.req_id.unwrap_or_default(),
550 con_id: msg.con_id.unwrap_or_default(),
551 exchange: msg.exchange.unwrap_or_default(),
552 })
553 }
554 Ok(Incoming::OpenOrder) => {
555 let msg = protobuf::OpenOrder::decode(payload)?;
556 Ok(Event::OpenOrder {
557 order_id: msg.order_id.unwrap_or_default(),
558 contract: Box::new(proto_contract_to_contract(msg.contract)),
559 order: Box::new(order_proto::proto_order_to_order(msg.order)?),
560 order_state: Box::new(order_proto::proto_order_state_to_order_state(
561 msg.order_state,
562 )),
563 })
564 }
565 Ok(Incoming::ContractData) => {
566 let msg = protobuf::ContractData::decode(payload)?;
567 Ok(Event::ContractDetails {
568 req_id: msg.req_id.unwrap_or_default(),
569 details: Box::new(proto_contract_details_to_contract_details(
570 msg.contract,
571 msg.contract_details,
572 )?),
573 })
574 }
575 Ok(Incoming::BondContractData) => {
576 let msg = protobuf::ContractData::decode(payload)?;
577 Ok(Event::BondContractDetails {
578 req_id: msg.req_id.unwrap_or_default(),
579 details: Box::new(proto_contract_details_to_contract_details(
580 msg.contract,
581 msg.contract_details,
582 )?),
583 })
584 }
585 Ok(Incoming::ContractDataEnd) => {
586 let msg = protobuf::ContractDataEnd::decode(payload)?;
587 Ok(Event::ContractDetailsEnd {
588 req_id: msg.req_id.unwrap_or_default(),
589 })
590 }
591 Ok(Incoming::ExecutionData) => {
592 let msg = protobuf::ExecutionDetails::decode(payload)?;
593 Ok(Event::ExecutionDetails {
594 req_id: msg.req_id.unwrap_or_default(),
595 contract: Box::new(proto_contract_to_contract(msg.contract)),
596 execution: proto_execution_to_execution(msg.execution)?,
597 })
598 }
599 Ok(Incoming::ExecutionDataEnd) => {
600 let msg = protobuf::ExecutionDetailsEnd::decode(payload)?;
601 Ok(Event::ExecutionDetailsEnd {
602 req_id: msg.req_id.unwrap_or_default(),
603 })
604 }
605 Ok(Incoming::HistoricalData) => {
606 let msg = protobuf::HistoricalData::decode(payload)?;
607 Ok(Event::HistoricalDataBars {
608 req_id: msg.req_id.unwrap_or_default(),
609 bars: msg
610 .historical_data_bars
611 .into_iter()
612 .map(proto_bar_to_bar)
613 .collect::<TwsApiResult<Vec<_>>>()?,
614 })
615 }
616 Ok(Incoming::HistoricalDataUpdate) => {
617 let msg = protobuf::HistoricalDataUpdate::decode(payload)?;
618 Ok(Event::HistoricalDataUpdate {
619 req_id: msg.req_id.unwrap_or_default(),
620 bar: proto_bar_to_bar(msg.historical_data_bar.unwrap_or_default())?,
621 })
622 }
623 Ok(Incoming::RealTimeBars) => {
624 let msg = protobuf::RealTimeBarTick::decode(payload)?;
625 Ok(Event::RealTimeBar {
626 req_id: msg.req_id.unwrap_or_default(),
627 time: msg.time.unwrap_or_default(),
628 bar: BarData {
629 date: msg.time.unwrap_or_default().to_string(),
630 open: msg.open.unwrap_or_default(),
631 high: msg.high.unwrap_or_default(),
632 low: msg.low.unwrap_or_default(),
633 close: msg.close.unwrap_or_default(),
634 volume: parse_decimal_string(msg.volume.as_deref())?,
635 wap: parse_decimal_string(msg.wap.as_deref())?,
636 bar_count: msg.count.unwrap_or_default(),
637 },
638 })
639 }
640 Ok(Incoming::HeadTimestamp) => {
641 let msg = protobuf::HeadTimestamp::decode(payload)?;
642 Ok(Event::HeadTimestamp {
643 req_id: msg.req_id.unwrap_or_default(),
644 head_timestamp: msg.head_timestamp.unwrap_or_default(),
645 })
646 }
647 Ok(Incoming::HistogramData) => {
648 let msg = protobuf::HistogramData::decode(payload)?;
649 Ok(Event::HistogramData {
650 req_id: msg.req_id.unwrap_or_default(),
651 items: msg
652 .histogram_data_entries
653 .into_iter()
654 .map(|entry| {
655 Ok(HistogramEntry {
656 price: entry.price.unwrap_or_default(),
657 size: parse_decimal_string(entry.size.as_deref())?,
658 })
659 })
660 .collect::<TwsApiResult<Vec<_>>>()?,
661 })
662 }
663 Ok(Incoming::ScannerParameters) => {
664 let msg = protobuf::ScannerParameters::decode(payload)?;
665 Ok(Event::ScannerParameters {
666 xml: msg.xml.unwrap_or_default(),
667 })
668 }
669 Ok(Incoming::ScannerData) => {
670 let msg = protobuf::ScannerData::decode(payload)?;
671 Ok(Event::ScannerData {
672 req_id: msg.req_id.unwrap_or_default(),
673 rows: msg
674 .scanner_data_element
675 .into_iter()
676 .map(|row| ScannerDataRow {
677 rank: row.rank.unwrap_or_default(),
678 contract: Box::new(proto_contract_to_contract(row.contract)),
679 market_name: row.market_name.unwrap_or_default(),
680 distance: row.distance.unwrap_or_default(),
681 benchmark: row.benchmark.unwrap_or_default(),
682 projection: row.projection.unwrap_or_default(),
683 combo_key: row.combo_key.unwrap_or_default(),
684 })
685 .collect(),
686 })
687 }
688 Ok(Incoming::SoftDollarTiers) => {
689 let msg = protobuf::SoftDollarTiers::decode(payload)?;
690 Ok(Event::SoftDollarTiers {
691 req_id: msg.req_id.unwrap_or_default(),
692 tiers: msg
693 .soft_dollar_tiers
694 .into_iter()
695 .map(|tier| SoftDollarTier {
696 name: tier.name.unwrap_or_default(),
697 value: tier.value.unwrap_or_default(),
698 display_name: tier.display_name.unwrap_or_default(),
699 })
700 .collect(),
701 })
702 }
703 Ok(Incoming::FamilyCodes) => {
704 let msg = protobuf::FamilyCodes::decode(payload)?;
705 Ok(Event::FamilyCodes {
706 family_codes: msg
707 .family_codes
708 .into_iter()
709 .map(|code| FamilyCode {
710 account_id: code.account_id.unwrap_or_default(),
711 family_code: code.family_code.unwrap_or_default(),
712 })
713 .collect(),
714 })
715 }
716 Ok(Incoming::SymbolSamples) => {
717 let msg = protobuf::SymbolSamples::decode(payload)?;
718 Ok(Event::SymbolSamples {
719 req_id: msg.req_id.unwrap_or_default(),
720 descriptions: msg
721 .contract_descriptions
722 .into_iter()
723 .map(proto_contract_description)
724 .collect(),
725 })
726 }
727 Ok(Incoming::MarketRule) => {
728 let msg = protobuf::MarketRule::decode(payload)?;
729 Ok(Event::MarketRule {
730 market_rule_id: msg.market_rule_id.unwrap_or_default(),
731 price_increments: msg
732 .price_increments
733 .into_iter()
734 .map(|increment| PriceIncrement {
735 low_edge: increment.low_edge.unwrap_or_default(),
736 increment: increment.increment.unwrap_or_default(),
737 })
738 .collect(),
739 })
740 }
741 Ok(Incoming::PositionData) => {
742 let msg = protobuf::Position::decode(payload)?;
743 Ok(Event::Position {
744 account: msg.account.unwrap_or_default(),
745 contract: Box::new(proto_contract_to_contract(msg.contract)),
746 position: parse_decimal_string(msg.position.as_deref())?,
747 avg_cost: msg.avg_cost.unwrap_or_default(),
748 })
749 }
750 Ok(Incoming::PositionEnd) => Ok(Event::PositionEnd),
751 Ok(Incoming::PositionMulti) => {
752 let msg = protobuf::PositionMulti::decode(payload)?;
753 Ok(Event::PositionMulti {
754 req_id: msg.req_id.unwrap_or_default(),
755 account: msg.account.unwrap_or_default(),
756 model_code: msg.model_code.unwrap_or_default(),
757 contract: Box::new(proto_contract_to_contract(msg.contract)),
758 position: parse_decimal_string(msg.position.as_deref())?,
759 avg_cost: msg.avg_cost.unwrap_or_default(),
760 })
761 }
762 Ok(Incoming::PositionMultiEnd) => {
763 let msg = protobuf::PositionMultiEnd::decode(payload)?;
764 Ok(Event::PositionMultiEnd {
765 req_id: msg.req_id.unwrap_or_default(),
766 })
767 }
768 Ok(Incoming::AccountSummary) => {
769 let msg = protobuf::AccountSummary::decode(payload)?;
770 Ok(Event::AccountSummary {
771 req_id: msg.req_id.unwrap_or_default(),
772 account: msg.account.unwrap_or_default(),
773 tag: msg.tag.unwrap_or_default(),
774 value: msg.value.unwrap_or_default(),
775 currency: msg.currency.unwrap_or_default(),
776 })
777 }
778 Ok(Incoming::AccountSummaryEnd) => {
779 let msg = protobuf::AccountSummaryEnd::decode(payload)?;
780 Ok(Event::AccountSummaryEnd {
781 req_id: msg.req_id.unwrap_or_default(),
782 })
783 }
784 Ok(Incoming::AccountUpdateMulti) => {
785 let msg = protobuf::AccountUpdateMulti::decode(payload)?;
786 Ok(Event::AccountUpdateMulti {
787 req_id: msg.req_id.unwrap_or_default(),
788 account: msg.account.unwrap_or_default(),
789 model_code: msg.model_code.unwrap_or_default(),
790 key: msg.key.unwrap_or_default(),
791 value: msg.value.unwrap_or_default(),
792 currency: msg.currency.unwrap_or_default(),
793 })
794 }
795 Ok(Incoming::AccountUpdateMultiEnd) => {
796 let msg = protobuf::AccountUpdateMultiEnd::decode(payload)?;
797 Ok(Event::AccountUpdateMultiEnd {
798 req_id: msg.req_id.unwrap_or_default(),
799 })
800 }
801 Ok(Incoming::HistoricalDataEnd) => {
802 let msg = protobuf::HistoricalDataEnd::decode(payload)?;
803 Ok(Event::HistoricalDataEnd {
804 req_id: msg.req_id.unwrap_or_default(),
805 start: msg.start_date_str.unwrap_or_default(),
806 end: msg.end_date_str.unwrap_or_default(),
807 })
808 }
809 Ok(Incoming::HistoricalTicks) => {
810 let msg = protobuf::HistoricalTicks::decode(payload)?;
811 Ok(Event::HistoricalTicks {
812 req_id: msg.req_id.unwrap_or_default(),
813 ticks: msg
814 .historical_ticks
815 .into_iter()
816 .map(proto_historical_tick)
817 .collect::<TwsApiResult<Vec<_>>>()?,
818 done: msg.is_done.unwrap_or_default(),
819 })
820 }
821 Ok(Incoming::HistoricalTicksBidAsk) => {
822 let msg = protobuf::HistoricalTicksBidAsk::decode(payload)?;
823 Ok(Event::HistoricalTicksBidAsk {
824 req_id: msg.req_id.unwrap_or_default(),
825 ticks: msg
826 .historical_ticks_bid_ask
827 .into_iter()
828 .map(proto_historical_tick_bid_ask)
829 .collect::<TwsApiResult<Vec<_>>>()?,
830 done: msg.is_done.unwrap_or_default(),
831 })
832 }
833 Ok(Incoming::HistoricalTicksLast) => {
834 let msg = protobuf::HistoricalTicksLast::decode(payload)?;
835 Ok(Event::HistoricalTicksLast {
836 req_id: msg.req_id.unwrap_or_default(),
837 ticks: msg
838 .historical_ticks_last
839 .into_iter()
840 .map(proto_historical_tick_last)
841 .collect::<TwsApiResult<Vec<_>>>()?,
842 done: msg.is_done.unwrap_or_default(),
843 })
844 }
845 Ok(Incoming::TickByTick) => {
846 let msg = protobuf::TickByTickData::decode(payload)?;
847 Ok(Event::TickByTick {
848 req_id: msg.req_id.unwrap_or_default(),
849 tick_type: msg.tick_type.unwrap_or_default(),
850 tick: proto_tick_by_tick(msg.tick)?,
851 })
852 }
853 Ok(Incoming::HistoricalSchedule) => {
854 let msg = protobuf::HistoricalSchedule::decode(payload)?;
855 Ok(Event::HistoricalSchedule {
856 req_id: msg.req_id.unwrap_or_default(),
857 start_date_time: msg.start_date_time.unwrap_or_default(),
858 end_date_time: msg.end_date_time.unwrap_or_default(),
859 time_zone: msg.time_zone.unwrap_or_default(),
860 sessions: msg
861 .historical_sessions
862 .into_iter()
863 .map(proto_historical_session)
864 .collect(),
865 })
866 }
867 Ok(Incoming::Pnl) => {
868 let msg = protobuf::PnL::decode(payload)?;
869 Ok(Event::Pnl {
870 req_id: msg.req_id.unwrap_or_default(),
871 daily_pnl: msg.daily_pn_l.unwrap_or_default(),
872 unrealized_pnl: msg.unrealized_pn_l.unwrap_or_default(),
873 realized_pnl: msg.realized_pn_l.unwrap_or_default(),
874 })
875 }
876 Ok(Incoming::PnlSingle) => {
877 let msg = protobuf::PnLSingle::decode(payload)?;
878 Ok(Event::PnlSingle {
879 req_id: msg.req_id.unwrap_or_default(),
880 position: parse_decimal_string(msg.position.as_deref())?,
881 daily_pnl: msg.daily_pn_l.unwrap_or_default(),
882 unrealized_pnl: msg.unrealized_pn_l.unwrap_or_default(),
883 realized_pnl: msg.realized_pn_l.unwrap_or_default(),
884 value: msg.value.unwrap_or_default(),
885 })
886 }
887 Ok(Incoming::NewsArticle) => {
888 let msg = protobuf::NewsArticle::decode(payload)?;
889 Ok(Event::NewsArticle {
890 req_id: msg.req_id.unwrap_or_default(),
891 article_type: msg.article_type.unwrap_or_default(),
892 article_text: msg.article_text.unwrap_or_default(),
893 })
894 }
895 Ok(Incoming::NewsBulletins) => {
896 let msg = protobuf::NewsBulletin::decode(payload)?;
897 Ok(Event::NewsBulletin {
898 news_msg_id: msg.news_msg_id.unwrap_or_default(),
899 news_msg_type: msg.news_msg_type.unwrap_or_default(),
900 news_message: msg.news_message.unwrap_or_default(),
901 originating_exch: msg.originating_exch.unwrap_or_default(),
902 })
903 }
904 Ok(Incoming::NewsProviders) => {
905 let msg = protobuf::NewsProviders::decode(payload)?;
906 Ok(Event::NewsProviders {
907 providers: msg
908 .news_providers
909 .into_iter()
910 .map(|provider| NewsProvider {
911 code: provider.provider_code.unwrap_or_default(),
912 name: provider.provider_name.unwrap_or_default(),
913 })
914 .collect(),
915 })
916 }
917 Ok(Incoming::HistoricalNews) => {
918 let msg = protobuf::HistoricalNews::decode(payload)?;
919 Ok(Event::HistoricalNews {
920 req_id: msg.req_id.unwrap_or_default(),
921 time: msg.time.unwrap_or_default(),
922 provider_code: msg.provider_code.unwrap_or_default(),
923 article_id: msg.article_id.unwrap_or_default(),
924 headline: msg.headline.unwrap_or_default(),
925 })
926 }
927 Ok(Incoming::HistoricalNewsEnd) => {
928 let msg = protobuf::HistoricalNewsEnd::decode(payload)?;
929 Ok(Event::HistoricalNewsEnd {
930 req_id: msg.req_id.unwrap_or_default(),
931 has_more: msg.has_more.unwrap_or_default(),
932 })
933 }
934 Ok(Incoming::TickNews) => {
935 let msg = protobuf::TickNews::decode(payload)?;
936 Ok(Event::TickNews {
937 req_id: msg.req_id.unwrap_or_default(),
938 timestamp: msg.timestamp.unwrap_or_default(),
939 provider_code: msg.provider_code.unwrap_or_default(),
940 article_id: msg.article_id.unwrap_or_default(),
941 headline: msg.headline.unwrap_or_default(),
942 extra_data: msg.extra_data.unwrap_or_default(),
943 })
944 }
945 Ok(Incoming::WshMetaData) => {
946 let msg = protobuf::WshMetaData::decode(payload)?;
947 Ok(Event::WshMetaData {
948 req_id: msg.req_id.unwrap_or_default(),
949 data_json: msg.data_json.unwrap_or_default(),
950 })
951 }
952 Ok(Incoming::WshEventData) => {
953 let msg = protobuf::WshEventData::decode(payload)?;
954 Ok(Event::WshEventData {
955 req_id: msg.req_id.unwrap_or_default(),
956 data_json: msg.data_json.unwrap_or_default(),
957 })
958 }
959 Ok(Incoming::ReceiveFa) => {
960 let msg = protobuf::ReceiveFa::decode(payload)?;
961 Ok(Event::ReceiveFa {
962 fa_data_type: msg.fa_data_type.unwrap_or_default(),
963 xml: msg.xml.unwrap_or_default(),
964 })
965 }
966 Ok(Incoming::ReplaceFaEnd) => {
967 let msg = protobuf::ReplaceFaEnd::decode(payload)?;
968 Ok(Event::ReplaceFaEnd {
969 req_id: msg.req_id.unwrap_or_default(),
970 text: msg.text.unwrap_or_default(),
971 })
972 }
973 Ok(Incoming::DisplayGroupList) => {
974 let msg = protobuf::DisplayGroupList::decode(payload)?;
975 Ok(Event::DisplayGroupList {
976 req_id: msg.req_id.unwrap_or_default(),
977 groups: msg.groups.unwrap_or_default(),
978 })
979 }
980 Ok(Incoming::DisplayGroupUpdated) => {
981 let msg = protobuf::DisplayGroupUpdated::decode(payload)?;
982 Ok(Event::DisplayGroupUpdated {
983 req_id: msg.req_id.unwrap_or_default(),
984 contract_info: msg.contract_info.unwrap_or_default(),
985 })
986 }
987 Ok(Incoming::VerifyMessageApi) => {
988 let msg = protobuf::VerifyMessageApi::decode(payload)?;
989 Ok(Event::VerifyMessageApi {
990 api_data: msg.api_data.unwrap_or_default(),
991 })
992 }
993 Ok(Incoming::VerifyCompleted) => {
994 let msg = protobuf::VerifyCompleted::decode(payload)?;
995 Ok(Event::VerifyCompleted {
996 is_successful: msg.is_successful.unwrap_or_default(),
997 error_text: msg.error_text.unwrap_or_default(),
998 })
999 }
1000 Ok(Incoming::OrderBound) => {
1001 let msg = protobuf::OrderBound::decode(payload)?;
1002 Ok(Event::OrderBound {
1003 perm_id: msg.perm_id.unwrap_or_default(),
1004 client_id: msg.client_id.unwrap_or_default(),
1005 order_id: msg.order_id.unwrap_or_default(),
1006 })
1007 }
1008 Ok(Incoming::CompletedOrder) => {
1009 let msg = protobuf::CompletedOrder::decode(payload)?;
1010 Ok(Event::CompletedOrder {
1011 contract: Box::new(proto_contract_to_contract(msg.contract)),
1012 order: Box::new(order_proto::proto_order_to_order(msg.order)?),
1013 order_state: Box::new(order_proto::proto_order_state_to_order_state(
1014 msg.order_state,
1015 )),
1016 })
1017 }
1018 Ok(Incoming::CompletedOrdersEnd) => Ok(Event::CompletedOrdersEnd),
1019 Ok(Incoming::UserInfo) => {
1020 let msg = protobuf::UserInfo::decode(payload)?;
1021 Ok(Event::UserInfo {
1022 req_id: msg.req_id.unwrap_or_default(),
1023 white_branding_id: msg.white_branding_id.unwrap_or_default(),
1024 })
1025 }
1026 Ok(Incoming::ConfigResponse) => {
1027 let msg = protobuf::ConfigResponse::decode(payload)?;
1028 Ok(Event::ConfigResponse {
1029 req_id: msg.req_id.unwrap_or_default(),
1030 status: String::new(),
1031 message: format!(
1032 "lock_and_exit={}, messages={}, api={}, orders={}",
1033 msg.lock_and_exit.is_some(),
1034 msg.messages.len(),
1035 msg.api.is_some(),
1036 msg.orders.is_some()
1037 ),
1038 })
1039 }
1040 Ok(Incoming::UpdateConfigResponse) => {
1041 let msg = protobuf::UpdateConfigResponse::decode(payload)?;
1042 Ok(Event::UpdateConfigResponse {
1043 req_id: msg.req_id.unwrap_or_default(),
1044 status: msg.status.unwrap_or_default(),
1045 message: msg.message.unwrap_or_default(),
1046 changed_fields: msg.changed_fields,
1047 errors: msg.errors,
1048 })
1049 }
1050 _ => Ok(Event::RawProtobuf {
1051 msg_id,
1052 payload: payload.to_vec(),
1053 }),
1054 }
1055}
1056
1057fn proto_market_depth_to_event(
1058 req_id: Option<i32>,
1059 data: Option<protobuf::MarketDepthData>,
1060) -> TwsApiResult<Event> {
1061 let data = data.unwrap_or_default();
1062 Ok(Event::MarketDepth {
1063 req_id: req_id.unwrap_or_default(),
1064 position: data.position.unwrap_or_default(),
1065 operation: data.operation.unwrap_or_default(),
1066 side: data.side.unwrap_or_default(),
1067 price: data.price.unwrap_or_default(),
1068 size: parse_decimal_string(data.size.as_deref())?,
1069 market_maker: data.market_maker.unwrap_or_default(),
1070 is_smart_depth: data.is_smart_depth.unwrap_or_default(),
1071 })
1072}
1073
1074fn proto_bar_to_bar(bar: protobuf::HistoricalDataBar) -> TwsApiResult<BarData> {
1075 Ok(BarData {
1076 date: bar.date.unwrap_or_default(),
1077 open: bar.open.unwrap_or_default(),
1078 high: bar.high.unwrap_or_default(),
1079 low: bar.low.unwrap_or_default(),
1080 close: bar.close.unwrap_or_default(),
1081 volume: parse_decimal_string(bar.volume.as_deref())?,
1082 wap: parse_decimal_string(bar.wap.as_deref())?,
1083 bar_count: bar.bar_count.unwrap_or_default(),
1084 })
1085}
1086
1087fn proto_historical_tick(tick: protobuf::HistoricalTick) -> TwsApiResult<HistoricalTick> {
1088 Ok(HistoricalTick {
1089 time: tick.time.unwrap_or_default(),
1090 price: tick.price.unwrap_or_default(),
1091 size: parse_decimal_string(tick.size.as_deref())?,
1092 })
1093}
1094
1095fn proto_tick_attrib_bid_ask(attrib: Option<protobuf::TickAttribBidAsk>) -> TickAttribBidAsk {
1096 let Some(attrib) = attrib else {
1097 return TickAttribBidAsk::default();
1098 };
1099
1100 TickAttribBidAsk {
1101 bid_past_low: attrib.bid_past_low.unwrap_or_default(),
1102 ask_past_high: attrib.ask_past_high.unwrap_or_default(),
1103 }
1104}
1105
1106fn proto_tick_attrib_last(attrib: Option<protobuf::TickAttribLast>) -> TickAttribLast {
1107 let Some(attrib) = attrib else {
1108 return TickAttribLast::default();
1109 };
1110
1111 TickAttribLast {
1112 past_limit: attrib.past_limit.unwrap_or_default(),
1113 unreported: attrib.unreported.unwrap_or_default(),
1114 }
1115}
1116
1117fn proto_historical_tick_bid_ask(
1118 tick: protobuf::HistoricalTickBidAsk,
1119) -> TwsApiResult<HistoricalTickBidAsk> {
1120 Ok(HistoricalTickBidAsk {
1121 time: tick.time.unwrap_or_default(),
1122 tick_attrib_bid_ask: proto_tick_attrib_bid_ask(tick.tick_attrib_bid_ask),
1123 price_bid: tick.price_bid.unwrap_or_default(),
1124 price_ask: tick.price_ask.unwrap_or_default(),
1125 size_bid: parse_decimal_string(tick.size_bid.as_deref())?,
1126 size_ask: parse_decimal_string(tick.size_ask.as_deref())?,
1127 })
1128}
1129
1130fn proto_historical_tick_last(
1131 tick: protobuf::HistoricalTickLast,
1132) -> TwsApiResult<HistoricalTickLast> {
1133 Ok(HistoricalTickLast {
1134 time: tick.time.unwrap_or_default(),
1135 tick_attrib_last: proto_tick_attrib_last(tick.tick_attrib_last),
1136 price: tick.price.unwrap_or_default(),
1137 size: parse_decimal_string(tick.size.as_deref())?,
1138 exchange: tick.exchange.unwrap_or_default(),
1139 special_conditions: tick.special_conditions.unwrap_or_default(),
1140 })
1141}
1142
1143fn proto_tick_by_tick(
1144 tick: Option<protobuf::tick_by_tick_data::Tick>,
1145) -> TwsApiResult<Option<TickByTick>> {
1146 let Some(tick) = tick else {
1147 return Ok(None);
1148 };
1149
1150 let tick = match tick {
1151 protobuf::tick_by_tick_data::Tick::HistoricalTickLast(tick) => {
1152 TickByTick::Last(proto_historical_tick_last(tick)?)
1153 }
1154 protobuf::tick_by_tick_data::Tick::HistoricalTickBidAsk(tick) => {
1155 TickByTick::BidAsk(proto_historical_tick_bid_ask(tick)?)
1156 }
1157 protobuf::tick_by_tick_data::Tick::HistoricalTickMidPoint(tick) => {
1158 TickByTick::MidPoint(proto_historical_tick(tick)?)
1159 }
1160 };
1161 Ok(Some(tick))
1162}
1163
1164fn proto_historical_session(session: protobuf::HistoricalSession) -> HistoricalSession {
1165 HistoricalSession {
1166 start_date_time: session.start_date_time.unwrap_or_default(),
1167 end_date_time: session.end_date_time.unwrap_or_default(),
1168 ref_date: session.ref_date.unwrap_or_default(),
1169 }
1170}
1171
1172fn proto_smart_component(component: protobuf::SmartComponent) -> SmartComponent {
1173 SmartComponent {
1174 bit_number: component.bit_number.unwrap_or_default(),
1175 exchange: component.exchange.unwrap_or_default(),
1176 exchange_letter: component.exchange_letter.unwrap_or_default(),
1177 }
1178}
1179
1180fn proto_contract_description(description: protobuf::ContractDescription) -> ContractDescription {
1181 ContractDescription {
1182 contract: proto_contract_to_contract(description.contract),
1183 derivative_sec_types: description.derivative_sec_types,
1184 }
1185}
1186
1187fn proto_execution_to_execution(execution: Option<protobuf::Execution>) -> TwsApiResult<Execution> {
1188 let Some(execution) = execution else {
1189 return Ok(Execution::default());
1190 };
1191
1192 Ok(Execution {
1193 order_id: execution.order_id.unwrap_or_default(),
1194 exec_id: execution.exec_id.unwrap_or_default(),
1195 time: execution.time.unwrap_or_default(),
1196 acct_number: execution.acct_number.unwrap_or_default(),
1197 exchange: execution.exchange.unwrap_or_default(),
1198 side: execution.side.unwrap_or_default(),
1199 shares: parse_decimal_string(execution.shares.as_deref())?,
1200 price: execution.price.unwrap_or_default(),
1201 perm_id: execution.perm_id.unwrap_or_default(),
1202 client_id: execution.client_id.unwrap_or_default(),
1203 liquidation: i32::from(execution.is_liquidation.unwrap_or_default()),
1204 cum_qty: parse_decimal_string(execution.cum_qty.as_deref())?,
1205 avg_price: execution.avg_price.unwrap_or_default(),
1206 order_ref: execution.order_ref.unwrap_or_default(),
1207 ev_rule: execution.ev_rule.unwrap_or_default(),
1208 ev_multiplier: execution.ev_multiplier.unwrap_or_default(),
1209 model_code: execution.model_code.unwrap_or_default(),
1210 last_liquidity: execution.last_liquidity.unwrap_or_default(),
1211 pending_price_revision: execution.is_price_revision_pending.unwrap_or_default(),
1212 submitter: execution.submitter.unwrap_or_default(),
1213 opt_exercise_or_lapse_type: execution.opt_exercise_or_lapse_type.unwrap_or_default(),
1214 })
1215}
1216
1217fn proto_contract_details_to_contract_details(
1218 contract: Option<protobuf::Contract>,
1219 details: Option<protobuf::ContractDetails>,
1220) -> TwsApiResult<ContractDetails> {
1221 let Some(details) = details else {
1222 return Ok(ContractDetails {
1223 contract: proto_contract_to_contract(contract),
1224 ..ContractDetails::default()
1225 });
1226 };
1227
1228 let mut sec_id_list = details
1229 .sec_id_list
1230 .into_iter()
1231 .map(|(tag, value)| TagValue { tag, value })
1232 .collect::<Vec<_>>();
1233 sec_id_list.sort_by(|left, right| left.tag.cmp(&right.tag));
1234
1235 Ok(ContractDetails {
1236 contract: proto_contract_to_contract(contract),
1237 market_name: details.market_name.unwrap_or_default(),
1238 min_tick: details
1239 .min_tick
1240 .as_deref()
1241 .and_then(|value| value.parse::<f64>().ok())
1242 .unwrap_or_default(),
1243 order_types: details.order_types.unwrap_or_default(),
1244 valid_exchanges: details.valid_exchanges.unwrap_or_default(),
1245 price_magnifier: details.price_magnifier.unwrap_or_default(),
1246 under_con_id: details.under_con_id.unwrap_or_default(),
1247 long_name: details.long_name.unwrap_or_default(),
1248 contract_month: details.contract_month.unwrap_or_default(),
1249 industry: details.industry.unwrap_or_default(),
1250 category: details.category.unwrap_or_default(),
1251 subcategory: details.subcategory.unwrap_or_default(),
1252 time_zone_id: details.time_zone_id.unwrap_or_default(),
1253 trading_hours: details.trading_hours.unwrap_or_default(),
1254 liquid_hours: details.liquid_hours.unwrap_or_default(),
1255 ev_rule: details.ev_rule.unwrap_or_default(),
1256 ev_multiplier: details.ev_multiplier.unwrap_or_default(),
1257 sec_id_list,
1258 aggregate_group: details.agg_group.unwrap_or_default(),
1259 under_symbol: details.under_symbol.unwrap_or_default(),
1260 under_sec_type: details.under_sec_type.unwrap_or_default(),
1261 market_rule_ids: details.market_rule_ids.unwrap_or_default(),
1262 cusip: details.cusip.unwrap_or_default(),
1263 issue_date: details.issue_date.unwrap_or_default(),
1264 ratings: details.ratings.unwrap_or_default(),
1265 bond_type: details.bond_type.unwrap_or_default(),
1266 coupon: details.coupon.unwrap_or_default(),
1267 coupon_type: details.coupon_type.unwrap_or_default(),
1268 convertible: details.convertible.unwrap_or_default(),
1269 callable: details.callable.unwrap_or_default(),
1270 puttable: details.puttable.unwrap_or_default(),
1271 desc_append: details.desc_append.unwrap_or_default(),
1272 next_option_date: details.next_option_date.unwrap_or_default(),
1273 next_option_type: details.next_option_type.unwrap_or_default(),
1274 next_option_partial: details.next_option_partial.unwrap_or_default(),
1275 bond_notes: details.bond_notes.unwrap_or_default(),
1276 real_expiration_date: details.real_expiration_date.unwrap_or_default(),
1277 stock_type: details.stock_type.unwrap_or_default(),
1278 min_size: parse_decimal_string(details.min_size.as_deref())?,
1279 size_increment: parse_decimal_string(details.size_increment.as_deref())?,
1280 suggested_size_increment: parse_decimal_string(
1281 details.suggested_size_increment.as_deref(),
1282 )?,
1283 fund_name: details.fund_name.unwrap_or_default(),
1284 fund_family: details.fund_family.unwrap_or_default(),
1285 fund_type: details.fund_type.unwrap_or_default(),
1286 fund_front_load: details.fund_front_load.unwrap_or_default(),
1287 fund_back_load: details.fund_back_load.unwrap_or_default(),
1288 fund_back_load_time_interval: details.fund_back_load_time_interval.unwrap_or_default(),
1289 fund_management_fee: details.fund_management_fee.unwrap_or_default(),
1290 fund_closed: details.fund_closed.unwrap_or_default(),
1291 fund_closed_for_new_investors: details.fund_closed_for_new_investors.unwrap_or_default(),
1292 fund_closed_for_new_money: details.fund_closed_for_new_money.unwrap_or_default(),
1293 fund_notify_amount: details.fund_notify_amount.unwrap_or_default(),
1294 fund_minimum_initial_purchase: details.fund_minimum_initial_purchase.unwrap_or_default(),
1295 fund_minimum_subsequent_purchase: details
1296 .fund_minimum_subsequent_purchase
1297 .unwrap_or_default(),
1298 fund_blue_sky_states: details.fund_blue_sky_states.unwrap_or_default(),
1299 fund_blue_sky_territories: details.fund_blue_sky_territories.unwrap_or_default(),
1300 fund_distribution_policy_indicator: details
1301 .fund_distribution_policy_indicator
1302 .unwrap_or_default(),
1303 fund_asset_type: details.fund_asset_type.unwrap_or_default(),
1304 ineligibility_reason_list: details
1305 .ineligibility_reason_list
1306 .into_iter()
1307 .map(|reason| IneligibilityReason {
1308 id: reason.id.unwrap_or_default(),
1309 description: reason.description.unwrap_or_default(),
1310 })
1311 .collect(),
1312 event_contract1: details.event_contract1.unwrap_or_default(),
1313 event_contract_description1: details.event_contract_description1.unwrap_or_default(),
1314 event_contract_description2: details.event_contract_description2.unwrap_or_default(),
1315 min_algo_size: parse_decimal_string(details.min_algo_size.as_deref())?,
1316 last_price_precision: parse_decimal_string(details.last_price_precision.as_deref())?,
1317 last_size_precision: parse_decimal_string(details.last_size_precision.as_deref())?,
1318 })
1319}
1320
1321fn proto_contract_to_contract(contract: Option<protobuf::Contract>) -> Contract {
1322 let Some(contract) = contract else {
1323 return Contract::default();
1324 };
1325
1326 Contract {
1327 con_id: contract.con_id.unwrap_or_default(),
1328 symbol: contract.symbol.unwrap_or_default(),
1329 sec_type: contract.sec_type.unwrap_or_default(),
1330 last_trade_date_or_contract_month: contract
1331 .last_trade_date_or_contract_month
1332 .unwrap_or_default(),
1333 last_trade_date: contract.last_trade_date.unwrap_or_default(),
1334 strike: contract.strike.unwrap_or_default(),
1335 right: contract.right.unwrap_or_default(),
1336 multiplier: contract
1337 .multiplier
1338 .map(|value| value.to_string())
1339 .unwrap_or_default(),
1340 exchange: contract.exchange.unwrap_or_default(),
1341 primary_exchange: contract.primary_exch.unwrap_or_default(),
1342 currency: contract.currency.unwrap_or_default(),
1343 local_symbol: contract.local_symbol.unwrap_or_default(),
1344 trading_class: contract.trading_class.unwrap_or_default(),
1345 include_expired: contract.include_expired.unwrap_or_default(),
1346 sec_id_type: contract.sec_id_type.unwrap_or_default(),
1347 sec_id: contract.sec_id.unwrap_or_default(),
1348 description: contract.description.unwrap_or_default(),
1349 issuer_id: contract.issuer_id.unwrap_or_default(),
1350 combo_legs_description: contract.combo_legs_descrip.unwrap_or_default(),
1351 combo_legs: contract
1352 .combo_legs
1353 .into_iter()
1354 .map(proto_combo_leg)
1355 .collect(),
1356 delta_neutral_contract: contract
1357 .delta_neutral_contract
1358 .map(|delta| DeltaNeutralContract {
1359 con_id: delta.con_id.unwrap_or_default(),
1360 delta: delta.delta.unwrap_or_default(),
1361 price: delta.price.unwrap_or_default(),
1362 }),
1363 }
1364}
1365
1366fn proto_combo_leg(leg: protobuf::ComboLeg) -> ComboLeg {
1367 ComboLeg {
1368 con_id: leg.con_id.unwrap_or_default(),
1369 ratio: leg.ratio.unwrap_or_default(),
1370 action: leg.action.unwrap_or_default(),
1371 exchange: leg.exchange.unwrap_or_default(),
1372 open_close: match leg.open_close.unwrap_or_default() {
1373 1 => LegOpenClose::OpenPosition,
1374 2 => LegOpenClose::ClosePosition,
1375 3 => LegOpenClose::Unknown,
1376 _ => LegOpenClose::SamePosition,
1377 },
1378 short_sale_slot: leg.short_sales_slot.unwrap_or_default(),
1379 designated_location: leg.designated_location.unwrap_or_default(),
1380 exempt_code: leg.exempt_code.unwrap_or(-1),
1381 }
1382}
1383
1384fn split_message_id(raw: bool, payload: &[u8]) -> TwsApiResult<(i32, &[u8])> {
1385 if raw {
1386 let (prefix, body) = payload
1387 .split_at_checked(4)
1388 .ok_or(TwsApiError::IncompleteFrame {
1389 needed: 4,
1390 available: payload.len(),
1391 })?;
1392 let prefix: [u8; 4] = prefix
1393 .try_into()
1394 .map_err(|_| TwsApiError::IncompleteFrame {
1395 needed: 4,
1396 available: payload.len(),
1397 })?;
1398 return Ok((i32::from_be_bytes(prefix), body));
1399 }
1400
1401 let nul = payload
1402 .iter()
1403 .position(|byte| *byte == 0)
1404 .ok_or(TwsApiError::MalformedHandshake)?;
1405 let (id, rest) = payload.split_at(nul);
1406 let rest = rest.get(1..).ok_or(TwsApiError::IncompleteFrame {
1407 needed: nul + 1,
1408 available: payload.len(),
1409 })?;
1410 let id = String::from_utf8_lossy(id).into_owned();
1411 let msg_id = id
1412 .parse::<i32>()
1413 .map_err(|source| TwsApiError::InvalidInteger { field: id, source })?;
1414 Ok((msg_id, rest))
1415}
1416
1417fn decode_portfolio_value_fields(fields: &[String]) -> TwsApiResult<Event> {
1418 let version = parse_i32(fields.first())?;
1419 let mut index = 1;
1420 let mut contract = Contract {
1421 con_id: next_i32(fields, &mut index)?,
1422 symbol: next_string(fields, &mut index),
1423 sec_type: next_string(fields, &mut index),
1424 last_trade_date_or_contract_month: next_string(fields, &mut index),
1425 strike: next_f64(fields, &mut index)?,
1426 right: next_string(fields, &mut index),
1427 ..Contract::default()
1428 };
1429
1430 if version >= 7 {
1431 contract.multiplier = next_string(fields, &mut index);
1432 contract.primary_exchange = next_string(fields, &mut index);
1433 }
1434
1435 contract.currency = next_string(fields, &mut index);
1436 contract.local_symbol = next_string(fields, &mut index);
1437 if version >= 8 {
1438 contract.trading_class = next_string(fields, &mut index);
1439 }
1440
1441 Ok(Event::PortfolioValue {
1442 contract: Box::new(contract),
1443 position: next_decimal(fields, &mut index)?,
1444 market_price: next_f64(fields, &mut index)?,
1445 market_value: next_f64(fields, &mut index)?,
1446 average_cost: next_f64(fields, &mut index)?,
1447 unrealized_pnl: next_f64(fields, &mut index)?,
1448 realized_pnl: next_f64(fields, &mut index)?,
1449 account_name: next_string(fields, &mut index),
1450 })
1451}
1452
1453fn decode_market_depth_fields(fields: &[String], is_l2: bool) -> TwsApiResult<Event> {
1454 let mut index = 1;
1455 let req_id = next_i32(fields, &mut index)?;
1456 let position = next_i32(fields, &mut index)?;
1457 let market_maker = if is_l2 {
1458 next_string(fields, &mut index)
1459 } else {
1460 String::new()
1461 };
1462 let operation = next_i32(fields, &mut index)?;
1463 let side = next_i32(fields, &mut index)?;
1464 let price = next_f64(fields, &mut index)?;
1465 let size = next_decimal(fields, &mut index)?;
1466 let is_smart_depth = is_l2 && parse_bool(fields.get(index));
1467
1468 Ok(Event::MarketDepth {
1469 req_id,
1470 position,
1471 operation,
1472 side,
1473 price,
1474 size,
1475 market_maker,
1476 is_smart_depth,
1477 })
1478}
1479
1480fn decode_tick_option_computation_fields(fields: &[String]) -> TwsApiResult<Event> {
1481 let has_version = fields.len() > 11;
1482 let mut index = usize::from(has_version);
1483 let req_id = next_i32(fields, &mut index)?;
1484 let tick_type = next_i32(fields, &mut index)?;
1485 let tick_attrib = if has_version && fields.len() == 10 {
1486 0
1487 } else {
1488 next_i32(fields, &mut index).unwrap_or_default()
1489 };
1490
1491 Ok(Event::TickOptionComputation {
1492 req_id,
1493 tick_type,
1494 tick_attrib,
1495 implied_vol: next_f64(fields, &mut index)?,
1496 delta: next_f64(fields, &mut index)?,
1497 opt_price: if index < fields.len() {
1498 next_f64(fields, &mut index)?
1499 } else {
1500 0.0
1501 },
1502 pv_dividend: if index < fields.len() {
1503 next_f64(fields, &mut index)?
1504 } else {
1505 0.0
1506 },
1507 gamma: if index < fields.len() {
1508 next_f64(fields, &mut index)?
1509 } else {
1510 0.0
1511 },
1512 vega: if index < fields.len() {
1513 next_f64(fields, &mut index)?
1514 } else {
1515 0.0
1516 },
1517 theta: if index < fields.len() {
1518 next_f64(fields, &mut index)?
1519 } else {
1520 0.0
1521 },
1522 und_price: if index < fields.len() {
1523 next_f64(fields, &mut index)?
1524 } else {
1525 0.0
1526 },
1527 })
1528}
1529
1530fn decode_scanner_data_fields(fields: &[String]) -> TwsApiResult<Event> {
1531 let mut index = 1;
1532 let req_id = next_i32(fields, &mut index)?;
1533 let row_count = next_i32(fields, &mut index)?.max(0) as usize;
1534 let mut rows = Vec::with_capacity(row_count);
1535
1536 for _ in 0..row_count {
1537 let rank = next_i32(fields, &mut index)?;
1538 let contract = Contract {
1539 con_id: next_i32(fields, &mut index)?,
1540 symbol: next_string(fields, &mut index),
1541 sec_type: next_string(fields, &mut index),
1542 last_trade_date_or_contract_month: next_string(fields, &mut index),
1543 strike: next_f64(fields, &mut index)?,
1544 right: next_string(fields, &mut index),
1545 exchange: next_string(fields, &mut index),
1546 currency: next_string(fields, &mut index),
1547 local_symbol: next_string(fields, &mut index),
1548 ..Contract::default()
1549 };
1550 let market_name = next_string(fields, &mut index);
1551 let mut contract = contract;
1552 contract.trading_class = next_string(fields, &mut index);
1553 rows.push(ScannerDataRow {
1554 rank,
1555 contract: Box::new(contract),
1556 market_name,
1557 distance: next_string(fields, &mut index),
1558 benchmark: next_string(fields, &mut index),
1559 projection: next_string(fields, &mut index),
1560 combo_key: next_string(fields, &mut index),
1561 });
1562 }
1563
1564 Ok(Event::ScannerData { req_id, rows })
1565}
1566
1567fn decode_soft_dollar_tiers_fields(fields: &[String]) -> TwsApiResult<Event> {
1568 let mut index = 0;
1569 let req_id = next_i32(fields, &mut index)?;
1570 let count = next_i32(fields, &mut index)?.max(0) as usize;
1571 let mut tiers = Vec::with_capacity(count);
1572 for _ in 0..count {
1573 tiers.push(SoftDollarTier {
1574 name: next_string(fields, &mut index),
1575 value: next_string(fields, &mut index),
1576 display_name: next_string(fields, &mut index),
1577 });
1578 }
1579 Ok(Event::SoftDollarTiers { req_id, tiers })
1580}
1581
1582fn decode_family_codes_fields(fields: &[String]) -> TwsApiResult<Event> {
1583 let mut index = 0;
1584 let count = next_i32(fields, &mut index)?.max(0) as usize;
1585 let mut family_codes = Vec::with_capacity(count);
1586 for _ in 0..count {
1587 family_codes.push(FamilyCode {
1588 account_id: next_string(fields, &mut index),
1589 family_code: next_string(fields, &mut index),
1590 });
1591 }
1592 Ok(Event::FamilyCodes { family_codes })
1593}
1594
1595fn decode_symbol_samples_fields(fields: &[String]) -> TwsApiResult<Event> {
1596 let mut index = 0;
1597 let req_id = next_i32(fields, &mut index)?;
1598 let count = next_i32(fields, &mut index)?.max(0) as usize;
1599 let mut descriptions = Vec::with_capacity(count);
1600 for _ in 0..count {
1601 let mut contract = Contract {
1602 con_id: next_i32(fields, &mut index)?,
1603 symbol: next_string(fields, &mut index),
1604 sec_type: next_string(fields, &mut index),
1605 primary_exchange: next_string(fields, &mut index),
1606 currency: next_string(fields, &mut index),
1607 ..Contract::default()
1608 };
1609 let derivative_count = next_i32(fields, &mut index)?.max(0) as usize;
1610 let mut derivative_sec_types = Vec::with_capacity(derivative_count);
1611 for _ in 0..derivative_count {
1612 derivative_sec_types.push(next_string(fields, &mut index));
1613 }
1614 if index + 1 < fields.len() {
1615 contract.description = next_string(fields, &mut index);
1616 contract.issuer_id = next_string(fields, &mut index);
1617 }
1618 descriptions.push(ContractDescription {
1619 contract,
1620 derivative_sec_types,
1621 });
1622 }
1623 Ok(Event::SymbolSamples {
1624 req_id,
1625 descriptions,
1626 })
1627}
1628
1629fn decode_smart_components_fields(fields: &[String]) -> TwsApiResult<Event> {
1630 let mut index = 0;
1631 let req_id = next_i32(fields, &mut index)?;
1632 let count = next_i32(fields, &mut index)?.max(0) as usize;
1633 let mut components = Vec::with_capacity(count);
1634 for _ in 0..count {
1635 components.push(SmartComponent {
1636 bit_number: next_i32(fields, &mut index)?,
1637 exchange: next_string(fields, &mut index),
1638 exchange_letter: next_string(fields, &mut index),
1639 });
1640 }
1641 Ok(Event::SmartComponents { req_id, components })
1642}
1643
1644fn decode_market_depth_exchanges_fields(fields: &[String]) -> TwsApiResult<Event> {
1645 let mut index = 0;
1646 let count = next_i32(fields, &mut index)?.max(0) as usize;
1647 let mut descriptions = Vec::with_capacity(count);
1648 for _ in 0..count {
1649 descriptions.push(DepthMarketDataDescription {
1650 exchange: next_string(fields, &mut index),
1651 security_type: next_string(fields, &mut index),
1652 listing_exchange: next_string(fields, &mut index),
1653 service_data_type: next_string(fields, &mut index),
1654 aggregate_group: next_i32(fields, &mut index).unwrap_or_default(),
1655 });
1656 }
1657 Ok(Event::MarketDepthExchanges { descriptions })
1658}
1659
1660fn decode_histogram_data_fields(fields: &[String]) -> TwsApiResult<Event> {
1661 let mut index = 0;
1662 let req_id = next_i32(fields, &mut index)?;
1663 let count = next_i32(fields, &mut index)?.max(0) as usize;
1664 let mut items = Vec::with_capacity(count);
1665 for _ in 0..count {
1666 items.push(HistogramEntry {
1667 price: next_f64(fields, &mut index)?,
1668 size: next_decimal(fields, &mut index)?,
1669 });
1670 }
1671 Ok(Event::HistogramData { req_id, items })
1672}
1673
1674fn decode_market_rule_fields(fields: &[String]) -> TwsApiResult<Event> {
1675 let mut index = 0;
1676 let market_rule_id = next_i32(fields, &mut index)?;
1677 let count = next_i32(fields, &mut index)?.max(0) as usize;
1678 let mut price_increments = Vec::with_capacity(count);
1679 for _ in 0..count {
1680 price_increments.push(PriceIncrement {
1681 low_edge: next_f64(fields, &mut index)?,
1682 increment: next_f64(fields, &mut index)?,
1683 });
1684 }
1685 Ok(Event::MarketRule {
1686 market_rule_id,
1687 price_increments,
1688 })
1689}
1690
1691fn decode_pnl_fields(fields: &[String]) -> TwsApiResult<Event> {
1692 let mut index = 0;
1693 Ok(Event::Pnl {
1694 req_id: next_i32(fields, &mut index)?,
1695 daily_pnl: next_f64(fields, &mut index)?,
1696 unrealized_pnl: if index < fields.len() {
1697 next_f64(fields, &mut index)?
1698 } else {
1699 0.0
1700 },
1701 realized_pnl: if index < fields.len() {
1702 next_f64(fields, &mut index)?
1703 } else {
1704 0.0
1705 },
1706 })
1707}
1708
1709fn decode_pnl_single_fields(fields: &[String]) -> TwsApiResult<Event> {
1710 let mut index = 0;
1711 let req_id = next_i32(fields, &mut index)?;
1712 let position = next_decimal(fields, &mut index)?;
1713 let daily_pnl = next_f64(fields, &mut index)?;
1714 let remaining = fields.len().saturating_sub(index);
1715 let unrealized_pnl = if remaining >= 3 {
1716 next_f64(fields, &mut index)?
1717 } else {
1718 0.0
1719 };
1720 let realized_pnl = if remaining >= 3 {
1721 next_f64(fields, &mut index)?
1722 } else {
1723 0.0
1724 };
1725 let value = if index < fields.len() {
1726 next_f64(fields, &mut index)?
1727 } else {
1728 0.0
1729 };
1730
1731 Ok(Event::PnlSingle {
1732 req_id,
1733 position,
1734 daily_pnl,
1735 unrealized_pnl,
1736 realized_pnl,
1737 value,
1738 })
1739}
1740
1741fn decode_news_providers_fields(fields: &[String]) -> TwsApiResult<Event> {
1742 let mut index = 0;
1743 let count = next_i32(fields, &mut index)?.max(0) as usize;
1744 let mut providers = Vec::with_capacity(count);
1745 for _ in 0..count {
1746 providers.push(NewsProvider {
1747 code: next_string(fields, &mut index),
1748 name: next_string(fields, &mut index),
1749 });
1750 }
1751 Ok(Event::NewsProviders { providers })
1752}
1753
1754fn decode_historical_schedule_fields(fields: &[String]) -> TwsApiResult<Event> {
1755 let mut index = 0;
1756 let req_id = next_i32(fields, &mut index)?;
1757 let start_date_time = next_string(fields, &mut index);
1758 let end_date_time = next_string(fields, &mut index);
1759 let time_zone = next_string(fields, &mut index);
1760 let count = next_i32(fields, &mut index)?.max(0) as usize;
1761 let mut sessions = Vec::with_capacity(count);
1762 for _ in 0..count {
1763 sessions.push(HistoricalSession {
1764 start_date_time: next_string(fields, &mut index),
1765 end_date_time: next_string(fields, &mut index),
1766 ref_date: next_string(fields, &mut index),
1767 });
1768 }
1769 Ok(Event::HistoricalSchedule {
1770 req_id,
1771 start_date_time,
1772 end_date_time,
1773 time_zone,
1774 sessions,
1775 })
1776}
1777
1778fn decode_execution_data_fields(fields: &[String]) -> TwsApiResult<Event> {
1779 let has_version = fields
1780 .get(3)
1781 .is_some_and(|field| field.parse::<i32>().is_ok());
1782 let mut index = 0;
1783 let version = if has_version {
1784 next_i32(fields, &mut index)?
1785 } else {
1786 i32::MAX
1787 };
1788 let req_id = if version >= 7 {
1789 next_i32(fields, &mut index)?
1790 } else {
1791 -1
1792 };
1793 let order_id = next_i32(fields, &mut index)?;
1794
1795 let mut contract = Contract {
1796 con_id: next_i32(fields, &mut index)?,
1797 symbol: next_string(fields, &mut index),
1798 sec_type: next_string(fields, &mut index),
1799 last_trade_date_or_contract_month: next_string(fields, &mut index),
1800 strike: next_f64(fields, &mut index)?,
1801 right: next_string(fields, &mut index),
1802 ..Contract::default()
1803 };
1804 if version >= 9 {
1805 contract.multiplier = next_string(fields, &mut index);
1806 }
1807 contract.exchange = next_string(fields, &mut index);
1808 contract.currency = next_string(fields, &mut index);
1809 contract.local_symbol = next_string(fields, &mut index);
1810 if version >= 10 {
1811 contract.trading_class = next_string(fields, &mut index);
1812 }
1813
1814 let mut execution = Execution {
1815 order_id,
1816 exec_id: next_string(fields, &mut index),
1817 time: next_string(fields, &mut index),
1818 acct_number: next_string(fields, &mut index),
1819 exchange: next_string(fields, &mut index),
1820 side: next_string(fields, &mut index),
1821 shares: next_decimal(fields, &mut index)?,
1822 price: next_f64(fields, &mut index)?,
1823 perm_id: i64::from(next_i32(fields, &mut index)?),
1824 client_id: next_i32(fields, &mut index)?,
1825 liquidation: next_i32(fields, &mut index)?,
1826 ..Execution::default()
1827 };
1828
1829 if version >= 6 {
1830 execution.cum_qty = next_decimal(fields, &mut index)?;
1831 execution.avg_price = next_f64(fields, &mut index)?;
1832 }
1833 if version >= 8 {
1834 execution.order_ref = next_string(fields, &mut index);
1835 }
1836 if version >= 9 {
1837 execution.ev_rule = next_string(fields, &mut index);
1838 execution.ev_multiplier = next_f64(fields, &mut index)?;
1839 }
1840 if index < fields.len() {
1841 execution.model_code = next_string(fields, &mut index);
1842 }
1843 if index < fields.len() {
1844 execution.last_liquidity = next_i32(fields, &mut index)?;
1845 }
1846 if index < fields.len() {
1847 execution.pending_price_revision = parse_bool(fields.get(index));
1848 index += 1;
1849 }
1850 if index < fields.len() {
1851 execution.submitter = next_string(fields, &mut index);
1852 }
1853
1854 Ok(Event::ExecutionDetails {
1855 req_id,
1856 contract: Box::new(contract),
1857 execution,
1858 })
1859}
1860
1861fn decode_contract_data_fields(fields: &[String]) -> TwsApiResult<Event> {
1862 let has_version = fields
1863 .get(1)
1864 .is_some_and(|field| field.parse::<i32>().is_ok());
1865 let mut index = 0;
1866 let version = if has_version {
1867 next_i32(fields, &mut index)?
1868 } else {
1869 8
1870 };
1871 let req_id = if version >= 3 {
1872 next_i32(fields, &mut index)?
1873 } else {
1874 -1
1875 };
1876
1877 let mut details = ContractDetails::default();
1878 details.contract.symbol = next_string(fields, &mut index);
1879 details.contract.sec_type = next_string(fields, &mut index);
1880 details.contract.last_trade_date_or_contract_month = next_string(fields, &mut index);
1881
1882 if !has_version
1883 || fields
1884 .get(index)
1885 .is_none_or(|field| field.parse::<f64>().is_err())
1886 {
1887 details.contract.last_trade_date = next_string(fields, &mut index);
1888 }
1889
1890 details.contract.strike = next_f64(fields, &mut index)?;
1891 details.contract.right = next_string(fields, &mut index);
1892 details.contract.exchange = next_string(fields, &mut index);
1893 details.contract.currency = next_string(fields, &mut index);
1894 details.contract.local_symbol = next_string(fields, &mut index);
1895 details.market_name = next_string(fields, &mut index);
1896 details.contract.trading_class = next_string(fields, &mut index);
1897 details.contract.con_id = next_i32(fields, &mut index)?;
1898 details.min_tick = next_f64(fields, &mut index)?;
1899 details.contract.multiplier = next_string(fields, &mut index);
1900 details.order_types = next_string(fields, &mut index);
1901 details.valid_exchanges = next_string(fields, &mut index);
1902 details.price_magnifier = next_i32(fields, &mut index)?;
1903
1904 if version >= 4 && index < fields.len() {
1905 details.under_con_id = next_i32(fields, &mut index).unwrap_or_default();
1906 }
1907 if version >= 5 && index + 1 < fields.len() {
1908 details.long_name = next_string(fields, &mut index);
1909 details.contract.primary_exchange = next_string(fields, &mut index);
1910 }
1911 if version >= 6 && index + 6 < fields.len() {
1912 details.contract_month = next_string(fields, &mut index);
1913 details.industry = next_string(fields, &mut index);
1914 details.category = next_string(fields, &mut index);
1915 details.subcategory = next_string(fields, &mut index);
1916 details.time_zone_id = next_string(fields, &mut index);
1917 details.trading_hours = next_string(fields, &mut index);
1918 details.liquid_hours = next_string(fields, &mut index);
1919 }
1920 if version >= 8 && index + 1 < fields.len() {
1921 details.ev_rule = next_string(fields, &mut index);
1922 details.ev_multiplier = next_f64(fields, &mut index).unwrap_or_default();
1923 }
1924 if version >= 7 && index < fields.len() {
1925 let sec_id_count = next_i32(fields, &mut index).unwrap_or_default().max(0) as usize;
1926 for _ in 0..sec_id_count {
1927 details.sec_id_list.push(TagValue {
1928 tag: next_string(fields, &mut index),
1929 value: next_string(fields, &mut index),
1930 });
1931 }
1932 }
1933 if index < fields.len() {
1934 details.aggregate_group = next_i32(fields, &mut index).unwrap_or_default();
1935 }
1936 if index + 1 < fields.len() {
1937 details.under_symbol = next_string(fields, &mut index);
1938 details.under_sec_type = next_string(fields, &mut index);
1939 }
1940 if index < fields.len() {
1941 details.market_rule_ids = next_string(fields, &mut index);
1942 }
1943
1944 if index < fields.len() {
1945 details.real_expiration_date = next_string(fields, &mut index);
1946 }
1947 if index < fields.len() {
1948 details.stock_type = next_string(fields, &mut index);
1949 }
1950 if index < fields.len() {
1951 details.min_size = next_decimal(fields, &mut index).unwrap_or_default();
1952 }
1953 if index < fields.len() {
1954 details.size_increment = next_decimal(fields, &mut index).unwrap_or_default();
1955 }
1956 if index < fields.len() {
1957 details.suggested_size_increment = next_decimal(fields, &mut index).unwrap_or_default();
1958 }
1959
1960 if details.contract.sec_type == "FUND" && index + 16 < fields.len() {
1961 details.fund_name = next_string(fields, &mut index);
1962 details.fund_family = next_string(fields, &mut index);
1963 details.fund_type = next_string(fields, &mut index);
1964 details.fund_front_load = next_string(fields, &mut index);
1965 details.fund_back_load = next_string(fields, &mut index);
1966 details.fund_back_load_time_interval = next_string(fields, &mut index);
1967 details.fund_management_fee = next_string(fields, &mut index);
1968 details.fund_closed = next_bool(fields, &mut index);
1969 details.fund_closed_for_new_investors = next_bool(fields, &mut index);
1970 details.fund_closed_for_new_money = next_bool(fields, &mut index);
1971 details.fund_notify_amount = next_string(fields, &mut index);
1972 details.fund_minimum_initial_purchase = next_string(fields, &mut index);
1973 details.fund_minimum_subsequent_purchase = next_string(fields, &mut index);
1974 details.fund_blue_sky_states = next_string(fields, &mut index);
1975 details.fund_blue_sky_territories = next_string(fields, &mut index);
1976 details.fund_distribution_policy_indicator = next_string(fields, &mut index);
1977 details.fund_asset_type = next_string(fields, &mut index);
1978 }
1979
1980 if index < fields.len() {
1981 let count = next_i32(fields, &mut index).unwrap_or_default().max(0) as usize;
1982 for _ in 0..count {
1983 if index + 1 >= fields.len() {
1984 break;
1985 }
1986 details.ineligibility_reason_list.push(IneligibilityReason {
1987 id: next_string(fields, &mut index),
1988 description: next_string(fields, &mut index),
1989 });
1990 }
1991 }
1992
1993 Ok(Event::ContractDetails {
1994 req_id,
1995 details: Box::new(details),
1996 })
1997}
1998
1999fn decode_bond_contract_data_fields(fields: &[String]) -> TwsApiResult<Event> {
2000 let has_version = fields
2001 .get(1)
2002 .is_some_and(|field| field.parse::<i32>().is_ok());
2003 let mut index = 0;
2004 let version = if has_version {
2005 next_i32(fields, &mut index)?
2006 } else {
2007 6
2008 };
2009 let req_id = if version >= 3 {
2010 next_i32(fields, &mut index)?
2011 } else {
2012 -1
2013 };
2014
2015 let mut details = ContractDetails::default();
2016 details.contract.symbol = next_string(fields, &mut index);
2017 details.contract.sec_type = next_string(fields, &mut index);
2018 details.cusip = next_string(fields, &mut index);
2019 details.coupon = next_f64(fields, &mut index)?;
2020 details.contract.last_trade_date_or_contract_month = next_string(fields, &mut index);
2021 details.issue_date = next_string(fields, &mut index);
2022 details.ratings = next_string(fields, &mut index);
2023 details.bond_type = next_string(fields, &mut index);
2024 details.coupon_type = next_string(fields, &mut index);
2025 details.convertible = next_bool(fields, &mut index);
2026 details.callable = next_bool(fields, &mut index);
2027 details.puttable = next_bool(fields, &mut index);
2028 details.desc_append = next_string(fields, &mut index);
2029 details.contract.exchange = next_string(fields, &mut index);
2030 details.contract.currency = next_string(fields, &mut index);
2031 details.market_name = next_string(fields, &mut index);
2032 details.contract.trading_class = next_string(fields, &mut index);
2033 details.contract.con_id = next_i32(fields, &mut index)?;
2034 details.min_tick = next_f64(fields, &mut index)?;
2035 details.order_types = next_string(fields, &mut index);
2036 details.valid_exchanges = next_string(fields, &mut index);
2037 details.next_option_date = next_string(fields, &mut index);
2038 details.next_option_type = next_string(fields, &mut index);
2039 details.next_option_partial = next_bool(fields, &mut index);
2040 details.bond_notes = next_string(fields, &mut index);
2041
2042 if version >= 4 && index < fields.len() {
2043 details.long_name = next_string(fields, &mut index);
2044 }
2045 if index + 2 < fields.len() {
2046 details.time_zone_id = next_string(fields, &mut index);
2047 details.trading_hours = next_string(fields, &mut index);
2048 details.liquid_hours = next_string(fields, &mut index);
2049 }
2050 if version >= 6 && index + 1 < fields.len() {
2051 details.ev_rule = next_string(fields, &mut index);
2052 details.ev_multiplier = next_f64(fields, &mut index).unwrap_or_default();
2053 }
2054 if version >= 5 && index < fields.len() {
2055 let sec_id_count = next_i32(fields, &mut index).unwrap_or_default().max(0) as usize;
2056 for _ in 0..sec_id_count {
2057 details.sec_id_list.push(TagValue {
2058 tag: next_string(fields, &mut index),
2059 value: next_string(fields, &mut index),
2060 });
2061 }
2062 }
2063 if index < fields.len() {
2064 details.aggregate_group = next_i32(fields, &mut index).unwrap_or_default();
2065 }
2066 if index < fields.len() {
2067 details.market_rule_ids = next_string(fields, &mut index);
2068 }
2069
2070 Ok(Event::ContractDetails {
2071 req_id,
2072 details: Box::new(details),
2073 })
2074}
2075
2076fn decode_historical_data_fields(fields: &[String]) -> TwsApiResult<Event> {
2077 let mut index = 0;
2078 let first = next_i32(fields, &mut index)?;
2079 let req_id = if fields
2080 .get(index)
2081 .and_then(|field| field.parse::<usize>().ok())
2082 .is_some_and(|count| fields.len() == index + 1 + count * 8)
2083 {
2084 first
2085 } else {
2086 next_i32(fields, &mut index)?
2087 };
2088
2089 let bar_count = next_i32(fields, &mut index)?.max(0) as usize;
2090 let mut bars = Vec::with_capacity(bar_count);
2091 for _ in 0..bar_count {
2092 bars.push(BarData {
2093 date: next_string(fields, &mut index),
2094 open: next_f64(fields, &mut index)?,
2095 high: next_f64(fields, &mut index)?,
2096 low: next_f64(fields, &mut index)?,
2097 close: next_f64(fields, &mut index)?,
2098 volume: next_decimal(fields, &mut index)?,
2099 wap: next_decimal(fields, &mut index)?,
2100 bar_count: next_i32(fields, &mut index)?,
2101 });
2102 }
2103
2104 Ok(Event::HistoricalDataBars { req_id, bars })
2105}
2106
2107fn decode_historical_data_update_fields(fields: &[String]) -> TwsApiResult<Event> {
2108 let mut index = 0;
2109 let req_id = next_i32(fields, &mut index)?;
2110 let bar_count = next_i32(fields, &mut index)?;
2111 let bar = BarData {
2112 date: next_string(fields, &mut index),
2113 open: next_f64(fields, &mut index)?,
2114 close: next_f64(fields, &mut index)?,
2115 high: next_f64(fields, &mut index)?,
2116 low: next_f64(fields, &mut index)?,
2117 wap: next_decimal(fields, &mut index)?,
2118 volume: next_decimal(fields, &mut index)?,
2119 bar_count,
2120 };
2121
2122 Ok(Event::HistoricalDataUpdate { req_id, bar })
2123}
2124
2125fn decode_historical_ticks_fields(fields: &[String]) -> TwsApiResult<Event> {
2126 let mut index = 0;
2127 let req_id = next_i32(fields, &mut index)?;
2128 let count = next_i32(fields, &mut index)?.max(0) as usize;
2129 let mut ticks = Vec::with_capacity(count);
2130 for _ in 0..count {
2131 let time = i64::from(next_i32(fields, &mut index)?);
2132 let _unused = next_string(fields, &mut index);
2133 ticks.push(HistoricalTick {
2134 time,
2135 price: next_f64(fields, &mut index)?,
2136 size: next_decimal(fields, &mut index)?,
2137 });
2138 }
2139 let done = next_bool(fields, &mut index);
2140 Ok(Event::HistoricalTicks {
2141 req_id,
2142 ticks,
2143 done,
2144 })
2145}
2146
2147fn decode_historical_ticks_bid_ask_fields(fields: &[String]) -> TwsApiResult<Event> {
2148 let mut index = 0;
2149 let req_id = next_i32(fields, &mut index)?;
2150 let count = next_i32(fields, &mut index)?.max(0) as usize;
2151 let mut ticks = Vec::with_capacity(count);
2152 for _ in 0..count {
2153 let time = i64::from(next_i32(fields, &mut index)?);
2154 let mask = next_i32(fields, &mut index)?;
2155 ticks.push(HistoricalTickBidAsk {
2156 time,
2157 tick_attrib_bid_ask: TickAttribBidAsk {
2158 ask_past_high: mask & 1 != 0,
2159 bid_past_low: mask & 2 != 0,
2160 },
2161 price_bid: next_f64(fields, &mut index)?,
2162 price_ask: next_f64(fields, &mut index)?,
2163 size_bid: next_decimal(fields, &mut index)?,
2164 size_ask: next_decimal(fields, &mut index)?,
2165 });
2166 }
2167 let done = next_bool(fields, &mut index);
2168 Ok(Event::HistoricalTicksBidAsk {
2169 req_id,
2170 ticks,
2171 done,
2172 })
2173}
2174
2175fn decode_historical_ticks_last_fields(fields: &[String]) -> TwsApiResult<Event> {
2176 let mut index = 0;
2177 let req_id = next_i32(fields, &mut index)?;
2178 let count = next_i32(fields, &mut index)?.max(0) as usize;
2179 let mut ticks = Vec::with_capacity(count);
2180 for _ in 0..count {
2181 let time = i64::from(next_i32(fields, &mut index)?);
2182 let mask = next_i32(fields, &mut index)?;
2183 ticks.push(HistoricalTickLast {
2184 time,
2185 tick_attrib_last: TickAttribLast {
2186 past_limit: mask & 1 != 0,
2187 unreported: mask & 2 != 0,
2188 },
2189 price: next_f64(fields, &mut index)?,
2190 size: next_decimal(fields, &mut index)?,
2191 exchange: next_string(fields, &mut index),
2192 special_conditions: next_string(fields, &mut index),
2193 });
2194 }
2195 let done = next_bool(fields, &mut index);
2196 Ok(Event::HistoricalTicksLast {
2197 req_id,
2198 ticks,
2199 done,
2200 })
2201}
2202
2203fn decode_tick_by_tick_fields(fields: &[String]) -> TwsApiResult<Event> {
2204 let mut index = 0;
2205 let req_id = next_i32(fields, &mut index)?;
2206 let tick_type = next_i32(fields, &mut index)?;
2207 let time = i64::from(next_i32(fields, &mut index)?);
2208 let tick = match tick_type {
2209 1 | 2 => {
2210 let price = next_f64(fields, &mut index)?;
2211 let size = next_decimal(fields, &mut index)?;
2212 let mask = next_i32(fields, &mut index)?;
2213 Some(TickByTick::Last(HistoricalTickLast {
2214 time,
2215 tick_attrib_last: TickAttribLast {
2216 past_limit: mask & 1 != 0,
2217 unreported: mask & 2 != 0,
2218 },
2219 price,
2220 size,
2221 exchange: next_string(fields, &mut index),
2222 special_conditions: next_string(fields, &mut index),
2223 }))
2224 }
2225 3 => {
2226 let price_bid = next_f64(fields, &mut index)?;
2227 let price_ask = next_f64(fields, &mut index)?;
2228 let size_bid = next_decimal(fields, &mut index)?;
2229 let size_ask = next_decimal(fields, &mut index)?;
2230 let mask = next_i32(fields, &mut index)?;
2231 Some(TickByTick::BidAsk(HistoricalTickBidAsk {
2232 time,
2233 tick_attrib_bid_ask: TickAttribBidAsk {
2234 bid_past_low: mask & 1 != 0,
2235 ask_past_high: mask & 2 != 0,
2236 },
2237 price_bid,
2238 price_ask,
2239 size_bid,
2240 size_ask,
2241 }))
2242 }
2243 4 => Some(TickByTick::MidPoint(HistoricalTick {
2244 time,
2245 price: next_f64(fields, &mut index)?,
2246 size: Decimal::default(),
2247 })),
2248 _ => None,
2249 };
2250
2251 Ok(Event::TickByTick {
2252 req_id,
2253 tick_type,
2254 tick,
2255 })
2256}
2257
2258fn decode_real_time_bar_fields(fields: &[String]) -> TwsApiResult<Event> {
2259 let mut index = 1;
2260 let req_id = next_i32(fields, &mut index)?;
2261 let bar = BarData {
2262 date: next_string(fields, &mut index),
2263 open: next_f64(fields, &mut index)?,
2264 high: next_f64(fields, &mut index)?,
2265 low: next_f64(fields, &mut index)?,
2266 close: next_f64(fields, &mut index)?,
2267 volume: next_decimal(fields, &mut index)?,
2268 wap: next_decimal(fields, &mut index)?,
2269 bar_count: next_i32(fields, &mut index)?,
2270 };
2271
2272 Ok(Event::RealTimeBar {
2273 req_id,
2274 time: bar.date.parse::<i64>().unwrap_or_default(),
2275 bar,
2276 })
2277}
2278
2279fn decode_position_fields(fields: &[String]) -> TwsApiResult<Event> {
2280 let version = parse_i32(fields.first())?;
2281 let mut index = 1;
2282 let account = next_string(fields, &mut index);
2283 let contract = decode_position_contract_fields(fields, &mut index, version >= 2)?;
2284 let position = next_decimal(fields, &mut index)?;
2285 let avg_cost = if version >= 3 {
2286 next_f64(fields, &mut index)?
2287 } else {
2288 0.0
2289 };
2290
2291 Ok(Event::Position {
2292 account,
2293 contract: Box::new(contract),
2294 position,
2295 avg_cost,
2296 })
2297}
2298
2299fn decode_position_multi_fields(fields: &[String]) -> TwsApiResult<Event> {
2300 let mut index = 1;
2301 let req_id = next_i32(fields, &mut index)?;
2302 let account = next_string(fields, &mut index);
2303 let contract = decode_position_contract_fields(fields, &mut index, true)?;
2304 let position = next_decimal(fields, &mut index)?;
2305 let avg_cost = next_f64(fields, &mut index)?;
2306 let model_code = next_string(fields, &mut index);
2307
2308 Ok(Event::PositionMulti {
2309 req_id,
2310 account,
2311 model_code,
2312 contract: Box::new(contract),
2313 position,
2314 avg_cost,
2315 })
2316}
2317
2318fn decode_position_contract_fields(
2319 fields: &[String],
2320 index: &mut usize,
2321 include_trading_class: bool,
2322) -> TwsApiResult<Contract> {
2323 let mut contract = Contract {
2324 con_id: next_i32(fields, index)?,
2325 symbol: next_string(fields, index),
2326 sec_type: next_string(fields, index),
2327 last_trade_date_or_contract_month: next_string(fields, index),
2328 strike: next_f64(fields, index)?,
2329 right: next_string(fields, index),
2330 multiplier: next_string(fields, index),
2331 exchange: next_string(fields, index),
2332 currency: next_string(fields, index),
2333 local_symbol: next_string(fields, index),
2334 ..Contract::default()
2335 };
2336
2337 if include_trading_class {
2338 contract.trading_class = next_string(fields, index);
2339 }
2340
2341 Ok(contract)
2342}
2343
2344fn decode_security_definition_option_parameter_fields(fields: &[String]) -> TwsApiResult<Event> {
2345 let req_id = parse_i32(fields.first())?;
2346 let exchange = fields.get(1).cloned().unwrap_or_default();
2347 let underlying_con_id = parse_i32(fields.get(2))?;
2348 let trading_class = fields.get(3).cloned().unwrap_or_default();
2349 let multiplier = fields.get(4).cloned().unwrap_or_default();
2350
2351 let mut index = 5;
2352 let expiration_count = parse_i32(fields.get(index)).unwrap_or_default().max(0) as usize;
2353 index += 1;
2354
2355 let mut expirations = Vec::with_capacity(expiration_count);
2356 for _ in 0..expiration_count {
2357 expirations.push(fields.get(index).cloned().unwrap_or_default());
2358 index += 1;
2359 }
2360
2361 let strike_count = parse_i32(fields.get(index)).unwrap_or_default().max(0) as usize;
2362 index += 1;
2363
2364 let mut strikes = Vec::with_capacity(strike_count);
2365 for _ in 0..strike_count {
2366 strikes.push(parse_f64(fields.get(index))?);
2367 index += 1;
2368 }
2369
2370 Ok(Event::SecurityDefinitionOptionParameter {
2371 req_id,
2372 exchange,
2373 underlying_con_id,
2374 trading_class,
2375 multiplier,
2376 expirations,
2377 strikes,
2378 })
2379}
2380
2381fn next_string(fields: &[String], index: &mut usize) -> String {
2382 let value = fields.get(*index).cloned().unwrap_or_default();
2383 *index += 1;
2384 value
2385}
2386
2387fn next_i32(fields: &[String], index: &mut usize) -> TwsApiResult<i32> {
2388 let value = parse_i32(fields.get(*index));
2389 *index += 1;
2390 value
2391}
2392
2393fn next_f64(fields: &[String], index: &mut usize) -> TwsApiResult<f64> {
2394 let value = parse_f64(fields.get(*index));
2395 *index += 1;
2396 value
2397}
2398
2399fn next_bool(fields: &[String], index: &mut usize) -> bool {
2400 let value = parse_bool(fields.get(*index));
2401 *index += 1;
2402 value
2403}
2404
2405fn next_decimal(fields: &[String], index: &mut usize) -> TwsApiResult<Decimal> {
2406 let value = parse_decimal(fields.get(*index));
2407 *index += 1;
2408 value
2409}
2410
2411fn parse_i32(value: Option<&String>) -> TwsApiResult<i32> {
2412 let field = value.cloned().unwrap_or_default();
2413 field
2414 .parse::<i32>()
2415 .map_err(|source| TwsApiError::InvalidInteger { field, source })
2416}
2417
2418fn parse_i64(value: Option<&String>) -> TwsApiResult<i64> {
2419 let field = value.cloned().unwrap_or_default();
2420 field
2421 .parse::<i64>()
2422 .map_err(|source| TwsApiError::InvalidInteger { field, source })
2423}
2424
2425fn parse_f64(value: Option<&String>) -> TwsApiResult<f64> {
2426 Ok(value
2427 .and_then(|field| field.parse::<f64>().ok())
2428 .unwrap_or_default())
2429}
2430
2431fn parse_bool(value: Option<&String>) -> bool {
2432 matches!(
2433 value.map(String::as_str),
2434 Some("1") | Some("true") | Some("True")
2435 )
2436}
2437
2438fn parse_decimal(value: Option<&String>) -> TwsApiResult<Decimal> {
2439 parse_decimal_string(value.map(String::as_str))
2440}
2441
2442fn parse_decimal_string(value: Option<&str>) -> TwsApiResult<Decimal> {
2443 let field = value.unwrap_or_default().to_owned();
2444 if field.is_empty() {
2445 return Ok(Decimal::default());
2446 }
2447 field
2448 .parse::<Decimal>()
2449 .map_err(|source| TwsApiError::InvalidDecimal { field, source })
2450}