1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2025 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// -------------------------------------------------------------------------------------------------
1516use std::fmt::{Debug, Display};
1718use nautilus_core::{UUID4, UnixNanos};
19use nautilus_model::{
20 enums::PositionSide,
21 identifiers::{AccountId, InstrumentId, PositionId},
22 types::Quantity,
23};
24use rust_decimal::Decimal;
25use serde::{Deserialize, Serialize};
2627/// Represents a position status at a point in time.
28#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(tag = "type")]
30#[cfg_attr(
31 feature = "python",
32 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.execution")
33)]
34pub struct PositionStatusReport {
35/// The account ID associated with the position.
36pub account_id: AccountId,
37/// The instrument ID associated with the event.
38pub instrument_id: InstrumentId,
39/// The position side.
40pub position_side: PositionSide,
41/// The current open quantity.
42pub quantity: Quantity,
43/// The current signed quantity as a decimal (positive for position side `LONG`, negative for `SHORT`).
44pub signed_decimal_qty: Decimal,
45/// The unique identifier for the event.
46pub report_id: UUID4,
47/// UNIX timestamp (nanoseconds) when the last event occurred.
48pub ts_last: UnixNanos,
49/// UNIX timestamp (nanoseconds) when the event was initialized.
50pub ts_init: UnixNanos,
51/// The position ID (assigned by the venue).
52pub venue_position_id: Option<PositionId>,
53}
5455impl PositionStatusReport {
56/// Creates a new [`PositionStatusReport`] instance with required fields.
57#[allow(clippy::too_many_arguments)]
58 #[must_use]
59pub fn new(
60 account_id: AccountId,
61 instrument_id: InstrumentId,
62 position_side: PositionSide,
63 quantity: Quantity,
64 venue_position_id: Option<PositionId>,
65 ts_last: UnixNanos,
66 ts_init: UnixNanos,
67 report_id: Option<UUID4>,
68 ) -> Self {
69// Calculate signed decimal quantity based on position side
70let signed_decimal_qty = match position_side {
71 PositionSide::Long => quantity.as_decimal(),
72 PositionSide::Short => -quantity.as_decimal(),
73 PositionSide::Flat => Decimal::ZERO,
74 PositionSide::NoPositionSide => Decimal::ZERO, // TODO: Consider disallowing this?
75};
7677Self {
78 account_id,
79 instrument_id,
80 position_side,
81 quantity,
82 signed_decimal_qty,
83 report_id: report_id.unwrap_or_default(),
84 ts_last,
85 ts_init,
86 venue_position_id,
87 }
88 }
8990/// Checks if the position has a venue position ID.
91#[must_use]
92pub const fn has_venue_position_id(&self) -> bool {
93self.venue_position_id.is_some()
94 }
9596/// Checks if this is a flat position (quantity is zero).
97#[must_use]
98pub const fn is_flat(&self) -> bool {
99matches!(
100self.position_side,
101 PositionSide::Flat | PositionSide::NoPositionSide
102 )
103 }
104105/// Checks if this is a long position.
106#[must_use]
107pub fn is_long(&self) -> bool {
108self.position_side == PositionSide::Long
109 }
110111/// Checks if this is a short position.
112#[must_use]
113pub fn is_short(&self) -> bool {
114self.position_side == PositionSide::Short
115 }
116}
117118impl Display for PositionStatusReport {
119fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120write!(
121 f,
122"PositionStatusReport(account={}, instrument={}, side={}, qty={}, venue_pos_id={:?}, ts_last={}, ts_init={})",
123self.account_id,
124self.instrument_id,
125self.position_side,
126self.signed_decimal_qty,
127self.venue_position_id,
128self.ts_last,
129self.ts_init
130 )
131 }
132}