Ë
    l§jFG  ã                  óÈ   — d dl mZ d dlmZ d dlmZ d dlZd dlZd dlZ	d dl
ZddlmZ dZ ed¬	«       G d
„ d«      «       Zdd„Zdd„Zdd„Z	 	 	 	 	 	 dd„Zdd„Zdd„Zdd„Zy)é    )Úannotations)Ú	dataclass)ÚPathNé   )ÚSettingsz0.1.0T)Úfrozenc                  ó6   — e Zd ZU ded<   ded<   ded<   ded<   y)ÚHotspotBuildResultÚintÚ
industriesÚhot_industriesÚ
candidatesr   Úoutput_pathN)Ú__name__Ú
__module__Ú__qualname__Ú__annotations__© ó    ú6C:\FundamentalWorkbench\src\fundamental_mvp\hotspot.pyr
   r
      s   … àƒOØÓØƒOØÔr   r
   c                óP   — t        j                  | d¬«      j                  d¬«      S )NÚcoerce)ÚerrorsT)Úpct)ÚpdÚ
to_numericÚrank)Úseriess    r   Ú	_pct_rankr      s!   € Ü�=‰=˜¨Ô1×6Ñ6¸4Ð6Ó@Ð@r   c                ó˜  — |t        j                  d¬«      z
  j                  «       j                  «       }t	        | j
                  «      j                  dd«      }d|› d|› d|j                  «       j                  «       › d�}t        j                  «       5 }|j                  |«      j                  «       cd d d «       S # 1 sw Y   y xY w)	Né–   )ÚdaysÚ'z''zÆ
    WITH recent AS (
      SELECT source_code, trade_date, close_qfq, amount_yuan,
             row_number() OVER (PARTITION BY source_code ORDER BY trade_date DESC) AS rn
      FROM read_parquet('z#')
      WHERE trade_date >= DATE 'z' AND trade_date <= DATE 'a‘  '
        AND close_qfq > 0
    ), points AS (
      SELECT source_code,
        max(CASE WHEN rn=1 THEN trade_date END) latest_trade_date,
        max(CASE WHEN rn=1 THEN close_qfq END) close_latest,
        max(CASE WHEN rn=6 THEN close_qfq END) close_5,
        max(CASE WHEN rn=11 THEN close_qfq END) close_10,
        max(CASE WHEN rn=21 THEN close_qfq END) close_20,
        max(CASE WHEN rn=61 THEN close_qfq END) close_60,
        avg(amount_yuan) FILTER (WHERE rn<=5) amount_avg_5,
        avg(amount_yuan) FILTER (WHERE rn<=20) amount_avg_20,
        avg(close_qfq) FILTER (WHERE rn<=20) close_ma20,
        max(close_qfq) FILTER (WHERE rn<=60) close_high_60,
        count(*) FILTER (WHERE rn<=61) observations
      FROM recent GROUP BY source_code
    )
    SELECT *,
      (close_latest/close_5-1)*100 AS return_5d,
      (close_latest/close_10-1)*100 AS return_10d,
      (close_latest/close_20-1)*100 AS return_20d,
      (close_latest/close_60-1)*100 AS return_60d,
      amount_avg_5/nullif(amount_avg_20,0) AS amount_ratio_5_20,
      close_latest/close_ma20-1 AS above_ma20_pct,
      close_latest/close_high_60 AS high_60_ratio
    FROM points
    )r   Ú	TimedeltaÚdateÚ	isoformatÚstrÚdaily_price_pathÚreplaceÚduckdbÚconnectÚexecuteÚfetchdf)ÚsettingsÚ	asof_dateÚstartÚpathÚqueryÚ
connections         r   Ú_load_recent_stock_metricsr4      s±   € ØœŸ™¨3Ô/Ñ/×5Ñ5Ó7×AÑAÓC€EÜˆx×(Ñ(Ó)×1Ñ1°#°tÓ<€Dðð ˜ð  !Ø!& Ð'AÀ)Ç.Á.ÓBR×B\ÑB\ÓB^ÐA_ð `ð€Eô@ 
�‰Ô	˜ZØ×!Ñ! %Ó(×0Ñ0Ó2÷ 
×	Ò	ús   ÂC Ã C	c                ó  — t        j                  |j                  dz  dz  j                  d¬«      «      }|d   }| j                  | d   j                  «       | d    z  | d   j                  d	«      z     j                  «       }|d
   j                  d«      |d<   |d   j                  d«      |d<   |d   j                  d«      |d<   |d   j                  d«      |d<   |g d¢   j                  d«      j                  d¬«      dz  |d<   g d¢}|j                  |d¬«      j                  ddddd d!d"d#d$d%d&d'¬(«      j                  «       }|d)   j                  t        |d*   «      «      |d+<   t        j                  |d,   j!                  d¬-«      «      |d.<   t#        j$                  t'        |d/   «      t'        |d0   «      t'        |d1   «      gd¬«      j)                  d¬«      t+        |d2   «      z  |d3<   t#        j$                  t'        |d4   «      t'        |d5   «      t'        |d6   «      gd¬«      j)                  d¬«      t+        |d7   «      z  |d8<   t#        j$                  t'        |d9   «      t'        |d.   «      gd¬«      j)                  d¬«      t+        |d:   «      z  |d;<   t#        j$                  t'        |d   «      t'        |d0   «      t'        |d1   «      gd¬«      j)                  d¬«      t+        |d<   «      z  |d=<   |g d>¢   j                  d¬«      |d?<   t        j,                  |j                  |d+    d?f<   |d?   j/                  dd@¬A«      j1                  dB«      |dC<   |dD   j/                  dd@¬A«      j1                  dB«      |dE<   |d0   j/                  dd@¬A«      j1                  dB«      |dF<   |d?   j                  t+        |dG   «      «      |dH<   t2        |dI<   |j5                  d?ddJ¬K«      S )LNÚconfigúfundamental_rules.tomlúutf-8©ÚencodingÚhotspotÚindustry_l2_codeÚis_st_currentÚobservationsé=   Ú
