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