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, IndexSet};
19use nautilus_model::{
20 enums::{AvgPxReconciliation, OrderSide, PositionSide},
21 identifiers::{AccountId, InstrumentId, TradeId, 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(super) struct FillSnapshot {
29 /// The venue order ID.
30 pub venue_order_id: VenueOrderId,
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 event timestamp (nanoseconds).
38 pub ts_event: u64,
39}
40
41/// Represents a position snapshot from the venue.
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub(super) struct VenuePositionSnapshot {
44 /// The position side (Long, Short, or Flat).
45 pub side: PositionSide,
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 /// How reconciliation may use the average entry price.
51 pub avg_px_reconciliation: AvgPxReconciliation,
52 /// The decimal places the venue kept for the average entry price, if it rounds or truncates.
53 pub avg_px_precision: Option<u8>,
54}
55
56impl VenuePositionSnapshot {
57 /// Returns the quantity signed by position side.
58 pub(super) fn signed_qty(&self) -> Decimal {
59 match self.side {
60 PositionSide::Long => self.qty,
61 PositionSide::Short => -self.qty,
62 PositionSide::Flat => Decimal::ZERO,
63 }
64 }
65}
66
67/// Result of the fill adjustment process.
68#[derive(Debug, Clone, PartialEq)]
69pub(super) enum FillAdjustmentResult {
70 /// No adjustment needed - return fills unchanged.
71 NoAdjustment,
72 /// Add synthetic opening fill to oldest lifecycle.
73 AddSyntheticOpening {
74 /// The synthetic fill to add at the beginning.
75 synthetic_fill: FillSnapshot,
76 /// All existing fills to keep.
77 existing_fills: Vec<FillSnapshot>,
78 },
79 /// Replace entire current lifecycle with single synthetic fill.
80 ReplaceCurrentLifecycle {
81 /// The single synthetic fill representing the entire position.
82 synthetic_fill: FillSnapshot,
83 },
84 /// Filter fills to current lifecycle only (after last zero-crossing).
85 FilterToCurrentLifecycle {
86 /// Timestamp of the last zero-crossing.
87 last_zero_crossing_ts: u64,
88 /// Fills from current lifecycle.
89 current_lifecycle_fills: Vec<FillSnapshot>,
90 },
91}
92
93impl FillSnapshot {
94 /// Create a new fill snapshot.
95 #[must_use]
96 pub(super) fn new(
97 venue_order_id: VenueOrderId,
98 side: OrderSide,
99 qty: Decimal,
100 px: Decimal,
101 ts_event: u64,
102 ) -> Self {
103 Self {
104 venue_order_id,
105 side,
106 qty,
107 px,
108 ts_event,
109 }
110 }
111
112 /// Return signed direction multiplier: +1 for BUY, -1 for SELL.
113 #[must_use]
114 pub(super) fn direction(&self) -> i8 {
115 match self.side {
116 OrderSide::Buy => 1,
117 OrderSide::Sell => -1,
118 }
119 }
120}
121
122/// Result of processing fill reports for reconciliation.
123#[derive(Debug, Clone)]
124pub struct ReconciliationResult {
125 /// Order status reports keyed by venue order ID.
126 pub orders: IndexMap<VenueOrderId, OrderStatusReport>,
127 /// Fill reports keyed by venue order ID.
128 pub fills: IndexMap<VenueOrderId, Vec<FillReport>>,
129 /// Orders whose fills recover order state only because synthetic fills replace their economics.
130 pub order_only_ids: IndexSet<VenueOrderId>,
131 /// Fills that recover order state only because they precede the current position lifecycle.
132 pub order_only_fill_keys: IndexSet<(AccountId, InstrumentId, TradeId)>,
133}