return_20dr   Úis_positive_20dÚ
return_10dÚis_positive_10dÚabove_ma20_pctÚis_above_ma20Úhigh_60_ratiogffffffî?Úis_near_high60)Ú	return_5dr@   Ú
return_60dr   ©Úaxisé   Útrend_alignment)Úindustry_l1_codeÚindustry_l1_namer<   Úindustry_l2_nameF)Údropna)Úsource_codeÚsize)rH   Úmedian)rB   rT   )r@   rT   )rI   rT   )rC   Úmean)rA   rU   )rE   rU   )rG   rU   )Úamount_ratio_5_20rT   )Úamount_avg_20Úsum)rM   rU   )Úmember_countÚreturn_5d_medianÚreturn_10d_medianÚreturn_20d_medianÚreturn_60d_medianÚpositive_10d_ratioÚpositive_20d_ratioÚabove_ma20_ratioÚnear_high60_ratioÚamount_ratio_5_20_medianÚamount_avg_20_totalrM   rY   Úminimum_membersÚmember_coverage_okrc   )ÚlowerÚamount_scalerZ   r\   r]   Úmomentum_weightÚmomentum_scorer_   r`   ra   Úbreadth_weightÚbreadth_scorerb   Úactivity_weightÚactivity_scoreÚpersistence_weightÚpersistence_score)ri   rk   rm   ro   Úhotspot_scoreÚmin)Ú	ascendingÚmethodÚInt64Úhotspot_rankr[   Úmomentum_10d_rankÚmomentum_20d_rankÚhot_score_thresholdÚis_hotÚmodel_versionÚlast)rr   Úna_position)ÚtomllibÚloadsÚproject_rootÚ	read_textÚlocÚnotnaÚgeÚcopyÚgtrX   ÚgroupbyÚaggÚreset_indexr   ÚnpÚlog1pÚclipr   Úconcatr   rU   ÚfloatÚnanr   ÚastypeÚHOTSPOT_MODEL_VERSIONÚsort_values)Ústockr.   Úrulesr6   ÚeligibleÚgroup_columnsÚindustrys          r   Ú_score_industriesr—   D   sy  € Ü�M‰M˜8×0Ñ0°8Ñ;Ð>VÑV×aÑaÐkrÐaÓsÓt€EØ�9Ñ€FØ�y‰yØÐ Ñ!×'Ñ'Ó)Ø�Ñ!Ð
!ñ	"à
�Ñ
×
"Ñ
" 2Ó
&ñ	'ñ÷ �dƒfð	 ð
 #+¨<Ñ"8×";Ñ";¸AÓ">€HÐÑØ"*¨<Ñ"8×";Ñ";¸AÓ">€HÐÑØ (Ð)9Ñ :× =Ñ =¸aÓ @€Hˆ_ÑØ!)¨/Ñ!:×!=Ñ!=¸dÓ!C€HÐÑØ"*Ò+TÑ"U×"XÑ"XÐYZÓ"[×"_Ñ"_ÐefÐ"_Ó"gÐjkÑ"k€HÐÑâd€MØ×Ñ °eÐÓ<×@Ñ@Ø,Ø0Ø2Ø2Ø2Ø6Ø6Ø2Ø4Ø!@Ø4Ø3ð Aó ÷ �kƒmð ð &.¨nÑ%=×%@Ñ%@ÄÀVÐL]ÑE^ÓA_Ó%`€HÐ!Ñ"Ü!Ÿx™x¨Ð1FÑ(G×(LÑ(LÐSTÐ(LÓ(UÓV€Hˆ^Ñä!#§¡Ü�(Ð-Ñ.Ó/´¸8ÐDWÑ;XÓ1YÜ�(Ð.Ñ/Ó0ð,ð ô"÷ ‰t˜ˆt‹|œe FÐ+<Ñ$=Ó>ñ"?€HÐÑô !#§	¡	Ü�(Ð/Ñ0Ó1´9¸XÐFXÑ=YÓ3ZÜ�(Ð.Ñ/Ó0ð+ð ô!÷ ‰t˜ˆt‹|œe FÐ+;Ñ$<Ó=ñ!>€Hˆ_Ñô "$§¡Ü�(Ð5Ñ6Ó7¼À8ÈNÑC[Ó9\ð,àô"ç‰t˜ˆt‹|œe FÐ+<Ñ$=Ó>ñ"?€HÐÑô %'§I¡IÜ�(Ð,Ñ-Ó.´	¸(ÐCVÑ:WÓ0XÜ�(Ð.Ñ/Ó0ð/ð ô%÷ ‰t˜ˆt‹|œe FÐ+?Ñ$@ÓAñ%B€HÐ Ñ!ð !)Ò)sÑ t× xÑ xÐ~Ð xó  !A€Hˆ_ÑÜEGÇVÁV€H‡L�L�(Ð/Ñ0Ð0°/ÐAÑBØ'¨Ñ8×=Ñ=ÈÐV[Ð=Ó\×cÑcÐdkÓl€Hˆ^ÑØ$,Ð-@Ñ$A×$FÑ$FÐQVÐ_dÐ$FÓ$e×$lÑ$lÐmtÓ$u€HÐ Ñ!Ø$,Ð-@Ñ$A×$FÑ$FÐQVÐ_dÐ$FÓ$e×$lÑ$lÐmtÓ$u€HÐ Ñ!Ø! /Ñ2×5Ñ5´e¸FÐCXÑ<YÓ6ZÓ[€HˆXÑÜ 5€Hˆ_ÑØ×Ñ ¸5ÈfÐÓUÐUr   c                óX  — t        j                  |j                  dz  dz  j                  d¬«      «      }|d   }|g d¢   }| j	                  |dd¬	«      }|j
                  d
