11use clap:: Parser ;
22use polars:: prelude:: * ;
3+ use std:: collections:: BTreeMap ;
34use time:: OffsetDateTime ;
45use 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 ) ]
99pub 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+
55101fn 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
189236impl < ' 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
298405fn 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