kiteconnect_async_wasm/models/auth/
margins.rs1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct MarginData {
12 pub equity: Option<SegmentMargin>,
14
15 pub commodity: Option<SegmentMargin>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct SegmentMargin {
22 pub available: MarginFunds,
24
25 pub utilised: MarginUtilisation,
27
28 pub net: f64,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct MarginFunds {
35 pub cash: f64,
37
38 pub opening_balance: f64,
40
41 pub live_balance: f64,
43
44 pub adhoc_margin: f64,
46
47 pub collateral: f64,
49
50 pub intraday_payin: f64,
52}
53
54impl MarginFunds {
55 pub fn total(&self) -> f64 {
57 self.cash + self.adhoc_margin + self.collateral + self.intraday_payin
58 }
59
60 pub fn has_sufficient_funds(&self, required: f64) -> bool {
62 self.total() >= required
63 }
64
65 pub fn cash_only(&self) -> f64 {
67 self.cash
68 }
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct MarginUtilisation {
74 pub debits: f64,
76
77 pub exposure: f64,
79
80 pub m2m_unrealised: f64,
82
83 pub m2m_realised: f64,
85
86 pub option_premium: f64,
88
89 pub payout: f64,
91
92 pub span: f64,
94
95 pub holding_sales: f64,
97
98 pub turnover: f64,
100
101 pub liquid: f64,
103
104 pub stock_collateral: f64,
106}
107
108impl MarginUtilisation {
109 pub fn total(&self) -> f64 {
111 self.debits
112 + self.exposure
113 + self.option_premium
114 + self.payout
115 + self.span
116 + self.turnover
117 + self.liquid
118 + self.stock_collateral
119 }
120
121 pub fn total_pnl(&self) -> f64 {
123 self.m2m_realised + self.m2m_unrealised
124 }
125
126 pub fn has_unrealised_losses(&self) -> bool {
128 self.m2m_unrealised < 0.0
129 }
130}
131
132impl SegmentMargin {
133 pub fn calculate_net(&self) -> f64 {
135 self.available.total() - self.utilised.total()
136 }
137
138 pub fn can_place_order(&self, required_margin: f64) -> bool {
140 self.net >= required_margin
141 }
142
143 pub fn utilisation_percentage(&self) -> f64 {
145 let total_available = self.available.total();
146 if total_available > 0.0 {
147 (self.utilised.total() / total_available) * 100.0
148 } else {
149 0.0
150 }
151 }
152}
153
154impl MarginData {
155 pub fn get_segment(&self, segment: TradingSegment) -> Option<&SegmentMargin> {
157 match segment {
158 TradingSegment::Equity => self.equity.as_ref(),
159 TradingSegment::Commodity => self.commodity.as_ref(),
160 }
161 }
162
163 pub fn total_cash(&self) -> f64 {
165 let mut total = 0.0;
166
167 if let Some(equity) = &self.equity {
168 total += equity.available.cash;
169 }
170
171 if let Some(commodity) = &self.commodity {
172 total += commodity.available.cash;
173 }
174
175 total
176 }
177
178 pub fn total_net_margin(&self) -> f64 {
180 let mut total = 0.0;
181
182 if let Some(equity) = &self.equity {
183 total += equity.net;
184 }
185
186 if let Some(commodity) = &self.commodity {
187 total += commodity.net;
188 }
189
190 total
191 }
192
193 pub fn has_sufficient_margin(&self, required: f64, segment: Option<TradingSegment>) -> bool {
195 match segment {
196 Some(seg) => self
197 .get_segment(seg)
198 .map(|margin| margin.can_place_order(required))
199 .unwrap_or(false),
200 None => {
201 self.equity
203 .as_ref()
204 .map(|m| m.can_place_order(required))
205 .unwrap_or(false)
206 || self
207 .commodity
208 .as_ref()
209 .map(|m| m.can_place_order(required))
210 .unwrap_or(false)
211 }
212 }
213 }
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
218#[serde(rename_all = "lowercase")]
219pub enum TradingSegment {
220 Equity,
221 Commodity,
222}
223
224impl std::fmt::Display for TradingSegment {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 match self {
227 TradingSegment::Equity => write!(f, "equity"),
228 TradingSegment::Commodity => write!(f, "commodity"),
229 }
230 }
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct FundTransaction {
236 pub id: String,
238
239 pub transaction_type: String,
241
242 pub amount: f64,
244
245 pub description: String,
247
248 pub date: String,
250
251 #[serde(default)]
253 pub segment: Option<String>,
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn test_margin_funds() {
262 let funds = MarginFunds {
263 cash: 10000.0,
264 opening_balance: 10000.0,
265 live_balance: 9500.0,
266 adhoc_margin: 2000.0,
267 collateral: 5000.0,
268 intraday_payin: 0.0,
269 };
270
271 assert_eq!(funds.total(), 17000.0);
272 assert!(funds.has_sufficient_funds(15000.0));
273 assert!(!funds.has_sufficient_funds(20000.0));
274 assert_eq!(funds.cash_only(), 10000.0);
275 }
276
277 #[test]
278 fn test_margin_utilisation() {
279 let utilisation = MarginUtilisation {
280 debits: 1000.0,
281 exposure: 2000.0,
282 m2m_unrealised: -500.0,
283 m2m_realised: 200.0,
284 option_premium: 0.0,
285 payout: 0.0,
286 span: 1500.0,
287 holding_sales: 0.0,
288 turnover: 50.0,
289 liquid: 0.0,
290 stock_collateral: 0.0,
291 };
292
293 assert_eq!(utilisation.total(), 4550.0);
294 assert_eq!(utilisation.total_pnl(), -300.0);
295 assert!(utilisation.has_unrealised_losses());
296 }
297
298 #[test]
299 fn test_segment_margin() {
300 let available = MarginFunds {
301 cash: 10000.0,
302 opening_balance: 10000.0,
303 live_balance: 9500.0,
304 adhoc_margin: 0.0,
305 collateral: 0.0,
306 intraday_payin: 0.0,
307 };
308
309 let utilised = MarginUtilisation {
310 debits: 2000.0,
311 exposure: 1000.0,
312 m2m_unrealised: 0.0,
313 m2m_realised: 0.0,
314 option_premium: 0.0,
315 payout: 0.0,
316 span: 0.0,
317 holding_sales: 0.0,
318 turnover: 0.0,
319 liquid: 0.0,
320 stock_collateral: 0.0,
321 };
322
323 let margin = SegmentMargin {
324 available,
325 utilised,
326 net: 7000.0,
327 };
328
329 assert_eq!(margin.calculate_net(), 7000.0);
330 assert!(margin.can_place_order(5000.0));
331 assert!(!margin.can_place_order(8000.0));
332 assert_eq!(margin.utilisation_percentage(), 30.0);
333 }
334
335 #[test]
336 fn test_margin_data() {
337 let equity_margin = SegmentMargin {
338 available: MarginFunds {
339 cash: 10000.0,
340 opening_balance: 10000.0,
341 live_balance: 9500.0,
342 adhoc_margin: 0.0,
343 collateral: 0.0,
344 intraday_payin: 0.0,
345 },
346 utilised: MarginUtilisation {
347 debits: 2000.0,
348 exposure: 0.0,
349 m2m_unrealised: 0.0,
350 m2m_realised: 0.0,
351 option_premium: 0.0,
352 payout: 0.0,
353 span: 0.0,
354 holding_sales: 0.0,
355 turnover: 0.0,
356 liquid: 0.0,
357 stock_collateral: 0.0,
358 },
359 net: 8000.0,
360 };
361
362 let margin_data = MarginData {
363 equity: Some(equity_margin),
364 commodity: None,
365 };
366
367 assert_eq!(margin_data.total_cash(), 10000.0);
368 assert_eq!(margin_data.total_net_margin(), 8000.0);
369 assert!(margin_data.has_sufficient_margin(5000.0, Some(TradingSegment::Equity)));
370 assert!(!margin_data.has_sufficient_margin(5000.0, Some(TradingSegment::Commodity)));
371 }
372}