z  }|j                  «       r<t        j                  |«      j                  g d¢d¬«      }	|j	                  |	dd¬	«      }|d   t        |d   «      z  |d   t        |d   «      z  z   |d<   t        j                  |d   |d   j                  d«      z  |d   j                  d«      z  |d   |d   j                  d«      z  |d   |d   j                  d«      z  |d   gg d¢d¬«      |d<   |d   |d   z  |d<   t        j                  j                  «       j!                  «       }
||
k  r;t#        t        j$                  |j'                  «       |
j'                  «       «      «      nd}||d<   ||d<   ||j(                  k  |d <   d!d"d#d$d%d&œ}|d   j+                  |«      |d'<   |j-                  g d(¢g d)¢¬*«      S )+Nr6   r7   r8   r9   Ú	candidate)r<   rp   ru   ry   r<   Úleft©ÚonÚhowzquality_signal_proxies.parquet)Úsecurity_idÚnamer<   rP   rz   Úignore)Úcolumnsr   rR   Útotal_scoreÚfundamental_weightrp   Úhotspot_weightÚcombined_scoreÚfundamental_eligibleéK   éA   )õ   ä¼˜å…ˆç ”ç©¶õ   çƒ­ç‚¹äº¤é›†õ   åŸºæœ¬é�¢ç²¾é€‰õ   è§‚å¯Ÿõ   æŽ’é™¤©ÚdefaultÚcandidate_tierÚbasic_pool_eligibleÚcandidate_eligibler   Úmarket_snapshot_dateÚmarket_data_business_days_staleÚis_actionable_snapshotr   é   rL   é   é   )r©   rª   r«   r¬   r­   Úcandidate_tier_order)r²   r¹   r¥   )FTF)rr   )r}   r~   r   r€   ÚmergeÚprocessed_dirÚexistsr   Úread_parquetÚdropr�   r‰   Úselectrƒ   Ú	TimestampÚtodayÚ	normalizer   Úbusday_countr%   Úmax_business_day_stalenessÚmapr‘   )Úfundamentalsr–   r/   r.   r“   r6   ÚhotÚframeÚquality_pathÚqualityrÁ   Ú	stalenessÚorders                r   Ú_build_candidatesrÍ   }   s~  € ä�M‰M˜8×0Ñ0°8Ñ;Ð>VÑV×aÑaÐkrÐaÓsÓt€EØ�;Ñ€FØ
ÒRÑ
S€CØ×Ñ˜sÐ'9¸vÐÓF€EØ×)Ñ)Ð,LÑL€LØ×ÑÔÜ—/‘/ ,Ó/×4Ñ4ò >
àð 5ó ˆð —‘˜G¨¸6�ÓBˆØ# MÑ2´U¸6ÐBVÑ;WÓ5XÑXÐ[`ÐapÑ[qÔtyð  {Að  BRñ  {Só  uTñ  \Tñ  T€EÐ
ÑÜ Ÿi™iØÐ$Ñ%¨¨mÑ(<×(?Ñ(?ÀÓ(CÑCÀeÈOÑF\×F_ÑF_Ð`bÓFcÑcØÐ$Ñ%¨¨oÑ(>×(AÑ(AÀ"Ó(EÑEØÐ$Ñ%¨¨mÑ(<×(?Ñ(?ÀÓ(CÑCØÐ$Ñ%ð	)ò
 EÈhôX€EÐ
Ñð #(Ð(>Ñ"?À%ÐH]ÑB^Ñ"^€EÐ
ÑÜ�L‰L×ÑÓ ×*Ñ*Ó,€EØHQÐTYÒHY””B—O‘O I§N¡NÓ$4°e·j±j³lÓCÔDÐ_`€IØ$-€EÐ
 Ñ!Ø/8€EÐ
+Ñ,Ø&/°8×3VÑ3VÑ&V€EÐ
"Ñ#Ø°ÀaÐSTÐ`aÑb€EØ$)Ð*:Ñ$;×$?Ñ$?ÀÓ$F€EÐ
 Ñ!Ø×ÑÒ]Òi}ÐÓ~Ð~r   c                ó@  — t        j                  |j                  dz  «      }|j                  rt        j                  g d¢¬«      }nPdd„ fdd„ fdœ}d	|j
                  v rd	d
