finance_query/models/chart/
events.rs1use crate::Provider;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::OnceLock;
9
10#[derive(Debug, Default, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16#[non_exhaustive]
17pub struct ChartEvents {
18 #[serde(default)]
20 pub(crate) dividends: HashMap<String, DividendEvent>,
21 #[serde(default)]
23 pub(crate) splits: HashMap<String, SplitEvent>,
24 #[serde(default)]
26 pub(crate) capital_gains: HashMap<String, CapitalGainEvent>,
27
28 #[serde(skip)]
30 dividends_cache: OnceLock<Vec<Dividend>>,
31 #[serde(skip)]
33 splits_cache: OnceLock<Vec<Split>>,
34 #[serde(skip)]
36 capital_gains_cache: OnceLock<Vec<CapitalGain>>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub(crate) struct DividendEvent {
42 pub amount: f64,
44 pub date: i64,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub(crate) struct SplitEvent {
52 pub date: i64,
54 pub numerator: f64,
56 pub denominator: f64,
58 pub split_ratio: String,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub(crate) struct CapitalGainEvent {
65 pub amount: f64,
67 pub date: i64,
69}
70
71#[non_exhaustive]
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
77pub struct Dividend {
78 pub timestamp: i64,
80 pub amount: f64,
82
83 #[serde(skip_serializing_if = "Option::is_none", default)]
85 pub provider_id: Option<Provider>,
86}
87
88#[non_exhaustive]
92#[derive(Debug, Clone, Serialize, Deserialize)]
93#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
94pub struct Split {
95 pub timestamp: i64,
97 pub numerator: f64,
99 pub denominator: f64,
101 pub ratio: String,
103
104 #[serde(skip_serializing_if = "Option::is_none", default)]
106 pub provider_id: Option<Provider>,
107}
108
109#[non_exhaustive]
113#[derive(Debug, Clone, Serialize, Deserialize)]
114#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
115pub struct CapitalGain {
116 pub timestamp: i64,
118 pub amount: f64,
120
121 #[serde(skip_serializing_if = "Option::is_none", default)]
123 pub provider_id: Option<Provider>,
124}
125
126impl Clone for ChartEvents {
127 fn clone(&self) -> Self {
128 fn clone_cache<T: Clone>(cache: &OnceLock<T>) -> OnceLock<T> {
130 let new_cache = OnceLock::new();
131 if let Some(value) = cache.get() {
132 let _ = new_cache.set(value.clone());
133 }
134 new_cache
135 }
136
137 Self {
138 dividends: self.dividends.clone(),
139 splits: self.splits.clone(),
140 capital_gains: self.capital_gains.clone(),
141 dividends_cache: clone_cache(&self.dividends_cache),
142 splits_cache: clone_cache(&self.splits_cache),
143 capital_gains_cache: clone_cache(&self.capital_gains_cache),
144 }
145 }
146}
147
148impl ChartEvents {
149 pub fn from_parts(
155 dividends: Vec<Dividend>,
156 splits: Vec<Split>,
157 capital_gains: Vec<CapitalGain>,
158 ) -> Self {
159 let events = Self {
160 dividends: dividends
161 .iter()
162 .map(|d| {
163 (
164 d.timestamp.to_string(),
165 DividendEvent {
166 amount: d.amount,
167 date: d.timestamp,
168 },
169 )
170 })
171 .collect(),
172 splits: splits
173 .iter()
174 .map(|s| {
175 (
176 s.timestamp.to_string(),
177 SplitEvent {
178 date: s.timestamp,
179 numerator: s.numerator,
180 denominator: s.denominator,
181 split_ratio: s.ratio.clone(),
182 },
183 )
184 })
185 .collect(),
186 capital_gains: capital_gains
187 .iter()
188 .map(|g| {
189 (
190 g.timestamp.to_string(),
191 CapitalGainEvent {
192 amount: g.amount,
193 date: g.timestamp,
194 },
195 )
196 })
197 .collect(),
198 ..Default::default()
199 };
200 let sorted = |mut v: Vec<Dividend>| {
202 v.sort_by_key(|d| d.timestamp);
203 v
204 };
205 let _ = events.dividends_cache.set(sorted(dividends));
206 let mut splits = splits;
207 splits.sort_by_key(|s| s.timestamp);
208 let _ = events.splits_cache.set(splits);
209 let mut capital_gains = capital_gains;
210 capital_gains.sort_by_key(|g| g.timestamp);
211 let _ = events.capital_gains_cache.set(capital_gains);
212 events
213 }
214
215 pub fn to_dividends(&self) -> Vec<Dividend> {
217 self.dividends_cache
218 .get_or_init(|| {
219 let mut dividends: Vec<Dividend> = self
220 .dividends
221 .values()
222 .map(|d| Dividend {
223 timestamp: d.date,
224 amount: d.amount,
225 provider_id: None,
226 })
227 .collect();
228 dividends.sort_by_key(|d| d.timestamp);
229 dividends
230 })
231 .clone()
232 }
233
234 pub fn to_splits(&self) -> Vec<Split> {
236 self.splits_cache
237 .get_or_init(|| {
238 let mut splits: Vec<Split> = self
239 .splits
240 .values()
241 .map(|s| Split {
242 timestamp: s.date,
243 numerator: s.numerator,
244 denominator: s.denominator,
245 ratio: s.split_ratio.clone(),
246 provider_id: None,
247 })
248 .collect();
249 splits.sort_by_key(|s| s.timestamp);
250 splits
251 })
252 .clone()
253 }
254
255 pub fn to_capital_gains(&self) -> Vec<CapitalGain> {
257 self.capital_gains_cache
258 .get_or_init(|| {
259 let mut gains: Vec<CapitalGain> = self
260 .capital_gains
261 .values()
262 .map(|g| CapitalGain {
263 timestamp: g.date,
264 amount: g.amount,
265 provider_id: None,
266 })
267 .collect();
268 gains.sort_by_key(|g| g.timestamp);
269 gains
270 })
271 .clone()
272 }
273}