Skip to main content

truefix_twsapi_client/
requests.rs

1use prost::Message;
2
3use crate::comm;
4use crate::constants::{UNSET_DOUBLE, UNSET_INTEGER};
5use crate::error::{TwsApiError, TwsApiResult};
6use crate::message::Outgoing;
7use crate::protobuf;
8use crate::server_versions::{
9    MAX_CLIENT_VER, MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1,
10    MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_2, MIN_SERVER_VER_ADVANCED_ORDER_REJECT,
11    MIN_SERVER_VER_ALGO_ID, MIN_SERVER_VER_ALGO_ORDERS, MIN_SERVER_VER_ATTACHED_ORDERS,
12    MIN_SERVER_VER_AUTO_CANCEL_PARENT, MIN_SERVER_VER_AUTO_PRICE_FOR_HEDGE,
13    MIN_SERVER_VER_BOND_ISSUERID, MIN_SERVER_VER_CASH_QTY, MIN_SERVER_VER_CME_TAGGING_FIELDS,
14    MIN_SERVER_VER_CONTRACT_DATA_CHAIN, MIN_SERVER_VER_CUSTOMER_ACCOUNT,
15    MIN_SERVER_VER_D_PEG_ORDERS, MIN_SERVER_VER_DECISION_MAKER, MIN_SERVER_VER_DELTA_NEUTRAL,
16    MIN_SERVER_VER_DELTA_NEUTRAL_CONID, MIN_SERVER_VER_DELTA_NEUTRAL_OPEN_CLOSE,
17    MIN_SERVER_VER_DURATION, MIN_SERVER_VER_EXECUTION_DATA_CHAIN, MIN_SERVER_VER_EXT_OPERATOR,
18    MIN_SERVER_VER_FA_PROFILE_DESUPPORT, MIN_SERVER_VER_HEDGE_MAX_SIZE,
19    MIN_SERVER_VER_HEDGE_ORDERS, MIN_SERVER_VER_HISTORICAL_TICKS, MIN_SERVER_VER_IMBALANCE_ONLY,
20    MIN_SERVER_VER_INCLUDE_OVERNIGHT, MIN_SERVER_VER_LINKING, MIN_SERVER_VER_MANUAL_ORDER_TIME,
21    MIN_SERVER_VER_MANUAL_ORDER_TIME_EXERCISE_OPTIONS, MIN_SERVER_VER_MIFID_EXECUTION,
22    MIN_SERVER_VER_MKT_DEPTH_PRIM_EXCHANGE, MIN_SERVER_VER_MODELS_SUPPORT,
23    MIN_SERVER_VER_NEWS_QUERY_ORIGINS, MIN_SERVER_VER_NOT_HELD,
24    MIN_SERVER_VER_OPT_OUT_SMART_ROUTING, MIN_SERVER_VER_ORDER_COMBO_LEGS_PRICE,
25    MIN_SERVER_VER_ORDER_CONTAINER, MIN_SERVER_VER_ORDER_SOLICITED,
26    MIN_SERVER_VER_PARAMETRIZED_DAYS_OF_EXECUTIONS, MIN_SERVER_VER_PEGBEST_PEGMID_OFFSETS,
27    MIN_SERVER_VER_PEGGED_TO_BENCHMARK, MIN_SERVER_VER_PLACE_ORDER_CONID,
28    MIN_SERVER_VER_POST_TO_ATS, MIN_SERVER_VER_PRICE_MGMT_ALGO, MIN_SERVER_VER_PRIMARYEXCH,
29    MIN_SERVER_VER_PROFESSIONAL_CUSTOMER, MIN_SERVER_VER_PROTOBUF,
30    MIN_SERVER_VER_PROTOBUF_ACCOUNTS_POSITIONS, MIN_SERVER_VER_PROTOBUF_COMPLETED_ORDER,
31    MIN_SERVER_VER_PROTOBUF_CONTRACT_DATA, MIN_SERVER_VER_PROTOBUF_HISTORICAL_DATA,
32    MIN_SERVER_VER_PROTOBUF_MARKET_DATA, MIN_SERVER_VER_PROTOBUF_NEWS_DATA,
33    MIN_SERVER_VER_PROTOBUF_PLACE_ORDER, MIN_SERVER_VER_PROTOBUF_REST_MESSAGES_1,
34    MIN_SERVER_VER_PROTOBUF_REST_MESSAGES_2, MIN_SERVER_VER_PROTOBUF_REST_MESSAGES_3,
35    MIN_SERVER_VER_PROTOBUF_SCAN_DATA, MIN_SERVER_VER_PTA_ORDERS,
36    MIN_SERVER_VER_RANDOMIZE_SIZE_AND_PRICE, MIN_SERVER_VER_REPLACE_FA_END,
37    MIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT, MIN_SERVER_VER_REQ_MKT_DATA_CONID,
38    MIN_SERVER_VER_REQ_SMART_COMPONENTS, MIN_SERVER_VER_RFQ_FIELDS, MIN_SERVER_VER_SCALE_ORDERS2,
39    MIN_SERVER_VER_SCALE_ORDERS3, MIN_SERVER_VER_SCALE_TABLE, MIN_SERVER_VER_SCANNER_GENERIC_OPTS,
40    MIN_SERVER_VER_SEC_ID_TYPE, MIN_SERVER_VER_SMART_COMBO_ROUTING_PARAMS,
41    MIN_SERVER_VER_SMART_DEPTH, MIN_SERVER_VER_SOFT_DOLLAR_TIER, MIN_SERVER_VER_SSHORTX_OLD,
42    MIN_SERVER_VER_SYNT_REALTIME_BARS, MIN_SERVER_VER_TICK_BY_TICK,
43    MIN_SERVER_VER_TICK_BY_TICK_IGNORE_SIZE, MIN_SERVER_VER_TRADING_CLASS,
44    MIN_SERVER_VER_TRAILING_PERCENT, MIN_SERVER_VER_UNDO_RFQ_FIELDS,
45};
46use crate::types::{
47    Contract, ExecutionFilter, Order, OrderCancel, ScannerSubscription, TagValue, TickerId,
48};
49
50/// A request that can be encoded as TWS NUL-separated fields.
51pub trait EncodableRequest {
52    /// Outgoing message id.
53    fn message(&self) -> Outgoing;
54
55    /// Appends fields after the message id.
56    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()>;
57
58    /// Appends fields for a negotiated server version.
59    fn encode_fields_for_server_version(
60        &self,
61        fields: &mut FieldSink,
62        _server_version: i32,
63    ) -> TwsApiResult<()> {
64        self.encode_fields(fields)
65    }
66
67    /// Encodes this request as protobuf when implemented for the request type.
68    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
69        Ok(None)
70    }
71}
72
73/// Encodes a request frame, preferring protobuf when supported by server and request type.
74pub fn encode_request_frame<R>(request: &R, server_version: i32) -> TwsApiResult<Vec<u8>>
75where
76    R: EncodableRequest,
77{
78    encode_request_frame_with_protobuf(request, server_version, true)
79}
80
81/// Encodes a request frame, optionally allowing protobuf when supported.
82pub fn encode_request_frame_with_protobuf<R>(
83    request: &R,
84    server_version: i32,
85    prefer_protobuf: bool,
86) -> TwsApiResult<Vec<u8>>
87where
88    R: EncodableRequest,
89{
90    if prefer_protobuf
91        && protobuf_min_server_version(request.message())
92            .is_some_and(|min_version| server_version >= min_version)
93        && let Some(payload) = request.encode_protobuf()?
94    {
95        return Ok(comm::make_msg_proto(
96            request.message().protobuf_id(),
97            &payload,
98        ));
99    }
100
101    let mut fields = FieldSink::default();
102    request.encode_fields_for_server_version(&mut fields, server_version)?;
103    comm::make_msg(
104        request.message().id(),
105        // Since protocol version 151, the official SDK frames *all* outgoing
106        // request ids as raw integers. `prefer_protobuf` chooses only the
107        // request body encoding; a field-encoded body still keeps this frame.
108        server_version >= MIN_SERVER_VER_PROTOBUF,
109        &fields.into_string(),
110    )
111}
112
113/// Returns the minimum server version for protobuf encoding of an outgoing message.
114pub const fn protobuf_min_server_version(message: Outgoing) -> Option<i32> {
115    Some(match message {
116        Outgoing::ReqExecutions => MIN_SERVER_VER_PROTOBUF,
117        Outgoing::PlaceOrder | Outgoing::CancelOrder | Outgoing::ReqGlobalCancel => {
118            MIN_SERVER_VER_PROTOBUF_PLACE_ORDER
119        }
120        Outgoing::ReqAllOpenOrders
121        | Outgoing::ReqAutoOpenOrders
122        | Outgoing::ReqOpenOrders
123        | Outgoing::ReqCompletedOrders => MIN_SERVER_VER_PROTOBUF_COMPLETED_ORDER,
124        Outgoing::ReqContractData => MIN_SERVER_VER_PROTOBUF_CONTRACT_DATA,
125        Outgoing::ReqMktData
126        | Outgoing::CancelMktData
127        | Outgoing::ReqMktDepth
128        | Outgoing::CancelMktDepth
129        | Outgoing::ReqMarketDataType => MIN_SERVER_VER_PROTOBUF_MARKET_DATA,
130        Outgoing::ReqAcctData
131        | Outgoing::ReqManagedAccounts
132        | Outgoing::ReqPositions
133        | Outgoing::CancelPositions
134        | Outgoing::ReqAccountSummary
135        | Outgoing::CancelAccountSummary
136        | Outgoing::ReqPositionsMulti
137        | Outgoing::CancelPositionsMulti
138        | Outgoing::ReqAccountUpdatesMulti
139        | Outgoing::CancelAccountUpdatesMulti => MIN_SERVER_VER_PROTOBUF_ACCOUNTS_POSITIONS,
140        Outgoing::ReqHistoricalData
141        | Outgoing::CancelHistoricalData
142        | Outgoing::ReqRealTimeBars
143        | Outgoing::CancelRealTimeBars
144        | Outgoing::ReqHeadTimestamp
145        | Outgoing::CancelHeadTimestamp
146        | Outgoing::ReqHistogramData
147        | Outgoing::CancelHistogramData
148        | Outgoing::ReqHistoricalTicks
149        | Outgoing::ReqTickByTickData
150        | Outgoing::CancelTickByTickData => MIN_SERVER_VER_PROTOBUF_HISTORICAL_DATA,
151        Outgoing::ReqNewsBulletins
152        | Outgoing::CancelNewsBulletins
153        | Outgoing::ReqNewsArticle
154        | Outgoing::ReqNewsProviders
155        | Outgoing::ReqHistoricalNews
156        | Outgoing::ReqWshMetaData
157        | Outgoing::CancelWshMetaData
158        | Outgoing::ReqWshEventData
159        | Outgoing::CancelWshEventData => MIN_SERVER_VER_PROTOBUF_NEWS_DATA,
160        Outgoing::ReqScannerParameters
161        | Outgoing::ReqScannerSubscription
162        | Outgoing::CancelScannerSubscription
163        | Outgoing::ReqPnl
164        | Outgoing::CancelPnl
165        | Outgoing::ReqPnlSingle
166        | Outgoing::CancelPnlSingle => MIN_SERVER_VER_PROTOBUF_SCAN_DATA,
167        Outgoing::ReqFa
168        | Outgoing::ReplaceFa
169        | Outgoing::ExerciseOptions
170        | Outgoing::ReqCalcImpliedVolat
171        | Outgoing::CancelCalcImpliedVolat
172        | Outgoing::ReqCalcOptionPrice
173        | Outgoing::CancelCalcOptionPrice => MIN_SERVER_VER_PROTOBUF_REST_MESSAGES_1,
174        Outgoing::ReqSecDefOptParams
175        | Outgoing::ReqSoftDollarTiers
176        | Outgoing::ReqFamilyCodes
177        | Outgoing::ReqMatchingSymbols
178        | Outgoing::ReqSmartComponents
179        | Outgoing::ReqMarketRule
180        | Outgoing::ReqUserInfo => MIN_SERVER_VER_PROTOBUF_REST_MESSAGES_2,
181        Outgoing::ReqIds
182        | Outgoing::ReqCurrentTime
183        | Outgoing::ReqCurrentTimeInMillis
184        | Outgoing::SetServerLogLevel
185        | Outgoing::VerifyRequest
186        | Outgoing::VerifyMessage
187        | Outgoing::QueryDisplayGroups
188        | Outgoing::SubscribeToGroupEvents
189        | Outgoing::UpdateDisplayGroup
190        | Outgoing::UnsubscribeFromGroupEvents
191        | Outgoing::ReqMktDepthExchanges => MIN_SERVER_VER_PROTOBUF_REST_MESSAGES_3,
192        Outgoing::VerifyAndAuthRequest
193        | Outgoing::VerifyAndAuthMessage
194        | Outgoing::StartApi
195        | Outgoing::CancelContractData
196        | Outgoing::CancelHistoricalTicks
197        | Outgoing::ReqConfig
198        | Outgoing::UpdateConfig => return None,
199    })
200}
201
202/// Field encoder with TWS sentinel handling.
203#[derive(Debug, Clone, Default)]
204pub struct FieldSink {
205    fields: String,
206}
207
208impl FieldSink {
209    /// Appends a regular field.
210    pub fn push<T>(&mut self, value: T) -> TwsApiResult<&mut Self>
211    where
212        T: comm::TwsField,
213    {
214        self.fields.push_str(&comm::make_field(value)?);
215        Ok(self)
216    }
217
218    /// Appends a field where TWS unset sentinels are encoded as empty values.
219    pub fn push_empty<T>(&mut self, value: T) -> TwsApiResult<&mut Self>
220    where
221        T: comm::TwsNullableField,
222    {
223        self.fields.push_str(&comm::make_field_handle_empty(value)?);
224        Ok(self)
225    }
226
227    /// Appends pre-encoded raw fields.
228    pub fn push_raw(&mut self, raw: &str) -> &mut Self {
229        self.fields.push_str(raw);
230        self
231    }
232
233    /// Returns encoded fields.
234    pub fn into_string(self) -> String {
235        self.fields
236    }
237}
238
239/// A raw request for migration and protocol coverage.
240#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct RawRequest {
242    /// Message id.
243    pub message: Outgoing,
244    /// Already encoded NUL-separated fields after the message id.
245    pub fields: String,
246}
247
248/// Result of validating order fields against a negotiated server version.
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250pub(crate) struct OrderFieldValidation {
251    /// Unsupported parameter name.
252    pub parameter: &'static str,
253    /// Minimum supported server version.
254    pub min_version: i32,
255}
256
257impl EncodableRequest for RawRequest {
258    fn message(&self) -> Outgoing {
259        self.message
260    }
261
262    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
263        fields.push_raw(&self.fields);
264        Ok(())
265    }
266}
267
268/// Request with no field payload.
269#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub struct EmptyRequest {
271    /// Message id.
272    pub message: Outgoing,
273}
274
275impl EncodableRequest for EmptyRequest {
276    fn message(&self) -> Outgoing {
277        self.message
278    }
279
280    fn encode_fields(&self, _fields: &mut FieldSink) -> TwsApiResult<()> {
281        Ok(())
282    }
283
284    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
285        let payload = match self.message {
286            Outgoing::ReqMktDepthExchanges => {
287                protobuf::MarketDepthExchangesRequest::default().encode_to_vec()
288            }
289            Outgoing::ReqNewsProviders => protobuf::NewsProvidersRequest::default().encode_to_vec(),
290            Outgoing::ReqFamilyCodes => protobuf::FamilyCodesRequest::default().encode_to_vec(),
291            Outgoing::ReqCurrentTimeInMillis => {
292                protobuf::CurrentTimeInMillisRequest::default().encode_to_vec()
293            }
294            _ => return Ok(None),
295        };
296        Ok(Some(payload))
297    }
298}
299
300/// Request for `startApi`.
301#[derive(Debug, Clone, Default, PartialEq, Eq)]
302pub struct StartApiRequest {
303    /// Client id.
304    pub client_id: i32,
305    /// Optional capabilities.
306    pub optional_capabilities: Option<String>,
307    /// Whether to encode optional capabilities.
308    pub include_optional_capabilities: bool,
309}
310
311impl EncodableRequest for StartApiRequest {
312    fn message(&self) -> Outgoing {
313        Outgoing::StartApi
314    }
315
316    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
317        fields.push(2)?.push(self.client_id)?;
318        if self.include_optional_capabilities {
319            fields.push(self.optional_capabilities.as_deref().unwrap_or(""))?;
320        }
321        Ok(())
322    }
323}
324
325/// Request with only a protocol version field.
326#[derive(Debug, Clone, Copy, PartialEq, Eq)]
327pub struct VersionedRequest {
328    /// Message id.
329    pub message: Outgoing,
330    /// Protocol version.
331    pub version: i32,
332}
333
334impl EncodableRequest for VersionedRequest {
335    fn message(&self) -> Outgoing {
336        self.message
337    }
338
339    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
340        fields.push(self.version)?;
341        Ok(())
342    }
343
344    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
345        let payload = match self.message {
346            Outgoing::ReqCurrentTime => protobuf::CurrentTimeRequest::default().encode_to_vec(),
347            Outgoing::ReqCurrentTimeInMillis => {
348                protobuf::CurrentTimeInMillisRequest::default().encode_to_vec()
349            }
350            Outgoing::ReqPositions => protobuf::PositionsRequest::default().encode_to_vec(),
351            Outgoing::CancelPositions => protobuf::CancelPositions::default().encode_to_vec(),
352            Outgoing::ReqOpenOrders => protobuf::OpenOrdersRequest::default().encode_to_vec(),
353            Outgoing::ReqAllOpenOrders => protobuf::AllOpenOrdersRequest::default().encode_to_vec(),
354            Outgoing::ReqManagedAccounts => {
355                protobuf::ManagedAccountsRequest::default().encode_to_vec()
356            }
357            Outgoing::ReqScannerParameters => {
358                protobuf::ScannerParametersRequest::default().encode_to_vec()
359            }
360            Outgoing::CancelNewsBulletins => {
361                protobuf::CancelNewsBulletins::default().encode_to_vec()
362            }
363            _ => return Ok(None),
364        };
365        Ok(Some(payload))
366    }
367}
368
369/// Request with only an id field.
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub struct IdRequest {
372    /// Message id.
373    pub message: Outgoing,
374    /// Protocol version.
375    pub version: Option<i32>,
376    /// Request id.
377    pub req_id: i32,
378}
379
380/// Server log-level request.
381#[derive(Debug, Clone, Copy, PartialEq, Eq)]
382pub struct SetServerLogLevelRequest {
383    /// Log level.
384    pub log_level: i32,
385}
386
387impl EncodableRequest for SetServerLogLevelRequest {
388    fn message(&self) -> Outgoing {
389        Outgoing::SetServerLogLevel
390    }
391
392    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
393        fields.push(1)?.push(self.log_level)?;
394        Ok(())
395    }
396
397    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
398        Ok(Some(
399            protobuf::SetServerLogLevelRequest {
400                log_level: Some(self.log_level),
401            }
402            .encode_to_vec(),
403        ))
404    }
405}
406
407/// Request a market data type switch.
408#[derive(Debug, Clone, Copy, PartialEq, Eq)]
409pub struct MarketDataTypeRequest {
410    /// Market data type.
411    pub market_data_type: i32,
412}
413
414impl EncodableRequest for MarketDataTypeRequest {
415    fn message(&self) -> Outgoing {
416        Outgoing::ReqMarketDataType
417    }
418
419    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
420        fields.push(1)?.push(self.market_data_type)?;
421        Ok(())
422    }
423
424    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
425        Ok(Some(
426            protobuf::MarketDataTypeRequest {
427                market_data_type: Some(self.market_data_type),
428            }
429            .encode_to_vec(),
430        ))
431    }
432}
433
434/// Smart components request.
435#[derive(Debug, Clone, PartialEq, Eq)]
436pub struct SmartComponentsRequest {
437    /// Request id.
438    pub req_id: i32,
439    /// BBO exchange.
440    pub bbo_exchange: String,
441}
442
443impl EncodableRequest for SmartComponentsRequest {
444    fn message(&self) -> Outgoing {
445        Outgoing::ReqSmartComponents
446    }
447
448    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
449        fields.push(self.req_id)?.push(&self.bbo_exchange)?;
450        Ok(())
451    }
452
453    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
454        Ok(Some(
455            protobuf::SmartComponentsRequest {
456                req_id: Some(self.req_id),
457                bbo_exchange: non_empty(self.bbo_exchange.clone()),
458            }
459            .encode_to_vec(),
460        ))
461    }
462}
463
464/// Cancel market depth request.
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466pub struct CancelMarketDepthRequest {
467    /// Request id.
468    pub req_id: i32,
469    /// Smart depth.
470    pub is_smart_depth: bool,
471}
472
473impl EncodableRequest for CancelMarketDepthRequest {
474    fn message(&self) -> Outgoing {
475        Outgoing::CancelMktDepth
476    }
477
478    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
479        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
480    }
481
482    fn encode_fields_for_server_version(
483        &self,
484        fields: &mut FieldSink,
485        server_version: i32,
486    ) -> TwsApiResult<()> {
487        fields.push(1)?.push(self.req_id)?;
488        if server_version >= MIN_SERVER_VER_SMART_DEPTH {
489            fields.push(self.is_smart_depth)?;
490        }
491        Ok(())
492    }
493
494    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
495        Ok(Some(
496            protobuf::CancelMarketDepth {
497                req_id: Some(self.req_id),
498                is_smart_depth: self.is_smart_depth.then_some(true),
499            }
500            .encode_to_vec(),
501        ))
502    }
503}
504
505/// Auto-open-orders request.
506#[derive(Debug, Clone, Copy, PartialEq, Eq)]
507pub struct AutoOpenOrdersRequest {
508    /// Auto-bind flag.
509    pub auto_bind: bool,
510}
511
512impl EncodableRequest for AutoOpenOrdersRequest {
513    fn message(&self) -> Outgoing {
514        Outgoing::ReqAutoOpenOrders
515    }
516
517    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
518        fields.push(1)?.push(self.auto_bind)?;
519        Ok(())
520    }
521
522    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
523        Ok(Some(
524            protobuf::AutoOpenOrdersRequest {
525                auto_bind: Some(self.auto_bind),
526            }
527            .encode_to_vec(),
528        ))
529    }
530}
531
532/// Global cancel request.
533#[derive(Debug, Clone, Default, PartialEq, Eq)]
534pub struct GlobalCancelRequest {
535    /// Cancel metadata.
536    pub order_cancel: OrderCancel,
537}
538
539impl EncodableRequest for GlobalCancelRequest {
540    fn message(&self) -> Outgoing {
541        Outgoing::ReqGlobalCancel
542    }
543
544    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
545        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
546    }
547
548    fn encode_fields_for_server_version(
549        &self,
550        fields: &mut FieldSink,
551        server_version: i32,
552    ) -> TwsApiResult<()> {
553        if server_version < MIN_SERVER_VER_CME_TAGGING_FIELDS {
554            fields.push(1)?;
555        } else {
556            fields
557                .push(&self.order_cancel.ext_operator)?
558                .push_empty(self.order_cancel.manual_order_indicator)?;
559        }
560        Ok(())
561    }
562
563    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
564        Ok(Some(
565            protobuf::GlobalCancelRequest {
566                order_cancel: Some(order_cancel_to_proto(&self.order_cancel)),
567            }
568            .encode_to_vec(),
569        ))
570    }
571}
572
573/// Account data subscription request.
574#[derive(Debug, Clone, PartialEq, Eq)]
575pub struct AccountDataRequest {
576    /// Subscribe or unsubscribe.
577    pub subscribe: bool,
578    /// Account code.
579    pub account_code: String,
580}
581
582impl EncodableRequest for AccountDataRequest {
583    fn message(&self) -> Outgoing {
584        Outgoing::ReqAcctData
585    }
586
587    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
588        fields
589            .push(2)?
590            .push(self.subscribe)?
591            .push(&self.account_code)?;
592        Ok(())
593    }
594
595    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
596        Ok(Some(
597            protobuf::AccountDataRequest {
598                subscribe: Some(self.subscribe),
599                acct_code: non_empty(self.account_code.clone()),
600            }
601            .encode_to_vec(),
602        ))
603    }
604}
605
606/// Account summary request.
607#[derive(Debug, Clone, PartialEq, Eq)]
608pub struct AccountSummaryRequest {
609    /// Request id.
610    pub req_id: i32,
611    /// Group name.
612    pub group_name: String,
613    /// Tags.
614    pub tags: String,
615}
616
617impl EncodableRequest for AccountSummaryRequest {
618    fn message(&self) -> Outgoing {
619        Outgoing::ReqAccountSummary
620    }
621
622    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
623        fields
624            .push(1)?
625            .push(self.req_id)?
626            .push(&self.group_name)?
627            .push(&self.tags)?;
628        Ok(())
629    }
630
631    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
632        Ok(Some(
633            protobuf::AccountSummaryRequest {
634                req_id: Some(self.req_id),
635                group: non_empty(self.group_name.clone()),
636                tags: non_empty(self.tags.clone()),
637            }
638            .encode_to_vec(),
639        ))
640    }
641}
642
643/// Positions multi request.
644#[derive(Debug, Clone, PartialEq, Eq)]
645pub struct PositionsMultiRequest {
646    /// Request id.
647    pub req_id: i32,
648    /// Account.
649    pub account: String,
650    /// Model code.
651    pub model_code: String,
652}
653
654impl EncodableRequest for PositionsMultiRequest {
655    fn message(&self) -> Outgoing {
656        Outgoing::ReqPositionsMulti
657    }
658
659    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
660        fields
661            .push(1)?
662            .push(self.req_id)?
663            .push(&self.account)?
664            .push(&self.model_code)?;
665        Ok(())
666    }
667
668    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
669        Ok(Some(
670            protobuf::PositionsMultiRequest {
671                req_id: Some(self.req_id),
672                account: non_empty(self.account.clone()),
673                model_code: non_empty(self.model_code.clone()),
674            }
675            .encode_to_vec(),
676        ))
677    }
678}
679
680/// Account updates multi request.
681#[derive(Debug, Clone, PartialEq, Eq)]
682pub struct AccountUpdatesMultiRequest {
683    /// Request id.
684    pub req_id: i32,
685    /// Account.
686    pub account: String,
687    /// Model code.
688    pub model_code: String,
689    /// Whether to include ledger and NLV.
690    pub ledger_and_nlv: bool,
691}
692
693impl EncodableRequest for AccountUpdatesMultiRequest {
694    fn message(&self) -> Outgoing {
695        Outgoing::ReqAccountUpdatesMulti
696    }
697
698    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
699        fields
700            .push(1)?
701            .push(self.req_id)?
702            .push(&self.account)?
703            .push(&self.model_code)?
704            .push(self.ledger_and_nlv)?;
705        Ok(())
706    }
707
708    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
709        Ok(Some(
710            protobuf::AccountUpdatesMultiRequest {
711                req_id: Some(self.req_id),
712                account: non_empty(self.account.clone()),
713                model_code: non_empty(self.model_code.clone()),
714                ledger_and_nlv: Some(self.ledger_and_nlv),
715            }
716            .encode_to_vec(),
717        ))
718    }
719}
720
721/// PnL request.
722#[derive(Debug, Clone, PartialEq, Eq)]
723pub struct PnlRequest {
724    /// Request id.
725    pub req_id: i32,
726    /// Account.
727    pub account: String,
728    /// Model code.
729    pub model_code: String,
730}
731
732impl EncodableRequest for PnlRequest {
733    fn message(&self) -> Outgoing {
734        Outgoing::ReqPnl
735    }
736
737    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
738        fields
739            .push(self.req_id)?
740            .push(&self.account)?
741            .push(&self.model_code)?;
742        Ok(())
743    }
744
745    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
746        Ok(Some(
747            protobuf::PnLRequest {
748                req_id: Some(self.req_id),
749                account: non_empty(self.account.clone()),
750                model_code: non_empty(self.model_code.clone()),
751            }
752            .encode_to_vec(),
753        ))
754    }
755}
756
757/// Single-position PnL request.
758#[derive(Debug, Clone, PartialEq, Eq)]
759pub struct PnlSingleRequest {
760    /// Request id.
761    pub req_id: i32,
762    /// Account.
763    pub account: String,
764    /// Model code.
765    pub model_code: String,
766    /// Contract id.
767    pub con_id: i32,
768}
769
770/// News bulletin subscription request.
771#[derive(Debug, Clone, Copy, PartialEq, Eq)]
772pub struct NewsBulletinsRequest {
773    /// Include all messages.
774    pub all_messages: bool,
775}
776
777impl EncodableRequest for NewsBulletinsRequest {
778    fn message(&self) -> Outgoing {
779        Outgoing::ReqNewsBulletins
780    }
781
782    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
783        fields.push(1)?.push(self.all_messages)?;
784        Ok(())
785    }
786
787    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
788        Ok(Some(
789            protobuf::NewsBulletinsRequest {
790                all_messages: Some(self.all_messages),
791            }
792            .encode_to_vec(),
793        ))
794    }
795}
796
797/// News article request.
798#[derive(Debug, Clone, Default, PartialEq, Eq)]
799pub struct NewsArticleRequest {
800    /// Request id.
801    pub req_id: i32,
802    /// Provider code.
803    pub provider_code: String,
804    /// Article id.
805    pub article_id: String,
806    /// Request options.
807    pub options: Vec<TagValue>,
808}
809
810impl EncodableRequest for NewsArticleRequest {
811    fn message(&self) -> Outgoing {
812        Outgoing::ReqNewsArticle
813    }
814
815    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
816        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
817    }
818
819    fn encode_fields_for_server_version(
820        &self,
821        fields: &mut FieldSink,
822        server_version: i32,
823    ) -> TwsApiResult<()> {
824        fields
825            .push(self.req_id)?
826            .push(&self.provider_code)?
827            .push(&self.article_id)?;
828        if server_version >= MIN_SERVER_VER_NEWS_QUERY_ORIGINS {
829            fields.push(tag_values_to_tws_options(&self.options))?;
830        }
831        Ok(())
832    }
833
834    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
835        Ok(Some(
836            protobuf::NewsArticleRequest {
837                req_id: Some(self.req_id),
838                provider_code: non_empty(self.provider_code.clone()),
839                article_id: non_empty(self.article_id.clone()),
840                news_article_options: tag_values_to_map(&self.options),
841            }
842            .encode_to_vec(),
843        ))
844    }
845}
846
847/// Historical news request.
848#[derive(Debug, Clone, Default, PartialEq, Eq)]
849pub struct HistoricalNewsRequest {
850    /// Request id.
851    pub req_id: i32,
852    /// Contract id.
853    pub con_id: i32,
854    /// Provider codes.
855    pub provider_codes: String,
856    /// Start date/time.
857    pub start_date_time: String,
858    /// End date/time.
859    pub end_date_time: String,
860    /// Total results.
861    pub total_results: i32,
862    /// Request options.
863    pub options: Vec<TagValue>,
864}
865
866impl EncodableRequest for HistoricalNewsRequest {
867    fn message(&self) -> Outgoing {
868        Outgoing::ReqHistoricalNews
869    }
870
871    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
872        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
873    }
874
875    fn encode_fields_for_server_version(
876        &self,
877        fields: &mut FieldSink,
878        server_version: i32,
879    ) -> TwsApiResult<()> {
880        fields
881            .push(self.req_id)?
882            .push(self.con_id)?
883            .push(&self.provider_codes)?
884            .push(&self.start_date_time)?
885            .push(&self.end_date_time)?
886            .push(self.total_results)?;
887        if server_version >= MIN_SERVER_VER_NEWS_QUERY_ORIGINS {
888            fields.push(tag_values_to_tws_options(&self.options))?;
889        }
890        Ok(())
891    }
892
893    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
894        Ok(Some(
895            protobuf::HistoricalNewsRequest {
896                req_id: Some(self.req_id),
897                con_id: Some(self.con_id),
898                provider_codes: non_empty(self.provider_codes.clone()),
899                start_date_time: non_empty(self.start_date_time.clone()),
900                end_date_time: non_empty(self.end_date_time.clone()),
901                total_results: Some(self.total_results),
902                historical_news_options: tag_values_to_map(&self.options),
903            }
904            .encode_to_vec(),
905        ))
906    }
907}
908
909/// Security-definition option parameters request.
910#[derive(Debug, Clone, PartialEq, Eq)]
911pub struct SecDefOptParamsRequest {
912    /// Request id.
913    pub req_id: i32,
914    /// Underlying symbol.
915    pub underlying_symbol: String,
916    /// FUT/FOP exchange.
917    pub fut_fop_exchange: String,
918    /// Underlying security type.
919    pub underlying_sec_type: String,
920    /// Underlying contract id.
921    pub underlying_con_id: i32,
922}
923
924impl EncodableRequest for SecDefOptParamsRequest {
925    fn message(&self) -> Outgoing {
926        Outgoing::ReqSecDefOptParams
927    }
928
929    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
930        fields
931            .push(self.req_id)?
932            .push(&self.underlying_symbol)?
933            .push(&self.fut_fop_exchange)?
934            .push(&self.underlying_sec_type)?
935            .push(self.underlying_con_id)?;
936        Ok(())
937    }
938
939    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
940        Ok(Some(
941            protobuf::SecDefOptParamsRequest {
942                req_id: Some(self.req_id),
943                underlying_symbol: non_empty(self.underlying_symbol.clone()),
944                fut_fop_exchange: non_empty(self.fut_fop_exchange.clone()),
945                underlying_sec_type: non_empty(self.underlying_sec_type.clone()),
946                underlying_con_id: Some(self.underlying_con_id),
947            }
948            .encode_to_vec(),
949        ))
950    }
951}
952
953/// Matching-symbols request.
954#[derive(Debug, Clone, PartialEq, Eq)]
955pub struct MatchingSymbolsRequest {
956    /// Request id.
957    pub req_id: i32,
958    /// Search pattern.
959    pub pattern: String,
960}
961
962impl EncodableRequest for MatchingSymbolsRequest {
963    fn message(&self) -> Outgoing {
964        Outgoing::ReqMatchingSymbols
965    }
966
967    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
968        fields.push(self.req_id)?.push(&self.pattern)?;
969        Ok(())
970    }
971
972    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
973        Ok(Some(
974            protobuf::MatchingSymbolsRequest {
975                req_id: Some(self.req_id),
976                pattern: non_empty(self.pattern.clone()),
977            }
978            .encode_to_vec(),
979        ))
980    }
981}
982
983/// Completed-orders request.
984#[derive(Debug, Clone, Copy, PartialEq, Eq)]
985pub struct CompletedOrdersRequest {
986    /// Include API-only orders.
987    pub api_only: bool,
988}
989
990impl EncodableRequest for CompletedOrdersRequest {
991    fn message(&self) -> Outgoing {
992        Outgoing::ReqCompletedOrders
993    }
994
995    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
996        fields.push(self.api_only)?;
997        Ok(())
998    }
999
1000    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1001        Ok(Some(
1002            protobuf::CompletedOrdersRequest {
1003                api_only: Some(self.api_only),
1004            }
1005            .encode_to_vec(),
1006        ))
1007    }
1008}
1009
1010/// Financial-advisor data request.
1011#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1012pub struct FinancialAdvisorRequest {
1013    /// FA data type.
1014    pub fa_data_type: i32,
1015}
1016
1017impl EncodableRequest for FinancialAdvisorRequest {
1018    fn message(&self) -> Outgoing {
1019        Outgoing::ReqFa
1020    }
1021
1022    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1023        fields.push(1)?.push(self.fa_data_type)?;
1024        Ok(())
1025    }
1026
1027    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1028        Ok(Some(
1029            protobuf::FaRequest {
1030                fa_data_type: Some(self.fa_data_type),
1031            }
1032            .encode_to_vec(),
1033        ))
1034    }
1035}
1036
1037/// Financial-advisor replace request.
1038#[derive(Debug, Clone, PartialEq, Eq)]
1039pub struct ReplaceFinancialAdvisorRequest {
1040    /// Request id.
1041    pub req_id: i32,
1042    /// FA data type.
1043    pub fa_data_type: i32,
1044    /// XML payload.
1045    pub xml: String,
1046}
1047
1048impl EncodableRequest for ReplaceFinancialAdvisorRequest {
1049    fn message(&self) -> Outgoing {
1050        Outgoing::ReplaceFa
1051    }
1052
1053    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1054        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
1055    }
1056
1057    fn encode_fields_for_server_version(
1058        &self,
1059        fields: &mut FieldSink,
1060        server_version: i32,
1061    ) -> TwsApiResult<()> {
1062        fields.push(1)?.push(self.fa_data_type)?.push(&self.xml)?;
1063        if server_version >= MIN_SERVER_VER_REPLACE_FA_END {
1064            fields.push(self.req_id)?;
1065        }
1066        Ok(())
1067    }
1068
1069    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1070        Ok(Some(
1071            protobuf::FaReplace {
1072                req_id: Some(self.req_id),
1073                fa_data_type: Some(self.fa_data_type),
1074                xml: non_empty(self.xml.clone()),
1075            }
1076            .encode_to_vec(),
1077        ))
1078    }
1079}
1080
1081/// Head timestamp request.
1082#[derive(Debug, Clone, Default, PartialEq)]
1083pub struct HeadTimestampRequest {
1084    /// Request id.
1085    pub req_id: i32,
1086    /// Contract.
1087    pub contract: Contract,
1088    /// Use regular trading hours.
1089    pub use_rth: bool,
1090    /// Data type.
1091    pub what_to_show: String,
1092    /// Format date.
1093    pub format_date: i32,
1094}
1095
1096impl EncodableRequest for HeadTimestampRequest {
1097    fn message(&self) -> Outgoing {
1098        Outgoing::ReqHeadTimestamp
1099    }
1100
1101    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1102        fields.push(self.req_id)?;
1103        encode_contract_head_or_histogram(fields, &self.contract)?;
1104        fields
1105            .push(self.use_rth)?
1106            .push(&self.what_to_show)?
1107            .push(self.format_date)?;
1108        Ok(())
1109    }
1110
1111    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1112        Ok(Some(
1113            protobuf::HeadTimestampRequest {
1114                req_id: Some(self.req_id),
1115                contract: Some(contract_to_proto(&self.contract, None)),
1116                use_rth: Some(self.use_rth),
1117                what_to_show: non_empty(self.what_to_show.clone()),
1118                format_date: Some(self.format_date),
1119            }
1120            .encode_to_vec(),
1121        ))
1122    }
1123}
1124
1125/// Histogram data request.
1126#[derive(Debug, Clone, Default, PartialEq)]
1127pub struct HistogramDataRequest {
1128    /// Request id.
1129    pub req_id: i32,
1130    /// Contract.
1131    pub contract: Contract,
1132    /// Use regular trading hours.
1133    pub use_rth: bool,
1134    /// Time period.
1135    pub time_period: String,
1136}
1137
1138impl EncodableRequest for HistogramDataRequest {
1139    fn message(&self) -> Outgoing {
1140        Outgoing::ReqHistogramData
1141    }
1142
1143    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1144        fields.push(self.req_id)?;
1145        encode_contract_head_or_histogram(fields, &self.contract)?;
1146        fields.push(self.use_rth)?.push(&self.time_period)?;
1147        Ok(())
1148    }
1149
1150    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1151        Ok(Some(
1152            protobuf::HistogramDataRequest {
1153                req_id: Some(self.req_id),
1154                contract: Some(contract_to_proto(&self.contract, None)),
1155                use_rth: Some(self.use_rth),
1156                time_period: non_empty(self.time_period.clone()),
1157            }
1158            .encode_to_vec(),
1159        ))
1160    }
1161}
1162
1163/// Real-time bars request.
1164#[derive(Debug, Clone, Default, PartialEq)]
1165pub struct RealTimeBarsRequest {
1166    /// Request id.
1167    pub req_id: i32,
1168    /// Contract.
1169    pub contract: Contract,
1170    /// Bar size.
1171    pub bar_size: i32,
1172    /// Data type.
1173    pub what_to_show: String,
1174    /// Use regular trading hours.
1175    pub use_rth: bool,
1176    /// Request options.
1177    pub options: Vec<TagValue>,
1178}
1179
1180impl EncodableRequest for RealTimeBarsRequest {
1181    fn message(&self) -> Outgoing {
1182        Outgoing::ReqRealTimeBars
1183    }
1184
1185    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1186        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
1187    }
1188
1189    fn encode_fields_for_server_version(
1190        &self,
1191        fields: &mut FieldSink,
1192        server_version: i32,
1193    ) -> TwsApiResult<()> {
1194        fields.push(3)?.push(self.req_id)?;
1195        encode_contract_real_time_bars(fields, &self.contract, server_version)?;
1196        fields
1197            .push(self.bar_size)?
1198            .push(&self.what_to_show)?
1199            .push(self.use_rth)?;
1200        if server_version >= MIN_SERVER_VER_LINKING {
1201            fields.push(tag_values_to_tws_options(&self.options))?;
1202        }
1203        Ok(())
1204    }
1205
1206    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1207        Ok(Some(
1208            protobuf::RealTimeBarsRequest {
1209                req_id: Some(self.req_id),
1210                contract: Some(contract_to_proto(&self.contract, None)),
1211                bar_size: Some(self.bar_size),
1212                what_to_show: non_empty(self.what_to_show.clone()),
1213                use_rth: Some(self.use_rth),
1214                real_time_bars_options: tag_values_to_map(&self.options),
1215            }
1216            .encode_to_vec(),
1217        ))
1218    }
1219}
1220
1221/// Subscribe to display-group events.
1222#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1223pub struct SubscribeToGroupEventsRequest {
1224    /// Request id.
1225    pub req_id: i32,
1226    /// Group id.
1227    pub group_id: i32,
1228}
1229
1230impl EncodableRequest for SubscribeToGroupEventsRequest {
1231    fn message(&self) -> Outgoing {
1232        Outgoing::SubscribeToGroupEvents
1233    }
1234
1235    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1236        fields.push(1)?.push(self.req_id)?.push(self.group_id)?;
1237        Ok(())
1238    }
1239
1240    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1241        Ok(Some(
1242            protobuf::SubscribeToGroupEventsRequest {
1243                req_id: Some(self.req_id),
1244                group_id: Some(self.group_id),
1245            }
1246            .encode_to_vec(),
1247        ))
1248    }
1249}
1250
1251/// Update display group.
1252#[derive(Debug, Clone, PartialEq, Eq)]
1253pub struct UpdateDisplayGroupRequest {
1254    /// Request id.
1255    pub req_id: i32,
1256    /// Contract info.
1257    pub contract_info: String,
1258}
1259
1260impl EncodableRequest for UpdateDisplayGroupRequest {
1261    fn message(&self) -> Outgoing {
1262        Outgoing::UpdateDisplayGroup
1263    }
1264
1265    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1266        fields
1267            .push(1)?
1268            .push(self.req_id)?
1269            .push(&self.contract_info)?;
1270        Ok(())
1271    }
1272
1273    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1274        Ok(Some(
1275            protobuf::UpdateDisplayGroupRequest {
1276                req_id: Some(self.req_id),
1277                contract_info: non_empty(self.contract_info.clone()),
1278            }
1279            .encode_to_vec(),
1280        ))
1281    }
1282}
1283
1284/// Verify request.
1285#[derive(Debug, Clone, PartialEq, Eq)]
1286pub struct VerifyRequest {
1287    /// API name.
1288    pub api_name: String,
1289    /// API version.
1290    pub api_version: String,
1291}
1292
1293impl EncodableRequest for VerifyRequest {
1294    fn message(&self) -> Outgoing {
1295        Outgoing::VerifyRequest
1296    }
1297
1298    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1299        fields
1300            .push(1)?
1301            .push(&self.api_name)?
1302            .push(&self.api_version)?;
1303        Ok(())
1304    }
1305
1306    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1307        Ok(Some(
1308            protobuf::VerifyRequest {
1309                api_name: non_empty(self.api_name.clone()),
1310                api_version: non_empty(self.api_version.clone()),
1311            }
1312            .encode_to_vec(),
1313        ))
1314    }
1315}
1316
1317/// Verify message request.
1318#[derive(Debug, Clone, PartialEq, Eq)]
1319pub struct VerifyMessageRequest {
1320    /// API data.
1321    pub api_data: String,
1322}
1323
1324impl EncodableRequest for VerifyMessageRequest {
1325    fn message(&self) -> Outgoing {
1326        Outgoing::VerifyMessage
1327    }
1328
1329    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1330        fields.push(1)?.push(&self.api_data)?;
1331        Ok(())
1332    }
1333
1334    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1335        Ok(Some(
1336            protobuf::VerifyMessageRequest {
1337                api_data: non_empty(self.api_data.clone()),
1338            }
1339            .encode_to_vec(),
1340        ))
1341    }
1342}
1343
1344/// Verify-and-auth request.
1345#[derive(Debug, Clone, PartialEq, Eq)]
1346pub struct VerifyAndAuthRequest {
1347    /// API name.
1348    pub api_name: String,
1349    /// API version.
1350    pub api_version: String,
1351    /// Opaque ISV key.
1352    pub opaque_isv_key: String,
1353}
1354
1355impl EncodableRequest for VerifyAndAuthRequest {
1356    fn message(&self) -> Outgoing {
1357        Outgoing::VerifyAndAuthRequest
1358    }
1359
1360    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1361        fields
1362            .push(1)?
1363            .push(&self.api_name)?
1364            .push(&self.api_version)?
1365            .push(&self.opaque_isv_key)?;
1366        Ok(())
1367    }
1368}
1369
1370/// Verify-and-auth message request.
1371#[derive(Debug, Clone, PartialEq, Eq)]
1372pub struct VerifyAndAuthMessageRequest {
1373    /// API data.
1374    pub api_data: String,
1375    /// Challenge response.
1376    pub xyz_response: String,
1377}
1378
1379impl EncodableRequest for VerifyAndAuthMessageRequest {
1380    fn message(&self) -> Outgoing {
1381        Outgoing::VerifyAndAuthMessage
1382    }
1383
1384    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1385        fields
1386            .push(1)?
1387            .push(&self.api_data)?
1388            .push(&self.xyz_response)?;
1389        Ok(())
1390    }
1391}
1392
1393/// WSH event data request.
1394#[derive(Debug, Clone, Default, PartialEq, Eq)]
1395pub struct WshEventDataRequest {
1396    /// Request id.
1397    pub req_id: i32,
1398    /// Contract id.
1399    pub con_id: i32,
1400    /// Filter JSON.
1401    pub filter: String,
1402    /// Fill watchlist flag.
1403    pub fill_watchlist: bool,
1404    /// Fill portfolio flag.
1405    pub fill_portfolio: bool,
1406    /// Fill competitors flag.
1407    pub fill_competitors: bool,
1408    /// Start date.
1409    pub start_date: String,
1410    /// End date.
1411    pub end_date: String,
1412    /// Total limit.
1413    pub total_limit: i32,
1414}
1415
1416impl EncodableRequest for WshEventDataRequest {
1417    fn message(&self) -> Outgoing {
1418        Outgoing::ReqWshEventData
1419    }
1420
1421    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1422        fields
1423            .push(self.req_id)?
1424            .push(self.con_id)?
1425            .push(&self.filter)?
1426            .push(self.fill_watchlist)?
1427            .push(self.fill_portfolio)?
1428            .push(self.fill_competitors)?
1429            .push(&self.start_date)?
1430            .push(&self.end_date)?
1431            .push(self.total_limit)?;
1432        Ok(())
1433    }
1434
1435    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1436        Ok(Some(
1437            protobuf::WshEventDataRequest {
1438                req_id: Some(self.req_id),
1439                con_id: valid_i32(self.con_id),
1440                filter: non_empty(self.filter.clone()),
1441                fill_watchlist: self.fill_watchlist.then_some(true),
1442                fill_portfolio: self.fill_portfolio.then_some(true),
1443                fill_competitors: self.fill_competitors.then_some(true),
1444                start_date: non_empty(self.start_date.clone()),
1445                end_date: non_empty(self.end_date.clone()),
1446                total_limit: valid_i32(self.total_limit),
1447            }
1448            .encode_to_vec(),
1449        ))
1450    }
1451}
1452
1453/// Tick-by-tick data request.
1454#[derive(Debug, Clone, Default, PartialEq)]
1455pub struct TickByTickRequest {
1456    /// Request id.
1457    pub req_id: i32,
1458    /// Contract.
1459    pub contract: Contract,
1460    /// Tick type.
1461    pub tick_type: String,
1462    /// Number of ticks.
1463    pub number_of_ticks: i32,
1464    /// Ignore size flag.
1465    pub ignore_size: bool,
1466}
1467
1468impl EncodableRequest for TickByTickRequest {
1469    fn message(&self) -> Outgoing {
1470        Outgoing::ReqTickByTickData
1471    }
1472
1473    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1474        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
1475    }
1476
1477    fn encode_fields_for_server_version(
1478        &self,
1479        fields: &mut FieldSink,
1480        server_version: i32,
1481    ) -> TwsApiResult<()> {
1482        ensure_server_version(server_version, MIN_SERVER_VER_TICK_BY_TICK)?;
1483        fields
1484            .push(self.req_id)?
1485            .push(self.contract.con_id)?
1486            .push(&self.contract.symbol)?
1487            .push(&self.contract.sec_type)?
1488            .push(&self.contract.last_trade_date_or_contract_month)?
1489            .push_empty(self.contract.strike)?
1490            .push(&self.contract.right)?
1491            .push(&self.contract.multiplier)?
1492            .push(&self.contract.exchange)?
1493            .push(&self.contract.primary_exchange)?
1494            .push(&self.contract.currency)?
1495            .push(&self.contract.local_symbol)?
1496            .push(&self.contract.trading_class)?
1497            .push(&self.tick_type)?;
1498        if server_version >= MIN_SERVER_VER_TICK_BY_TICK_IGNORE_SIZE {
1499            fields.push(self.number_of_ticks)?.push(self.ignore_size)?;
1500        }
1501        Ok(())
1502    }
1503
1504    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1505        Ok(Some(
1506            protobuf::TickByTickRequest {
1507                req_id: Some(self.req_id),
1508                contract: Some(contract_to_proto(&self.contract, None)),
1509                tick_type: non_empty(self.tick_type.clone()),
1510                number_of_ticks: valid_i32(self.number_of_ticks),
1511                ignore_size: self.ignore_size.then_some(true),
1512            }
1513            .encode_to_vec(),
1514        ))
1515    }
1516}
1517
1518/// Calculate implied volatility request.
1519#[derive(Debug, Clone, Default, PartialEq)]
1520pub struct CalculateImpliedVolatilityRequest {
1521    /// Request id.
1522    pub req_id: i32,
1523    /// Contract.
1524    pub contract: Contract,
1525    /// Option price.
1526    pub option_price: f64,
1527    /// Underlying price.
1528    pub under_price: f64,
1529    /// Request options.
1530    pub options: Vec<TagValue>,
1531}
1532
1533impl EncodableRequest for CalculateImpliedVolatilityRequest {
1534    fn message(&self) -> Outgoing {
1535        Outgoing::ReqCalcImpliedVolat
1536    }
1537
1538    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1539        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
1540    }
1541
1542    fn encode_fields_for_server_version(
1543        &self,
1544        fields: &mut FieldSink,
1545        server_version: i32,
1546    ) -> TwsApiResult<()> {
1547        ensure_server_version(server_version, MIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT)?;
1548        fields.push(3)?.push(self.req_id)?;
1549        encode_contract_for_calculation(fields, &self.contract, server_version)?;
1550        fields.push(self.option_price)?.push(self.under_price)?;
1551        if server_version >= MIN_SERVER_VER_LINKING {
1552            fields.push(tag_values_to_tws_options(&self.options))?;
1553        }
1554        Ok(())
1555    }
1556
1557    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1558        Ok(Some(
1559            protobuf::CalculateImpliedVolatilityRequest {
1560                req_id: Some(self.req_id),
1561                contract: Some(contract_to_proto(&self.contract, None)),
1562                option_price: valid_f64(self.option_price),
1563                under_price: valid_f64(self.under_price),
1564                implied_volatility_options: tag_values_to_map(&self.options),
1565            }
1566            .encode_to_vec(),
1567        ))
1568    }
1569}
1570
1571/// Calculate option price request.
1572#[derive(Debug, Clone, Default, PartialEq)]
1573pub struct CalculateOptionPriceRequest {
1574    /// Request id.
1575    pub req_id: i32,
1576    /// Contract.
1577    pub contract: Contract,
1578    /// Volatility.
1579    pub volatility: f64,
1580    /// Underlying price.
1581    pub under_price: f64,
1582    /// Request options.
1583    pub options: Vec<TagValue>,
1584}
1585
1586impl EncodableRequest for CalculateOptionPriceRequest {
1587    fn message(&self) -> Outgoing {
1588        Outgoing::ReqCalcOptionPrice
1589    }
1590
1591    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1592        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
1593    }
1594
1595    fn encode_fields_for_server_version(
1596        &self,
1597        fields: &mut FieldSink,
1598        server_version: i32,
1599    ) -> TwsApiResult<()> {
1600        ensure_server_version(server_version, MIN_SERVER_VER_REQ_CALC_IMPLIED_VOLAT)?;
1601        fields.push(3)?.push(self.req_id)?;
1602        encode_contract_for_calculation(fields, &self.contract, server_version)?;
1603        fields.push(self.volatility)?.push(self.under_price)?;
1604        if server_version >= MIN_SERVER_VER_LINKING {
1605            fields.push(tag_values_to_tws_options(&self.options))?;
1606        }
1607        Ok(())
1608    }
1609
1610    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1611        Ok(Some(
1612            protobuf::CalculateOptionPriceRequest {
1613                req_id: Some(self.req_id),
1614                contract: Some(contract_to_proto(&self.contract, None)),
1615                volatility: valid_f64(self.volatility),
1616                under_price: valid_f64(self.under_price),
1617                option_price_options: tag_values_to_map(&self.options),
1618            }
1619            .encode_to_vec(),
1620        ))
1621    }
1622}
1623
1624/// Exercise options request.
1625#[derive(Debug, Clone, Default, PartialEq)]
1626pub struct ExerciseOptionsRequest {
1627    /// Order/request id.
1628    pub order_id: i32,
1629    /// Contract.
1630    pub contract: Contract,
1631    /// Exercise action.
1632    pub exercise_action: i32,
1633    /// Exercise quantity.
1634    pub exercise_quantity: i32,
1635    /// Account.
1636    pub account: String,
1637    /// Override natural action.
1638    pub override_system_action: bool,
1639    /// Manual order time.
1640    pub manual_order_time: String,
1641    /// Customer account.
1642    pub customer_account: String,
1643    /// Professional customer flag.
1644    pub professional_customer: bool,
1645}
1646
1647impl EncodableRequest for ExerciseOptionsRequest {
1648    fn message(&self) -> Outgoing {
1649        Outgoing::ExerciseOptions
1650    }
1651
1652    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1653        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
1654    }
1655
1656    fn encode_fields_for_server_version(
1657        &self,
1658        fields: &mut FieldSink,
1659        server_version: i32,
1660    ) -> TwsApiResult<()> {
1661        fields.push(2)?.push(self.order_id)?;
1662        if server_version >= MIN_SERVER_VER_TRADING_CLASS {
1663            fields.push(self.contract.con_id)?;
1664        }
1665        fields
1666            .push(&self.contract.symbol)?
1667            .push(&self.contract.sec_type)?
1668            .push(&self.contract.last_trade_date_or_contract_month)?
1669            .push_empty(self.contract.strike)?
1670            .push(&self.contract.right)?
1671            .push(&self.contract.multiplier)?
1672            .push(&self.contract.exchange)?
1673            .push(&self.contract.currency)?
1674            .push(&self.contract.local_symbol)?;
1675        if server_version >= MIN_SERVER_VER_TRADING_CLASS {
1676            fields.push(&self.contract.trading_class)?;
1677        }
1678        fields
1679            .push(self.exercise_action)?
1680            .push(self.exercise_quantity)?
1681            .push(&self.account)?
1682            .push(self.override_system_action)?;
1683        if server_version >= MIN_SERVER_VER_MANUAL_ORDER_TIME_EXERCISE_OPTIONS {
1684            fields.push(&self.manual_order_time)?;
1685        }
1686        if server_version >= MIN_SERVER_VER_CUSTOMER_ACCOUNT {
1687            fields.push(&self.customer_account)?;
1688        }
1689        if server_version >= MIN_SERVER_VER_PROFESSIONAL_CUSTOMER {
1690            fields.push(self.professional_customer)?;
1691        }
1692        Ok(())
1693    }
1694
1695    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1696        Ok(Some(
1697            protobuf::ExerciseOptionsRequest {
1698                order_id: Some(self.order_id),
1699                contract: Some(contract_to_proto(&self.contract, None)),
1700                exercise_action: Some(self.exercise_action),
1701                exercise_quantity: Some(self.exercise_quantity),
1702                account: non_empty(self.account.clone()),
1703                r#override: Some(self.override_system_action),
1704                manual_order_time: non_empty(self.manual_order_time.clone()),
1705                customer_account: non_empty(self.customer_account.clone()),
1706                professional_customer: self.professional_customer.then_some(true),
1707            }
1708            .encode_to_vec(),
1709        ))
1710    }
1711}
1712
1713/// Historical ticks request.
1714#[derive(Debug, Clone, Default, PartialEq)]
1715pub struct HistoricalTicksRequest {
1716    /// Request id.
1717    pub req_id: i32,
1718    /// Contract.
1719    pub contract: Contract,
1720    /// Start date/time.
1721    pub start_date_time: String,
1722    /// End date/time.
1723    pub end_date_time: String,
1724    /// Number of ticks.
1725    pub number_of_ticks: i32,
1726    /// Data type.
1727    pub what_to_show: String,
1728    /// Use regular trading hours.
1729    pub use_rth: bool,
1730    /// Ignore size flag.
1731    pub ignore_size: bool,
1732    /// Request options.
1733    pub misc_options: Vec<TagValue>,
1734}
1735
1736impl EncodableRequest for HistoricalTicksRequest {
1737    fn message(&self) -> Outgoing {
1738        Outgoing::ReqHistoricalTicks
1739    }
1740
1741    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1742        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
1743    }
1744
1745    fn encode_fields_for_server_version(
1746        &self,
1747        fields: &mut FieldSink,
1748        server_version: i32,
1749    ) -> TwsApiResult<()> {
1750        ensure_server_version(server_version, MIN_SERVER_VER_HISTORICAL_TICKS)?;
1751        fields
1752            .push(self.req_id)?
1753            .push(self.contract.con_id)?
1754            .push(&self.contract.symbol)?
1755            .push(&self.contract.sec_type)?
1756            .push(&self.contract.last_trade_date_or_contract_month)?
1757            .push_empty(self.contract.strike)?
1758            .push(&self.contract.right)?
1759            .push(&self.contract.multiplier)?
1760            .push(&self.contract.exchange)?
1761            .push(&self.contract.primary_exchange)?
1762            .push(&self.contract.currency)?
1763            .push(&self.contract.local_symbol)?
1764            .push(&self.contract.trading_class)?
1765            .push(self.contract.include_expired)?
1766            .push(&self.start_date_time)?
1767            .push(&self.end_date_time)?
1768            .push(self.number_of_ticks)?
1769            .push(&self.what_to_show)?
1770            .push(self.use_rth)?
1771            .push(self.ignore_size)?
1772            .push(tag_values_to_tws_options(&self.misc_options))?;
1773        Ok(())
1774    }
1775
1776    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1777        Ok(Some(
1778            protobuf::HistoricalTicksRequest {
1779                req_id: Some(self.req_id),
1780                contract: Some(contract_to_proto(&self.contract, None)),
1781                start_date_time: non_empty(self.start_date_time.clone()),
1782                end_date_time: non_empty(self.end_date_time.clone()),
1783                number_of_ticks: Some(self.number_of_ticks),
1784                what_to_show: non_empty(self.what_to_show.clone()),
1785                use_rth: Some(self.use_rth),
1786                ignore_size: self.ignore_size.then_some(true),
1787                misc_options: tag_values_to_map(&self.misc_options),
1788            }
1789            .encode_to_vec(),
1790        ))
1791    }
1792}
1793
1794impl EncodableRequest for PnlSingleRequest {
1795    fn message(&self) -> Outgoing {
1796        Outgoing::ReqPnlSingle
1797    }
1798
1799    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1800        fields
1801            .push(self.req_id)?
1802            .push(&self.account)?
1803            .push(&self.model_code)?
1804            .push(self.con_id)?;
1805        Ok(())
1806    }
1807
1808    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1809        Ok(Some(
1810            protobuf::PnLSingleRequest {
1811                req_id: Some(self.req_id),
1812                account: non_empty(self.account.clone()),
1813                model_code: non_empty(self.model_code.clone()),
1814                con_id: Some(self.con_id),
1815            }
1816            .encode_to_vec(),
1817        ))
1818    }
1819}
1820
1821impl EncodableRequest for IdRequest {
1822    fn message(&self) -> Outgoing {
1823        self.message
1824    }
1825
1826    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1827        if let Some(version) = self.version {
1828            fields.push(version)?;
1829        }
1830        fields.push(self.req_id)?;
1831        Ok(())
1832    }
1833
1834    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
1835        let payload = match self.message {
1836            Outgoing::ReqIds => protobuf::IdsRequest {
1837                num_ids: Some(self.req_id),
1838            }
1839            .encode_to_vec(),
1840            Outgoing::CancelMktData => protobuf::CancelMarketData {
1841                req_id: Some(self.req_id),
1842            }
1843            .encode_to_vec(),
1844            Outgoing::CancelMktDepth => protobuf::CancelMarketDepth {
1845                req_id: Some(self.req_id),
1846                is_smart_depth: None,
1847            }
1848            .encode_to_vec(),
1849            Outgoing::CancelAccountSummary => protobuf::CancelAccountSummary {
1850                req_id: Some(self.req_id),
1851            }
1852            .encode_to_vec(),
1853            Outgoing::CancelPositionsMulti => protobuf::CancelPositionsMulti {
1854                req_id: Some(self.req_id),
1855            }
1856            .encode_to_vec(),
1857            Outgoing::CancelAccountUpdatesMulti => protobuf::CancelAccountUpdatesMulti {
1858                req_id: Some(self.req_id),
1859            }
1860            .encode_to_vec(),
1861            Outgoing::CancelPnl => protobuf::CancelPnL {
1862                req_id: Some(self.req_id),
1863            }
1864            .encode_to_vec(),
1865            Outgoing::CancelPnlSingle => protobuf::CancelPnLSingle {
1866                req_id: Some(self.req_id),
1867            }
1868            .encode_to_vec(),
1869            Outgoing::CancelCalcImpliedVolat => protobuf::CancelCalculateImpliedVolatility {
1870                req_id: Some(self.req_id),
1871            }
1872            .encode_to_vec(),
1873            Outgoing::CancelCalcOptionPrice => protobuf::CancelCalculateOptionPrice {
1874                req_id: Some(self.req_id),
1875            }
1876            .encode_to_vec(),
1877            Outgoing::CancelContractData => protobuf::CancelContractData {
1878                req_id: Some(self.req_id),
1879            }
1880            .encode_to_vec(),
1881            Outgoing::CancelHistoricalData => protobuf::CancelHistoricalData {
1882                req_id: Some(self.req_id),
1883            }
1884            .encode_to_vec(),
1885            Outgoing::CancelScannerSubscription => protobuf::CancelScannerSubscription {
1886                req_id: Some(self.req_id),
1887            }
1888            .encode_to_vec(),
1889            Outgoing::CancelTickByTickData => protobuf::CancelTickByTick {
1890                req_id: Some(self.req_id),
1891            }
1892            .encode_to_vec(),
1893            Outgoing::CancelWshMetaData => protobuf::CancelWshMetaData {
1894                req_id: Some(self.req_id),
1895            }
1896            .encode_to_vec(),
1897            Outgoing::CancelWshEventData => protobuf::CancelWshEventData {
1898                req_id: Some(self.req_id),
1899            }
1900            .encode_to_vec(),
1901            Outgoing::CancelHeadTimestamp => protobuf::CancelHeadTimestamp {
1902                req_id: Some(self.req_id),
1903            }
1904            .encode_to_vec(),
1905            Outgoing::CancelHistogramData => protobuf::CancelHistogramData {
1906                req_id: Some(self.req_id),
1907            }
1908            .encode_to_vec(),
1909            Outgoing::CancelRealTimeBars => protobuf::CancelRealTimeBars {
1910                req_id: Some(self.req_id),
1911            }
1912            .encode_to_vec(),
1913            Outgoing::QueryDisplayGroups => protobuf::QueryDisplayGroupsRequest {
1914                req_id: Some(self.req_id),
1915            }
1916            .encode_to_vec(),
1917            Outgoing::UnsubscribeFromGroupEvents => protobuf::UnsubscribeFromGroupEventsRequest {
1918                req_id: Some(self.req_id),
1919            }
1920            .encode_to_vec(),
1921            Outgoing::ReqMarketRule => protobuf::MarketRuleRequest {
1922                market_rule_id: Some(self.req_id),
1923            }
1924            .encode_to_vec(),
1925            Outgoing::ReqSoftDollarTiers => protobuf::SoftDollarTiersRequest {
1926                req_id: Some(self.req_id),
1927            }
1928            .encode_to_vec(),
1929            Outgoing::ReqWshMetaData => protobuf::WshMetaDataRequest {
1930                req_id: Some(self.req_id),
1931            }
1932            .encode_to_vec(),
1933            Outgoing::ReqUserInfo => protobuf::UserInfoRequest {
1934                req_id: Some(self.req_id),
1935            }
1936            .encode_to_vec(),
1937            _ => return Ok(None),
1938        };
1939        Ok(Some(payload))
1940    }
1941}
1942
1943/// Market data request.
1944#[derive(Debug, Clone, Default, PartialEq)]
1945pub struct MarketDataRequest {
1946    /// Request id.
1947    pub req_id: TickerId,
1948    /// Contract.
1949    pub contract: Contract,
1950    /// Generic tick list.
1951    pub generic_tick_list: String,
1952    /// Snapshot flag.
1953    pub snapshot: bool,
1954    /// Regulatory snapshot flag.
1955    pub regulatory_snapshot: bool,
1956    /// Market data options.
1957    pub market_data_options: Vec<TagValue>,
1958}
1959
1960impl EncodableRequest for MarketDataRequest {
1961    fn message(&self) -> Outgoing {
1962        Outgoing::ReqMktData
1963    }
1964
1965    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
1966        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
1967    }
1968
1969    fn encode_fields_for_server_version(
1970        &self,
1971        fields: &mut FieldSink,
1972        server_version: i32,
1973    ) -> TwsApiResult<()> {
1974        fields.push(11)?.push(self.req_id)?;
1975        if server_version >= MIN_SERVER_VER_REQ_MKT_DATA_CONID {
1976            fields.push(self.contract.con_id)?;
1977        }
1978        encode_contract_market_data(fields, &self.contract, server_version)?;
1979
1980        if self.contract.sec_type == "BAG" {
1981            fields.push(self.contract.combo_legs.len())?;
1982            for leg in &self.contract.combo_legs {
1983                fields
1984                    .push(leg.con_id)?
1985                    .push(leg.ratio)?
1986                    .push(&leg.action)?
1987                    .push(&leg.exchange)?;
1988            }
1989        }
1990
1991        if server_version >= MIN_SERVER_VER_DELTA_NEUTRAL {
1992            if let Some(delta_neutral) = &self.contract.delta_neutral_contract {
1993                fields
1994                    .push(true)?
1995                    .push(delta_neutral.con_id)?
1996                    .push(delta_neutral.delta)?
1997                    .push(delta_neutral.price)?;
1998            } else {
1999                fields.push(false)?;
2000            }
2001        }
2002
2003        fields.push(&self.generic_tick_list)?.push(self.snapshot)?;
2004        if server_version >= MIN_SERVER_VER_REQ_SMART_COMPONENTS {
2005            fields.push(self.regulatory_snapshot)?;
2006        }
2007        if server_version >= MIN_SERVER_VER_LINKING {
2008            fields.push(tag_values_to_tws_options(&self.market_data_options))?;
2009        }
2010        Ok(())
2011    }
2012
2013    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
2014        let mut msg = protobuf::MarketDataRequest {
2015            req_id: Some(self.req_id),
2016            contract: Some(contract_to_proto(&self.contract, None)),
2017            generic_tick_list: non_empty(self.generic_tick_list.clone()),
2018            snapshot: self.snapshot.then_some(true),
2019            regulatory_snapshot: self.regulatory_snapshot.then_some(true),
2020            market_data_options: tag_values_to_map(&self.market_data_options),
2021        };
2022        if msg.market_data_options.is_empty() {
2023            msg.market_data_options = Default::default();
2024        }
2025        Ok(Some(msg.encode_to_vec()))
2026    }
2027}
2028
2029/// Market depth request.
2030#[derive(Debug, Clone, Default, PartialEq)]
2031pub struct MarketDepthRequest {
2032    /// Request id.
2033    pub req_id: TickerId,
2034    /// Contract.
2035    pub contract: Contract,
2036    /// Number of rows.
2037    pub num_rows: i32,
2038    /// Smart depth.
2039    pub is_smart_depth: bool,
2040    /// Market depth options.
2041    pub market_depth_options: Vec<TagValue>,
2042}
2043
2044impl EncodableRequest for MarketDepthRequest {
2045    fn message(&self) -> Outgoing {
2046        Outgoing::ReqMktDepth
2047    }
2048
2049    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
2050        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
2051    }
2052
2053    fn encode_fields_for_server_version(
2054        &self,
2055        fields: &mut FieldSink,
2056        server_version: i32,
2057    ) -> TwsApiResult<()> {
2058        fields.push(5)?.push(self.req_id)?;
2059        if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2060            fields.push(self.contract.con_id)?;
2061        }
2062        fields
2063            .push(&self.contract.symbol)?
2064            .push(&self.contract.sec_type)?
2065            .push(&self.contract.last_trade_date_or_contract_month)?
2066            .push_empty(self.contract.strike)?
2067            .push(&self.contract.right)?
2068            .push(&self.contract.multiplier)?
2069            .push(&self.contract.exchange)?;
2070        if server_version >= MIN_SERVER_VER_MKT_DEPTH_PRIM_EXCHANGE {
2071            fields.push(&self.contract.primary_exchange)?;
2072        }
2073        fields
2074            .push(&self.contract.currency)?
2075            .push(&self.contract.local_symbol)?;
2076        if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2077            fields.push(&self.contract.trading_class)?;
2078        }
2079        fields.push(self.num_rows)?;
2080        if server_version >= MIN_SERVER_VER_SMART_DEPTH {
2081            fields.push(self.is_smart_depth)?;
2082        }
2083        if server_version >= MIN_SERVER_VER_LINKING {
2084            fields.push(tag_values_to_tws_options(&self.market_depth_options))?;
2085        }
2086        Ok(())
2087    }
2088
2089    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
2090        let msg = protobuf::MarketDepthRequest {
2091            req_id: Some(self.req_id),
2092            contract: Some(contract_to_proto(&self.contract, None)),
2093            num_rows: Some(self.num_rows),
2094            is_smart_depth: self.is_smart_depth.then_some(true),
2095            market_depth_options: tag_values_to_map(&self.market_depth_options),
2096        };
2097        Ok(Some(msg.encode_to_vec()))
2098    }
2099}
2100
2101/// Place order request.
2102#[derive(Debug, Clone, Default, PartialEq)]
2103pub struct PlaceOrderRequest {
2104    /// Order id.
2105    pub order_id: i32,
2106    /// Contract.
2107    pub contract: Contract,
2108    /// Order.
2109    pub order: Order,
2110    /// Extra raw fields for TWS server-version-specific order tail fields.
2111    pub extra_fields: String,
2112}
2113
2114impl EncodableRequest for PlaceOrderRequest {
2115    fn message(&self) -> Outgoing {
2116        Outgoing::PlaceOrder
2117    }
2118
2119    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
2120        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
2121    }
2122
2123    fn encode_fields_for_server_version(
2124        &self,
2125        fields: &mut FieldSink,
2126        server_version: i32,
2127    ) -> TwsApiResult<()> {
2128        encode_place_order_fields(
2129            fields,
2130            server_version,
2131            self.order_id,
2132            &self.contract,
2133            &self.order,
2134            &self.extra_fields,
2135        )
2136    }
2137
2138    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
2139        let msg = protobuf::PlaceOrderRequest {
2140            order_id: Some(self.order_id),
2141            contract: Some(contract_to_proto(&self.contract, Some(&self.order))),
2142            order: Some(order_to_proto(&self.order)),
2143            attached_orders: attached_orders_to_proto(&self.order),
2144        };
2145        Ok(Some(msg.encode_to_vec()))
2146    }
2147}
2148
2149/// Cancel order request.
2150#[derive(Debug, Clone, Default, PartialEq, Eq)]
2151pub struct CancelOrderRequest {
2152    /// Order id.
2153    pub order_id: i32,
2154    /// Cancel metadata.
2155    pub order_cancel: OrderCancel,
2156}
2157
2158impl EncodableRequest for CancelOrderRequest {
2159    fn message(&self) -> Outgoing {
2160        Outgoing::CancelOrder
2161    }
2162
2163    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
2164        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
2165    }
2166
2167    fn encode_fields_for_server_version(
2168        &self,
2169        fields: &mut FieldSink,
2170        server_version: i32,
2171    ) -> TwsApiResult<()> {
2172        if server_version < MIN_SERVER_VER_CME_TAGGING_FIELDS {
2173            fields.push(1)?;
2174        }
2175        fields.push(self.order_id)?;
2176        if server_version >= MIN_SERVER_VER_MANUAL_ORDER_TIME {
2177            fields.push(&self.order_cancel.manual_order_cancel_time)?;
2178        }
2179        if (MIN_SERVER_VER_RFQ_FIELDS..MIN_SERVER_VER_UNDO_RFQ_FIELDS).contains(&server_version) {
2180            fields.push("")?.push("")?.push_empty(UNSET_INTEGER)?;
2181        }
2182        if server_version >= MIN_SERVER_VER_CME_TAGGING_FIELDS {
2183            fields
2184                .push(&self.order_cancel.ext_operator)?
2185                .push_empty(self.order_cancel.manual_order_indicator)?;
2186        }
2187        Ok(())
2188    }
2189
2190    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
2191        let msg = protobuf::CancelOrderRequest {
2192            order_id: Some(self.order_id),
2193            order_cancel: Some(order_cancel_to_proto(&self.order_cancel)),
2194        };
2195        Ok(Some(msg.encode_to_vec()))
2196    }
2197}
2198
2199/// Execution request.
2200#[derive(Debug, Clone, Default, PartialEq, Eq)]
2201pub struct ExecutionRequest {
2202    /// Request id.
2203    pub req_id: i32,
2204    /// Execution filter.
2205    pub filter: ExecutionFilter,
2206}
2207
2208impl EncodableRequest for ExecutionRequest {
2209    fn message(&self) -> Outgoing {
2210        Outgoing::ReqExecutions
2211    }
2212
2213    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
2214        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
2215    }
2216
2217    fn encode_fields_for_server_version(
2218        &self,
2219        fields: &mut FieldSink,
2220        server_version: i32,
2221    ) -> TwsApiResult<()> {
2222        fields.push(3)?;
2223        if server_version >= MIN_SERVER_VER_EXECUTION_DATA_CHAIN {
2224            fields.push(self.req_id)?;
2225        }
2226        fields
2227            .push(self.filter.client_id)?
2228            .push(&self.filter.acct_code)?
2229            .push(&self.filter.time)?
2230            .push(&self.filter.symbol)?
2231            .push(&self.filter.sec_type)?
2232            .push(&self.filter.exchange)?
2233            .push(&self.filter.side)?;
2234        if server_version >= MIN_SERVER_VER_PARAMETRIZED_DAYS_OF_EXECUTIONS {
2235            fields
2236                .push(self.filter.last_n_days)?
2237                .push(self.filter.specific_dates.len())?;
2238            for date in &self.filter.specific_dates {
2239                fields.push(*date)?;
2240            }
2241        }
2242        Ok(())
2243    }
2244
2245    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
2246        let msg = protobuf::ExecutionRequest {
2247            req_id: Some(self.req_id),
2248            execution_filter: Some(execution_filter_to_proto(&self.filter)),
2249        };
2250        Ok(Some(msg.encode_to_vec()))
2251    }
2252}
2253
2254/// Contract details request.
2255#[derive(Debug, Clone, Default, PartialEq)]
2256pub struct ContractDetailsRequest {
2257    /// Request id.
2258    pub req_id: i32,
2259    /// Contract.
2260    pub contract: Contract,
2261}
2262
2263impl EncodableRequest for ContractDetailsRequest {
2264    fn message(&self) -> Outgoing {
2265        Outgoing::ReqContractData
2266    }
2267
2268    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
2269        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
2270    }
2271
2272    fn encode_fields_for_server_version(
2273        &self,
2274        fields: &mut FieldSink,
2275        server_version: i32,
2276    ) -> TwsApiResult<()> {
2277        fields.push(8)?;
2278        if server_version >= MIN_SERVER_VER_CONTRACT_DATA_CHAIN {
2279            fields.push(self.req_id)?;
2280        }
2281
2282        fields
2283            .push(self.contract.con_id)?
2284            .push(&self.contract.symbol)?
2285            .push(&self.contract.sec_type)?
2286            .push(&self.contract.last_trade_date_or_contract_month)?
2287            .push_empty(self.contract.strike)?
2288            .push(&self.contract.right)?
2289            .push(&self.contract.multiplier)?;
2290
2291        if server_version >= MIN_SERVER_VER_PRIMARYEXCH {
2292            fields
2293                .push(&self.contract.exchange)?
2294                .push(&self.contract.primary_exchange)?;
2295        } else if server_version >= MIN_SERVER_VER_LINKING {
2296            let exchange = if !self.contract.primary_exchange.is_empty()
2297                && (self.contract.exchange == "BEST" || self.contract.exchange == "SMART")
2298            {
2299                format!(
2300                    "{}:{}",
2301                    self.contract.exchange, self.contract.primary_exchange
2302                )
2303            } else {
2304                self.contract.exchange.clone()
2305            };
2306            fields.push(exchange)?;
2307        }
2308
2309        fields
2310            .push(&self.contract.currency)?
2311            .push(&self.contract.local_symbol)?;
2312        if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2313            fields.push(&self.contract.trading_class)?;
2314        }
2315        fields.push(self.contract.include_expired)?;
2316        if server_version >= MIN_SERVER_VER_SEC_ID_TYPE {
2317            fields
2318                .push(&self.contract.sec_id_type)?
2319                .push(&self.contract.sec_id)?;
2320        }
2321        if server_version >= MIN_SERVER_VER_BOND_ISSUERID {
2322            fields.push(&self.contract.issuer_id)?;
2323        }
2324        Ok(())
2325    }
2326
2327    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
2328        let msg = protobuf::ContractDataRequest {
2329            req_id: Some(self.req_id),
2330            contract: Some(contract_to_proto(&self.contract, None)),
2331        };
2332        Ok(Some(msg.encode_to_vec()))
2333    }
2334}
2335
2336/// Historical data request.
2337#[derive(Debug, Clone, Default, PartialEq)]
2338pub struct HistoricalDataRequest {
2339    /// Request id.
2340    pub req_id: i32,
2341    /// Contract.
2342    pub contract: Contract,
2343    /// End date/time.
2344    pub end_date_time: String,
2345    /// Duration string.
2346    pub duration_str: String,
2347    /// Bar size setting.
2348    pub bar_size_setting: String,
2349    /// What to show.
2350    pub what_to_show: String,
2351    /// Use RTH.
2352    pub use_rth: i32,
2353    /// Format date.
2354    pub format_date: i32,
2355    /// Keep up to date.
2356    pub keep_up_to_date: bool,
2357    /// Chart options.
2358    pub chart_options: Vec<TagValue>,
2359}
2360
2361impl EncodableRequest for HistoricalDataRequest {
2362    fn message(&self) -> Outgoing {
2363        Outgoing::ReqHistoricalData
2364    }
2365
2366    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
2367        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
2368    }
2369
2370    fn encode_fields_for_server_version(
2371        &self,
2372        fields: &mut FieldSink,
2373        server_version: i32,
2374    ) -> TwsApiResult<()> {
2375        if server_version < MIN_SERVER_VER_SYNT_REALTIME_BARS {
2376            fields.push(6)?;
2377        }
2378        fields.push(self.req_id)?;
2379        if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2380            fields.push(self.contract.con_id)?;
2381        }
2382        encode_contract_historical_data(fields, &self.contract, server_version)?;
2383        fields
2384            .push(&self.end_date_time)?
2385            .push(&self.bar_size_setting)?
2386            .push(&self.duration_str)?
2387            .push(self.use_rth)?
2388            .push(&self.what_to_show)?
2389            .push(self.format_date)?;
2390
2391        if self.contract.sec_type == "BAG" {
2392            fields.push(self.contract.combo_legs.len())?;
2393            for leg in &self.contract.combo_legs {
2394                fields
2395                    .push(leg.con_id)?
2396                    .push(leg.ratio)?
2397                    .push(&leg.action)?
2398                    .push(&leg.exchange)?;
2399            }
2400        }
2401
2402        if server_version >= MIN_SERVER_VER_SYNT_REALTIME_BARS {
2403            fields.push(self.keep_up_to_date)?;
2404        }
2405        if server_version >= MIN_SERVER_VER_LINKING {
2406            fields.push(tag_values_to_tws_options(&self.chart_options))?;
2407        }
2408        Ok(())
2409    }
2410
2411    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
2412        let msg = protobuf::HistoricalDataRequest {
2413            req_id: Some(self.req_id),
2414            contract: Some(contract_to_proto(&self.contract, None)),
2415            end_date_time: non_empty(self.end_date_time.clone()),
2416            bar_size_setting: non_empty(self.bar_size_setting.clone()),
2417            duration: non_empty(self.duration_str.clone()),
2418            use_rth: (self.use_rth != 0).then_some(true),
2419            what_to_show: non_empty(self.what_to_show.clone()),
2420            format_date: Some(self.format_date),
2421            keep_up_to_date: self.keep_up_to_date.then_some(true),
2422            chart_options: tag_values_to_map(&self.chart_options),
2423        };
2424        Ok(Some(msg.encode_to_vec()))
2425    }
2426}
2427
2428/// Scanner subscription request.
2429#[derive(Debug, Clone, Default, PartialEq)]
2430pub struct ScannerSubscriptionRequest {
2431    /// Request id.
2432    pub req_id: i32,
2433    /// Subscription.
2434    pub subscription: ScannerSubscription,
2435    /// Scanner subscription options.
2436    pub scanner_subscription_options: Vec<TagValue>,
2437    /// Scanner subscription filter options.
2438    pub scanner_subscription_filter_options: Vec<TagValue>,
2439}
2440
2441impl EncodableRequest for ScannerSubscriptionRequest {
2442    fn message(&self) -> Outgoing {
2443        Outgoing::ReqScannerSubscription
2444    }
2445
2446    fn encode_fields(&self, fields: &mut FieldSink) -> TwsApiResult<()> {
2447        self.encode_fields_for_server_version(fields, MAX_CLIENT_VER)
2448    }
2449
2450    fn encode_fields_for_server_version(
2451        &self,
2452        fields: &mut FieldSink,
2453        server_version: i32,
2454    ) -> TwsApiResult<()> {
2455        let sub = &self.subscription;
2456        if server_version < MIN_SERVER_VER_SCANNER_GENERIC_OPTS {
2457            fields.push(4)?;
2458        }
2459        fields
2460            .push(self.req_id)?
2461            .push(sub.number_of_rows)?
2462            .push(&sub.instrument)?
2463            .push(&sub.location_code)?
2464            .push(&sub.scan_code)?
2465            .push_empty(sub.above_price)?
2466            .push_empty(sub.below_price)?
2467            .push_empty(sub.above_volume)?
2468            .push_empty(sub.market_cap_above)?
2469            .push_empty(sub.market_cap_below)?
2470            .push(&sub.moody_rating_above)?
2471            .push(&sub.moody_rating_below)?
2472            .push(&sub.sp_rating_above)?
2473            .push(&sub.sp_rating_below)?
2474            .push(&sub.maturity_date_above)?
2475            .push(&sub.maturity_date_below)?
2476            .push_empty(sub.coupon_rate_above)?
2477            .push_empty(sub.coupon_rate_below)?
2478            .push(sub.exclude_convertible)?
2479            .push_empty(sub.average_option_volume_above)?
2480            .push(&sub.scanner_setting_pairs)?
2481            .push(&sub.stock_type_filter)?;
2482        if server_version >= MIN_SERVER_VER_SCANNER_GENERIC_OPTS {
2483            fields.push(tag_values_to_tws_options(
2484                &self.scanner_subscription_filter_options,
2485            ))?;
2486        }
2487        if server_version >= MIN_SERVER_VER_LINKING {
2488            fields.push(tag_values_to_tws_options(
2489                &self.scanner_subscription_options,
2490            ))?;
2491        }
2492        Ok(())
2493    }
2494
2495    fn encode_protobuf(&self) -> TwsApiResult<Option<Vec<u8>>> {
2496        let mut subscription = scanner_subscription_to_proto(&self.subscription);
2497        subscription.scanner_subscription_options =
2498            tag_values_to_map(&self.scanner_subscription_options);
2499        subscription.scanner_subscription_filter_options =
2500            tag_values_to_map(&self.scanner_subscription_filter_options);
2501
2502        let msg = protobuf::ScannerSubscriptionRequest {
2503            req_id: Some(self.req_id),
2504            scanner_subscription: Some(subscription),
2505        };
2506        Ok(Some(msg.encode_to_vec()))
2507    }
2508}
2509
2510fn encode_contract_head_or_histogram(
2511    fields: &mut FieldSink,
2512    contract: &Contract,
2513) -> TwsApiResult<()> {
2514    fields
2515        .push(contract.con_id)?
2516        .push(&contract.symbol)?
2517        .push(&contract.sec_type)?
2518        .push(&contract.last_trade_date_or_contract_month)?
2519        .push_empty(contract.strike)?
2520        .push(&contract.right)?
2521        .push(&contract.multiplier)?
2522        .push(&contract.exchange)?
2523        .push(&contract.primary_exchange)?
2524        .push(&contract.currency)?
2525        .push(&contract.local_symbol)?
2526        .push(&contract.trading_class)?
2527        .push(contract.include_expired)?;
2528    Ok(())
2529}
2530
2531fn encode_contract_real_time_bars(
2532    fields: &mut FieldSink,
2533    contract: &Contract,
2534    server_version: i32,
2535) -> TwsApiResult<()> {
2536    if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2537        fields.push(contract.con_id)?;
2538    }
2539    fields
2540        .push(&contract.symbol)?
2541        .push(&contract.sec_type)?
2542        .push(&contract.last_trade_date_or_contract_month)?
2543        .push_empty(contract.strike)?
2544        .push(&contract.right)?
2545        .push(&contract.multiplier)?
2546        .push(&contract.exchange)?
2547        .push(&contract.primary_exchange)?
2548        .push(&contract.currency)?
2549        .push(&contract.local_symbol)?;
2550    if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2551        fields.push(&contract.trading_class)?;
2552    }
2553    Ok(())
2554}
2555
2556fn encode_contract_market_data(
2557    fields: &mut FieldSink,
2558    contract: &Contract,
2559    server_version: i32,
2560) -> TwsApiResult<()> {
2561    fields
2562        .push(&contract.symbol)?
2563        .push(&contract.sec_type)?
2564        .push(&contract.last_trade_date_or_contract_month)?
2565        .push_empty(contract.strike)?
2566        .push(&contract.right)?
2567        .push(&contract.multiplier)?
2568        .push(&contract.exchange)?
2569        .push(&contract.primary_exchange)?
2570        .push(&contract.currency)?
2571        .push(&contract.local_symbol)?;
2572    if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2573        fields.push(&contract.trading_class)?;
2574    }
2575    Ok(())
2576}
2577
2578fn encode_contract_historical_data(
2579    fields: &mut FieldSink,
2580    contract: &Contract,
2581    server_version: i32,
2582) -> TwsApiResult<()> {
2583    fields
2584        .push(&contract.symbol)?
2585        .push(&contract.sec_type)?
2586        .push(&contract.last_trade_date_or_contract_month)?
2587        .push_empty(contract.strike)?
2588        .push(&contract.right)?
2589        .push(&contract.multiplier)?
2590        .push(&contract.exchange)?
2591        .push(&contract.primary_exchange)?
2592        .push(&contract.currency)?
2593        .push(&contract.local_symbol)?;
2594    if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2595        fields.push(&contract.trading_class)?;
2596    }
2597    fields.push(contract.include_expired)?;
2598    Ok(())
2599}
2600
2601fn encode_contract_for_calculation(
2602    fields: &mut FieldSink,
2603    contract: &Contract,
2604    server_version: i32,
2605) -> TwsApiResult<()> {
2606    fields
2607        .push(contract.con_id)?
2608        .push(&contract.symbol)?
2609        .push(&contract.sec_type)?
2610        .push(&contract.last_trade_date_or_contract_month)?
2611        .push_empty(contract.strike)?
2612        .push(&contract.right)?
2613        .push(&contract.multiplier)?
2614        .push(&contract.exchange)?
2615        .push(&contract.primary_exchange)?
2616        .push(&contract.currency)?
2617        .push(&contract.local_symbol)?;
2618    if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2619        fields.push(&contract.trading_class)?;
2620    }
2621    Ok(())
2622}
2623
2624fn encode_place_order_fields(
2625    fields: &mut FieldSink,
2626    server_version: i32,
2627    order_id: i32,
2628    contract: &Contract,
2629    order: &Order,
2630    extra_fields: &str,
2631) -> TwsApiResult<()> {
2632    if server_version < MIN_SERVER_VER_ORDER_CONTAINER {
2633        fields.push(45)?;
2634    }
2635
2636    fields.push(order_id)?;
2637    if server_version >= MIN_SERVER_VER_PLACE_ORDER_CONID {
2638        fields.push(contract.con_id)?;
2639    }
2640    fields
2641        .push(&contract.symbol)?
2642        .push(&contract.sec_type)?
2643        .push(&contract.last_trade_date_or_contract_month)?
2644        .push_empty(contract.strike)?
2645        .push(&contract.right)?
2646        .push(&contract.multiplier)?
2647        .push(&contract.exchange)?
2648        .push(&contract.primary_exchange)?
2649        .push(&contract.currency)?
2650        .push(&contract.local_symbol)?;
2651    if server_version >= MIN_SERVER_VER_TRADING_CLASS {
2652        fields.push(&contract.trading_class)?;
2653    }
2654    if server_version >= MIN_SERVER_VER_SEC_ID_TYPE {
2655        fields.push(&contract.sec_id_type)?.push(&contract.sec_id)?;
2656    }
2657
2658    fields.push(&order.action)?;
2659    fields.push(order.total_quantity.to_string())?;
2660    fields
2661        .push(&order.order_type)?
2662        .push_empty(order.limit_price)?
2663        .push_empty(order.aux_price)?
2664        .push(&order.tif)?
2665        .push(&order.oca_group)?
2666        .push(&order.account)?
2667        .push(&order.open_close)?
2668        .push(order.origin as i32)?
2669        .push(&order.order_ref)?
2670        .push(order.transmit)?
2671        .push(order.parent_id)?
2672        .push(order.block_order)?
2673        .push(order.sweep_to_fill)?
2674        .push(order.display_size)?
2675        .push(order.trigger_method)?
2676        .push(order.outside_rth)?
2677        .push(order.hidden)?;
2678
2679    if contract.sec_type == "BAG" {
2680        fields.push(contract.combo_legs.len())?;
2681        for leg in &contract.combo_legs {
2682            fields
2683                .push(leg.con_id)?
2684                .push(leg.ratio)?
2685                .push(&leg.action)?
2686                .push(&leg.exchange)?
2687                .push(leg.open_close as i32)?
2688                .push(leg.short_sale_slot)?
2689                .push(&leg.designated_location)?;
2690            if server_version >= MIN_SERVER_VER_SSHORTX_OLD {
2691                fields.push(leg.exempt_code)?;
2692            }
2693        }
2694    }
2695
2696    if server_version >= MIN_SERVER_VER_ORDER_COMBO_LEGS_PRICE && contract.sec_type == "BAG" {
2697        fields.push(order.order_combo_legs.len())?;
2698        for leg in &order.order_combo_legs {
2699            fields.push_empty(leg.price)?;
2700        }
2701    }
2702
2703    if server_version >= MIN_SERVER_VER_SMART_COMBO_ROUTING_PARAMS && contract.sec_type == "BAG" {
2704        encode_tag_values(fields, &order.smart_combo_routing_params)?;
2705    }
2706
2707    fields
2708        .push("")?
2709        .push(order.discretionary_amount)?
2710        .push(&order.good_after_time)?
2711        .push(&order.good_till_date)?
2712        .push(&order.fa_group)?
2713        .push(&order.fa_method)?
2714        .push(&order.fa_percentage)?;
2715    if server_version < MIN_SERVER_VER_FA_PROFILE_DESUPPORT {
2716        fields.push("")?;
2717    }
2718    if server_version >= MIN_SERVER_VER_MODELS_SUPPORT {
2719        fields.push(&order.model_code)?;
2720    }
2721
2722    fields
2723        .push(order.short_sale_slot)?
2724        .push(&order.designated_location)?;
2725    if server_version >= MIN_SERVER_VER_SSHORTX_OLD {
2726        fields.push(order.exempt_code)?;
2727    }
2728
2729    fields
2730        .push(order.oca_type)?
2731        .push(&order.rule80a)?
2732        .push(&order.settling_firm)?
2733        .push(order.all_or_none)?
2734        .push_empty(order.min_qty)?
2735        .push_empty(order.percent_offset)?
2736        .push(false)?
2737        .push(false)?
2738        .push_empty(UNSET_DOUBLE)?
2739        .push(order.auction_strategy as i32)?
2740        .push_empty(order.starting_price)?
2741        .push_empty(order.stock_ref_price)?
2742        .push_empty(order.delta)?
2743        .push_empty(order.stock_range_lower)?
2744        .push_empty(order.stock_range_upper)?
2745        .push(order.override_percentage_constraints)?
2746        .push_empty(order.volatility)?
2747        .push_empty(order.volatility_type)?
2748        .push(&order.delta_neutral_order_type)?
2749        .push_empty(order.delta_neutral_aux_price)?;
2750
2751    if server_version >= MIN_SERVER_VER_DELTA_NEUTRAL_CONID
2752        && !order.delta_neutral_order_type.is_empty()
2753    {
2754        fields
2755            .push(order.delta_neutral_con_id)?
2756            .push(&order.delta_neutral_settling_firm)?
2757            .push(&order.delta_neutral_clearing_account)?
2758            .push(&order.delta_neutral_clearing_intent)?;
2759    }
2760    if server_version >= MIN_SERVER_VER_DELTA_NEUTRAL_OPEN_CLOSE
2761        && !order.delta_neutral_order_type.is_empty()
2762    {
2763        fields
2764            .push(&order.delta_neutral_open_close)?
2765            .push(order.delta_neutral_short_sale)?
2766            .push(order.delta_neutral_short_sale_slot)?
2767            .push(&order.delta_neutral_designated_location)?;
2768    }
2769
2770    fields
2771        .push(order.continuous_update)?
2772        .push_empty(order.reference_price_type)?
2773        .push_empty(order.trail_stop_price)?;
2774    if server_version >= MIN_SERVER_VER_TRAILING_PERCENT {
2775        fields.push_empty(order.trailing_percent)?;
2776    }
2777
2778    if server_version >= MIN_SERVER_VER_SCALE_ORDERS2 {
2779        fields
2780            .push_empty(order.scale_init_level_size)?
2781            .push_empty(order.scale_subs_level_size)?;
2782    } else {
2783        fields.push("")?.push_empty(order.scale_init_level_size)?;
2784    }
2785    fields.push_empty(order.scale_price_increment)?;
2786    if server_version >= MIN_SERVER_VER_SCALE_ORDERS3
2787        && order.scale_price_increment != UNSET_DOUBLE
2788        && order.scale_price_increment > 0.0
2789    {
2790        fields
2791            .push_empty(order.scale_price_adjust_value)?
2792            .push_empty(order.scale_price_adjust_interval)?
2793            .push_empty(order.scale_profit_offset)?
2794            .push(order.scale_auto_reset)?
2795            .push_empty(order.scale_init_position)?
2796            .push_empty(order.scale_init_fill_qty)?
2797            .push(order.scale_random_percent)?;
2798    }
2799    if server_version >= MIN_SERVER_VER_SCALE_TABLE {
2800        fields
2801            .push(&order.scale_table)?
2802            .push(&order.active_start_time)?
2803            .push(&order.active_stop_time)?;
2804    }
2805
2806    if server_version >= MIN_SERVER_VER_HEDGE_ORDERS {
2807        fields.push(&order.hedge_type)?;
2808        if !order.hedge_type.is_empty() {
2809            fields.push(&order.hedge_param)?;
2810        }
2811    }
2812    if server_version >= MIN_SERVER_VER_OPT_OUT_SMART_ROUTING {
2813        fields.push(order.opt_out_smart_routing)?;
2814    }
2815    if server_version >= MIN_SERVER_VER_PTA_ORDERS {
2816        fields
2817            .push(&order.clearing_account)?
2818            .push(&order.clearing_intent)?;
2819    }
2820    if server_version >= MIN_SERVER_VER_NOT_HELD {
2821        fields.push(order.not_held)?;
2822    }
2823    if server_version >= MIN_SERVER_VER_DELTA_NEUTRAL {
2824        if let Some(delta) = &contract.delta_neutral_contract {
2825            fields
2826                .push(true)?
2827                .push(delta.con_id)?
2828                .push(delta.delta)?
2829                .push(delta.price)?;
2830        } else {
2831            fields.push(false)?;
2832        }
2833    }
2834    if server_version >= MIN_SERVER_VER_ALGO_ORDERS {
2835        fields.push(&order.algo_strategy)?;
2836        if !order.algo_strategy.is_empty() {
2837            encode_tag_values(fields, &order.algo_params)?;
2838        }
2839    }
2840    if server_version >= MIN_SERVER_VER_ALGO_ID {
2841        fields.push(&order.algo_id)?;
2842    }
2843    fields.push(order.what_if)?;
2844    if server_version >= MIN_SERVER_VER_LINKING {
2845        fields.push(tag_values_to_tws_options(&order.order_misc_options))?;
2846    }
2847    if server_version >= MIN_SERVER_VER_ORDER_SOLICITED {
2848        fields.push(order.solicited)?;
2849    }
2850    if server_version >= MIN_SERVER_VER_RANDOMIZE_SIZE_AND_PRICE {
2851        fields
2852            .push(order.randomize_size)?
2853            .push(order.randomize_price)?;
2854    }
2855    if server_version >= MIN_SERVER_VER_PEGGED_TO_BENCHMARK {
2856        if is_peg_benchmark_order(&order.order_type) {
2857            fields
2858                .push(order.reference_contract_id)?
2859                .push(order.is_pegged_change_amount_decrease)?
2860                .push(order.pegged_change_amount)?
2861                .push(order.reference_change_amount)?
2862                .push(&order.reference_exchange_id)?;
2863        }
2864        fields.push(order.conditions.len())?;
2865        if !order.conditions.is_empty() {
2866            for condition in &order.conditions {
2867                encode_order_condition(fields, condition)?;
2868            }
2869            fields
2870                .push(order.conditions_ignore_rth)?
2871                .push(order.conditions_cancel_order)?;
2872        }
2873        fields
2874            .push(&order.adjusted_order_type)?
2875            .push(order.trigger_price)?
2876            .push(order.limit_price_offset)?
2877            .push(order.adjusted_stop_price)?
2878            .push(order.adjusted_stop_limit_price)?
2879            .push(order.adjusted_trailing_amount)?
2880            .push(order.adjustable_trailing_unit)?;
2881    }
2882    if server_version >= MIN_SERVER_VER_EXT_OPERATOR {
2883        fields.push(&order.ext_operator)?;
2884    }
2885    if server_version >= MIN_SERVER_VER_SOFT_DOLLAR_TIER {
2886        fields
2887            .push(&order.soft_dollar_tier.name)?
2888            .push(&order.soft_dollar_tier.value)?;
2889    }
2890    if server_version >= MIN_SERVER_VER_CASH_QTY {
2891        fields.push(order.cash_qty)?;
2892    }
2893    if server_version >= MIN_SERVER_VER_DECISION_MAKER {
2894        fields
2895            .push(&order.mifid2_decision_maker)?
2896            .push(&order.mifid2_decision_algo)?;
2897    }
2898    if server_version >= MIN_SERVER_VER_MIFID_EXECUTION {
2899        fields
2900            .push(&order.mifid2_execution_trader)?
2901            .push(&order.mifid2_execution_algo)?;
2902    }
2903    if server_version >= MIN_SERVER_VER_AUTO_PRICE_FOR_HEDGE {
2904        fields.push(order.dont_use_auto_price_for_hedge)?;
2905    }
2906    if server_version >= MIN_SERVER_VER_ORDER_CONTAINER {
2907        fields.push(order.is_oms_container)?;
2908    }
2909    if server_version >= MIN_SERVER_VER_D_PEG_ORDERS {
2910        fields.push(order.discretionary_up_to_limit_price)?;
2911    }
2912    if server_version >= MIN_SERVER_VER_PRICE_MGMT_ALGO {
2913        if order.use_price_mgmt_algo == UNSET_INTEGER {
2914            fields.push_empty(UNSET_INTEGER)?;
2915        } else {
2916            fields.push(order.use_price_mgmt_algo != 0)?;
2917        }
2918    }
2919    if server_version >= MIN_SERVER_VER_DURATION {
2920        fields.push(order.duration)?;
2921    }
2922    if server_version >= MIN_SERVER_VER_POST_TO_ATS {
2923        fields.push(order.post_to_ats)?;
2924    }
2925    if server_version >= MIN_SERVER_VER_AUTO_CANCEL_PARENT {
2926        fields.push(order.auto_cancel_parent)?;
2927    }
2928    if server_version >= MIN_SERVER_VER_ADVANCED_ORDER_REJECT {
2929        fields.push(&order.advanced_error_override)?;
2930    }
2931    if server_version >= MIN_SERVER_VER_MANUAL_ORDER_TIME {
2932        fields.push(&order.manual_order_time)?;
2933    }
2934    if server_version >= MIN_SERVER_VER_PEGBEST_PEGMID_OFFSETS {
2935        let mut send_mid_offsets = false;
2936        if contract.exchange == "IBKRATS" {
2937            fields.push_empty(order.min_trade_qty)?;
2938        }
2939        if is_peg_best_order(&order.order_type) {
2940            fields
2941                .push_empty(order.min_compete_size)?
2942                .push_empty(order.compete_against_best_offset)?;
2943            send_mid_offsets = order.compete_against_best_offset
2944                == crate::constants::COMPETE_AGAINST_BEST_OFFSET_UP_TO_MID;
2945        } else if is_peg_mid_order(&order.order_type) {
2946            send_mid_offsets = true;
2947        }
2948        if send_mid_offsets {
2949            fields
2950                .push_empty(order.mid_offset_at_whole)?
2951                .push_empty(order.mid_offset_at_half)?;
2952        }
2953    }
2954    if server_version >= MIN_SERVER_VER_CUSTOMER_ACCOUNT {
2955        fields.push(&order.customer_account)?;
2956    }
2957    if server_version >= MIN_SERVER_VER_PROFESSIONAL_CUSTOMER {
2958        fields.push(order.professional_customer)?;
2959    }
2960    if (MIN_SERVER_VER_RFQ_FIELDS..MIN_SERVER_VER_UNDO_RFQ_FIELDS).contains(&server_version) {
2961        fields.push("")?.push_empty(UNSET_INTEGER)?;
2962    }
2963    if server_version >= MIN_SERVER_VER_INCLUDE_OVERNIGHT {
2964        fields.push(order.include_overnight)?;
2965    }
2966    if server_version >= MIN_SERVER_VER_CME_TAGGING_FIELDS {
2967        fields.push(order.manual_order_indicator)?;
2968    }
2969    if server_version >= MIN_SERVER_VER_IMBALANCE_ONLY {
2970        fields.push(order.imbalance_only)?;
2971    }
2972
2973    fields.push_raw(extra_fields);
2974    Ok(())
2975}
2976
2977fn encode_order_condition(
2978    fields: &mut FieldSink,
2979    condition: &crate::types::OrderCondition,
2980) -> TwsApiResult<()> {
2981    use crate::types::OrderCondition;
2982
2983    match condition {
2984        OrderCondition::Price {
2985            is_conjunction_connection,
2986            trigger_method,
2987            con_id,
2988            exchange,
2989            is_more,
2990            price,
2991        } => {
2992            fields
2993                .push(1)?
2994                .push(if *is_conjunction_connection { "a" } else { "o" })?
2995                .push(*is_more)?
2996                .push(*price)?
2997                .push(*con_id)?
2998                .push(&exchange[..])?
2999                .push(*trigger_method)?;
3000        }
3001        OrderCondition::Time {
3002            is_conjunction_connection,
3003            is_more,
3004            time,
3005        } => {
3006            fields
3007                .push(3)?
3008                .push(if *is_conjunction_connection { "a" } else { "o" })?
3009                .push(*is_more)?
3010                .push(&time[..])?;
3011        }
3012        OrderCondition::Margin {
3013            is_conjunction_connection,
3014            is_more,
3015            percent,
3016        } => {
3017            fields
3018                .push(4)?
3019                .push(if *is_conjunction_connection { "a" } else { "o" })?
3020                .push(*is_more)?
3021                .push(*percent)?;
3022        }
3023        OrderCondition::Execution {
3024            is_conjunction_connection,
3025            sec_type,
3026            exchange,
3027            symbol,
3028        } => {
3029            fields
3030                .push(5)?
3031                .push(if *is_conjunction_connection { "a" } else { "o" })?
3032                .push(&sec_type[..])?
3033                .push(&exchange[..])?
3034                .push(&symbol[..])?;
3035        }
3036        OrderCondition::Volume {
3037            is_conjunction_connection,
3038            con_id,
3039            exchange,
3040            is_more,
3041            volume,
3042        } => {
3043            fields
3044                .push(6)?
3045                .push(if *is_conjunction_connection { "a" } else { "o" })?
3046                .push(*is_more)?
3047                .push(*volume)?
3048                .push(*con_id)?
3049                .push(&exchange[..])?;
3050        }
3051        OrderCondition::PercentChange {
3052            is_conjunction_connection,
3053            con_id,
3054            exchange,
3055            is_more,
3056            change_percent,
3057        } => {
3058            fields
3059                .push(7)?
3060                .push(if *is_conjunction_connection { "a" } else { "o" })?
3061                .push(*is_more)?
3062                .push(*change_percent)?
3063                .push(*con_id)?
3064                .push(&exchange[..])?;
3065        }
3066    }
3067    Ok(())
3068}
3069
3070fn is_peg_benchmark_order(order_type: &str) -> bool {
3071    matches!(order_type, "PEG BENCH" | "PEGBENCH")
3072}
3073
3074fn is_peg_mid_order(order_type: &str) -> bool {
3075    matches!(order_type, "PEG MID" | "PEGMID")
3076}
3077
3078fn is_peg_best_order(order_type: &str) -> bool {
3079    matches!(order_type, "PEG BEST" | "PEGBEST")
3080}
3081
3082fn encode_tag_values(fields: &mut FieldSink, values: &[TagValue]) -> TwsApiResult<()> {
3083    fields.push(values.len())?;
3084    for value in values {
3085        fields.push(&value.tag)?.push(&value.value)?;
3086    }
3087    Ok(())
3088}
3089
3090fn tag_values_to_tws_options(values: &[TagValue]) -> String {
3091    values
3092        .iter()
3093        .map(|value| format!("{}={};", value.tag, value.value))
3094        .collect::<String>()
3095}
3096
3097fn tag_values_to_map(values: &[TagValue]) -> std::collections::HashMap<String, String> {
3098    values
3099        .iter()
3100        .map(|value| (value.tag.clone(), value.value.clone()))
3101        .collect()
3102}
3103
3104fn contract_to_proto(contract: &Contract, order: Option<&Order>) -> protobuf::Contract {
3105    protobuf::Contract {
3106        con_id: valid_i32(contract.con_id),
3107        symbol: non_empty(contract.symbol.clone()),
3108        sec_type: non_empty(contract.sec_type.clone()),
3109        last_trade_date_or_contract_month: non_empty(
3110            contract.last_trade_date_or_contract_month.clone(),
3111        ),
3112        strike: valid_f64(contract.strike),
3113        right: non_empty(contract.right.clone()),
3114        multiplier: contract.multiplier.parse::<f64>().ok(),
3115        exchange: non_empty(contract.exchange.clone()),
3116        primary_exch: non_empty(contract.primary_exchange.clone()),
3117        currency: non_empty(contract.currency.clone()),
3118        local_symbol: non_empty(contract.local_symbol.clone()),
3119        trading_class: non_empty(contract.trading_class.clone()),
3120        sec_id_type: non_empty(contract.sec_id_type.clone()),
3121        sec_id: non_empty(contract.sec_id.clone()),
3122        description: non_empty(contract.description.clone()),
3123        issuer_id: non_empty(contract.issuer_id.clone()),
3124        delta_neutral_contract: contract.delta_neutral_contract.as_ref().map(|delta| {
3125            protobuf::DeltaNeutralContract {
3126                con_id: valid_i32(delta.con_id),
3127                delta: valid_f64(delta.delta),
3128                price: valid_f64(delta.price),
3129            }
3130        }),
3131        include_expired: contract.include_expired.then_some(true),
3132        combo_legs_descrip: non_empty(contract.combo_legs_description.clone()),
3133        combo_legs: contract
3134            .combo_legs
3135            .iter()
3136            .enumerate()
3137            .map(|(idx, leg)| {
3138                let per_leg_price = order
3139                    .and_then(|order| order.order_combo_legs.get(idx))
3140                    .and_then(|leg| valid_f64(leg.price));
3141                protobuf::ComboLeg {
3142                    con_id: valid_i32(leg.con_id),
3143                    ratio: valid_i32(leg.ratio),
3144                    action: non_empty(leg.action.clone()),
3145                    exchange: non_empty(leg.exchange.clone()),
3146                    open_close: valid_i32(leg.open_close as i32),
3147                    short_sales_slot: valid_i32(leg.short_sale_slot),
3148                    designated_location: non_empty(leg.designated_location.clone()),
3149                    exempt_code: valid_i32(leg.exempt_code),
3150                    per_leg_price,
3151                }
3152            })
3153            .collect(),
3154        last_trade_date: non_empty(contract.last_trade_date.clone()),
3155    }
3156}
3157
3158fn order_to_proto(order: &Order) -> protobuf::Order {
3159    let mut msg = protobuf::Order {
3160        client_id: valid_i32(order.client_id),
3161        order_id: valid_i32(order.order_id),
3162        perm_id: Some(order.perm_id),
3163        parent_id: valid_i32(order.parent_id),
3164        action: non_empty(order.action.clone()),
3165        total_quantity: Some(order.total_quantity.to_string()),
3166        display_size: valid_i32(order.display_size),
3167        order_type: non_empty(order.order_type.clone()),
3168        lmt_price: valid_f64(order.limit_price),
3169        aux_price: valid_f64(order.aux_price),
3170        tif: non_empty(order.tif.clone()),
3171        account: non_empty(order.account.clone()),
3172        settling_firm: non_empty(order.settling_firm.clone()),
3173        clearing_account: non_empty(order.clearing_account.clone()),
3174        clearing_intent: non_empty(order.clearing_intent.clone()),
3175        all_or_none: order.all_or_none.then_some(true),
3176        block_order: order.block_order.then_some(true),
3177        hidden: order.hidden.then_some(true),
3178        outside_rth: order.outside_rth.then_some(true),
3179        sweep_to_fill: order.sweep_to_fill.then_some(true),
3180        percent_offset: valid_f64(order.percent_offset),
3181        trailing_percent: valid_f64(order.trailing_percent),
3182        trail_stop_price: valid_f64(order.trail_stop_price),
3183        min_qty: valid_i32(order.min_qty),
3184        good_after_time: non_empty(order.good_after_time.clone()),
3185        good_till_date: non_empty(order.good_till_date.clone()),
3186        oca_group: non_empty(order.oca_group.clone()),
3187        order_ref: non_empty(order.order_ref.clone()),
3188        rule80_a: non_empty(order.rule80a.clone()),
3189        oca_type: valid_i32(order.oca_type),
3190        trigger_method: valid_i32(order.trigger_method),
3191        active_start_time: non_empty(order.active_start_time.clone()),
3192        active_stop_time: non_empty(order.active_stop_time.clone()),
3193        fa_group: non_empty(order.fa_group.clone()),
3194        fa_method: non_empty(order.fa_method.clone()),
3195        fa_percentage: non_empty(order.fa_percentage.clone()),
3196        volatility: valid_f64(order.volatility),
3197        volatility_type: valid_i32(order.volatility_type),
3198        continuous_update: order.continuous_update.then_some(true),
3199        reference_price_type: valid_i32(order.reference_price_type),
3200        delta_neutral_order_type: non_empty(order.delta_neutral_order_type.clone()),
3201        delta_neutral_aux_price: valid_f64(order.delta_neutral_aux_price),
3202        delta_neutral_con_id: valid_i32(order.delta_neutral_con_id),
3203        delta_neutral_open_close: non_empty(order.delta_neutral_open_close.clone()),
3204        delta_neutral_short_sale: order.delta_neutral_short_sale.then_some(true),
3205        delta_neutral_short_sale_slot: valid_i32(order.delta_neutral_short_sale_slot),
3206        delta_neutral_designated_location: non_empty(
3207            order.delta_neutral_designated_location.clone(),
3208        ),
3209        scale_init_level_size: valid_i32(order.scale_init_level_size),
3210        scale_subs_level_size: valid_i32(order.scale_subs_level_size),
3211        scale_price_increment: valid_f64(order.scale_price_increment),
3212        scale_price_adjust_value: valid_f64(order.scale_price_adjust_value),
3213        scale_price_adjust_interval: valid_i32(order.scale_price_adjust_interval),
3214        scale_profit_offset: valid_f64(order.scale_profit_offset),
3215        scale_auto_reset: order.scale_auto_reset.then_some(true),
3216        scale_init_position: valid_i32(order.scale_init_position),
3217        scale_init_fill_qty: valid_i32(order.scale_init_fill_qty),
3218        scale_random_percent: order.scale_random_percent.then_some(true),
3219        scale_table: non_empty(order.scale_table.clone()),
3220        hedge_type: non_empty(order.hedge_type.clone()),
3221        hedge_param: non_empty(order.hedge_param.clone()),
3222        algo_strategy: non_empty(order.algo_strategy.clone()),
3223        algo_id: non_empty(order.algo_id.clone()),
3224        what_if: order.what_if.then_some(true),
3225        transmit: order.transmit.then_some(true),
3226        override_percentage_constraints: order.override_percentage_constraints.then_some(true),
3227        open_close: non_empty(order.open_close.clone()),
3228        origin: valid_i32(order.origin as i32),
3229        short_sale_slot: valid_i32(order.short_sale_slot),
3230        designated_location: non_empty(order.designated_location.clone()),
3231        exempt_code: valid_i32(order.exempt_code),
3232        delta_neutral_settling_firm: non_empty(order.delta_neutral_settling_firm.clone()),
3233        delta_neutral_clearing_account: non_empty(order.delta_neutral_clearing_account.clone()),
3234        delta_neutral_clearing_intent: non_empty(order.delta_neutral_clearing_intent.clone()),
3235        discretionary_amt: valid_f64(order.discretionary_amount),
3236        opt_out_smart_routing: order.opt_out_smart_routing.then_some(true),
3237        starting_price: valid_f64(order.starting_price),
3238        stock_ref_price: valid_f64(order.stock_ref_price),
3239        delta: valid_f64(order.delta),
3240        stock_range_lower: valid_f64(order.stock_range_lower),
3241        stock_range_upper: valid_f64(order.stock_range_upper),
3242        not_held: order.not_held.then_some(true),
3243        solicited: order.solicited.then_some(true),
3244        randomize_size: order.randomize_size.then_some(true),
3245        randomize_price: order.randomize_price.then_some(true),
3246        reference_contract_id: valid_i32(order.reference_contract_id),
3247        pegged_change_amount: valid_f64(order.pegged_change_amount),
3248        is_pegged_change_amount_decrease: order.is_pegged_change_amount_decrease.then_some(true),
3249        reference_change_amount: valid_f64(order.reference_change_amount),
3250        reference_exchange_id: non_empty(order.reference_exchange_id.clone()),
3251        adjusted_order_type: non_empty(order.adjusted_order_type.clone()),
3252        trigger_price: valid_f64(order.trigger_price),
3253        adjusted_stop_price: valid_f64(order.adjusted_stop_price),
3254        adjusted_stop_limit_price: valid_f64(order.adjusted_stop_limit_price),
3255        adjusted_trailing_amount: valid_f64(order.adjusted_trailing_amount),
3256        adjustable_trailing_unit: valid_i32(order.adjustable_trailing_unit),
3257        lmt_price_offset: valid_f64(order.limit_price_offset),
3258        model_code: non_empty(order.model_code.clone()),
3259        ext_operator: non_empty(order.ext_operator.clone()),
3260        soft_dollar_tier: Some(protobuf::SoftDollarTier {
3261            name: non_empty(order.soft_dollar_tier.name.clone()),
3262            value: non_empty(order.soft_dollar_tier.value.clone()),
3263            display_name: non_empty(order.soft_dollar_tier.display_name.clone()),
3264        }),
3265        cash_qty: valid_f64(order.cash_qty),
3266        mifid2_decision_maker: non_empty(order.mifid2_decision_maker.clone()),
3267        mifid2_decision_algo: non_empty(order.mifid2_decision_algo.clone()),
3268        mifid2_execution_trader: non_empty(order.mifid2_execution_trader.clone()),
3269        mifid2_execution_algo: non_empty(order.mifid2_execution_algo.clone()),
3270        dont_use_auto_price_for_hedge: order.dont_use_auto_price_for_hedge.then_some(true),
3271        is_oms_container: order.is_oms_container.then_some(true),
3272        discretionary_up_to_limit_price: order.discretionary_up_to_limit_price.then_some(true),
3273        auto_cancel_date: non_empty(order.auto_cancel_date.clone()),
3274        filled_quantity: non_empty(order.filled_quantity.to_string()),
3275        ref_futures_con_id: valid_i32(order.ref_futures_con_id),
3276        auto_cancel_parent: order.auto_cancel_parent.then_some(true),
3277        shareholder: non_empty(order.shareholder.clone()),
3278        imbalance_only: order.imbalance_only.then_some(true),
3279        route_marketable_to_bbo: valid_i32(order.route_marketable_to_bbo),
3280        parent_perm_id: Some(order.parent_perm_id),
3281        use_price_mgmt_algo: valid_i32(order.use_price_mgmt_algo),
3282        duration: valid_i32(order.duration),
3283        post_to_ats: valid_i32(order.post_to_ats),
3284        advanced_error_override: non_empty(order.advanced_error_override.clone()),
3285        manual_order_time: non_empty(order.manual_order_time.clone()),
3286        min_trade_qty: valid_i32(order.min_trade_qty),
3287        min_compete_size: valid_i32(order.min_compete_size),
3288        compete_against_best_offset: valid_f64(order.compete_against_best_offset),
3289        mid_offset_at_whole: valid_f64(order.mid_offset_at_whole),
3290        mid_offset_at_half: valid_f64(order.mid_offset_at_half),
3291        customer_account: non_empty(order.customer_account.clone()),
3292        professional_customer: order.professional_customer.then_some(true),
3293        bond_accrued_interest: non_empty(order.bond_accrued_interest.clone()),
3294        include_overnight: order.include_overnight.then_some(true),
3295        manual_order_indicator: valid_i32(order.manual_order_indicator),
3296        submitter: non_empty(order.submitter.clone()),
3297        deactivate: order.deactivate.then_some(true),
3298        post_only: order.post_only.then_some(true),
3299        allow_pre_open: order.allow_pre_open.then_some(true),
3300        ignore_open_auction: order.ignore_open_auction.then_some(true),
3301        seek_price_improvement: valid_i32(order.seek_price_improvement),
3302        what_if_type: valid_i32(order.what_if_type),
3303        hedge_max_size: valid_i32(order.hedge_max_size),
3304        ..protobuf::Order::default()
3305    };
3306    msg.algo_params = tag_values_to_map(&order.algo_params);
3307    msg.smart_combo_routing_params = tag_values_to_map(&order.smart_combo_routing_params);
3308    msg.order_misc_options = tag_values_to_map(&order.order_misc_options);
3309    msg
3310}
3311
3312fn attached_orders_to_proto(order: &Order) -> Option<protobuf::AttachedOrders> {
3313    let attached = protobuf::AttachedOrders {
3314        sl_order_id: valid_i32(order.stop_loss_order_id),
3315        sl_order_type: non_empty(order.stop_loss_order_type.clone()),
3316        pt_order_id: valid_i32(order.profit_taker_order_id),
3317        pt_order_type: non_empty(order.profit_taker_order_type.clone()),
3318    };
3319    (attached.sl_order_id.is_some()
3320        || attached.sl_order_type.is_some()
3321        || attached.pt_order_id.is_some()
3322        || attached.pt_order_type.is_some())
3323    .then_some(attached)
3324}
3325
3326pub(crate) fn validate_order_parameters(
3327    order: &Order,
3328    server_version: i32,
3329) -> Option<OrderFieldValidation> {
3330    if server_version < MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1 {
3331        if order.deactivate {
3332            return Some(OrderFieldValidation {
3333                parameter: "deactivate",
3334                min_version: MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1,
3335            });
3336        }
3337        if order.post_only {
3338            return Some(OrderFieldValidation {
3339                parameter: "postOnly",
3340                min_version: MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1,
3341            });
3342        }
3343        if order.allow_pre_open {
3344            return Some(OrderFieldValidation {
3345                parameter: "allowPreOpen",
3346                min_version: MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1,
3347            });
3348        }
3349        if order.ignore_open_auction {
3350            return Some(OrderFieldValidation {
3351                parameter: "ignoreOpenAuction",
3352                min_version: MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1,
3353            });
3354        }
3355    }
3356
3357    if server_version < MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_2 {
3358        if order.route_marketable_to_bbo != UNSET_INTEGER {
3359            return Some(OrderFieldValidation {
3360                parameter: "routeMarketableToBbo",
3361                min_version: MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_2,
3362            });
3363        }
3364        if order.seek_price_improvement != UNSET_INTEGER {
3365            return Some(OrderFieldValidation {
3366                parameter: "seekPriceImprovement",
3367                min_version: MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_2,
3368            });
3369        }
3370        if order.what_if_type != UNSET_INTEGER {
3371            return Some(OrderFieldValidation {
3372                parameter: "whatIfType",
3373                min_version: MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_2,
3374            });
3375        }
3376    }
3377
3378    if server_version < MIN_SERVER_VER_HEDGE_MAX_SIZE && order.hedge_max_size != UNSET_INTEGER {
3379        return Some(OrderFieldValidation {
3380            parameter: "hedgeMaxSize",
3381            min_version: MIN_SERVER_VER_HEDGE_MAX_SIZE,
3382        });
3383    }
3384
3385    None
3386}
3387
3388pub(crate) fn validate_attached_orders_parameters(
3389    order: &Order,
3390    server_version: i32,
3391) -> Option<OrderFieldValidation> {
3392    if server_version < MIN_SERVER_VER_ATTACHED_ORDERS {
3393        if order.stop_loss_order_id != UNSET_INTEGER {
3394            return Some(OrderFieldValidation {
3395                parameter: "slOrderId",
3396                min_version: MIN_SERVER_VER_ATTACHED_ORDERS,
3397            });
3398        }
3399        if !order.stop_loss_order_type.is_empty() {
3400            return Some(OrderFieldValidation {
3401                parameter: "slOrderType",
3402                min_version: MIN_SERVER_VER_ATTACHED_ORDERS,
3403            });
3404        }
3405        if order.profit_taker_order_id != UNSET_INTEGER {
3406            return Some(OrderFieldValidation {
3407                parameter: "ptOrderId",
3408                min_version: MIN_SERVER_VER_ATTACHED_ORDERS,
3409            });
3410        }
3411        if !order.profit_taker_order_type.is_empty() {
3412            return Some(OrderFieldValidation {
3413                parameter: "ptOrderType",
3414                min_version: MIN_SERVER_VER_ATTACHED_ORDERS,
3415            });
3416        }
3417    }
3418
3419    None
3420}
3421
3422#[cfg(test)]
3423#[allow(clippy::items_after_test_module, clippy::field_reassign_with_default)]
3424mod tests {
3425    use super::*;
3426    use crate::server_versions::{
3427        MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1, MIN_SERVER_VER_ATTACHED_ORDERS,
3428        MIN_SERVER_VER_HEDGE_MAX_SIZE,
3429    };
3430
3431    #[test]
3432    fn validate_order_parameters_rejects_new_fields_on_old_server_versions() {
3433        let mut order = Order::default();
3434        order.deactivate = true;
3435        let validation =
3436            validate_order_parameters(&order, MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1 - 1)
3437                .expect("expected deactivate to be rejected");
3438        assert_eq!(validation.parameter, "deactivate");
3439        assert_eq!(
3440            validation.min_version,
3441            MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1
3442        );
3443
3444        let mut order = Order::default();
3445        order.hedge_max_size = 1;
3446        let validation = validate_order_parameters(&order, MIN_SERVER_VER_HEDGE_MAX_SIZE - 1)
3447            .expect("expected hedgeMaxSize to be rejected");
3448        assert_eq!(validation.parameter, "hedgeMaxSize");
3449        assert_eq!(validation.min_version, MIN_SERVER_VER_HEDGE_MAX_SIZE);
3450    }
3451
3452    #[test]
3453    fn validate_attached_orders_parameters_rejects_low_versions() {
3454        let mut order = Order::default();
3455        order.stop_loss_order_id = 1;
3456        let validation =
3457            validate_attached_orders_parameters(&order, MIN_SERVER_VER_ATTACHED_ORDERS - 1)
3458                .expect("expected attached orders to be rejected");
3459        assert_eq!(validation.parameter, "slOrderId");
3460        assert_eq!(validation.min_version, MIN_SERVER_VER_ATTACHED_ORDERS);
3461    }
3462
3463    #[test]
3464    fn validate_order_parameters_allows_supported_defaults() {
3465        let order = Order::default();
3466        assert!(
3467            validate_order_parameters(&order, MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_1 - 1)
3468                .is_none()
3469        );
3470        assert!(
3471            validate_attached_orders_parameters(&order, MIN_SERVER_VER_ATTACHED_ORDERS - 1)
3472                .is_none()
3473        );
3474    }
3475}
3476
3477fn order_cancel_to_proto(order_cancel: &OrderCancel) -> protobuf::OrderCancel {
3478    protobuf::OrderCancel {
3479        manual_order_cancel_time: non_empty(order_cancel.manual_order_cancel_time.clone()),
3480        ext_operator: non_empty(order_cancel.ext_operator.clone()),
3481        manual_order_indicator: valid_i32(order_cancel.manual_order_indicator),
3482    }
3483}
3484
3485fn execution_filter_to_proto(filter: &ExecutionFilter) -> protobuf::ExecutionFilter {
3486    protobuf::ExecutionFilter {
3487        client_id: valid_i32(filter.client_id),
3488        acct_code: non_empty(filter.acct_code.clone()),
3489        time: non_empty(filter.time.clone()),
3490        symbol: non_empty(filter.symbol.clone()),
3491        sec_type: non_empty(filter.sec_type.clone()),
3492        exchange: non_empty(filter.exchange.clone()),
3493        side: non_empty(filter.side.clone()),
3494        last_n_days: valid_i32(filter.last_n_days),
3495        specific_dates: filter.specific_dates.clone(),
3496    }
3497}
3498
3499fn scanner_subscription_to_proto(
3500    subscription: &ScannerSubscription,
3501) -> protobuf::ScannerSubscription {
3502    protobuf::ScannerSubscription {
3503        number_of_rows: valid_i32(subscription.number_of_rows),
3504        instrument: non_empty(subscription.instrument.clone()),
3505        location_code: non_empty(subscription.location_code.clone()),
3506        scan_code: non_empty(subscription.scan_code.clone()),
3507        above_price: valid_f64(subscription.above_price),
3508        below_price: valid_f64(subscription.below_price),
3509        above_volume: (subscription.above_volume != UNSET_INTEGER)
3510            .then_some(i64::from(subscription.above_volume)),
3511        market_cap_above: valid_f64(subscription.market_cap_above),
3512        market_cap_below: valid_f64(subscription.market_cap_below),
3513        moody_rating_above: non_empty(subscription.moody_rating_above.clone()),
3514        moody_rating_below: non_empty(subscription.moody_rating_below.clone()),
3515        sp_rating_above: non_empty(subscription.sp_rating_above.clone()),
3516        sp_rating_below: non_empty(subscription.sp_rating_below.clone()),
3517        maturity_date_above: non_empty(subscription.maturity_date_above.clone()),
3518        maturity_date_below: non_empty(subscription.maturity_date_below.clone()),
3519        coupon_rate_above: valid_f64(subscription.coupon_rate_above),
3520        coupon_rate_below: valid_f64(subscription.coupon_rate_below),
3521        exclude_convertible: subscription.exclude_convertible.then_some(true),
3522        average_option_volume_above: (subscription.average_option_volume_above != UNSET_INTEGER)
3523            .then_some(i64::from(subscription.average_option_volume_above)),
3524        scanner_setting_pairs: non_empty(subscription.scanner_setting_pairs.clone()),
3525        stock_type_filter: non_empty(subscription.stock_type_filter.clone()),
3526        scanner_subscription_filter_options: Default::default(),
3527        scanner_subscription_options: Default::default(),
3528    }
3529}
3530
3531fn non_empty(value: String) -> Option<String> {
3532    (!value.is_empty()).then_some(value)
3533}
3534
3535fn valid_i32(value: i32) -> Option<i32> {
3536    (value != UNSET_INTEGER).then_some(value)
3537}
3538
3539fn valid_f64(value: f64) -> Option<f64> {
3540    (value != UNSET_DOUBLE && value.is_finite()).then_some(value)
3541}
3542
3543fn ensure_server_version(server_version: i32, min_version: i32) -> TwsApiResult<()> {
3544    if server_version < min_version {
3545        return Err(TwsApiError::UnsupportedServerVersion {
3546            server_version,
3547            min_version,
3548        });
3549    }
3550    Ok(())
3551}