„ f|d<    |j                  d«      j                  d(i |¤Žj                  «       }| j                  |dd¬«      }|j                  dz  }|j                  «       rYt        j                  |«      g d¢   }|j                  |dd¬«      }t        j                  |d   j                  «       dd«      |d<   |j                  «       rdnd|d<   d)d„}|j                  |d¬«      |d<   |d   j!                  d«      |d<   |d   j!                  d«      |d<   d|v r|d   j!                  d«      |d<   t        j"                  |d   j$                  j'                  d «      |d   j$                  j'                  d!«      gd"d#gd$¬%«      |d&<   g d'¢}	||	D �
cg c]  }
|
|j
                  v sŒ|
‘Œ c}
   S c c}
w )*Nzfundamental_risk_flags.csv)rR   Ú
risk_codesÚ
risk_notes)r¡   Ú	rule_codec                óp   — dj                  t        j                  | j                  t        «      «      «      S ©NÚ;©ÚjoinÚdictÚfromkeysr�   r'   ©Úxs    r   Ú<lambda>z%_build_health_cards.<locals>.<lambda>¡   s   € °#·(±(¼4¿=¹=ÈÏÉÔRUËÓ;WÔ2Xr   Údescriptionc                óp   — dj                  t        j                  | j                  t        «      «      «      S )Nõ   ï¼›rÕ   rÙ   s    r   rÛ   z%_build_health_cards.<locals>.<lambda>¢   s   € °E·J±J¼t¿}¹}ÈQÏXÉXÔVYË]Ó?[Ô4\r   )rÏ   rÐ   Ú
source_urlc                óŒ   — dj                  t        j                  | j                  «       j	                  t
        «      «      «      S rÓ   )rÖ   r×   rØ   rQ   r�   r'   rÙ   s    r   rÛ   z%_build_health_cards.<locals>.<lambda>¦   s(   € ¨¯©´·±¸q¿x¹x»z×?PÑ?PÔQTÓ?UÓ1VÔ(Wr   Úrisk_source_urlsrR   rš   r›   zexternal_pledge_latest.parquet)rR   Úpledge_dateÚpledge_ratio_pctÚpledge_deal_countrã   ÚCURRENT_RECORD_FOUNDÚNO_CURRENT_RECORDÚpledge_record_statusÚ	AVAILABLEÚNOT_REFRESHEDÚgovernance_data_statusc           
     ó<  — | j                  dd«      rÛ| j                  dt        j                  «      dk\  df| j                  dt        j                  «      dk\  df| j                  d	t        j                  «      d
