kiteconnect_async_wasm/models/portfolio/
holdings.rs

1use crate::models::common::{Exchange, Product};
2use serde::{Deserialize, Serialize};
3
4/// Holdings data structure
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Holding {
7    /// Account ID
8    pub account_id: String,
9
10    /// Trading symbol
11    #[serde(rename = "tradingsymbol")]
12    pub trading_symbol: String,
13
14    /// Exchange
15    pub exchange: Exchange,
16
17    /// ISIN (International Securities Identification Number)
18    pub isin: String,
19
20    /// Product type
21    pub product: Product,
22
23    /// Instrument token
24    #[serde(rename = "instrument_token")]
25    pub instrument_token: u32,
26
27    /// Quantity in the holding
28    pub quantity: i32,
29
30    /// T1 quantity (can be sold after T+1 day)
31    #[serde(rename = "t1_quantity")]
32    pub t1_quantity: i32,
33
34    /// Realised quantity (can be sold immediately)
35    #[serde(rename = "realised_quantity")]
36    pub realised_quantity: i32,
37
38    /// Authorized quantity (pledged/unpledged)
39    #[serde(rename = "authorised_quantity")]
40    pub authorised_quantity: i32,
41
42    /// Authorised date
43    #[serde(rename = "authorised_date")]
44    pub authorised_date: Option<String>,
45
46    /// Opening quantity at the start of the day
47    #[serde(rename = "opening_quantity")]
48    pub opening_quantity: i32,
49
50    /// Collateral quantity
51    #[serde(rename = "collateral_quantity")]
52    pub collateral_quantity: i32,
53
54    /// Collateral type
55    #[serde(rename = "collateral_type")]
56    pub collateral_type: Option<String>,
57
58    /// Collateral update quantity
59    #[serde(rename = "collateral_update_quantity")]
60    pub collateral_update_quantity: i32,
61
62    /// Discrepancy flag
63    pub discrepancy: bool,
64
65    /// Average price at which the stock was bought
66    #[serde(rename = "average_price")]
67    pub average_price: f64,
68
69    /// Last price from exchange
70    #[serde(rename = "last_price")]
71    pub last_price: f64,
72
73    /// Close price
74    #[serde(rename = "close_price")]
75    pub close_price: f64,
76
77    /// Price change
78    #[serde(rename = "price_change")]
79    pub price_change: f64,
80
81    /// P&L (profit and loss)
82    pub pnl: f64,
83
84    /// Day change
85    #[serde(rename = "day_change")]
86    pub day_change: f64,
87
88    /// Day change percentage
89    #[serde(rename = "day_change_percentage")]
90    pub day_change_percentage: f64,
91
92    /// Used quantity (used for pledging)
93    #[serde(rename = "used_quantity")]
94    pub used_quantity: i32,
95}
96
97/// Holdings summary
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct HoldingsSummary {
100    /// Total holdings value at current market price
101    pub total_value: f64,
102
103    /// Total investment value (at average price)
104    pub total_investment: f64,
105
106    /// Total P&L across all holdings
107    pub total_pnl: f64,
108
109    /// Total day change
110    pub total_day_change: f64,
111
112    /// Total day change percentage
113    pub total_day_change_percentage: f64,
114
115    /// Number of holdings
116    pub holdings_count: usize,
117}
118
119/// Portfolio profile
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct PortfolioProfile {
122    /// User ID
123    #[serde(rename = "user_id")]
124    pub user_id: String,
125
126    /// Equity used
127    #[serde(rename = "equity_used")]
128    pub equity_used: f64,
129
130    /// Equity available
131    #[serde(rename = "equity_available")]
132    pub equity_available: f64,
133
134    /// Commodity used
135    #[serde(rename = "commodity_used")]
136    pub commodity_used: f64,
137
138    /// Commodity available
139    #[serde(rename = "commodity_available")]
140    pub commodity_available: f64,
141}
142
143impl Holding {
144    /// Calculate current market value of the holding
145    pub fn market_value(&self) -> f64 {
146        self.last_price * self.quantity as f64
147    }
148
149    /// Calculate investment value (at average price)
150    pub fn investment_value(&self) -> f64 {
151        self.average_price * self.quantity as f64
152    }
153
154    /// Calculate the P&L percentage
155    pub fn pnl_percentage(&self) -> f64 {
156        let investment = self.investment_value();
157        if investment > 0.0 {
158            (self.pnl / investment) * 100.0
159        } else {
160            0.0
161        }
162    }
163
164    /// Check if the holding is profitable
165    pub fn is_profitable(&self) -> bool {
166        self.pnl > 0.0
167    }
168
169    /// Check if the holding is in loss
170    pub fn is_loss(&self) -> bool {
171        self.pnl < 0.0
172    }
173
174    /// Get available quantity for trading
175    pub fn available_quantity(&self) -> i32 {
176        self.realised_quantity + self.t1_quantity
177    }
178
179    /// Check if quantity can be sold today
180    pub fn can_sell_today(&self) -> bool {
181        self.realised_quantity > 0
182    }
183
184    /// Get quantity that can be sold today
185    pub fn sellable_today(&self) -> i32 {
186        self.realised_quantity
187    }
188
189    /// Get quantity that can be sold tomorrow (T+1)
190    pub fn sellable_tomorrow(&self) -> i32 {
191        self.t1_quantity
192    }
193
194    /// Check if the holding has any discrepancy
195    pub fn has_discrepancy(&self) -> bool {
196        self.discrepancy
197    }
198
199    /// Get the change from previous day close
200    pub fn change_from_close(&self) -> f64 {
201        self.last_price - self.close_price
202    }
203
204    /// Get the change percentage from previous day close
205    pub fn change_percentage_from_close(&self) -> f64 {
206        if self.close_price > 0.0 {
207            ((self.last_price - self.close_price) / self.close_price) * 100.0
208        } else {
209            0.0
210        }
211    }
212
213    /// Check if the holding is pledged
214    pub fn is_pledged(&self) -> bool {
215        self.used_quantity > 0
216    }
217
218    /// Get unpledged quantity
219    pub fn unpledged_quantity(&self) -> i32 {
220        self.quantity - self.used_quantity
221    }
222}
223
224impl HoldingsSummary {
225    /// Calculate from a list of holdings
226    pub fn from_holdings(holdings: &[Holding]) -> Self {
227        let total_value = holdings.iter().map(|h| h.market_value()).sum();
228        let total_investment = holdings.iter().map(|h| h.investment_value()).sum();
229        let total_pnl = holdings.iter().map(|h| h.pnl).sum();
230        let total_day_change = holdings.iter().map(|h| h.day_change).sum();
231
232        let total_day_change_percentage = if total_investment > 0.0 {
233            (total_day_change / total_investment) * 100.0
234        } else {
235            0.0
236        };
237
238        Self {
239            total_value,
240            total_investment,
241            total_pnl,
242            total_day_change,
243            total_day_change_percentage,
244            holdings_count: holdings.len(),
245        }
246    }
247
248    /// Get the overall P&L percentage
249    pub fn pnl_percentage(&self) -> f64 {
250        if self.total_investment > 0.0 {
251            (self.total_pnl / self.total_investment) * 100.0
252        } else {
253            0.0
254        }
255    }
256
257    /// Check if the overall portfolio is profitable
258    pub fn is_profitable(&self) -> bool {
259        self.total_pnl > 0.0
260    }
261
262    /// Check if the overall portfolio is in loss
263    pub fn is_loss(&self) -> bool {
264        self.total_pnl < 0.0
265    }
266}