:3i%dZddlZddlmZmZddlmZmZmZm Z ddl m Z eje Z eGddZGdd Zy) zZ Portfolio Core Module. Manages portfolio holdings, calculations (P&L, weights, values). N) dataclassfield)ListDictTupleOptional)datetimeceZdZUdZeed<eed<eed<eed<eed<dZeeed<d Z eed <d Z eed <d Z eed <d Z eed <dZ eeed<ddZdefdZy)Holdingz-Represents a single holding in the portfolio.tickername asset_typequantity avg_priceN current_price current_value pnl_amount pnl_percent weight_pct last_updatedreturncr|j|jdkDrp|j|jz|_|j|jz}|j|z |_|dkDr|j|z dz|_yd|_y|j|jz|_d|_d|_y)z Calculate current value and P&L.Nrdr)rrrrrr)selfinvesteds @/mnt/ssd/data/python-lab/portfolio-manager/src/core/portfolio.pycalculate_valueszHolding.calculate_valuess    )d.@.@1.D!%1C1C!CD }}t~~5H"008;DO!|$(OOh$>##E #& "&!?D !DO"D c |j|j|j|j|j|j |j |j|j|jd S)z*Convert holding to dictionary for display. r r rrrrrrrrr!rs rto_dictzHolding.to_dict0sZkkII// !//!////++//  rrN)__name__ __module__ __qualname____doc__str__annotations__floatrrrrrrrr rrr#rrr r su7 K IOO%)M8E?)M5JKJ'+L(8$+#"    rr c eZdZdZdZdeefdZdefdZ defdZ de eeffdZ dd Z d eeefddfd Zd ed ededededdf dZd edeefdZd edededdfdZdeefdZdefdZdedeefdZdefdZy) Portfolioz,Manages portfolio holdings and calculations.c<||_g|_d|_d|_y)za Initialize portfolio. Args: db_manager: DBManager instance rN) db_managerholdings _total_value_total_invested)rr0s r__init__zPortfolio.__init__Cs"%') #&&)rrc |jj}g|_|D]}t|d|dxs|d|dxsd|d|d|d|j drt j |dnd }|j|jj||jtjd t|jd |jS) zd Load holdings from database. Returns: List of Holding objects r r rUnknownrrrrN)r r rrrrrzLoaded z holdings from database) r0get_all_holdingsr1r getr fromisoformatrappendcalculate_weightsloggerinfolen)r holdings_datadataholdings r load_holdingszPortfolio.load_holdingsOs88:  ! *DH~&\3T(^ -:j){+"?3MQXXVdMeX33D4HIkoG  $ $ & MM  ) *   gc$--011HIJ}}rc\td|jD|_|jS)zt Calculate total portfolio value. Returns: Total current value of all holdings c34K|]}|jywNr.0hs r z,Portfolio.get_total_value..ssGAG)sumr1r2r"s rget_total_valuezPortfolio.get_total_valuels' GGG   rc\td|jD|_|jS)u Calculate total amount invested. Returns: Total amount invested (qty × avg_price for all holdings) c3NK|]}|j|jzywrE)rrrGs rrJz/Portfolio.get_total_invested..}s"S1:: #;"Ss#%)rLr1r3r"s rget_total_investedzPortfolio.get_total_investedvs' #"ST]]"SS###rcz|j}|j}||z }|dkDr ||z dz}||fSd}||fS)zg Calculate total P&L. Returns: Tuple of (pnl_amount, pnl_percent) rrr)rMrP)r total_valuetotal_investedrrs r get_total_pnlzPortfolio.get_total_pnls`**, 002 >1 A %6#=KK((KK((rNc|j}|dkDr)|jD]}|j|z dz|_n|jD] }d|_ tj dt |jdy)z-Calculate weight percentage for each holding.rrrzCalculated weights for holdingsN)rMr1rrr<debugr>)rrRrAs rr;zPortfolio.calculate_weightss**, ?== Q&-&;&;k&IS%P" Q == )%(" )  .s4==/A.B)LMrpricesc  d}|jD]}|j|vs|j}||j|_|j|jj |j|j|j |dz }tjd|jd|d|j|jtjd|dt|jd y ) z{ Update current prices for holdings. Args: prices: Dictionary mapping ticker -> price r)rrzUpdated price for z: z -> zUpdated prices for /rVN) r1r rrr0update_holdingrr<rWr;r=r>)rrX updated_countrA old_prices r update_priceszPortfolio.update_pricess }} lG~~'#11 (.w~~(>%((*..NN")"7"7")"7"7/ "  1'..1AI;dSZShShRijk l"   )-#dmm:L9MYWXrr r rrrc |jj|||||t|||||}|j|jj ||j tjd|d|d|dy)a Add a new holding to the portfolio. Args: ticker: Stock ticker symbol name: Full name asset_type: 'ETF', 'Stock', or 'Cash' quantity: Number of shares avg_price: Average purchase price )r r rrrzAdded new holding: z (z @ )N) r0 add_holdingr rr1r:r;r<r=)rr r rrrrAs rrbzPortfolio.add_holdings& ##FD*h R!     " W%   )&H:S 1MNrcL|jD]}|j|k(s|cSy)z Get a specific holding by ticker. Args: ticker: Ticker to find Returns: Holding object or None if not found N)r1r )rr rAs r get_holdingzPortfolio.get_holdings-}} G~~' r new_quantity new_avg_pricec|j|}|rv||_||_|j|jj ||||j |jtjd|d|d|yy)z Update holding quantity and average price. Args: ticker: Ticker to update new_quantity: New quantity new_avg_price: New average price )rrrzUpdated holding z: qty=z , avg_price=N) rdrrrr0r\rr;r<r=)rr rerfrAs rupdate_holding_quantityz!Portfolio.update_holding_quantitys""6* +G  -G   $ $ & OO * *%'%33 +   " " $ KK*6(&lS`Rab c! rc\|jDcgc]}|jc}Scc}w)zx Get holdings summary for table display. Returns: List of holdings as dictionaries )r1r#)rrIs rget_holdings_summaryzPortfolio.get_holdings_summary s"&*]]3 333s)c|jDcgc]}|jdk(s|}}td|DScc}w)zZ Get total cash position. Returns: Total cash amount Cashc34K|]}|jywrErFrGs rrJz.Portfolio.get_cash_position..s:q1??:rK)r1rrL)rrI cash_holdingss rget_cash_positionzPortfolio.get_cash_positions=%)MMLqQ\\V5KL L:M:::Ms<<c`|jDcgc]}|j|k(s|c}Scc}w)z Get holdings filtered by asset type. Args: asset_type: 'ETF', 'Stock', or 'Cash' Returns: List of holdings matching the type )r1r)rrrIs rget_holdings_by_typezPortfolio.get_holdings_by_type s' ==GaALLJ,FGGGs++c|j}|j}|j\}}|j}|||||t |j dS)zv Get comprehensive portfolio summary. Returns: Dictionary with portfolio metrics )rRrSrrcashholdings_count)rMrPrTror>r1)rrRrSrrrss rget_portfolio_summaryzPortfolio.get_portfolio_summary,sh**, 002"&"4"4"6 K%%'',$&!$--0   rr$)r%r&r'r(r4rr rBr+rMrPrrTr;rr)r_rbrrdrhrjrorqrur,rrr.r.@s56 *tG}:!!$E$)uUE\2)& NYDe$4YY<"O"O"O "O  "O  "O "OH # (7*; dcddW\daed84d4j4;5; Hs HtG} H t rr.)r(logging dataclassesrrtypingrrrrr getLoggerr%r<r r.r,rrrzsS (..   8 $ - -  - `  r