deribit_http/model/
portfolio_simulation.rs1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct SimulatePortfolioRequest {
11 pub currency: String,
13 #[serde(skip_serializing_if = "Option::is_none")]
15 pub add_positions: Option<bool>,
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub simulated_positions: Option<HashMap<String, f64>>,
19}
20
21impl SimulatePortfolioRequest {
22 #[must_use]
24 pub fn new(currency: impl Into<String>) -> Self {
25 Self {
26 currency: currency.into(),
27 add_positions: None,
28 simulated_positions: None,
29 }
30 }
31
32 #[must_use]
34 pub fn with_add_positions(mut self, add: bool) -> Self {
35 self.add_positions = Some(add);
36 self
37 }
38
39 #[must_use]
41 pub fn with_simulated_positions(mut self, positions: HashMap<String, f64>) -> Self {
42 self.simulated_positions = Some(positions);
43 self
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49pub struct SimulatePortfolioResponse {
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub projected_initial_margin: Option<f64>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub projected_maintenance_margin: Option<f64>,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub projected_delta_total: Option<f64>,
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub margin_change: Option<f64>,
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub available_funds: Option<f64>,
65 #[serde(flatten)]
67 pub additional: HashMap<String, serde_json::Value>,
68}
69
70#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
72pub struct PmeSimulateResponse {
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub projected_margin: Option<f64>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub liquidation_price: Option<f64>,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub risk_value: Option<f64>,
82 #[serde(flatten)]
84 pub additional: HashMap<String, serde_json::Value>,
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn test_simulate_portfolio_request_builder() {
93 let mut positions = HashMap::new();
94 positions.insert("BTC-PERPETUAL".to_string(), 1.0);
95
96 let request = SimulatePortfolioRequest::new("BTC")
97 .with_add_positions(true)
98 .with_simulated_positions(positions);
99
100 assert_eq!(request.currency, "BTC");
101 assert_eq!(request.add_positions, Some(true));
102 assert!(request.simulated_positions.is_some());
103 }
104
105 #[test]
106 fn test_simulate_portfolio_response_deserialization() {
107 let json = r#"{
108 "projected_initial_margin": 0.05,
109 "projected_maintenance_margin": 0.03,
110 "projected_delta_total": 1.5,
111 "margin_change": 0.01,
112 "available_funds": 0.95
113 }"#;
114
115 let response: SimulatePortfolioResponse =
116 serde_json::from_str(json).expect("Failed to parse");
117 assert_eq!(response.projected_initial_margin, Some(0.05));
118 assert_eq!(response.projected_maintenance_margin, Some(0.03));
119 }
120
121 #[test]
122 fn test_pme_simulate_response_deserialization() {
123 let json = r#"{
124 "projected_margin": 0.1,
125 "liquidation_price": 50000.0,
126 "risk_value": 0.05
127 }"#;
128
129 let response: PmeSimulateResponse = serde_json::from_str(json).expect("Failed to parse");
130 assert_eq!(response.projected_margin, Some(0.1));
131 assert_eq!(response.liquidation_price, Some(50000.0));
132 }
133}