Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/client/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/client/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/connection/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
}
Expand Down
75 changes: 75 additions & 0 deletions src/orders/common/encoders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
}
}
4 changes: 4 additions & 0 deletions src/orders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
}

/// Enumerates possible results from querying an [Execution].
Expand Down
1 change: 1 addition & 0 deletions src/orders/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
33 changes: 18 additions & 15 deletions src/transport/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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(),
&[
Expand Down Expand Up @@ -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||",
&[
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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
];
Expand All @@ -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|"]),
];
Expand Down Expand Up @@ -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|"]),
];

Expand Down Expand Up @@ -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|"]),
];
Expand Down Expand Up @@ -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|"]),
];

Expand Down