diff --git a/src/client/async.rs b/src/client/async.rs index 77ff380c..486d0764 100644 --- a/src/client/async.rs +++ b/src/client/async.rs @@ -3290,6 +3290,7 @@ mod tests { security_type: "".to_string(), // Empty means all types exchange: "".to_string(), // Empty means all exchanges side: "".to_string(), // Empty means all sides + ..Default::default() }; // Request executions diff --git a/src/client/sync.rs b/src/client/sync.rs index 239940a6..3fabfba3 100644 --- a/src/client/sync.rs +++ b/src/client/sync.rs @@ -3426,6 +3426,7 @@ mod tests { security_type: "".to_string(), // Empty means all types exchange: "".to_string(), // Empty means all exchanges side: "".to_string(), // Empty means all sides + ..Default::default() }; // Request executions diff --git a/src/connection/common.rs b/src/connection/common.rs index 3cc38385..77bc02cc 100644 --- a/src/connection/common.rs +++ b/src/connection/common.rs @@ -65,7 +65,7 @@ impl Default for ConnectionHandler { fn default() -> Self { Self { min_version: 100, - max_version: server_versions::WSH_EVENT_DATA_FILTERS_DATE, + max_version: server_versions::PARAMETRIZED_DAYS_OF_EXECUTIONS, } } } diff --git a/src/orders/common/encoders.rs b/src/orders/common/encoders.rs index 3e712a8d..aec918e5 100644 --- a/src/orders/common/encoders.rs +++ b/src/orders/common/encoders.rs @@ -488,6 +488,14 @@ pub(crate) fn encode_executions(server_version: i32, request_id: i32, filter: &E message.push_field(&filter.exchange); message.push_field(&filter.side); + if server_version >= server_versions::PARAMETRIZED_DAYS_OF_EXECUTIONS { + message.push_field(&filter.last_n_days); + message.push_field(&(filter.specific_dates.len() as i32)); + for date in &filter.specific_dates { + message.push_field(date); + } + } + Ok(message) } @@ -817,4 +825,71 @@ pub(crate) mod tests { assert_eq!(field_vec[4], "0"); // is_more (false) assert_eq!(field_vec[5], "5.5"); // percent } + + #[test] + fn test_encode_executions_without_date_filter() { + let filter = ExecutionFilter { + client_id: Some(1), + account_code: "DU123456".to_string(), + time: "20260101 09:30:00".to_string(), + symbol: "AAPL".to_string(), + security_type: "STK".to_string(), + exchange: "SMART".to_string(), + side: "BUY".to_string(), + ..Default::default() + }; + + // Version below PARAMETRIZED_DAYS_OF_EXECUTIONS should not include date fields + let result = encode_executions(server_versions::WSH_EVENT_DATA_FILTERS_DATE, 9000, &filter).unwrap(); + let fields = result.encode(); + let field_vec: Vec<&str> = fields.split('\0').collect(); + + assert_eq!(field_vec[0], "7"); // RequestExecutions + assert_eq!(field_vec[1], "3"); // VERSION + assert_eq!(field_vec[2], "9000"); // request_id + assert_eq!(field_vec[3], "1"); // client_id + assert_eq!(field_vec[4], "DU123456"); // account_code + assert_eq!(field_vec[5], "20260101 09:30:00"); // time + assert_eq!(field_vec[6], "AAPL"); // symbol + assert_eq!(field_vec[7], "STK"); // security_type + assert_eq!(field_vec[8], "SMART"); // exchange + assert_eq!(field_vec[9], "BUY"); // side + assert_eq!(field_vec.len(), 11); // 10 fields + trailing empty + } + + #[test] + fn test_encode_executions_with_date_filter() { + let filter = ExecutionFilter { + client_id: Some(1), + account_code: "DU123456".to_string(), + time: "".to_string(), + symbol: "".to_string(), + security_type: "".to_string(), + exchange: "".to_string(), + side: "".to_string(), + last_n_days: 7, + specific_dates: vec!["20260125".to_string(), "20260126".to_string()], + }; + + // Version at PARAMETRIZED_DAYS_OF_EXECUTIONS should include date fields + let result = encode_executions(server_versions::PARAMETRIZED_DAYS_OF_EXECUTIONS, 9000, &filter).unwrap(); + let fields = result.encode(); + let field_vec: Vec<&str> = fields.split('\0').collect(); + + assert_eq!(field_vec[0], "7"); // RequestExecutions + assert_eq!(field_vec[1], "3"); // VERSION + assert_eq!(field_vec[2], "9000"); // request_id + assert_eq!(field_vec[3], "1"); // client_id + assert_eq!(field_vec[4], "DU123456"); // account_code + assert_eq!(field_vec[5], ""); // time + assert_eq!(field_vec[6], ""); // symbol + assert_eq!(field_vec[7], ""); // security_type + assert_eq!(field_vec[8], ""); // exchange + assert_eq!(field_vec[9], ""); // side + assert_eq!(field_vec[10], "7"); // last_n_days + assert_eq!(field_vec[11], "2"); // specific_dates count + assert_eq!(field_vec[12], "20260125"); // specific_dates[0] + assert_eq!(field_vec[13], "20260126"); // specific_dates[1] + assert_eq!(field_vec.len(), 15); // 14 fields + trailing empty + } } diff --git a/src/orders/mod.rs b/src/orders/mod.rs index 4234559d..0ffa43cd 100644 --- a/src/orders/mod.rs +++ b/src/orders/mod.rs @@ -1446,6 +1446,10 @@ pub struct ExecutionFilter { pub exchange: String, /// The Contract's side (BUY or SELL) pub side: String, + /// Filter executions from the last N days (0 = no filter). + pub last_n_days: i32, + /// Filter executions for specific dates (format: yyyymmdd, e.g., "20260130"). + pub specific_dates: Vec, } /// Enumerates possible results from querying an [Execution]. diff --git a/src/orders/sync.rs b/src/orders/sync.rs index b42f0aa4..e970de70 100644 --- a/src/orders/sync.rs +++ b/src/orders/sync.rs @@ -954,6 +954,7 @@ mod tests { security_type: "STK".to_owned(), exchange: "ISLAND".to_owned(), side: "BUY".to_owned(), + ..Default::default() }; let results = client.executions(filter); diff --git a/src/transport/sync.rs b/src/transport/sync.rs index d7185fdd..d0f26ec8 100644 --- a/src/transport/sync.rs +++ b/src/transport/sync.rs @@ -822,6 +822,9 @@ mod tests { packet.push_field(&contract.security_id_type); packet.push_field(&contract.security_id); + // Server version 200 includes issuer_id (>= 176) + packet.push_field(&contract.issuer_id); + Ok(packet) } @@ -1047,7 +1050,7 @@ mod tests { let request = encode_place_order(176, 5, contract, &order)?; let events = vec![ - Exchange::simple("v100..173", &["173|20250415 19:38:30 British Summer Time|"]), + Exchange::simple("v100..200", &["200|20250415 19:38:30 British Summer Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|5|"]), Exchange::request(request.clone(), &[ @@ -1080,7 +1083,7 @@ mod tests { #[test] fn test_connection_establish_connection() -> Result<(), Error> { let events = vec![ - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple( "71|2|28||", &[ @@ -1102,7 +1105,7 @@ mod tests { #[test] fn test_reconnect_failed() -> Result<(), Error> { let events = vec![ - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|", "\0"]), // RESTART ]; let socket = MockSocket::new(events, MAX_RECONNECT_ATTEMPTS as usize + 1); @@ -1122,9 +1125,9 @@ mod tests { #[test] fn test_reconnect_success() -> Result<(), Error> { let events = vec![ - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|", "\0"]), // RESTART - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), ]; let socket = MockSocket::new(events, MAX_RECONNECT_ATTEMPTS as usize - 1); @@ -1141,10 +1144,10 @@ mod tests { #[test] fn test_client_reconnect() -> Result<(), Error> { let events = vec![ - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), Exchange::simple("17|1|", &["\0"]), // ManagedAccounts RESTART - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), Exchange::simple("17|1|", &["15|1|DU1234567|"]), // ManagedAccounts ]; @@ -1170,9 +1173,9 @@ mod tests { let expected_response = &format!("10|9000|{AAPL_CONTRACT_RESPONSE}"); let events = vec![ - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|", "\0"]), // RESTART - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), Exchange::request(packet.clone(), &[expected_response, "52|1|9001|"]), ]; @@ -1204,10 +1207,10 @@ mod tests { let packet = encode_request_contract_data(173, 9000, &Contract::stock("AAPL").build())?; let events = vec![ - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), Exchange::request(packet.clone(), &["\0"]), // RESTART - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), ]; @@ -1236,9 +1239,9 @@ mod tests { let packet = encode_request_contract_data(173, 9000, &Contract::stock("AAPL").build())?; let events = vec![ - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|", "\0"]), // RESTART - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::request(packet.clone(), &[]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), ]; @@ -1268,10 +1271,10 @@ mod tests { let packet = encode_request_contract_data(173, 9000, contract)?; let events = vec![ - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), Exchange::request(packet.clone(), &["\0"]), - Exchange::simple("v100..173", &["173|20250323 22:21:01 Greenwich Mean Time|"]), + Exchange::simple("v100..200", &["200|20250323 22:21:01 Greenwich Mean Time|"]), Exchange::simple("71|2|28||", &["15|1|DU1234567|", "9|1|1|"]), ];