Skip to content

Commit 55f2e85

Browse files
committed
- Added perf month dividend income
1 parent c9cbd71 commit 55f2e85

1 file changed

Lines changed: 150 additions & 22 deletions

File tree

src/divportfolio/main.rs

Lines changed: 150 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use clap::Parser;
22
use polars::prelude::*;
3+
use std::collections::BTreeMap;
34
use time::OffsetDateTime;
45
use yahoo_finance_api as yahoo;
56

6-
// TODO: check YQuoteSummary if yahoo finance api can get us dividend rate
7-
// // TODO: wazone srednia yield
7+
// // TODO: Print dividend data and then add in summary the dividend per month
88
#[derive(Debug, PartialEq, Eq, Hash)]
99
pub enum Exchange {
1010
EUR(String),
@@ -52,6 +52,52 @@ impl Currency {
5252
}
5353
}
5454

55+
fn print_monthly_dividends_distribution(stocks: &[Stock]) {
56+
println!("Dividend distribution per month:");
57+
let mut monthly_distribution = BTreeMap::new();
58+
59+
// USD / PLN
60+
let mut exchange_rate_usd_pln = 0.0;
61+
let provider = yahoo::YahooConnector::new().unwrap();
62+
match provider.get_latest_quotes("USDPLN=X", "1d") {
63+
Ok(response) => {
64+
if let Ok(quotes) = response.quotes() {
65+
println!("Kurs USD/PLN: {:?}", quotes.last().unwrap().close);
66+
exchange_rate_usd_pln = quotes.last().unwrap().close;
67+
}
68+
}
69+
Err(e) => eprintln!("Error: {:?}", e),
70+
}
71+
// EUR / PLN
72+
let mut exchange_rate_eur_pln = 0.0;
73+
let provider = yahoo::YahooConnector::new().unwrap();
74+
match provider.get_latest_quotes("EURPLN=X", "1d") {
75+
Ok(response) => {
76+
if let Ok(quotes) = response.quotes() {
77+
println!("Kurs USD/PLN: {:?}", quotes.last().unwrap().close);
78+
exchange_rate_eur_pln = quotes.last().unwrap().close;
79+
}
80+
}
81+
Err(e) => eprintln!("Error: {:?}", e),
82+
}
83+
84+
// convert all dividends into PLN
85+
for stock in stocks {
86+
for (month, amount) in &stock.monthly_dividends {
87+
let amount_pln = match stock.current_value {
88+
Currency::USD(_) => amount * exchange_rate_usd_pln,
89+
Currency::EUR(_) => amount * exchange_rate_eur_pln,
90+
Currency::PLN(_) => *amount,
91+
};
92+
*monthly_distribution.entry(month.clone()).or_insert(0.0) += amount_pln;
93+
}
94+
}
95+
96+
monthly_distribution.iter().for_each(|(m, v)| {
97+
println!("{m} : {v:0.2} PLN ");
98+
});
99+
}
100+
55101
fn print_summary(data: &[Stock]) {
56102
let mut total_investement = 0.0;
57103
let mut annual_dividend = 0.0;
@@ -131,7 +177,7 @@ fn print_summary(data: &[Stock]) {
131177
);
132178
}
133179
}
134-
Err(e) => eprintln!("Błąd: {:?}", e),
180+
Err(e) => eprintln!("Error: {:?}", e),
135181
}
136182
}
137183
}
@@ -184,6 +230,7 @@ struct Stock<'a> {
184230
current_yield: f64,
185231
yield_on_invested: f64,
186232
annualized_dividend: Currency,
233+
monthly_dividends: BTreeMap<String, f64>,
187234
}
188235

