Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
258 lines
5.6 KiB
Rust
258 lines
5.6 KiB
Rust
//! Order test fixtures
|
|
//!
|
|
//! Provides mock order and trade builders used across 22+ test files.
|
|
|
|
use chrono::{DateTime, Utc};
|
|
use uuid::Uuid;
|
|
|
|
/// Mock order builder for testing
|
|
///
|
|
/// Pattern found in 22+ test files with `create_mock_order()` functions.
|
|
pub struct MockOrderBuilder {
|
|
symbol: String,
|
|
quantity: f64,
|
|
price: Option<f64>,
|
|
side: Side,
|
|
order_type: OrderType,
|
|
status: OrderStatus,
|
|
timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum Side {
|
|
Buy,
|
|
Sell,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum OrderType {
|
|
Market,
|
|
Limit,
|
|
Stop,
|
|
StopLimit,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum OrderStatus {
|
|
Pending,
|
|
PartiallyFilled,
|
|
Filled,
|
|
Cancelled,
|
|
Rejected,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct MockOrder {
|
|
pub id: String,
|
|
pub symbol: String,
|
|
pub quantity: f64,
|
|
pub price: Option<f64>,
|
|
pub side: Side,
|
|
pub order_type: OrderType,
|
|
pub status: OrderStatus,
|
|
pub timestamp: DateTime<Utc>,
|
|
pub filled_quantity: f64,
|
|
pub average_fill_price: Option<f64>,
|
|
}
|
|
|
|
impl MockOrderBuilder {
|
|
/// Create a new order builder with defaults
|
|
pub fn new() -> Self {
|
|
Self {
|
|
symbol: "AAPL".to_string(),
|
|
quantity: 100.0,
|
|
price: None,
|
|
side: Side::Buy,
|
|
order_type: OrderType::Market,
|
|
status: OrderStatus::Pending,
|
|
timestamp: Utc::now(),
|
|
}
|
|
}
|
|
|
|
/// Set the symbol
|
|
pub fn symbol(mut self, symbol: &str) -> Self {
|
|
self.symbol = symbol.to_string();
|
|
self
|
|
}
|
|
|
|
/// Set buy side
|
|
pub fn buy(mut self) -> Self {
|
|
self.side = Side::Buy;
|
|
self
|
|
}
|
|
|
|
/// Set sell side
|
|
pub fn sell(mut self) -> Self {
|
|
self.side = Side::Sell;
|
|
self
|
|
}
|
|
|
|
/// Set quantity
|
|
pub fn quantity(mut self, quantity: f64) -> Self {
|
|
self.quantity = quantity;
|
|
self
|
|
}
|
|
|
|
/// Set limit price
|
|
pub fn limit_price(mut self, price: f64) -> Self {
|
|
self.price = Some(price);
|
|
self.order_type = OrderType::Limit;
|
|
self
|
|
}
|
|
|
|
/// Set as market order
|
|
pub fn market(mut self) -> Self {
|
|
self.order_type = OrderType::Market;
|
|
self.price = None;
|
|
self
|
|
}
|
|
|
|
/// Set as filled
|
|
pub fn filled(mut self) -> Self {
|
|
self.status = OrderStatus::Filled;
|
|
self
|
|
}
|
|
|
|
/// Build the mock order
|
|
pub fn build(self) -> MockOrder {
|
|
let is_filled = matches!(self.status, OrderStatus::Filled);
|
|
MockOrder {
|
|
id: Uuid::new_v4().to_string(),
|
|
symbol: self.symbol,
|
|
quantity: self.quantity,
|
|
price: self.price,
|
|
side: self.side,
|
|
order_type: self.order_type,
|
|
status: self.status,
|
|
timestamp: self.timestamp,
|
|
filled_quantity: if is_filled { self.quantity } else { 0.0 },
|
|
average_fill_price: self.price,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for MockOrderBuilder {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// Mock trade builder
|
|
pub struct MockTradeBuilder {
|
|
symbol: String,
|
|
price: f64,
|
|
quantity: f64,
|
|
side: Side,
|
|
timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct MockTrade {
|
|
pub id: String,
|
|
pub symbol: String,
|
|
pub price: f64,
|
|
pub quantity: f64,
|
|
pub side: Side,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
impl MockTradeBuilder {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
symbol: "AAPL".to_string(),
|
|
price: 150.0,
|
|
quantity: 100.0,
|
|
side: Side::Buy,
|
|
timestamp: Utc::now(),
|
|
}
|
|
}
|
|
|
|
pub fn symbol(mut self, symbol: &str) -> Self {
|
|
self.symbol = symbol.to_string();
|
|
self
|
|
}
|
|
|
|
pub fn price(mut self, price: f64) -> Self {
|
|
self.price = price;
|
|
self
|
|
}
|
|
|
|
pub fn quantity(mut self, quantity: f64) -> Self {
|
|
self.quantity = quantity;
|
|
self
|
|
}
|
|
|
|
pub fn buy(mut self) -> Self {
|
|
self.side = Side::Buy;
|
|
self
|
|
}
|
|
|
|
pub fn sell(mut self) -> Self {
|
|
self.side = Side::Sell;
|
|
self
|
|
}
|
|
|
|
pub fn build(self) -> MockTrade {
|
|
MockTrade {
|
|
id: Uuid::new_v4().to_string(),
|
|
symbol: self.symbol,
|
|
price: self.price,
|
|
quantity: self.quantity,
|
|
side: self.side,
|
|
timestamp: self.timestamp,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for MockTradeBuilder {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_order_builder_defaults() {
|
|
let order = MockOrderBuilder::new().build();
|
|
assert_eq!(order.symbol, "AAPL");
|
|
assert_eq!(order.quantity, 100.0);
|
|
assert!(matches!(order.side, Side::Buy));
|
|
}
|
|
|
|
#[test]
|
|
fn test_order_builder_customization() {
|
|
let order = MockOrderBuilder::new()
|
|
.symbol("TSLA")
|
|
.sell()
|
|
.quantity(50.0)
|
|
.limit_price(200.0)
|
|
.filled()
|
|
.build();
|
|
|
|
assert_eq!(order.symbol, "TSLA");
|
|
assert_eq!(order.quantity, 50.0);
|
|
assert!(matches!(order.side, Side::Sell));
|
|
assert_eq!(order.price, Some(200.0));
|
|
assert!(matches!(order.status, OrderStatus::Filled));
|
|
assert_eq!(order.filled_quantity, 50.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_trade_builder() {
|
|
let trade = MockTradeBuilder::new()
|
|
.symbol("NVDA")
|
|
.price(450.0)
|
|
.quantity(25.0)
|
|
.sell()
|
|
.build();
|
|
|
|
assert_eq!(trade.symbol, "NVDA");
|
|
assert_eq!(trade.price, 450.0);
|
|
assert_eq!(trade.quantity, 25.0);
|
|
assert!(matches!(trade.side, Side::Sell));
|
|
}
|
|
}
|