Skip to main content

nautilus_infrastructure/sql/models/
positions.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
16use std::str::FromStr;
17
18use nautilus_core::{DurationNanos, UnixNanos};
19use nautilus_model::{
20    enums::{OrderSide, PositionSide},
21    events::PositionSnapshot,
22    identifiers::{AccountId, ClientOrderId, InstrumentId, PositionId, StrategyId, TraderId},
23    types::{Currency, Money, Quantity},
24};
25use sqlx::{FromRow, Row, postgres::PgRow};
26
27use crate::sql::models::decode_error;
28
29#[derive(Debug)]
30pub struct PositionSnapshotRow(pub PositionSnapshot);
31
32impl<'r> FromRow<'r, PgRow> for PositionSnapshotRow {
33    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
34        let id = row.try_get::<&str, _>("id").map(PositionId::from)?;
35        let trader_id = row.try_get::<&str, _>("trader_id").map(TraderId::from)?;
36        let strategy_id = row
37            .try_get::<&str, _>("strategy_id")
38            .map(StrategyId::from)?;
39        let instrument_id = row
40            .try_get::<&str, _>("instrument_id")
41            .map(InstrumentId::from)?;
42        let account_id = row.try_get::<&str, _>("account_id").map(AccountId::from)?;
43        let opening_order_id = row
44            .try_get::<&str, _>("opening_order_id")
45            .map(ClientOrderId::from)?;
46        let closing_order_id = row
47            .try_get::<Option<&str>, _>("closing_order_id")
48            .ok()
49            .and_then(|x| x.map(ClientOrderId::from));
50        let entry = row
51            .try_get::<&str, _>("entry")
52            .map(OrderSide::from_str)?
53            .expect("Invalid `OrderSide`");
54        let side = row
55            .try_get::<&str, _>("side")
56            .map(PositionSide::from_str)?
57            .expect("Invalid `PositionSide`");
58        let signed_qty = row.try_get::<f64, _>("signed_qty")?;
59        let quantity = row.try_get::<&str, _>("quantity").map(Quantity::from)?;
60        let peak_qty = row.try_get::<&str, _>("peak_qty").map(Quantity::from)?;
61        let quote_currency = row
62            .try_get::<&str, _>("quote_currency")
63            .map(Currency::from)?;
64        let base_currency = row
65            .try_get::<Option<&str>, _>("base_currency")
66            .ok()
67            .and_then(|x| x.map(Currency::from));
68        let settlement_currency = row
69            .try_get::<&str, _>("settlement_currency")
70            .map(Currency::from)?;
71        let avg_px_open = row.try_get::<f64, _>("avg_px_open")?;
72        let avg_px_close = row.try_get::<Option<f64>, _>("avg_px_close")?;
73        let realized_return = row.try_get::<Option<f64>, _>("realized_return")?;
74        let realized_pnl = row
75            .try_get::<Option<&str>, _>("realized_pnl")?
76            .map(Money::from);
77        let unrealized_pnl = row
78            .try_get::<Option<&str>, _>("unrealized_pnl")
79            .ok()
80            .and_then(|x| x.map(Money::from));
81        let commissions = row
82            .try_get::<Option<Vec<String>>, _>("commissions")?
83            .map_or_else(Vec::new, |c| {
84                c.into_iter().map(|s| Money::from(&s)).collect()
85            });
86        let duration_ns = row
87            .try_get::<Option<&str>, _>("duration_ns")?
88            .map(|value| {
89                value
90                    .parse::<u64>()
91                    .map(DurationNanos::new)
92                    .map_err(|e| decode_error("duration_ns", value, e))
93            })
94            .transpose()?;
95        let ts_opened = row.try_get::<String, _>("ts_opened").map(UnixNanos::from)?;
96        let ts_closed: Option<UnixNanos> = row
97            .try_get::<Option<String>, _>("ts_closed")?
98            .map(UnixNanos::from);
99        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
100        let ts_last = row.try_get::<String, _>("ts_last").map(UnixNanos::from)?;
101        let replay_state = row.try_get::<Option<serde_json::Value>, _>("replay_state")?;
102
103        let snapshot = PositionSnapshot {
104            trader_id,
105            strategy_id,
106            instrument_id,
107            position_id: id,
108            account_id,
109            opening_order_id,
110            closing_order_id,
111            entry,
112            side,
113            signed_qty,
114            quantity,
115            peak_qty,
116            quote_currency,
117            base_currency,
118            settlement_currency,
119            avg_px_open,
120            avg_px_close,
121            realized_return,
122            realized_pnl,
123            unrealized_pnl,
124            commissions,
125            duration_ns,
126            ts_opened,
127            ts_closed,
128            ts_last,
129            ts_init,
130            replay_state,
131        };
132
133        Ok(Self(snapshot))
134    }
135}