189236
impl<'a> Stock<'a> {
@@ -192,6 +239,7 @@ impl<'a> Stock<'a> {
192239
invested_value: Currency,
193240
current_value: Currency,
194241
current_yield: f64,
242+
monthly_dividends: BTreeMap<String, f64>,
195243
) -> Self {
196244
// compute yield on invested
197245
Self {
@@ -205,6 +253,7 @@ impl<'a> Stock<'a> {
205253
current_yield,
206254
),
207255
annualized_dividend: current_value.derive(current_yield * current_value.value()),
256+
monthly_dividends,
208257
}
209258
}
210259
}
@@ -292,7 +341,65 @@ fn get_data(
292341

293342
let value = investement.derive(response.last_quote()?.close * num_shares);
294343

295-
Ok(Stock::new(symbol, investement, value, dividend_yield))
344+
let monthly_dividends = get_dividend_history(symbol, num_shares)?;
345+
346+
Ok(Stock::new(
347+
symbol,
348+
investement,
349+
value,
350+
dividend_yield,
351+
monthly_dividends,
352+
))
353+
}
354+
355+
fn get_dividend_history(
356+
symbol: &str,
357+
num_shares: f64,
358+
) -> Result<BTreeMap<String, f64>, Box<dyn std::error::Error>> {
359+
let provider = yahoo::YahooConnector::new()?;
360+
361+
// Get scope of for dividends (previous year)
362+
let now = OffsetDateTime::now_utc();
363+
let prev_year = now.year() - 1;
364+
365+
let start = time::Date::from_calendar_date(prev_year, time::Month::January, 1)?
366+
.with_hms(0, 0, 0)?
367+
.assume_utc();
368+
let end = time::Date::from_calendar_date(prev_year, time::Month::December, 31)?
369+
.with_hms(23, 59, 59)?
370+
.assume_utc();
371+
372+
let resp = provider.get_quote_history(symbol, start, end)?;
373+
374+
// Zbierz dywidendy per miesiac
375+
let mut monthly_dividends: BTreeMap<String, f64> = BTreeMap::new();
376+
377+
if let Ok(quotes) = resp.quotes() {
378+
// quotes to ceny — szukamy eventow dywidendowych
379+
// Yahoo finance zwraca dividendy w polu adjclose vs close diff
380+
// Ale lepiej uzyc get_quote_history i sprawdzic eventy
381+
}
382+
383+
// Probujemy wyciagnac dywidendy z odpowiedzi (pole events.dividends w Yahoo API)
384+
// yahoo_finance_api parsuje to w strukt YResponse
385+
if let Some(result) = resp.chart.result.as_ref() {
386+
for r in result {
387+
if let Some(events) = &r.events {
388+
if let Some(dividends) = &events.dividends {
389+
for (_timestamp_str, div) in dividends {
390+
let dt = OffsetDateTime::from_unix_timestamp(div.date as i64)?;
391+
let month_key = format!("{:02}", dt.month() as u8);
392+
let dividend_income = div.amount * num_shares;
393+
*monthly_dividends.entry(month_key).or_insert(0.0) += dividend_income;
394+
}
395+
}
396+
}
397+
}
398+
}
399+
400+
// println!("Monthly dividends for {}: {:?}", symbol, monthly_dividends);
401+
402+
Ok(monthly_dividends)
296403
}
297404