k\  df| j                  dt        j                  «      dk\  df| j                  dt        j                  «      dk\  df| j                  dt        j                  «      dk\  dfg}nþ| j                  dt        j                  «      d
k\  df| j                  dt        j                  «      d
k\  df| j                  dt        j                  «      d
k\  df| j                  dt        j                  «      dk\  df| j                  dt        j                  «      dk\  df| j                  dt        j                  «      d k\  df| j                  dt        j                  «      dk\  dfg}|D ��cg c]
  \  }}|sŒ	|‘Œ }}}|rd!j                  |«      S d"S c c}}w )#NÚis_financial_sectorFÚfinancial_profitability_scoreé   u   é‡‘èž�ç›ˆåˆ©èƒ½åŠ›è¡Œä¸šå‰�åˆ—Úfinancial_growth_scoreg     €1@u   é‡‘èž�æˆ�é•¿è¡¨çŽ°è¡Œä¸šå‰�åˆ—Ú financial_capital_strength_scoreé   u   èµ„æœ¬å¼ºåº¦è¡Œä¸šå‰�åˆ—Ú financial_earnings_quality_scoreg      %@u   ç›ˆåˆ©è´¨é‡�è¡Œä¸šå‰�åˆ—Úfinancial_valuation_scoreé   u!   ä¼°å€¼è¡Œä¸šå†…ç›¸å¯¹æœ‰å�¸å¼•åŠ›rp   éF   u   æ‰€å±žè¡Œä¸šå½“å‰�è¾ƒçƒ­Úprofitability_scoreu   ç›ˆåˆ©èƒ½åŠ›è¡Œä¸šå‰�åˆ—Úgrowth_scoreu   æˆ�é•¿è¡¨çŽ°è¡Œä¸šå‰�åˆ—Úcashflow_scoreu   çŽ°é‡‘æµ�è´¨é‡�è¾ƒå¥½Úbalance_sheet_scoreu   èµ„äº§è´Ÿå€ºè¡¨ç›¸å¯¹ç¨³å�¥Úefficiency_scoreu   è�¥è¿�æ•ˆçŽ‡è¡Œä¸šå‰�åˆ—Úvaluation_scoreg      @rÞ   u   æš‚æ— é‡�åŒ–çª�å‡ºé¡¹)Úgetr‰   rŽ   rÖ   )ÚrowÚtestsÚpassedÚtextÚnotess        r   Ú
highlightsz'_build_health_cards.<locals>.highlights±   sÜ  € Ø�7‰7Ð(¨%Ô0à—‘Ð8¼"¿&¹&ÓAÀRÑGÐIiÐjØ—‘Ð1´2·6±6Ó:¸dÑBÐDdÐeØ—‘Ð;¼R¿V¹VÓDÈÑJÐLfÐgØ—‘Ð;¼R¿V¹VÓDÈÑLÐNhÐiØ—‘Ð4´b·f±fÓ=ÀÑBÐDgÐhØ—‘˜¬"¯&©&Ó1°RÑ7Ð9SÐTð‰Eð —‘Ð.´·±Ó7¸2Ñ=Ð?YÐZØ—‘˜¬¯©Ó0°BÑ6Ð8RÐSØ—‘Ð)¬2¯6©6Ó2°bÑ8Ð:QÐRØ—‘Ð.´·±Ó7¸4Ñ?ÐA^Ð_Ø—‘Ð+¬R¯V©VÓ4¸Ñ9Ð;UÐVØ—‘Ð*¬B¯F©FÓ3°sÑ:Ð<_Ð`Ø—‘˜¬"¯&©&Ó1°RÑ7Ð9SÐTðˆEñ +0Ô:©%™,˜& $²6’¨%ˆÑ:Ù$)ˆu�z‰z˜%Ó ÐFÐ/FÐFùó ;s   Ç1
HÇ<Hr   rJ   Úquant_highlightsrÏ   Ú rÐ   u!   æœªå‘½ä¸­å·²å»ºé‡�åŒ–é£Žé™©è§„åˆ™ÚAUDIT_ADVERSEÚAUDIT_QUALIFIEDÚKNOWN_ADVERSE_OR_DISCLAIMERÚKNOWN_QUALIFIED_OR_NONSTANDARDÚNOT_VERIFIED_BY_CURRENT_DATASETr®   Úaudit_verification_status)Nrž   rR   rŸ   rO   rP   Úreport_periodÚannouncement_dateÚavailable_trade_dater³   Úfinancial_health_statusÚhard_risk_countÚwarning_countrÏ   rÐ   rá   rê   r
  rç   râ   rã   rä   r  Úfundamental_grader¢   Úscore_coveragerö   r÷   rø   rù   rú   rû   Ú
