3i) zdZddlZddlmZmZmZej eZdddddddddddd d Z Gd d Z y) zh Basic Analytics Module. Provides portfolio analytics: allocations, risk metrics, performance summary. N)DictListAnyWorldEuropeEmergingGlobalItalyCash) zVWCE.MIzMEUD.MIzSWDA.MIzEIMI.MIzAGGH.MIzAZM.MIzENI.MIzISP.MIzUCG.MIzTIT.MIzRACE.MICASHceZdZdZdZdeeeffdZdeeeffdZ deee ffdZ dde de efdZdefd Zdeee ffd Zde efd Zdeee ffd Zy )AnalyticsEnginez)Provides portfolio analytics and metrics.c ||_||_y)z Initialize analytics engine. Args: portfolio: Portfolio instance db_manager: DBManager instance N) portfolio db_manager)selfrrs K/mnt/ssd/data/python-lab/portfolio-manager/src/analytics/basic_analytics.py__init__zAnalyticsEngine.__init__"s#$returnc|jj}|dk(riSi}|jjD]4}|j}||vrd||<||xx|j|z dzz cc<6t j d||S)z Calculate allocation by asset type. Returns: Dictionary mapping asset_type -> weight percentage Example: {'ETF': 89.9, 'Stock': 3.4, 'Cash': 6.7} rdzAllocation by type: )rget_total_valueholdings asset_type current_valueloggerdebug)r total_value allocationholdingrs rget_allocation_by_typez&AnalyticsEngine.get_allocation_by_type-snn446 ! I ~~.. RG ++J+), :& z "w'<'<{'Jc&Q Q "  R  +J<89rc>|jj}|dk(riSi}|jjD]H}tj |j d}||vrd||<||xx|j |z dzz cc<Jtjd||S)a Calculate allocation by geography. Uses GEOGRAPHY_MAP to map tickers to regions. Returns: Dictionary mapping geography -> weight percentage Example: {'World': 70.2, 'Europe': 19.6, 'Italy': 3.4, 'Cash': 6.7} rOtherrrzAllocation by geography: ) rrr GEOGRAPHY_MAPgettickerrrr)rr r!r" geographys rget_allocation_by_geographyz+AnalyticsEngine.get_allocation_by_geographyGsnn446 ! I ~~.. QG%))'..'BI *(+ 9% y !g&;&;k&IS%P P !  Q  0 =>rc|j}d}d}|jjD]*}|j|kDs|j}|j},|j dd}|j dd}|j dd}|dkDrd}n |dkDrd }nd }||f||||d } t jd | | S) a Calculate risk metrics. Returns: Dictionary with risk metrics: - max_single_holding: (ticker, weight_pct) - stock_concentration: total % in stocks - etf_concentration: total % in ETFs - cash_concentration: total % in cash - concentration_warning: warning level ('ok', 'warning', 'danger') Example: { 'max_single_holding': ('VWCE.MI', 70.2), 'stock_concentration': 3.4, 'etf_concentration': 89.9, 'cash_concentration': 6.7, 'concentration_warning': 'danger' } NrStockETFr <danger(warningok)max_single_holdingstock_concentrationetf_concentrationcash_concentrationconcentration_warningzRisk metrics: )r#rr weight_pctr(r'rr) rallocation_by_type max_holding max_weightr" stock_concetf_conc cash_concr1metricss rget_risk_metricsz AnalyticsEngine.get_risk_metricses*"88:  ~~.. -G!!J.$// %nn  - (++GS9 %))%5&**637  ?G "_GG$/ ";#-!)"+%,    ~gY/0rnc |t|jjdd}|d|}|Dcgc]R}|j|j|j |j |j|j|jdT}}tjd|d|Dcgc]}|d c}|Scc}wcc}w) a Get top N holdings by value. Args: n: Number of top holdings to return Returns: List of holding dictionaries sorted by current_value (descending) Example: [ {'ticker': 'VWCE.MI', 'current_value': 126240, 'weight_pct': 70.2}, {'ticker': 'MEUD.MI', 'current_value': 35325, 'weight_pct': 19.6}, ... ] c|jS)N)r)hs rz2AnalyticsEngine.get_top_holdings..s !//rTkeyreverseN)r(namerrr8 pnl_amount pnl_percentzTop z holdings: r() sortedrrr(rIrrr8rJrKrr)rrAsorted_holdingstop_nrDresults rget_top_holdingsz AnalyticsEngine.get_top_holdingss$! NN # #)  #  ((ll!"llll }}       tA3k*G11X;*G)HIJ   +Hs AB4 B9c|jjd}td|D}tj d||S)zc Get total dividends received. Returns: Total dividend amount DIVIDEND)tx_typec3&K|] }|d yw)amountN).0txs r z6AnalyticsEngine.get_total_dividends..s8RBxL8szTotal dividends: )rget_transactionssumrr)r transactionstotals rget_total_dividendsz#AnalyticsEngine.get_total_dividendssD77 7K 8<88 (01 rc L|jj}|jj}|jj\}}|j }|jj }||z}||||||||d}t jd||S)a0 Get comprehensive performance summary. Returns: Dictionary with performance metrics: - total_invested - current_value - total_pnl_amount - total_pnl_percent - total_dividends - cash_position - unrealized_pnl - total_return (includes dividends) Example: { 'total_invested': 166469.0, 'current_value': 179705.0, 'total_pnl_amount': 13236.0, 'total_pnl_percent': 7.9, 'total_dividends': 520.0, 'cash_position': 12000.0, 'unrealized_pnl': 13236.0, 'total_return': 13756.0 # P&L + dividends } )total_investedrtotal_pnl_amounttotal_pnl_percenttotal_dividends cash_positionunrealized_pnl total_returnzPerformance summary: )rget_total_investedr get_total_pnlr^get_cash_positionrr) rr`rrJrKrcrdrfsummarys rget_performance_summaryz'AnalyticsEngine.get_performance_summarys6::<668 "&..">">"@ K22488: "O3 -* *!,.*((    ,WI67rc4|jj}|j}g}|jD]"\}}|dz |z}|j |||d$|j ddt jdt|d|S)a Get allocation data formatted for table display. Returns: List of allocation items with type/geography/value/weight Example: [ {'category': 'ETF', 'value': 161565, 'weight_pct': 89.9}, {'category': 'Stock', 'value': 6140, 'weight_pct': 3.4}, {'category': 'Cash', 'value': 12000, 'weight_pct': 6.7} ] r)categoryvaluer8c |dS)NrnrV)xs rrEz6AnalyticsEngine.get_allocation_table..$s !G*rTrFzAllocation table: z items) rrr#itemsappendsortrrlen)rr r9rOrr8rns rget_allocation_tablez$AnalyticsEngine.get_allocation_tablesnn446 !88:&8&>&>&@  "J #%4E MM&(    ,d ; )#f+f=> rc>t|jj}td|jjD}|dkDrd|z nd}t d|dz dz}|dk\rd}n|dk\rd }n |d k\rd }nd }||||d }t j d||S)aY Calculate simple diversification score. Returns: Dictionary with diversification metrics: - holdings_count: number of holdings - effective_holdings: effective number (weighted by allocation) - diversification_score: 0-100 (higher is better) - assessment: 'Poor', 'Fair', 'Good', 'Excellent' Example: { 'holdings_count': 4, 'effective_holdings': 2.1, 'diversification_score': 42, 'assessment': 'Fair' } c3@K|]}|jdz dzyw)rN)r8)rWrDs rrYz.?sM1<<#%!+MsrrgQ8&@K Excellent2GoodFairPoor)holdings_counteffective_holdingsdiversification_score assessmentzDiversification score: )rtrrr[minrr)rrhhirscorerrOs rget_diversification_scorez)AnalyticsEngine.get_diversification_score)s&T^^445MT^^5L5LMM(+aQWQC,q0E9: B;$J b[J b[JJ-"4%*$    .vh78 rN))__name__ __module__ __qualname____doc__rrstrfloatr#r*rr@intrrPr^rkrurrVrrrrs3 %S%Z(84T#u*-=<6$sCx.6p*#*d4j*X U 0c3h0dd4jB/4S>/rr) rloggingtypingrrr getLoggerrrr&rrVrrrs` ""   8 $   "yyr