drm_core/models/
position.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Position {
6    pub market_id: String,
7    pub outcome: String,
8    pub size: f64,
9    pub average_price: f64,
10    pub current_price: f64,
11}
12
13impl Position {
14    pub fn cost_basis(&self) -> f64 {
15        self.size * self.average_price
16    }
17
18    pub fn current_value(&self) -> f64 {
19        self.size * self.current_price
20    }
21
22    pub fn unrealized_pnl(&self) -> f64 {
23        self.current_value() - self.cost_basis()
24    }
25
26    pub fn unrealized_pnl_percent(&self) -> f64 {
27        let cost = self.cost_basis();
28        if cost == 0.0 {
29            return 0.0;
30        }
31        (self.unrealized_pnl() / cost) * 100.0
32    }
33}
34
35#[derive(Debug, Clone, Default, Serialize, Deserialize)]
36pub struct DeltaInfo {
37    pub delta: f64,
38    pub max_outcome: Option<String>,
39    pub max_position: f64,
40}
41
42pub fn calculate_delta(positions: &HashMap<String, f64>) -> DeltaInfo {
43    if positions.is_empty() {
44        return DeltaInfo::default();
45    }
46
47    let mut max_outcome: Option<String> = None;
48    let mut max_position: f64 = 0.0;
49
50    for (outcome, &size) in positions {
51        if size > max_position {
52            max_position = size;
53            max_outcome = Some(outcome.clone());
54        }
55    }
56
57    let delta = if positions.len() == 2 {
58        let values: Vec<f64> = positions.values().copied().collect();
59        (values[0] - values[1]).abs()
60    } else {
61        max_position
62    };
63
64    DeltaInfo {
65        delta,
66        max_outcome,
67        max_position,
68    }
69}
70
71#[derive(Debug, Clone, Default, Serialize, Deserialize)]
72pub struct PositionBreakdown {
73    pub outcome: String,
74    pub size: f64,
75    pub current_price: f64,
76    pub value: f64,
77}
78
79#[derive(Debug, Clone, Default, Serialize, Deserialize)]
80pub struct Nav {
81    pub nav: f64,
82    pub cash: f64,
83    pub positions_value: f64,
84    pub positions: Vec<PositionBreakdown>,
85}
86
87impl Nav {
88    pub fn calculate(cash: f64, positions: &[Position]) -> Self {
89        let mut positions_value = 0.0;
90        let mut breakdown = Vec::with_capacity(positions.len());
91
92        for pos in positions {
93            let value = pos.current_value();
94            positions_value += value;
95            breakdown.push(PositionBreakdown {
96                outcome: pos.outcome.clone(),
97                size: pos.size,
98                current_price: pos.current_price,
99                value,
100            });
101        }
102
103        Self {
104            nav: cash + positions_value,
105            cash,
106            positions_value,
107            positions: breakdown,
108        }
109    }
110}