use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use super::{OrderState, OrderType, Side, TimeInForce};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SendOrderRequest {
pub instrument_id: u64,
#[serde(rename = "OMSId")]
pub oms_id: u64,
pub account_id: u64,
pub time_in_force: TimeInForce,
pub client_order_id: u64,
#[serde(rename = "OrderIdOCO")]
pub order_id_oco: u64,
pub use_display_quantity: bool,
pub side: Side,
pub quantity: Decimal,
pub order_type: OrderType,
pub peg_price_type: u8,
pub limit_price: Decimal,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_price: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_quantity: Option<Decimal>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendOrderResponse {
pub status: String,
pub errormsg: Option<String>,
#[serde(rename = "OrderId")]
pub order_id: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CancelOrderRequest {
#[serde(rename = "OMSId")]
pub oms_id: u64,
pub account_id: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_order_id: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CancelAllOrdersRequest {
#[serde(rename = "OMSId")]
pub oms_id: u64,
pub account_id: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CancelReplaceOrderRequest {
#[serde(rename = "OMSId")]
pub oms_id: u64,
pub order_id: u64,
pub instrument_id: u64,
pub previous_order_revision: u64,
pub quantity: Decimal,
pub limit_price: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct GetOpenOrdersRequest {
#[serde(rename = "OMSId")]
pub oms_id: u64,
pub account_id: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct GetOrderStatusRequest {
#[serde(rename = "OMSId")]
pub oms_id: u64,
pub account_id: u64,
pub order_id: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct OrderInfo {
pub side: Side,
pub order_id: u64,
pub price: Decimal,
pub quantity: Decimal,
pub display_quantity: Decimal,
pub instrument: u64,
pub account: u64,
pub order_type: OrderType,
pub client_order_id: u64,
pub order_state: OrderState,
pub receive_time: Option<String>,
#[serde(default)]
pub receive_time_ticks: Option<u64>,
pub orig_quantity: Decimal,
pub quantity_executed: Decimal,
pub avg_price: Decimal,
#[serde(default)]
pub counter_party_id: Option<u64>,
#[serde(default)]
pub change_reason: Option<String>,
#[serde(default)]
pub orig_order_id: Option<u64>,
#[serde(default)]
pub orig_cl_ord_id: Option<u64>,
#[serde(default)]
pub entered_by: Option<u64>,
#[serde(default)]
pub is_quote: Option<bool>,
#[serde(default)]
pub inside_ask: Option<Decimal>,
#[serde(default)]
pub inside_bid: Option<Decimal>,
#[serde(default)]
pub last_trade_price: Option<Decimal>,
#[serde(default)]
pub reject_reason: Option<String>,
#[serde(default)]
pub is_locked_in: Option<bool>,
#[serde(default)]
pub cancel_reason: Option<String>,
#[serde(rename = "OMSId")]
pub oms_id: u64,
}
pub fn create_limit_order(
instrument_id: u64,
account_id: u64,
side: Side,
quantity: Decimal,
price: Decimal,
) -> SendOrderRequest {
SendOrderRequest {
instrument_id,
oms_id: 1,
account_id,
time_in_force: TimeInForce::GTC,
client_order_id: 0,
order_id_oco: 0,
use_display_quantity: false,
side,
quantity,
order_type: OrderType::Limit,
peg_price_type: 1,
limit_price: price,
stop_price: None,
display_quantity: None,
}
}
pub fn create_market_order(
instrument_id: u64,
account_id: u64,
side: Side,
quantity: Decimal,
) -> SendOrderRequest {
SendOrderRequest {
instrument_id,
oms_id: 1,
account_id,
time_in_force: TimeInForce::IOC,
client_order_id: 0,
order_id_oco: 0,
use_display_quantity: false,
side,
quantity,
order_type: OrderType::Market,
peg_price_type: 1,
limit_price: Decimal::ZERO,
stop_price: None,
display_quantity: None,
}
}
#[cfg(test)]
mod tests {
use rust_decimal_macros::dec;
use super::*;
#[test]
fn test_send_order_request_serialize() {
let order = SendOrderRequest {
instrument_id: 1,
oms_id: 1,
account_id: 12345,
time_in_force: TimeInForce::GTC,
client_order_id: 0,
order_id_oco: 0,
use_display_quantity: false,
side: Side::Buy,
quantity: dec!(0.001),
order_type: OrderType::Limit,
peg_price_type: 1,
limit_price: dec!(50000.00),
stop_price: None,
display_quantity: None,
};
let json = serde_json::to_string(&order).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["InstrumentId"], 1);
assert_eq!(parsed["OMSId"], 1);
assert_eq!(parsed["AccountId"], 12345);
assert_eq!(parsed["TimeInForce"], 1); assert_eq!(parsed["Side"], 0); assert_eq!(parsed["OrderType"], 2); assert_eq!(parsed["UseDisplayQuantity"], false);
}
#[test]
fn test_order_info_deserialize() {
let json = r#"{
"Side": 0,
"OrderId": 98765,
"Price": "50000.00",
"Quantity": "0.001",
"DisplayQuantity": "0",
"Instrument": 1,
"Account": 12345,
"OrderType": 2,
"ClientOrderId": 0,
"OrderState": 0,
"ReceiveTime": "2024-01-15T10:30:00Z",
"OrigQuantity": "0.001",
"QuantityExecuted": "0",
"AvgPrice": "0",
"OMSId": 1
}"#;
let info: OrderInfo = serde_json::from_str(json).unwrap();
assert_eq!(info.side, Side::Buy);
assert_eq!(info.order_id, 98765);
assert_eq!(info.price, dec!(50000.00));
assert_eq!(info.quantity, dec!(0.001));
assert_eq!(info.instrument, 1);
assert_eq!(info.account, 12345);
assert_eq!(info.order_type, OrderType::Limit);
assert_eq!(info.order_state, OrderState::Working);
assert_eq!(info.oms_id, 1);
}
#[test]
fn test_create_limit_order() {
let order =
create_limit_order(1, 12345, Side::Buy, dec!(0.001), dec!(50000.00));
assert_eq!(order.instrument_id, 1);
assert_eq!(order.account_id, 12345);
assert_eq!(order.side, Side::Buy);
assert_eq!(order.quantity, dec!(0.001));
assert_eq!(order.limit_price, dec!(50000.00));
assert_eq!(order.order_type, OrderType::Limit);
assert_eq!(order.time_in_force, TimeInForce::GTC);
assert_eq!(order.oms_id, 1);
assert!(!order.use_display_quantity);
}
#[test]
fn test_cancel_replace_request_serialize() {
let request = CancelReplaceOrderRequest {
oms_id: 1,
order_id: 98765,
instrument_id: 1,
previous_order_revision: 0,
quantity: dec!(0.002),
limit_price: dec!(51000.00),
};
let json = serde_json::to_string(&request).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["OMSId"], 1);
assert_eq!(parsed["OrderId"], 98765);
assert_eq!(parsed["InstrumentId"], 1);
assert_eq!(parsed["PreviousOrderRevision"], 0);
}
#[test]
fn test_create_market_order() {
let order = create_market_order(1, 12345, Side::Sell, dec!(0.5));
assert_eq!(order.instrument_id, 1);
assert_eq!(order.account_id, 12345);
assert_eq!(order.side, Side::Sell);
assert_eq!(order.quantity, dec!(0.5));
assert_eq!(order.order_type, OrderType::Market);
assert_eq!(order.time_in_force, TimeInForce::IOC);
assert_eq!(order.limit_price, Decimal::ZERO);
}
#[test]
fn test_send_order_response_accepted() {
let json = r#"{"status":"Accepted","errormsg":"","OrderId":98765}"#;
let response: SendOrderResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.status, "Accepted");
assert_eq!(response.order_id, Some(98765));
}
#[test]
fn test_send_order_response_rejected() {
let json =
r#"{"status":"Rejected","errormsg":"Insufficient funds","OrderId":null}"#;
let response: SendOrderResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.status, "Rejected");
assert_eq!(response.errormsg, Some("Insufficient funds".to_string()));
assert!(response.order_id.is_none());
}
#[test]
fn test_cancel_order_request_by_order_id() {
let request = CancelOrderRequest {
oms_id: 1,
account_id: 12345,
order_id: Some(98765),
client_order_id: None,
};
let json = serde_json::to_string(&request).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["OMSId"], 1);
assert_eq!(parsed["AccountId"], 12345);
assert_eq!(parsed["OrderId"], 98765);
assert!(parsed.get("ClientOrderId").is_none());
}
#[test]
fn test_cancel_all_orders_request() {
let request = CancelAllOrdersRequest {
oms_id: 1,
account_id: 12345,
};
let json = serde_json::to_string(&request).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["OMSId"], 1);
assert_eq!(parsed["AccountId"], 12345);
}
#[test]
fn test_get_open_orders_request() {
let request = GetOpenOrdersRequest {
oms_id: 1,
account_id: 12345,
};
let json = serde_json::to_string(&request).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["OMSId"], 1);
assert_eq!(parsed["AccountId"], 12345);
}
#[test]
fn test_get_order_status_request() {
let request = GetOrderStatusRequest {
oms_id: 1,
account_id: 12345,
order_id: 98765,
};
let json = serde_json::to_string(&request).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["OMSId"], 1);
assert_eq!(parsed["AccountId"], 12345);
assert_eq!(parsed["OrderId"], 98765);
}
}