nautilus_infrastructure/sql/models/
positions.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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::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
27#[derive(Debug)]
28pub struct PositionSnapshotModel(pub PositionSnapshot);
29
30impl<'r> FromRow<'r, PgRow> for PositionSnapshotModel {
31    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
32        let id = row.try_get::<&str, _>("id").map(PositionId::from)?;
33        let trader_id = row.try_get::<&str, _>("trader_id").map(TraderId::from)?;
34        let strategy_id = row
35            .try_get::<&str, _>("strategy_id")
36            .map(StrategyId::from)?;
37        let instrument_id = row
38            .try_get::<&str, _>("instrument_id")
39            .map(InstrumentId::from)?;
40        let account_id = row.try_get::<&str, _>("account_id").map(AccountId::from)?;
41        let opening_order_id = row
42            .try_get::<&str, _>("opening_order_id")
43            .map(ClientOrderId::from)?;
44        let closing_order_id = row
45            .try_get::<Option<&str>, _>("closing_order_id")
46            .ok()
47            .and_then(|x| x.map(ClientOrderId::from));
48        let entry = row
49            .try_get::<&str, _>("entry")
50            .map(OrderSide::from_str)?
51            .expect("Invalid `OrderSide`");
52        let side = row
53            .try_get::<&str, _>("side")
54            .map(PositionSide::from_str)?
55            .expect("Invalid `PositionSide`");
56        let signed_qty = row.try_get::<f64, _>("signed_qty")?;
57        let quantity = row.try_get::<&str, _>("quantity").map(Quantity::from)?;
58        let peak_qty = row.try_get::<&str, _>("peak_qty").map(Quantity::from)?;
59        let quote_currency = row
60            .try_get::<&str, _>("quote_currency")
61            .map(Currency::from)?;
62        let base_currency = row
63            .try_get::<Option<&str>, _>("base_currency")
64            .ok()
65            .and_then(|x| x.map(Currency::from));
66        let settlement_currency = row
67            .try_get::<&str, _>("settlement_currency")
68            .map(Currency::from)?;
69        let avg_px_open = row.try_get::<f64, _>("avg_px_open")?;
70        let avg_px_close = row.try_get::<Option<f64>, _>("avg_px_close")?;
71        let realized_return = row.try_get::<Option<f64>, _>("realized_return")?;
72        let realized_pnl = row.try_get::<&str, _>("realized_pnl").map(Money::from)?;
73        let unrealized_pnl = row
74            .try_get::<Option<&str>, _>("unrealized_pnl")
75            .ok()
76            .and_then(|x| x.map(Money::from));
77        let commissions = row
78            .try_get::<Option<Vec<String>>, _>("commissions")?
79            .map_or_else(Vec::new, |c| {
80                c.into_iter().map(|s| Money::from(&s)).collect()
81            });
82        let duration_ns: Option<u64> = row
83            .try_get::<Option<i64>, _>("duration_ns")?
84            .map(|value| value as u64);
85        let ts_opened = row.try_get::<String, _>("ts_opened").map(UnixNanos::from)?;
86        let ts_closed: Option<UnixNanos> = row
87            .try_get::<Option<String>, _>("ts_closed")?
88            .map(UnixNanos::from);
89        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
90        let ts_last = row.try_get::<String, _>("ts_last").map(UnixNanos::from)?;
91
92        let snapshot = PositionSnapshot {
93            trader_id,
94            strategy_id,
95            instrument_id,
96            position_id: id,
97            account_id,
98            opening_order_id,
99            closing_order_id,
100            entry,
101            side,
102            signed_qty,
103            quantity,
104            peak_qty,
105            quote_currency,
106            base_currency,
107            settlement_currency,
108            avg_px_open,
109            avg_px_close,
110            realized_return,
111            realized_pnl: Some(realized_pnl), // TODO: Standardize
112            unrealized_pnl,
113            commissions,
114            duration_ns,
115            ts_opened,
116            ts_closed,
117            ts_last,
118            ts_init,
119        };
120
121        Ok(Self(snapshot))
122    }
123}