Skip to content

Commit

Permalink
Added justfile (#202)
Browse files Browse the repository at this point in the history
  • Loading branch information
wboayue authored Dec 25, 2024
1 parent 6a048d7 commit 137878a
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 29 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ibapi"
version = "1.0.7"
version = "1.0.8"
edition = "2021"
authors = ["Wil Boayue <[email protected]>"]
description = "A Rust implementation of the Interactive Brokers TWS API, providing a reliable and user friendly interface for TWS and IB Gateway. Designed with a focus on simplicity and performance."
Expand All @@ -12,7 +12,7 @@ license = "MIT"
keywords = ["algo-trading", "interactive-brokers", "tws"]
categories = ["finance", "api-bindings"]
exclude = [
"yo",
"justfile",
]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
15 changes: 15 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

# Generate and save coverage report using tarpaulin
cover:
cargo tarpaulin -o html
echo "coverage report saved to tarpaulin-report.html"

# Tags repo with specified version
tag VERSION:
echo "Tagging repo with version {{VERSION}}"
git tag {{VERSION}} -m "Version {{VERSION}}"
git push origin {{VERSION}}

# Lists all available versions
versions:
@git tag
3 changes: 3 additions & 0 deletions src/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use crate::{

mod decoders;

#[cfg(test)]
mod tests;

// Requests an XML list of scanner parameters valid in TWS.
pub(super) fn scanner_parameters(client: &Client) -> Result<String, Error> {
let request = encoders::encode_scanner_parameters()?;
Expand Down
121 changes: 121 additions & 0 deletions src/scanner/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::sync::{Arc, RwLock};

use crate::contracts::SecurityType;
use crate::orders::TagValue;
use crate::server_versions;
use crate::stubs::MessageBusStub;

use super::*;

#[test]
fn test_scanner_parameters() {
let message_bus = Arc::new(MessageBusStub {
request_messages: RwLock::new(vec![]),
response_messages: vec![
"19|2|<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ScanParameterResponse>\n<InstrumentList>...</InstrumentList>\n</ScanParameterResponse>".to_owned(),
],
});

let client = Client::stubbed(message_bus, server_versions::SCANNER_GENERIC_OPTS);

let result = client.scanner_parameters();
assert!(result.is_ok(), "failed to request scanner parameters: {}", result.err().unwrap());

let request_messages = client.message_bus.request_messages();
assert_eq!(request_messages[0].encode_simple(), "24|1|");

let scanner_params = result.unwrap();
assert!(scanner_params.contains("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
assert!(scanner_params.contains("<ScanParameterResponse>"));
assert!(scanner_params.contains("<InstrumentList>"));
}

#[test]
fn test_scanner_subscription() {
let message_bus = Arc::new(MessageBusStub {
request_messages: RwLock::new(vec![]),
response_messages: vec![
"20\03\09000\010\00\0670777621\0SVMH\0STK\0\00\0\0SMART\0USD\0SVMH\0NMS\0NMS\0\0\0\0\01\0536918651\0GTI\0STK\0\00\0\0SMART\0USD\0GTI\0NMS\0NMS\0\0\0\0\02\0526726639\0LITM\0STK\0\00\0\0SMART\0USD\0LITM\0SCM\0SCM\0\0\0\0\03\0504716446\0LCID\0STK\0\00\0\0SMART\0USD\0LCID\0NMS\0NMS\0\0\0\0\04\0547605251\0RGTI\0STK\0\00\0\0SMART\0USD\0RGTI\0SCM\0SCM\0\0\0\0\05\0653568762\0AVGR\0STK\0\00\0\0SMART\0USD\0AVGR\0SCM\0SCM\0\0\0\0\06\04815747\0NVDA\0STK\0\00\0\0SMART\0USD\0NVDA\0NMS\0NMS\0\0\0\0\07\0534453483\0HOUR\0STK\0\00\0\0SMART\0USD\0HOUR\0SCM\0SCM\0\0\0\0\08\0631370187\0LAES\0STK\0\00\0\0SMART\0USD\0LAES\0SCM\0SCM\0\0\0\0\09\0689954925\0XTIA\0STK\0\00\0\0SMART\0USD\0XTIA\0SCM\0SCM\0\0\0\0\0".to_owned(),
],
});

let client = Client::stubbed(message_bus, server_versions::SCANNER_GENERIC_OPTS);

let subscription = ScannerSubscription {
number_of_rows: 10,
instrument: Some("FUT".to_string()),
location_code: Some("FUT.US".to_string()),
scan_code: Some("TOP_PERC_GAIN".to_string()),
above_price: Some(50.0),
below_price: Some(100.0),
above_volume: Some(1000),
average_option_volume_above: Some(100),
market_cap_above: Some(1000000.0),
market_cap_below: Some(10000000.0),
moody_rating_above: Some("A".to_string()),
moody_rating_below: Some("AAA".to_string()),
sp_rating_above: Some("A".to_string()),
sp_rating_below: Some("AAA".to_string()),
maturity_date_above: Some("20230101".to_string()),
maturity_date_below: Some("20231231".to_string()),
coupon_rate_above: Some(2.0),
coupon_rate_below: Some(5.0),
exclude_convertible: true,
scanner_setting_pairs: Some("Annual,true".to_string()),
stock_type_filter: Some("CORP".to_string()),
};

let filter = vec![
TagValue {
tag: "scannerType".to_string(),
value: "TOP_PERC_GAIN".to_string(),
},
TagValue {
tag: "numberOfRows".to_string(),
value: "10".to_string(),
},
];

let result = client.scanner_subscription(&subscription, &filter);
assert!(result.is_ok(), "failed to request scanner subscription: {}", result.err().unwrap());

let request_messages = client.message_bus.request_messages();

// Verify request parameters were encoded correctly
let expected_request = format!(
"22|9000|10|FUT|FUT.US|TOP_PERC_GAIN|50|100|1000|1000000|10000000|A|AAA|A|AAA|20230101|20231231|2|5|1|100|Annual,true|CORP|scannerType=TOP_PERC_GAIN;numberOfRows=10;||",
);
assert_eq!(request_messages[0].encode_simple(), expected_request);

// Now verify we can parse the scanner data responses
let subscription = result.unwrap();
let scanner_data: Vec<Vec<ScannerData>> = subscription.iter().collect();

assert!(
subscription.error().is_none(),
"error getting scanner results: {}",
subscription.error().unwrap()
);
assert_eq!(scanner_data.len(), 1);

// Verify first scanner data entry
let first = &scanner_data[0][0];
assert_eq!(first.rank, 0);
assert_eq!(first.contract_details.contract.symbol, "SVMH");
assert_eq!(first.contract_details.contract.security_type, SecurityType::Stock);
assert_eq!(first.contract_details.contract.exchange, "SMART");

// Verify second scanner data entry
let second = &scanner_data[0][1];
assert_eq!(second.rank, 1);
assert_eq!(second.contract_details.contract.symbol, "GTI");
assert_eq!(second.contract_details.contract.security_type, SecurityType::Stock);
assert_eq!(second.contract_details.contract.exchange, "SMART");

// Verify third scanner data entry
let third = &scanner_data[0][2];
assert_eq!(third.rank, 2);
assert_eq!(third.contract_details.contract.symbol, "LITM");
assert_eq!(third.contract_details.contract.security_type, SecurityType::Stock);
assert_eq!(third.contract_details.contract.exchange, "SMART");
}
27 changes: 0 additions & 27 deletions yo

This file was deleted.

0 comments on commit 137878a

Please sign in to comment.