debt_ratioÚcurrent_ratioÚcash_short_debt_ratiorí   rï   rð   rò   ró   Úfinancial_core_marginÚfinancial_solvencyÚfinancial_bufferÚnet_interest_marginÚprovision_coverage_ratioÚcapital_adequacy_ratioÚrisk_coverage_ratioÚnet_capital_liabilities_ratioÚsolvency_adequacy_ratioÚmoat_quant_proxy_scoreÚmoat_proxy_levelÚinnovation_quant_proxy_scoreÚinnovation_proxy_levelÚmanagement_quant_proxy_scoreÚquality_signal_coverageÚprofitable_years_5yÚroe_above_10_years_5yÚgross_margin_stability_5yÚevidence_statusÚmoat_evidence_statusÚgoodwill_equity_ratioÚweighted_roe_reportedÚgross_margin_reportedÚoperating_revenue_yoy_reportedÚnet_profit_yoy_reportedÚrevenue_cagr_3yÚparent_profit_cagr_3yÚcfo_to_parent_profitÚpe_ttmÚpbÚps_ttmrp   ru   r°   r¥   r²   rµ   r´   rz   r   )rý   ú	pd.SeriesÚreturnr'   )r   Úread_csvr»   ÚemptyÚ	DataFramer¡   r†   r‡   rˆ   rº   r¼   r½   r‰   Úwherer‚   ÚapplyÚfillnar¿   r'   Úcontains)r   r.   ÚflagsÚcautionsÚaggregationsÚcardsÚpledge_pathÚpledger  Úcard_columnsÚcolumns              r   Ú_build_health_cardsrF  ›   s<  € Ü�K‰K˜×.Ñ.Ð1MÑMÓN€EØ‡{‚{Ü—<‘<Ò(SÔT‰ð 'Ñ(XÐYØ(Ñ*\Ð]ñ
ˆð ˜5Ÿ=™=Ñ(àÑWð0ˆLÐ+Ñ,ð 4�5—=‘= Ó/×3Ñ3ÑC°lÑC×OÑOÓQˆØ×Ñ˜X¨-¸VÐÓD€EØ×(Ñ(Ð+KÑK€KØ×ÑÔÜ—‘ Ó-Ò.uÑvˆØ—‘˜F }¸&�ÓAˆÜ(*¯©°Ð7IÑ1J×1PÑ1PÓ1RÐTjÐló  )AˆÐ$Ñ%Ø5@×5GÑ5GÔ5I¡kÈ€EÐ
"Ñ#óGð. !&§¡¨J¸Q Ó ?€EÐ
ÑØ Ñ-×4Ñ4°RÓ8€Eˆ,ÑØ Ñ-×4Ñ4Ð5XÓY€Eˆ,ÑØ˜UÑ"Ø$)Ð*<Ñ$=×$DÑ$DÀRÓ$HˆÐ Ñ!Ü)+¯©ØˆlÑ×Ñ×(Ñ(¨Ó9ØˆlÑ×Ñ×(Ñ(Ð):Ó;ð4ð 	&Ð'GÐHÐRsô*u€EÐ
%Ñ&ò€Lð0 ¡|ÓO¡|˜V°vÀÇÁÒ7N’& |ÑOÑPÐPùÒOs   Ç>HÈHc                óÆ  — t        j                  | j                  dz  «      }t        j                  | j                  dz  «      }t        j                  |d   j	                  «       «      }t        | |«      }|g d¢   j                  |dd¬«      }t        || «      }t        |||| «      }t        || «      }| j                  dz  }	| j                  d	z  }
|j                  |	d
¬«       |j                  | j                  dz  d
d¬«       |j                  | j                  dz  d
¬«       |j                  | j                  dz  d
d¬«       |j                  |
d
¬«       |j                  | j                  dz  d
d¬«       |j                  | j                  dz  d
¬«       |j                  | j                  dz  d
d¬«       t        j                  t        | j                  dz  «      «      5 }|j!                  d|«       |j!                  d|«       |j!                  d|«       |j!                  d|«       |j#                  d«       |j#                  d«       |j#                  d«       |j#                  d«       d d d «       t%        | |||«       t'        t)        |«      t+        |d   j-                  «       «      t+        |d   j-                  «       «      |
«      S # 1 sw Y   ŒbxY w)Nzsecurity_master.parquetzfundamental_scores.parquetÚ	last_date)	rž   rR   rŸ   rN   rO   r<   rP   r=   r±   rR   rš   r›   zindustry_hotspot_scores.parquetzstock_screen_candidates.parquetF©Úindexzindustry_hotspot_scores.csvz	utf-8-sig)rJ  r:   zstock_momentum_snapshot.parquetzstock_momentum_snapshot.csvzstock_screen_candidates.csvzstock_health_cards.parquetzstock_health_cards.csvzfundamental_mvp.duckdbÚindustry_dfÚcandidates_dfÚcards_dfÚstock_momentum_dfzLCREATE OR REPLACE TABLE industry_hotspot_scores AS SELECT * FROM industry_dfzNCREATE OR REPLACE TABLE stock_screen_candidates AS SELECT * FROM candidates_dfzDCREATE OR REPLACE TABLE stock_health_cards AS SELECT * FROM cards_dfzRCREATE OR REPLACE TABLE stock_momentum_snapshot AS SELECT * FROM stock_momentum_dfry   r²   )r   r½   r»   rÀ   Úmaxr4   rº   r—   rÍ   rF  Ú
to_parquetÚto_csvr*   r+   r'   Údatabase_dirÚregisterr,   Ú_write_reportr
   Úlenr   rX   )r.   ÚmasterrÆ   r/   ÚrecentÚstock_momentumr–   r   rA  Úindustry_pathÚcandidate_pathr3   s               r   Úbuild_hotspotsr[  ì   s¾  € Ü�_‰_˜X×3Ñ3Ð6OÑOÓP€FÜ—?‘? 8×#9Ñ#9Ð<XÑ#XÓY€LÜ—‘˜V KÑ0×4Ñ4Ó6Ó7€IÜ'¨°)Ó<€FØò ñ ÷ �uˆV˜¨6€uÓ2ð ô ! °Ó:€HÜ" <°¸9ÀhÓO€JÜ 
¨HÓ5€Eà×*Ñ*Ð-NÑN€MØ×+Ñ+Ð.OÑO€NØ×Ñ˜¨UÐÔ3Ø‡O�O�H×*Ñ*Ð-JÑJÐRWÐbm€OÔnØ×Ñ˜h×4Ñ4Ð7XÑXÐ`eÐÔfØ×Ñ˜(×0Ñ0Ð3PÑPÐX]ÐhsÐÔtØ×Ñ˜.°ÐÔ6Ø×Ñ�h×,Ñ,Ð/LÑLÐTYÐdoÐÔpØ	×Ñ�X×+Ñ+Ð.JÑJÐRWÐÔXØ	‡L�L�×'Ñ'Ð*BÑBÈ%ÐZe€LÔfÜ	�‰œ˜H×1Ñ1Ð4LÑLÓMÔ	NÐR\Ø×Ñ˜M¨8Ô4Ø×Ñ˜O¨ZÔ8Ø×Ñ˜J¨Ô.Ø×ÑÐ/°Ô@Ø×ÑÐiÔjØ×ÑÐkÔlØ×ÑÐaÔbØ×ÑÐoÔp÷ 
Oô �(˜H j°)Ô<Üœc (›m¬S°¸(Ñ1C×1GÑ1GÓ1IÓ-JÌCÐPZÐ[oÑPp×PtÑPtÓPvÓLwð  zHó  Ið  I÷ 
OÐ	Nús   Ç)BKËK c                ój  — dj                  d„ |j                  d«      j                  d¬«      D «       «      }|j                  |d      j                  d«      }dj                  d„ |j                  d¬«      D «       «      }t	        |d	   j
                  d
   «      }dt        › d|j                  «       j                  «       › d|› dt        |«      › dt	        |d   j                  «       «      › dt	        |d   j                  «       «      › d|› d|› d�}| j                  dz  j                  |d¬«       y )NÚ
c              3  ó0  K  — | ]Ž  }t        j                  |j                  «      rmd t        |j                  «      › d|j
                  › d|j                  › d|j                  d›d|j                  d›d|j                  d›d|j                  d›d�–— Œ� y­w)ú| ú | ú.2fz% | z.1%ú |N)
r   r‚   rp   r   ru   rP   rY   r\   r_   rb   ©Ú.0rý   s     r   Ú	<genexpr>z _write_report.<locals>.<genexpr>  sè   è ø€ ð á<ˆCÄÇÁÈ×IZÑIZÔ@[ð ŒS�×!Ñ!Ó"Ð# 3 s×';Ñ';Ð&<¸CÀ×@PÑ@PÐ?QÐQTÐUX×UfÑUfÐgjÐTkÐknÐor÷  pEñ  pEð  FIð  oJð  JNð  OR÷  Oeñ  Oeð  fið  Njð  jmð  nq÷  nJñ  nJð  KNð  mOð  OQô  	RÙ<ùs   ‚BBé   FrI  r²   é2   c              3  óÞ   K  — | ]e  }d |j                   › d|j                  › d|j                  › d|j                  › d|j                  d›d|j
                  d›d|j                  d›d�–— Œg y­w)r_  r`  ra  rb  N)rž   rŸ   rP   r°   r¢   rp   r¥   rc  s     r   re  z _write_report.<locals>.<genexpr>  s°   è ø€ ð  á3ˆCð ˆS�_‰_Ð˜S §¡ 
