nautilus_execution/reconciliation/types.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Shared reconciliation value types.
17
18use indexmap::IndexMap;
19use nautilus_model::{
20 enums::OrderSide,
21 identifiers::VenueOrderId,
22 reports::{FillReport, OrderStatusReport},
23};
24use rust_decimal::Decimal;
25
26/// Immutable snapshot of fill data for position simulation.
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct FillSnapshot {
29 /// The event timestamp (nanoseconds).
30 pub ts_event: u64,
31 /// The order side (BUY or SELL).
32 pub side: OrderSide,
33 /// The fill quantity.
34 pub qty: Decimal,
35 /// The fill price.
36 pub px: Decimal,
37 /// The venue order ID.
38 pub venue_order_id: VenueOrderId,
39}
40
41/// Represents a position snapshot from the venue.
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct VenuePositionSnapshot {
44 /// The position side (LONG, SHORT, or FLAT).
45 pub side: OrderSide, // Using OrderSide to represent position side for simplicity
46 /// The position quantity (always positive, even for SHORT).
47 pub qty: Decimal,
48 /// The average entry price (can be zero for FLAT positions).
49 pub avg_px: Decimal,
50}
51
52/// Result of the fill adjustment process.
53#[derive(Debug, Clone, PartialEq)]
54pub enum FillAdjustmentResult {
55 /// No adjustment needed - return fills unchanged.
56 NoAdjustment,
57 /// Add synthetic opening fill to oldest lifecycle.
58 AddSyntheticOpening {
59 /// The synthetic fill to add at the beginning.
60 synthetic_fill: FillSnapshot,
61 /// All existing fills to keep.
62 existing_fills: Vec<FillSnapshot>,
63 },
64 /// Replace entire current lifecycle with single synthetic fill.
65 ReplaceCurrentLifecycle {
66 /// The single synthetic fill representing the entire position.
67 synthetic_fill: FillSnapshot,
68 /// The first venue order ID to use.
69 first_venue_order_id: VenueOrderId,
70 },
71 /// Filter fills to current lifecycle only (after last zero-crossing).
72 FilterToCurrentLifecycle {
73 /// Timestamp of the last zero-crossing.
74 last_zero_crossing_ts: u64,
75 /// Fills from current lifecycle.
76 current_lifecycle_fills: Vec<FillSnapshot>,
77 },
78}
79
80impl FillSnapshot {
81 /// Create a new fill snapshot.
82 #[must_use]
83 pub fn new(
84 ts_event: u64,
85 side: OrderSide,
86 qty: Decimal,
87 px: Decimal,
88 venue_order_id: VenueOrderId,
89 ) -> Self {
90 Self {
91 ts_event,
92 side,
93 qty,
94 px,
95 venue_order_id,
96 }
97 }
98
99 /// Return signed direction multiplier: +1 for BUY, -1 for SELL.
100 #[must_use]
101 pub fn direction(&self) -> i8 {
102 match self.side {
103 OrderSide::Buy => 1,
104 OrderSide::Sell => -1,
105 _ => 0,
106 }
107 }
108}
109
110/// Result of processing fill reports for reconciliation.
111#[derive(Debug, Clone)]
112pub struct ReconciliationResult {
113 /// Order status reports keyed by venue order ID.
114 pub orders: IndexMap<VenueOrderId, OrderStatusReport>,
115 /// Fill reports keyed by venue order ID.
116 pub fills: IndexMap<VenueOrderId, Vec<FillReport>>,
117}