298405
fn main() -> Result<(), String> {
@@ -308,51 +415,72 @@ fn main() -> Result<(), String> {
308415
//Stock::new("ABEV", Currency::USD(121+11.91), Currency::USD(240.84), 0.0789),
309416
get_data(
310417
"ABEV",
311-
Currency::USD(121.0 + 11.20 + 80.0 + 11.91),
418+
Currency::USD(121.0 + 11.20 + 80.0 + 1.91),
312419
82.09,
313420
None,
314421
)
315422
.unwrap(),
316423
get_data(
317424
"BBY",
318-
Currency::USD(1000.0 + 2.0 + 3.62 + 94.73),
425+
Currency::USD(1000.0 + 92.0 + 93.62 + 94.73),
319426
11.63,
320427
None,
321428
)
322429
.unwrap(),
323430
get_data("GOOGL", Currency::USD(58.68 + 60.0), 0.81, None).unwrap(),
324-
get_data("GSL", Currency::USD(260.0 + 4.36), 7.11, None).unwrap(),
431+
get_data("GSL", Currency::USD(276.0 + 44.36 + 2.92), 7.19, None).unwrap(),
325432
get_data("KO", Currency::USD(23.06 + 70.0 + 5.88 + 5.92), 1.75, None).unwrap(),
326-
get_data("LX", Currency::USD(30.49), 1.9, None).unwrap(),
327-
get_data("SM", Currency::USD(2.74), 1.0, None).unwrap(),
328-
get_data("TGT", Currency::USD(8.78 + 1.0 + 8.88), 0.25, None).unwrap(),
329-
get_data("VZ", Currency::USD(1.85), 0.09, None).unwrap(),
433+
get_data("LX", Currency::USD(3.49 + 9.72), 1.9, None).unwrap(),
434+
get_data("SM", Currency::USD(27.74), 2.0, None).unwrap(),
435+
get_data(
436+
"TGT",
437+
Currency::USD(26.9 + 744.63 + 2.0 + 8.78 + 1.0 + 8.88),
438+
9.25,
439+
None,
440+
)
441+
.unwrap(),
442+
//Stock::new("VZ", Currency::USD(19.85), Currency::USD(24.98), 0.0671),
443+
get_data("VZ", Currency::USD(19.85), 0.49, None).unwrap(),
330444
];
331445

332446
print_data_frame(&ania);
333447

334448
let jacek = vec![
335-
//Stock::new("AHOG", Currency::EUR(80.74), Currency::EUR(69.21), 0.0347),
336-
get_data("AHOG.DE", Currency::USD(80.74), 1.11, None).unwrap(),
337-
get_data("BMO", Currency::USD(215.0), 2.23, None).unwrap(),
338-
get_data("CNQ", Currency::USD(30.0 + 98.51), 1.65, None).unwrap(),
339-
get_data("CVX", Currency::USD(100.0), 1.43, None).unwrap(),
340-
get_data("EIX", Currency::USD(20.0), 0.23, None).unwrap(),
341-
get_data("EPR", Currency::USD(500.0 + 5340.12), 98.24, None).unwrap(),
449+
get_data("AHOG.DE", Currency::EUR(5980.74), 179.11, None).unwrap(),
450+
get_data("BMO", Currency::USD(2100.0), 28.23, None).unwrap(),
451+
get_data("CNQ", Currency::USD(300.0 + 298.51), 138.65, None).unwrap(),
452+
get_data("CVX", Currency::USD(100.0), 6.43, None).unwrap(),
453+
get_data("EIX", Currency::USD(200.0), 6.23, None).unwrap(),
454+
get_data(
455+
"EPR",
456+
Currency::USD(500.0 + 500.0 + 987.80 + 496.86 + 5150.48 + 100.0 + 100.0 + 5340.12),
457+
79.24,
458+
None,
459+
)
460+
.unwrap(),
342461
get_data("NVS", Currency::USD(201.0), 1.89, None).unwrap(),
343462
get_data("O", Currency::USD(336.99), 5.2, None).unwrap(),
344463
get_data("SNY", Currency::USD(200.0), 4.02, None).unwrap(),
345-
get_data("TW10.F", Currency::EUR(20.66 + 0.52), 1.02, None).unwrap(),
346-
get_data("TRN.MI", Currency::EUR(9.29), 1.86, None).unwrap(),
347-
get_data("UPS", Currency::USD(10.0), 1.02, None).unwrap(),
348-
get_data("VVD.DE", Currency::EUR(5.23), 1.7, None).unwrap(),
464+
get_data("TW10.F", Currency::EUR(200.66 + 52.0), 12.02, None).unwrap(),
465+
get_data("TRN.MI", Currency::EUR(92.29), 18.86, None).unwrap(),
466+
get_data(
467+
"UPS",
468+
Currency::USD(1999.99 + 200.0 + 1000.0 + 1000.0),
469+
15.02,
470+
None,
471+
)
472+
.unwrap(),
473+
get_data("VVD.DE", Currency::EUR(569.23), 15.77, None).unwrap(),
474+
// Stock::new("DE000A289XJ2",Currency::USD(2594.37), Currency::EUR(2627.66), 0.0501),
349475
];
350476
print_data_frame(&jacek);
351477

352478
println!("ANIA:");
479+
print_monthly_dividends_distribution(&ania);
353480
print_summary(&ania);
354481

355482
println!("JACEK:");
483+
print_monthly_dividends_distribution(&jacek);
356484
print_summary(&jacek);
357485

358486
// Compute summary in PLN

0 commit comments

Comments
 (0)