¨#¨c×.BÑ.BÐ-CÀ3Às×GYÑGYÐFZÐZ]Ð^a×^mÑ^mÐnqÐ]rÐruÐvy÷  wHñ  wHð  ILð  vMð  MPð  QT÷  Qcñ  Qcð  dgð  Phð  hjô  	kÙ3ùs   ‚A+A-r´   r   u=   # M1 è¡Œä¸šçƒ­ç‚¹ä¸Žå€™é€‰è‚¡æ± æŠ¥å‘Š

çƒ­ç‚¹æ¨¡åž‹ç‰ˆæœ¬ï¼šu     
å¸‚åœºæˆªé�¢ï¼šu     
æˆªé�¢æ»žå�Žï¼šu    ä¸ªå·¥ä½œæ—¥  
äºŒçº§è¡Œä¸šï¼šu    ä¸ª  
çƒ­ç‚¹å¾—åˆ†è¾¾ 70ï¼šry   u1    ä¸ª  
é€šè¿‡åŸºç¡€è¯�åˆ¸æ± ä¸ŽåŸºæœ¬é�¢é—¨æ§›ï¼šu1   å�ª

> å½“å‰�è¡Œæƒ…å·²è¶…è¿‡æ—¶æ•ˆé—¨æ§›ï¼Œæœ¬æŠ¥å‘Šå�ªèƒ½ç”¨äºŽæ¨¡åž‹éªŒè¯�å’ŒåŽ†å�²æˆªé�¢ç ”ç©¶ï¼Œä¸�èƒ½ä½œä¸ºå½“æ—¥äº¤æ˜“ä¾�æ�®ã€‚

