1use serde::Deserialize;
2use std::collections::HashMap;
3
4pub use crate::strnum::FloatOrString;
5
6#[derive(Deserialize, Debug)]
8#[serde(deny_unknown_fields)]
9pub struct Guru {
10 pub id: String,
12 pub name: String,
14 pub url: Option<String>,
16 pub company: String,
18 pub num_of_stocks: FloatOrString,
20 pub value: FloatOrString,
22 pub turnover: FloatOrString,
24 pub latest_update: String,
26}
27
28#[derive(Deserialize, Debug)]
30#[serde(deny_unknown_fields)]
31pub struct Gurus {
32 pub all: HashMap<String, Vec<Guru>>,
34 pub my: HashMap<String, Vec<String>>,
37}
38
39#[derive(Deserialize, Debug)]
41#[serde(deny_unknown_fields)]
42pub struct GuruTrades {
43 pub holdings: Vec<GuruHoldings>,
45 pub picks: Vec<GuruPicks2>,
47}
48
49#[derive(Deserialize, Debug)]
51#[serde(deny_unknown_fields)]
52pub struct GuruHoldings {
53 pub change: FloatOrString,
54 pub current_shares: FloatOrString,
55 pub date: String,
56 pub guru: String,
57 pub guru_id: String,
58 pub perc_assets: FloatOrString,
59 pub perc_shares: FloatOrString,
60}
61
62#[derive(Deserialize, Debug)]
64#[serde(deny_unknown_fields)]
65pub struct GuruPicks2 {
66 #[serde(rename = "Avg")]
67 pub avg: FloatOrString,
68 pub action: String,
69 pub comment: String,
70 pub current_shares: FloatOrString,
71 pub date: String,
72 pub guru: String,
73 pub guru_id: String,
74 pub impact: FloatOrString,
75 pub price_max: FloatOrString,
76 pub price_min: FloatOrString,
77}
78
79#[derive(Deserialize, Debug)]
87#[serde(deny_unknown_fields)]
88pub struct GuruPick {
89 #[serde(rename = "GuruName")]
90 pub guru_name: String,
91 #[serde(rename = "RecmAction")]
92 pub recm_action: String,
93 #[serde(rename = "RecmDate")]
94 pub recm_date: String,
95 #[serde(rename = "RecmPrice")]
96 pub recm_price: FloatOrString,
97 pub change: FloatOrString,
98 pub comment: String,
99 pub company: String,
100 pub currency: String,
101 pub currency_txt: String,
102 pub price: FloatOrString,
103 pub price_max: FloatOrString,
104 pub price_min: FloatOrString,
105 pub sector: String,
106 pub share_current: FloatOrString,
107 pub symbol: String,
108 pub symbol_ori: String,
109 pub trans_share: FloatOrString,
110 #[serde(rename = "type")]
111 pub transaction_type: String,
112 pub exchange: String,
113 pub industry: String,
114}
115
116#[derive(Deserialize, Debug)]
117#[serde(deny_unknown_fields)]
118pub struct GuruPortfolio {
119 pub summary: GuruPortSummary,
120 pub port: Vec<GuruPosition>,
121}
122
123#[derive(Deserialize, Debug)]
124#[serde(deny_unknown_fields)]
125pub struct GuruPortSummary {
126 pub country: String,
127 pub date: String,
128 pub equity: FloatOrString,
129 pub firm: String,
130 pub num_new: FloatOrString,
131 pub number_of_stocks: FloatOrString,
132 pub turnover: FloatOrString,
133}
134
135#[derive(Deserialize, Debug)]
136#[serde(deny_unknown_fields)]
137pub struct GuruPosition {
138 #[serde(rename = "13f_date")]
139 pub date_13f: String,
140 #[serde(rename = "52h")]
141 pub num_52h: FloatOrString,
142 #[serde(rename = "52l")]
143 pub num_52l: FloatOrString,
144 pub change: FloatOrString,
145 pub company: String,
146 pub currency: String,
147 pub currency_txt: String,
148 pub exchange: String,
149 pub impact: FloatOrString,
150 pub industry: String,
151 pub mktcap: FloatOrString,
152 pub pct: FloatOrString,
153 pub pe: FloatOrString,
154 pub position: FloatOrString,
155 pub price: FloatOrString,
156 pub sector: String,
157 pub share: FloatOrString,
158 pub symbol: String,
159 pub symbol_ori: String,
160 pub value: FloatOrString,
161 #[serde(rename = "yield")]
162 pub transaction_yield: FloatOrString,
163 pub class: String,
164 pub share_change_pct: FloatOrString,
165}
166
167#[derive(Deserialize, Debug)]
169#[serde(deny_unknown_fields)]
170pub struct Politician {
171 pub id: u32,
173 pub full_name: String,
175 pub position: String,
177 pub party: String,
179 pub district: Option<String>,
181 pub state: String,
183}
184
185#[derive(Deserialize, Debug)]
187#[serde(deny_unknown_fields)]
188pub struct PoliticianTransaction {
189 pub symbol: String,
190 pub company: String,
191 pub exchange: String,
192 pub industry: usize,
193 pub class: AssetType,
194 pub stockid: String,
195 pub option_type: Option<String>,
196 pub strike_price: Option<FloatOrString>,
197 pub trans_type: String,
198 pub amount: String,
199 pub disclosure_date: String,
200 pub transaction_date: String,
201 pub expiration_date: Option<String>,
202 pub id: u32,
203 pub full_name: String,
204 pub official_full: Option<String>,
205 pub position: String,
206 pub state: String,
207 pub party: String,
208}
209
210#[derive(Deserialize, Debug)]
212pub enum AssetType {
213 #[serde(rename = "Common Stock")]
214 CommonStock,
215 Option,
216 #[serde(rename = "ETF")]
217 Etf,
218 #[serde(rename = "Preferred Stock")]
219 PreferredStock,
220 Bond,
221 Units,
222 Warrant,
223 Other,
224}
225
226#[derive(Deserialize, Debug)]
228pub struct PoliticianTransactionList {
229 pub count: usize,
230 #[serde(rename = "currentPage")]
231 pub current_page: u32,
232 #[serde(rename = "lastPage")]
233 pub last_page: u32,
234 pub total: u32,
235 pub data: Vec<PoliticianTransaction>,
236}
237
238#[cfg(test)]
239mod tests {
240 use super::super::*;
241 use super::*;
242 use crate::serde_json::Error;
243 use std::{convert::TryFrom, env};
244 use time::{Date, UtcDateTime};
245
246 fn get_months_before(date: Date, months: u8) -> Date {
247 let mut day = date.day();
248 let mut month = date.month() as u8;
249 let mut year = date.year();
250
251 while month + 1 < months {
252 month += 12;
253 year -= 1;
254 }
255
256 month -= months;
257 let month: time::Month = time::Month::try_from(month).unwrap();
258 day = day.min(month.length(year));
259
260 Date::from_calendar_date(year, month, day).unwrap()
261 }
262
263 #[tokio::test]
264 async fn test_guru_trades() {
265 if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
266 if !token.is_empty() {
267 let gf_connect = GuruFocusConnector::new(token);
268 let stock = "WMT";
269 let trades = gf_connect.get_guru_trades(stock).await;
270 assert!(trades.is_ok());
271 let trades = serde_json::from_value::<HashMap<String, GuruTrades>>(trades.unwrap());
272 assert!(trades.is_ok());
273 }
274 }
275 }
276
277 #[tokio::test]
278 async fn test_guru_picks() {
279 if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
280 if !token.is_empty() {
281 let gf_connect = GuruFocusConnector::new(token);
282 let gurus = ["7", "16", "28"];
284 let now = UtcDateTime::now().date();
285 let three_months_ago = get_months_before(now, 3);
286 let page = 1;
287 let trades = gf_connect
288 .get_guru_picks(&gurus, three_months_ago, page)
289 .await;
290 assert!(trades.is_ok());
291 }
292 }
293 }
294
295 #[tokio::test]
296 async fn test_gurulist() {
297 if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
298 if !token.is_empty() {
299 let gf_connect = GuruFocusConnector::new(token);
300 let guru_data = gf_connect.get_gurus().await;
301 assert!(guru_data.is_ok());
302 }
303 }
304 }
305
306 #[tokio::test]
307 async fn test_guru_portfolios() {
308 if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
309 if !token.is_empty() {
310 let gf_connect = GuruFocusConnector::new(token);
311 let gurus = ["47", "39"];
313 let portfolios = gf_connect.get_guru_portfolios(&gurus).await;
314 assert!(portfolios.is_ok());
315 let portfolios =
316 serde_json::from_value::<HashMap<String, GuruPortfolio>>(portfolios.unwrap());
317 assert!(portfolios.is_ok());
318 }
319 }
320 }
321
322 #[tokio::test]
323 async fn test_politicianlist() {
324 if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
325 if !token.is_empty() {
326 let gf_connect = GuruFocusConnector::new(token);
327 let politicians = gf_connect.get_politicians().await;
328 assert!(politicians.is_ok());
329 }
330 }
331 }
332
333 #[tokio::test]
334 async fn test_politiciantransactions() {
335 if let Ok(token) = env::var("GURUFOCUS_TOKEN") {
336 if !token.is_empty() {
337 let gf_connect = GuruFocusConnector::new(token);
338 let politician_transactions = gf_connect.get_politician_transactions(1, None).await;
339 assert!(politician_transactions.is_ok());
340 let politician_transactions: Result<PoliticianTransactionList, Error> =
341 serde_json::from_value(politician_transactions.unwrap());
342 assert!(politician_transactions.is_ok());
343 }
344 }
345 }
346}