nautilus_infrastructure/sql/models/
positions.rs1use 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.try_get::<&str, _>("realized_pnl").map(Money::from)?;
75 let unrealized_pnl = row
76 .try_get::<Option<&str>, _>("unrealized_pnl")
77 .ok()
78 .and_then(|x| x.map(Money::from));
79 let commissions = row
80 .try_get::<Option<Vec<String>>, _>("commissions")?
81 .map_or_else(Vec::new, |c| {
82 c.into_iter().map(|s| Money::from(&s)).collect()
83 });
84 let duration_ns = row
85 .try_get::<Option<&str>, _>("duration_ns")?
86 .map(|value| {
87 value
88 .parse::<u64>()
89 .map(DurationNanos::new)
90 .map_err(|e| decode_error("duration_ns", value, e))
91 })
92 .transpose()?;
93 let ts_opened = row.try_get::<String, _>("ts_opened").map(UnixNanos::from)?;
94 let ts_closed: Option<UnixNanos> = row
95 .try_get::<Option<String>, _>("ts_closed")?
96 .map(UnixNanos::from);
97 let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
98 let ts_last = row.try_get::<String, _>("ts_last").map(UnixNanos::from)?;
99 let replay_state = row.try_get::<Option<serde_json::Value>, _>("replay_state")?;
100
101 let snapshot = PositionSnapshot {
102 trader_id,
103 strategy_id,
104 instrument_id,
105 position_id: id,
106 account_id,
107 opening_order_id,
108 closing_order_id,
109 entry,
110 side,
111 signed_qty,
112 quantity,
113 peak_qty,
114 quote_currency,
115 base_currency,
116 settlement_currency,
117 avg_px_open,
118 avg_px_close,
119 realized_return,
120 realized_pnl: Some(realized_pnl), unrealized_pnl,
122 commissions,
123 duration_ns,
124 ts_opened,
125 ts_closed,
126 ts_last,
127 ts_init,
128 replay_state,
129 };
130
131 Ok(Self(snapshot))
132 }
133}