;+idZddlZddlmZddlmZddlmZmZddlZddl m Z ddl m Z ddlmZddlmZGd d Zy) z Market Radar Module Tracks and visualizes market-level trends for Warren AI. This module aggregates analysis results across all stocks in a market, calculates market sentiment, and generates trend visualizations. N)datetime)BytesIO)DictList)logger)DatabaseManager)MarketSnapshotc eZdZdZdefdZdedeeddfdZ deedefd Z ded eddfd Z d)d eedefd Z d eedeeeffdZ dedefdZdedefdZdedefdZdedefdZded eedefdZdedeedefdZdeeefdefdZdededededef dZded eedefdZded eedefdZded eedefd Z d)d!ed"ed#ed$ed%eedef d&Zd'edefd(Zy)* MarketRadaraj Market-level trend analysis and visualization engine. Responsibilities: - Calculate market aggregate metrics (avg discount, sentiment, sector breakdown) - Store historical snapshots in database - Generate matplotlib visualizations (trend charts, sector breakdown) - Produce HTML report blocks for injection into warren_scan reports db_managerc<||_tjdy)z Initialize Market Radar. Args: db_manager: DatabaseManager instance for data persistence zMarket Radar initializedN)r rinfo)selfr s =/mnt/ssd/data/python-lab/Trading/src/analysis/market_radar.py__init__zMarketRadar.__init__!s% ./ market_coderesultsreturnNcz|stjd|dy |j|} |j ||tj d|y#t$r%}tjd|d|Yd}~yd}~wwxYw#t$r%}tjd|d|Yd}~yd}~wwxYw)a Calculate market metrics and store snapshot in database. This method performs ONLY data calculation and persistence. No HTML generation or visualization. Used during market scanning. Args: market_code: Market identifier (IT, FR, DE, USA) results: List of analysis results from warren_scan Each dict must contain: 'ticker', 'price', 'fair_value', 'sector' zMarket Radar: No results for z, skipping snapshotNz,Market Radar: Error calculating metrics for : u✅ Market snapshot stored for z(Market Radar: Error saving snapshot for )rwarning_calculate_market_metrics Exceptionerror_save_snapshotr)rrrmetricses rstore_snapshotzMarketRadar.store_snapshot+s NN:;-GZ[ \  44W=G  X    W 5 KK9+G H  LLG }TVWXVYZ [   X LLCK=PRSTRUV W W Xs.A*B  B $BB  B:B55B:c  t|}g}d}d}i}|D]}|jdd}|jdd} |jdd} | dkDr | |z | z dz} nd} |j| | dkDr|dz }n | dkr|dz }| |vrg|| <|| j| |rt|t|z nd} |j D cic]\} } | t| t| z }} } |Dcgc]P}|jd d |jdd|jddz |jddz dzd R}}t |d d dd}|t | d||||dScc} } wcc}w)aH Calculate aggregate market metrics from individual stock results. Args: results: List of stock analysis results Returns: Dict with keys: ticker_count, avg_discount_pct, undervalued_count, overvalued_count, sector_breakdown, top_opportunities rprice fair_valuesectorUnknowndtickerN/A)r( discount_pctc |dS)Nr*xs rz7MarketRadar._calculate_market_metrics..s !N+rTkeyreverseN) ticker_countavg_discount_pctundervalued_countovervalued_countsector_breakdowntop_opportunities)lengetappendsumitemssortedround)rrr5 discounts undervalued overvaluedsector_discountsitemr!r"r#r*r6valuesr9results_with_discountr:s rrz%MarketRadar._calculate_market_metricsJs7|     :DHHWa(E,2JXXh 2FA~!+e!3z ASH "   \ *aq !a --+- ( V $ + +L 91 :6?H3y>C N:S #3"8"8":  CK#f+- -   !  ((8U3"&((<";dhhwPQ>R"R"&((<";"AFrc~|jj5}ttj||d|d|d|dt j |dt j |d}|j|tjd|d |dd d d d d y #1swYy xYw) z Save market snapshot to database. Args: market_code: Market identifier metrics: Calculated market metrics dict r5r6r7r8r9r:)datemarketr5r6r7r8r9r:zMarket snapshot saved for rz.1fz % discountN) r get_sessionr rutcnowjsondumpsaddrr)rrrsessionsnapshots rrzMarketRadar._save_snapshots__ ( ( * qg%__&"$^4!();!<")*=">!();!>y'R  "%hoo')*<< "" WK }}[1HW')O446 '#MM.9#VN$9$9[$HI#VN$7$7;$FG%Xn&9&9: SU (%4D+22(, 484I4I4  KK"8 ROH\G]]s tu"&"C"CKQ`"aK#'#E#EhOaFb#cL$(#E#E#X{L$L)// =?" WL44 +-?AQ05  nnZ0 NN=1 9o +-  8+ t45*)E:)  #DD  j#8 &A % &k39 &Q % & KK> @ KK,ZL9 : KK- }5 6z? "[  LLLQCP Q    4!WLL#B;-rRSQT!UVVW: & & & &   LL>qcB C  sAOP 4B0O<%A8P 2QP;Q(Q:AQ O9O44O9<P P  P8P33P8;QQQ Q Q:Q55Q:c i}|jj5}|D]}|jtj |j tj jj}|r tj|j}tj|j}|j |j |j"|j$|j&|j(||d||<d||< ddd|S#tj$r)}tjd|d|i}g}Yd}~d}~wwxYw#1swY|SxYw)u Retrieve latest snapshot for each market from database. Args: markets: List of market codes Returns: Dict mapping market_code → snapshot data (or None if no data) )rKzInvalid JSON in snapshot for rN)rKrJr5r6r7r8r9r:)r rLrur filter_byrwrJdescfirstrNloadsr9r:JSONDecodeErrorrrrKr5r6r7r8) rrSrrQrrcr9r:rs rrjz!MarketRadar._get_latest_snapshots*sP __ ( ( * 2g& 2 MM.1YkY2Xn116689UW /+/::f6M6M+N(,0JJv7O7O,P)#)-- & (.(;(;,2,C,C-3-E-E,2,C,C,<-> .Ik*.2Ik*; 2 2@% /// 'D[MQSTUSV%WX+-(,.)/ 2@s7A&E>DAEE2EEEEE(r6c Pd|dzz}tdtdt|S)a8 Convert discount percentage to sentiment score (0-100). Formula: score = 50 + (avg_discount_pct * 5) Clamped to [0, 100] Args: avg_discount_pct: Average discount percentage Returns: int: Sentiment score (0=max bearish, 100=max bullish) 2rr%)maxminint)rr6scores r_calculate_sentiment_scorez&MarketRadar._calculate_sentiment_scoreXs,&*+1c#s5z*++rc(|dkDry|dkDry|dkDryy)z Get sentiment label and color based on discount percentage. Args: avg_discount_pct: Average discount percentage Returns: Tuple of (sentiment_label, sentiment_emoji, color_code) )Bullishu🟢#27ae60r)zNeutral-Bullishu🟡#f39c12)zNeutral-Bearishu🟠#e67e22)Bearishu🔴#e74c3cr,)rr6s r_get_sentiment_labelz MarketRadar._get_sentiment_labelhs( b 1  !9  #91rr9cD|syt|jd}|S)z Find sector with highest average discount percentage. Args: sector_breakdown: Dict of {sector: avg_discount_pct} Returns: Tuple of (sector_name, discount_pct) or ("N/A", 0.0) )r)r&c |dSNr'r,r-s rr/z-MarketRadar._get_top_sector..s !A$rr1)rr?)rr9tops r_get_top_sectorzMarketRadar._get_top_sector{s% "((*? rc8ddddd}|j|d|fS)z Get market display name and flag emoji for a market code. Args: market_code: Market code (IT, FR, DE, USA) Returns: Tuple of (flag_emoji, full_name) )🇮🇹zItaly (FTSE MIB))🇫🇷zFrance (CAC 40))🇩🇪z Germany (DAX))🇺🇸zUnited States (S&P 500)rWu🌍)r<)rrMARKET_METADATAs r_get_market_metadataz MarketRadar._get_market_metadatas131/:   "";0EFFrrc g}g}g}ddddd}|D]}|j|}|s|j|j|||d} |j| | dkDr|jde| d kDr|jd || d kDr|jd |jd |sCtjd\} } | j dddddd| j dnFtjd\} } | j |||ddd} | jd ddddd | D]W} | j}| j | j| jd!z z||d"d#d|d k\rd$nd%d&d'(Y| jd)d*d&+| jd,dd&d-.| jd/d0d1d2d d3lm}|d4d5d5ddd67|d4d5d5d dd87|d4d5d5d dd97|d4d5d5d dd:7g}| j!|d;dtj"t%}tj&|d?d@dAB|j)d t+j,|j/j1dC}|j3tj2| |S)Dz Generate grouped bar chart comparing discount % across markets. Args: snapshots: Dict of market snapshots markets: List of market codes Returns: Base64-encoded PNG image u🇮🇹 Italyu🇫🇷 Franceu🇩🇪 Germanyu 🇺🇸 USArWr6rrrrrrr)rfigsize?zNo market data availablecenter)havafontsizeoff) 皙?black?coloralpha edgecolor linewidthgray--r4ffffff? Equilibriumyr linestylerrlabel@+.1f%bottomrboldr)rr fontweightrAverage Discount (%)rrz.Global Market Valuations - Discount ComparisonrrpadTr333333?axisrr) Rectangle)rrr'zBullish (>10%))fcrrzNeutral-Bullish (0-10%)zNeutral-Bearish (0 to -10%)zBearish (<-10%)z upper right ?)handleslocr framealphapngxtightformatdpi bbox_inchesrf)r<r=pltsubplotstextrbaraxhline get_heightget_x get_width set_ylabel set_titlegridmatplotlib.patchesrlegend tight_layoutrsavefigseekbase64 b64encodereaddecodeclose)rrrS market_namesrBcolors MARKET_NAMESrrRdiscountfigaxbarsrheightrlegend_elementsbuffer image_base64s rrsz(MarketRadar._generate_consolidated_charts  ##$!  # -K }}[1H##L$4$4[+$NO#$67  *b=MM),\MM),^MM),MM), -"ll73GC GGC8X(]_G ` GGENll73GC66, sV]il6mD JJ&DASXeJ f 7) cmmob&88& a("6Q;xE"(27 7 MM02&M Q LLITVcioqL r GGDs#G > 5%! DTU%! D]^%! Dab%! DUV O IIo=2Z]I ^     F5cwG A'' 6==gF   #rhistorical_snapshotsc z|rt|dkry |Dcgc]}|d }}|Dcgc]}|d }}tjd\}}|j||ddd d d |j d ddddd|j ||d |Dcgc]}|d k\ c}dddd|j ||d |Dcgc]}|d k c}dddd|j |\} } |jddd|jddd|j| d| d d!dd"#|jddd$|jd%d&d'(d d)l m } |jj| j!d*|jj#| j%tj&d+d,-tj(t+} tj,| d.d/d01| j/d t1j2| j5j7d2} | j9tj8|| Scc}wcc}wcc}wcc}w#t:$r:}t=j>d3|d4|tj8d5Yd)}~yd)}~wwxYw)6a  Generate historical trend line chart for a single market. Args: market_code: Market code (IT, FR, DE, USA) historical_snapshots: List of dicts with keys 'date' and 'avg_discount_pct' Returns: Base64-encoded PNG image r4rJr6)rrrz#3498dbg@orzAvg Discount %)rrmarker markersizerrrrrzEquilibrium (0%)rrrT Undervalued)whererr interpolaterr OvervaluedrrrrDate z" - Historical Trend (Last 90 Days)rrr)rrbestrr)rrrNz%d/%m-right)rotationrrrrrrfz#Failed to generate trend chart for rrx) r;rrplotr fill_betweenrr set_xlabelrrrmatplotlib.datesdatesxaxisset_major_formatter DateFormatterset_major_locatorAutoDateLocatorxticksrrrrrr r r r rrr)rrrrRr,rBrrdflag market_namemdatesrrrs rryz(MarketRadar._generate_market_trend_charts$s+?'@1'D3 6JK(Xf%KEKFZ[("45[I[ll73GC GGE9IS]^fvG w JJ&DASXjJ k OOE9a2;! . HH ( ()=)=g)F G HH & &v'='='? @ JJw /    YF KKu#7 K KKN!++FKKM:AA'JL LLN IIcN YL["="<:  LL>{m2aSQ R IIe  sKI7 I#I7 I(AI7 I-I7; I2FI7#I77 J:0J55J:c |sy t|jdd}|Dcgc]}|d }}|Dcgc]}|d }}g}|D]*}|dkDr|jd|jd,tjd t d t |d zf \}} | j|||d dd} | jdddddtt| |D]n\} \} } | j}| j|| j| jdz z|dd|dk\rdnddddt|dkDrdnd p| j!d!d"d#| j#d$d%dd&'| j%dd(d)d*tj&t)}tj*|d+d,d-.|j-dt/j0|j3j5d/}|j7tj6||Scc}wcc}w#t8$r7}t;j<d0|tj6d1Yd2}~yd2}~wwxYw)3z Generate horizontal bar chart for sector breakdown of a single market. Args: sector_breakdown: Dict of {sector: avg_discount_pct} Returns: Base64-encoded PNG image rc |dSrr,r-s rr/z;MarketRadar._generate_market_sector_chart..Is AaDrTr0rr'rrrrrrrrrrrrr4r)r.rrrrrrrleftr&rrrrwhite)rrrrrrrrzSector Breakdownrrrr.rrrrrrrfz!Failed to generate sector chart: rxN)r@r?r=rrrr;barhaxvline enumerateziprrget_yrabsr*rrrrrrrr r r r rrr)rr9sectors_sortedrFsectorsrBrrrrrirvaluewidthrrrs rrzz)MarketRadar._generate_market_sector_chart:sc 6 #$4$:$:$<.Z^_N+9:4tAw:G:-;    YF KKu#7 K KKN!++FKKM:AA'JL LLN IIcN ];<^  LLNrr(r)r*r&z$
  • z<: rz %
  • zz:

    No opportunities identified

    z0

    r#a'

    Market Sentiment

    z (Score: a)

    Average Discount

    Stocks Analyzed

    r5uO

    Valuation Split

    ▲ r7uH | ▼ r8a?

    Top Sector

    razm%)

    u

    📈 Historical Trend (Last 90 Days)

    Trend Chart
    u

    🏭 Sector Breakdown

    Sector Chart
    u

    💎 Top Investment Opportunities

    z+
    )rrrrr<join)rrrRrFrGr4r5sentiment_labelsentiment_emojirsentiment_scoretop_sector_nametop_sector_discountopportunities_htmloppr(rhtmls rr{z)MarketRadar._generate_market_section_htmlsS&!55kBk262K2KHUgLh2i/%99(CU:VW/3/C/CHM_D`/a,,  ' (!W  34Ra8 n5177>37"(LVHUQRZ[_Q``m'nn" n ' ) !] ; ; 0; 0;};  =*; +0; 1; f; ; "]; #(; .).w/; ./]/; 8^c\c9; 8d9; :--;; :./;; :0?.?;; :@I;; :JYHY;; :Z];; D^c\cE; DdE; F&&89$?G; F@G; R&n56S; R7?S; ^@HH[?\>]_; ^^?_; `@HHZ?[>\a; `]a; l--m; l.0m; l1DD/Im; lJ m; |   12A0AB   D   12B0BC   D $$%    rcdddddddddd d dd d d dd}g}|D]}|j|}|j|d||d}|r|j|d\}} } |j|d} |j|d\} } |ddkDrd}n|ddkDrd}n |ddkDrd}nd}| }|dj d}|j d|d|dd|dd |d!d"| d#|d$|dd%d&| d'| d%d(| d)|d*|d+d,|d-|j d.|dd|dd |d!d/d0dj |d1S)2z Generate HTML comparison table for all markets. Args: snapshots: Dict of market snapshots markets: List of market codes Returns: HTML table string rrXFTSE MIB)r4name full_namerrYCAC 40rrZDAX 40rr[ Dow Jones 30rWrr6r9rz#d4eddarz#fff3cdrz#ffe5d0z#f8d7darJz %d/%m %H:%Mz( r4r#rTzE
    rUz zn rz% raz%) z z r5zy z, z u N/A N/A N/A ⚠️ No Data - - ak z/
    Market Sentiment Score Discount % Top Sector Status Tickers Last Updated
    )r<rrrrmr=rI)rrrS MARKET_INFOrowsrrRrrJrKrrL top_sectorrNrow_bgdiscount_color last_updateds rrrz+MarketRadar._generate_comparison_table_htmlsN&t*M%t(K%t(K&NS  ": K }}[1H??;[_j0klD:>:S:ST\]oTp:q7%"&"A"A(K]B^"_262F2FxPbGc2d/ /./"4&F01A5&F01C7&F&F!&'/88G  !((.x0!!%faV ~>88<[8I7JKccrbstiiwhxy!"45d;<$ B':4&@A))?2CDccklzc{b|}tuAtBB#, !!"&faV ~>88<[8I7J K  [: x   rc Xddddddddddd dd }i}d }d }g}|D]}|j|} |j|||d} | r|d z }|j|| d f|j| d \} } } |j| d } |j | d\}}|| d| d| dj | dt | d d| d| d| | d| d| d|t |ddd||<|d z }d||<|rtd|Dt|z nd}|rt|dd nd}|rt|d d nd}tjj d!z|||t |d||d"d#}tj|dd$%}d&|d'S)(z Generate AI-readable JSON data block. Args: snapshots: Dict of market snapshots markets: List of market codes Returns: JSON script tag with structured data rSzBorsa Italiana)rTrUrVzEuronext ParisrWzFrankfurt Stock ExchangerXzNew York Stock ExchangerWrr'r6r9rTrUrJr5r4r7r8)r5r6r7r8rLrJr:)rTr6)rr5rU snapshot_daterr9r:r[Nc3&K|] }|d yw)r'Nr,).0r3s r z6MarketRadar._generate_ai_json_block..s!>1!A$!>sr&c |dSrr,r-s rr/z5MarketRadar._generate_ai_json_block.. !rrr)c |dSrr,r-s rr/z5MarketRadar._generate_ai_json_block..rerZ)markets_scannedmarkets_missingglobal_avg_discount most_bullish most_bearish) generated_atrSsummaryF)indent ensure_asciiz3 )r<r=rrr isoformatrAr>r;rrrrMrNrO)rrrSrY markets_datarhri all_discountsrrRrrJ_rLr[rNrjrkrldatajson_strs rrqz#MarketRadar._generate_ai_json_blockwsP&4DE#2BC#2LM*9RS    "" 1K }}[1H??;S^0_`D1$$$k8 !>>]ASSjmDQs=n=a@W\ DQs=n=a@W\ %OO-779C?##2#2',-@!'D , ,   ::d15A   rc g}|Dcgc]}|j|s|||df }}|rt|d\}}t|d\}} |jd|d|dd|jd |d| dd|D]} |j| } | s| dd ks | d j D cic]\} } | d kDs | | }} } |sOt|j d }|jd |d d| d|dddn|Dcgc]}|j|r|}}|rI|jdt |t |z dt |ddj |dn|jdt |dddj |dScc}wcc} } wcc}w)z Generate key insights bullet points. Args: snapshots: Dict of market snapshots markets: List of market codes Returns: HTML with insights r6c |dSrr,r-s rr/z8MarketRadar._generate_summary_insights.. VWXYVZrrc |dSrr,r-s rr/z8MarketRadar._generate_summary_insights..ryrz*
  • Most Bullish Market: rarz% avg discount)
  • z*
  • Most Bearish Market: rr9c |dSrr,r-s rr/z8MarketRadar._generate_summary_insights..s TUVWTXrz/
  • Cross-Market Opportunity: z sector in z shows r'z/% discount while market overall is bearish
  • z$
  • Data Coverage: /z markets scanned successfully (z, z& missing - check data collection)
  • z/
  • Data Coverage: 100% - All z" markets scanned successfully
  • u

    💡 Key Insights

      rz*
    )r<rrr=r?r;rI)rrrSinsightsmrBmost_bullish_marketmost_bullish_discountmost_bearish_marketmost_bearish_discountrrRkvundervalued_sectorsr[missings rrtz&MarketRadar._generate_summary_insightssfEL`qy}}]^O_a1&89:` ` 9Y]]1-=1>>  OOB3w/F>rrrrrbc tjjd}d}|rddj|d}d|d|d|d|d |d |d S) a Build complete standalone HTML file with global comparison and individual market sections. Args: ai_json_block: JSON script tag comparison_table: HTML table consolidated_chart: Base64 chart image summary_insights: Insights HTML market_sections: List of HTML strings for individual market sections Returns: Complete HTML document string z%d/%m/%Y %H:%M:%S UTCru

    🔍 Individual Market Deep Dive

    Detailed analysis for each market including historical trends, sector breakdowns, and top opportunities.

    z z Warren AI - Multi-Market Radar u

    📡 Warren AI - Multi-Market Radar AI-Ready Data

    Generated: u

    Analysis Type: Consolidated Multi-Market Value Screening

    🌍 Market Comparison Dashboard

    z u

    📊 Global Market Valuations

    Discount percentage comparison across all analyzed markets

    Market Discount Comparison Chart
    u
    )rrMrmrI)rrrrrrbrlrs rr|z$MarketRadar._build_consolidated_htmls oo(()@A "  (   !"#  $  OF,L-051  - .@,@A   wgg r output_pathctd}t|dd5}|j|ddd|S#1swY|SxYw)z;Generate placeholder HTML when no market data is available.u| Warren AI - No Data

    📡 Market Radar - No Data Available

    No market snapshots found in database. Please run market scans first.

    rerfrgN)r}r~)rrrQrs rroz)MarketRadar._generate_no_data_placeholders@ +sW 5  GGDM  s-7)N)__name__ __module__ __qualname____doc__rrrprrrrrrrjfloatrrtuplerrrrsryrzr{rrrqrtr|ror,rrr r s_0?0X#XT XtX>J dJ J Xq#qqq.{DI{{z,T#Y,4T ?,\,5,S, 2U2u2& GGG$WdWT#YWSVWrBBSWX\S]BbeBHCd3:>NCSVCJTTT T  T Tl` ` S ` VY` DRRS RcRh3 D3 493 QT3 n?CIcISI58ILOI26s)IGJIVrr )rrNriortypingrrrmatplotlib.pyplotpyplotrlogurursrc.database.db_managerrsrc.database.modelsr r r,rrrs3  3.EEr