rust_order_book/report.rs
1//! Types representing the result of order processing.
2//!
3//! This module defines the output types returned by the order book after
4//! processing orders, such as [`ExecutionReport`] and [`FillReport`].
5//!
6//! These types are used to track the outcome of submitted market or limit orders,
7//! including how much was executed, any remaining quantity, and the resulting trades.
8use crate::{
9    journal::JournalLog,
10    order::{get_order_time_in_force, OrderId, Price, Quantity},
11    OrderStatus, OrderType, Side, TimeInForce,
12};
13
14/// A report for an individual fill that occurred during order execution.
15///
16/// A single order may generate multiple fills if matched across
17/// multiple price levels or counter-orders.
18///
19/// # Fields
20/// - `order_id`: The ID of the counterparty order involved in the fill
21/// - `price`: The execution price
22/// - `quantity`: The quantity filled
23/// - `status`: The status of the order after the fill
24#[derive(Debug)]
25pub struct FillReport {
26    pub order_id: OrderId,
27    pub price: u64,
28    pub quantity: u64,
29    pub status: OrderStatus,
30}
31
32#[derive(Debug)]
33pub struct ExecutionReportParams {
34    pub id: OrderId,
35    pub order_type: OrderType,
36    pub side: Side,
37    pub quantity: Quantity,
38    pub status: OrderStatus,
39    pub time_in_force: Option<TimeInForce>,
40    pub price: Option<Price>,
41    pub post_only: bool,
42}
43
44/// A comprehensive report describing the result of a submitted order.
45///
46/// The report includes the amount filled, remaining quantity, order status,
47/// any matched trades (`fills`), and optional journaling info.
48///
49/// # Fields
50/// - `order_id`: ID assigned to the order
51/// - `orig_qty`: Quantity originally requested
52/// - `executed_qty`: Total quantity filled
53/// - `remaining_qty`: Quantity still unfilled
54/// - `taker_qty`: Quantity matched as taker (aggressive side)
55/// - `maker_qty`: Quantity resting as maker (passive side)
56/// - `order_type`: Market or Limit
57/// - `side`: Buy or Sell
58/// - `price`: For limit orders, this is the limit price; for market is 0
59/// - `status`: Final status of the order
60/// - `time_in_force`: Time-in-force policy applied
61/// - `post_only`: Whether the order was post-only
62/// - `fills`: Vector of individual fills
63/// - `log`: Optional journal log (if journaling is enabled)
64#[derive(Debug)]
65pub struct ExecutionReport {
66    pub order_id: OrderId,
67    pub orig_qty: u64,
68    pub executed_qty: u64,
69    pub remaining_qty: u64,
70    pub taker_qty: u64,
71    pub maker_qty: u64,
72    pub order_type: OrderType,
73    pub side: Side,
74    pub price: u64,
75    pub status: OrderStatus,
76    pub time_in_force: TimeInForce,
77    pub post_only: bool,
78    pub fills: Vec<FillReport>,
79    pub log: Option<JournalLog>,
80}
81
82impl ExecutionReport {
83    /// Creates a new execution report for a submitted order.
84    ///
85    /// Usually called internally by the order book engine.
86    ///
87    /// # Parameters
88    /// - `id`: The order ID
89    /// - `order_type`: Market or Limit
90    /// - `side`: Buy or Sell
91    /// - `quantity`: Requested quantity
92    /// - `status`: Initial order status (usually `New`)
93    /// - `time_in_force`: Optional TIF value (e.g., GTC, IOC)
94    /// - `price`: Optional limit price (or placeholder for market orders)
95    /// - `post_only`: Whether the order was post-only
96    pub fn new(params: ExecutionReportParams) -> Self {
97        Self {
98            order_id: params.id,
99            orig_qty: params.quantity,
100            executed_qty: 0,
101            remaining_qty: params.quantity,
102            status: params.status,
103            taker_qty: 0,
104            maker_qty: 0,
105            order_type: params.order_type,
106            side: params.side,
107            price: params.price.unwrap_or(0),
108            // market order are always IOC
109            time_in_force: if params.order_type == OrderType::Market {
110                TimeInForce::IOC
111            } else {
112                get_order_time_in_force(params.time_in_force)
113            },
114            post_only: params.post_only,
115            fills: Vec::new(),
116            log: None,
117        }
118    }
119}