mod helpers;
use tradestation_api::{OrderGroupRequest, OrderRequest, TimeInForce};
use wiremock::matchers::{method, path};
use wiremock::{Mock, ResponseTemplate};
fn sample_order() -> OrderRequest {
OrderRequest {
account_id: "123456789".to_string(),
symbol: "AAPL".to_string(),
quantity: "100".to_string(),
order_type: "Market".to_string(),
trade_action: "Buy".to_string(),
time_in_force: TimeInForce::day(),
limit_price: None,
stop_price: None,
}
}
fn sample_limit_order(price: &str, action: &str) -> OrderRequest {
OrderRequest {
account_id: "123456789".to_string(),
symbol: "AAPL".to_string(),
quantity: "100".to_string(),
order_type: "Limit".to_string(),
trade_action: action.to_string(),
time_in_force: TimeInForce::gtc(),
limit_price: Some(price.to_string()),
stop_price: None,
}
}
fn order_success_response() -> serde_json::Value {
serde_json::json!({
"Orders": [
{
"OrderId": "ORD12345",
"Message": "Order has been accepted"
}
]
})
}
#[tokio::test]
async fn test_place_order() {
let server = helpers::setup_mock_server().await;
let mut client = helpers::mock_client(&server);
Mock::given(method("POST"))
.and(path("/v3/orderexecution/orders"))
.respond_with(ResponseTemplate::new(200).set_body_json(order_success_response()))
.mount(&server)
.await;
let order = sample_order();
let result = client.place_order(&order).await.unwrap();
let orders = result.orders.unwrap();
assert_eq!(orders.len(), 1);
assert_eq!(orders[0].order_id.as_deref(), Some("ORD12345"));
assert_eq!(
orders[0].message.as_deref(),
Some("Order has been accepted")
);
}
#[tokio::test]
async fn test_replace_order() {
let server = helpers::setup_mock_server().await;
let mut client = helpers::mock_client(&server);
Mock::given(method("PUT"))
.and(path("/v3/orderexecution/orders/ORD12345"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"Orders": [
{
"OrderId": "ORD12346",
"Message": "Order has been replaced"
}
]
})))
.mount(&server)
.await;
let mut order = sample_limit_order("190.00", "Buy");
order.limit_price = Some("192.00".to_string());
let result = client.replace_order("ORD12345", &order).await.unwrap();
let orders = result.orders.unwrap();
assert_eq!(orders[0].order_id.as_deref(), Some("ORD12346"));
assert_eq!(
orders[0].message.as_deref(),
Some("Order has been replaced")
);
}
#[tokio::test]
async fn test_cancel_order() {
let server = helpers::setup_mock_server().await;
let mut client = helpers::mock_client(&server);
Mock::given(method("DELETE"))
.and(path("/v3/orderexecution/orders/ORD12345"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"Orders": [
{
"OrderId": "ORD12345",
"Message": "Order has been cancelled"
}
]
})))
.mount(&server)
.await;
let result = client.cancel_order("ORD12345").await.unwrap();
let orders = result.orders.unwrap();
assert_eq!(orders[0].order_id.as_deref(), Some("ORD12345"));
assert_eq!(
orders[0].message.as_deref(),
Some("Order has been cancelled")
);
}
#[tokio::test]
async fn test_confirm_order() {
let server = helpers::setup_mock_server().await;
let mut client = helpers::mock_client(&server);
Mock::given(method("POST"))
.and(path("/v3/orderexecution/orderconfirm"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"Orders": [
{
"OrderId": null,
"Message": "Estimated cost: $18,530.00"
}
]
})))
.mount(&server)
.await;
let order = sample_order();
let result = client.confirm_order(&order).await.unwrap();
let orders = result.orders.unwrap();
assert_eq!(orders.len(), 1);
assert!(
orders[0]
.message
.as_deref()
.unwrap()
.contains("Estimated cost")
);
}
#[tokio::test]
async fn test_confirm_order_group() {
let server = helpers::setup_mock_server().await;
let mut client = helpers::mock_client(&server);
Mock::given(method("POST"))
.and(path("/v3/orderexecution/ordergroupconfirm"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"Orders": [
{
"OrderId": null,
"Message": "Estimated cost for OCO group"
}
]
})))
.mount(&server)
.await;
let group = OrderGroupRequest {
group_type: "OCO".to_string(),
orders: vec![
sample_limit_order("190.00", "Sell"),
sample_limit_order("175.00", "Sell"),
],
};
let result = client.confirm_order_group(&group).await.unwrap();
let orders = result.orders.unwrap();
assert_eq!(orders.len(), 1);
assert!(
orders[0]
.message
.as_deref()
.unwrap()
.contains("Estimated cost")
);
}
#[tokio::test]
async fn test_place_order_group() {
let server = helpers::setup_mock_server().await;
let mut client = helpers::mock_client(&server);
Mock::given(method("POST"))
.and(path("/v3/orderexecution/ordergroups"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"Orders": [
{
"OrderId": "ORD10001",
"Message": "OCO order accepted"
},
{
"OrderId": "ORD10002",
"Message": "OCO order accepted"
}
]
})))
.mount(&server)
.await;
let group = OrderGroupRequest {
group_type: "OCO".to_string(),
orders: vec![
sample_limit_order("190.00", "Sell"),
OrderRequest {
account_id: "123456789".to_string(),
symbol: "AAPL".to_string(),
quantity: "100".to_string(),
order_type: "StopMarket".to_string(),
trade_action: "Sell".to_string(),
time_in_force: TimeInForce::gtc(),
limit_price: None,
stop_price: Some("175.00".to_string()),
},
],
};
let result = client.place_order_group(&group).await.unwrap();
let orders = result.orders.unwrap();
assert_eq!(orders.len(), 2);
assert_eq!(orders[0].order_id.as_deref(), Some("ORD10001"));
assert_eq!(orders[1].order_id.as_deref(), Some("ORD10002"));
}
#[tokio::test]
async fn test_get_activation_triggers() {
let server = helpers::setup_mock_server().await;
let mut client = helpers::mock_client(&server);
Mock::given(method("GET"))
.and(path("/v3/orderexecution/activationtriggers"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"ActivationTriggers": [
{
"Key": "SGL",
"Name": "Single",
"Description": "Single trade trigger"
},
{
"Key": "DBL",
"Name": "Double",
"Description": "Double trade trigger"
}
]
})))
.mount(&server)
.await;
let triggers = client.get_activation_triggers().await.unwrap();
assert_eq!(triggers.len(), 2);
assert_eq!(triggers[0].key, "SGL");
assert_eq!(triggers[0].name, "Single");
assert_eq!(
triggers[0].description.as_deref(),
Some("Single trade trigger")
);
assert_eq!(triggers[1].key, "DBL");
}
#[tokio::test]
async fn test_get_routes() {
let server = helpers::setup_mock_server().await;
let mut client = helpers::mock_client(&server);
Mock::given(method("GET"))
.and(path("/v3/orderexecution/routes"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"Routes": [
{
"Id": "Intelligent",
"Name": "Intelligent",
"AssetTypes": ["EQ", "OP"]
},
{
"Id": "AMEX",
"Name": "AMEX",
"AssetTypes": ["EQ"]
}
]
})))
.mount(&server)
.await;
let routes = client.get_routes().await.unwrap();
assert_eq!(routes.len(), 2);
assert_eq!(routes[0].id, "Intelligent");
assert_eq!(routes[0].name, "Intelligent");
assert_eq!(routes[0].asset_types, vec!["EQ", "OP"]);
assert_eq!(routes[1].id, "AMEX");
assert_eq!(routes[1].asset_types, vec!["EQ"]);
}