## 1. äºŒçº§è¡Œä¸šçƒ­åº¦æ¦œ

| æŽ’å�� | äºŒçº§è¡Œä¸š | æˆ�åˆ†æ•° | çƒ­åº¦ | 20æ—¥ä¸­ä½�æ¶¨å¹… | ä¸Šæ¶¨æ‰©æ•£åº¦ | 5/20æ—¥é‡�èƒ½æ¯” |
|---:|---|---:|---:|---:|---:|---:|
u2  

çƒ­åº¦ç”±ä»·æ ¼å¼ºåº¦ 35%ã€�ä¸Šæ¶¨æ‰©æ•£åº¦ 25%ã€�æˆ�äº¤æ´»è·ƒåº¦ 20%ã€�è¶‹åŠ¿æŒ�ç»­æ€§ 20% å�ˆæˆ�ï¼Œå�‡ä¸ºå½“å‰� 128 ä¸ªäºŒçº§è¡Œä¸šçš„æ¨ªæˆªé�¢åˆ†ä½�å¾—åˆ†ã€‚

## 2. åˆ�ç‰ˆå€™é€‰è‚¡æ± 

| ä»£ç � | å��ç§° | äºŒçº§è¡Œä¸š | åˆ†å±‚ | åŸºæœ¬é�¢ | è¡Œä¸šçƒ­åº¦ | ç»¼å�ˆåˆ† |
|---|---|---|---|---:|---:|---:|
u³   

ç»¼å�ˆåˆ†ä¸ºåŸºæœ¬é�¢ 70% + è¡Œä¸šçƒ­åº¦ 30%ã€‚å€™é€‰è‚¡å¿…é¡»å�Œæ—¶é€šè¿‡ä¸Šå¸‚æ—¶é•¿ã€�æµ�åŠ¨æ€§ã€�ST/é€€å¸‚æ ‡ç­¾ã€�æ•°æ�®æ–°é²œåº¦ã€�è´¢åŠ¡ç¡¬ä¼¤å’ŒåŸºæœ¬é�¢å¾—åˆ†é—¨æ§›ã€‚
u!   M1_è¡Œä¸šçƒ­ç‚¹ä¸Žå€™é€‰è‚¡æ± .mdr8   r9   )rÖ   ÚheadÚ
itertuplesr�   r   Úilocr�   r%   r&   rU  rX   Ú
report_dirÚ
write_text)	r.   r–   r   r/   Úindustry_linesÚselectedÚcandidate_linesÚstaleÚreports	            r   rT  rT    s_  € Ø—Y‘Yñ à—=‘= Ó$×/Ñ/°eÐ/Ô<óó €Nð �~‰~˜jÐ)=Ñ>Ñ?×DÑDÀRÓH€HØ—i‘iñ  à×&Ñ&¨UÐ&Ô3ó ó €Oô �
Ð<Ñ=×BÑBÀ1ÑEÓF€Eðä+Ð,ð -Ø—‘Ó ×*Ñ*Ó,Ð-ð .Øˆwð Ü�H“ˆð Ü˜( 8Ñ,×0Ñ0Ó2Ó3Ð4ð 5+Ü+.¨zÐ:NÑ/O×/SÑ/SÓ/UÓ+VÐ*Wð Xð Ð ð ð Ð ð ð/€Fð6 ×ÑÐ>Ñ>×JÑJÈ6Ð\cÐJÕdr   )r   r5  r6  r5  )r.   r   r/   úpd.Timestampr6  úpd.DataFrame)r’   rt  r.   r   r6  rt  )
rÆ   rt  r–   rt  r/   rs  r.   r   r6  rt  )r   rt  r.   r   r6  rt  )r.   r   r6  r
   )
r.   r   r–   rt  r   rt  r/   rs  r6  ÚNone)Ú
__future__r   Údataclassesr   Úpathlibr   r}   r*   Únumpyr‰   Úpandasr   r6   r   r�   r
   r   r4   r—   rÍ   rF  r[  rT  r   r   r   Ú<module>r{     sŒ   ðÝ "å !Ý Û ã Û Û å ð  Ð ñ �$Ô÷ð ó ðóAó$3óN6VðrØ!-ðØ9AðØFRóó<NQób!IôH&er   