Ë
    ÐK¦jŠP  ã                  ó,  — d Z ddlmZ ddlmZ ddlmZmZ ddlZ	ddl
Z ed¬«       G d„ d	«      «       Z ed¬«       G d
„ d«      «       Zg d¢Zg d¢Zg d¢Zg d¢Zd-d„Zd.d„Zddddddœ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d/d„Zd0d„Zd1d„Zd2d„Zd2d„Zd3d„Zddddddddd d!œ		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d4d"„Z	 d5	 	 	 d6d$„Zdd%œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d7d&„Zddddd'd(d)d#d*ddddd d+œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d8d,„ZeZeZy)9a¨  Point-in-time cross-sectional factor research tools.

This module deliberately accepts already assembled panels instead of reading the
latest processed files.  A caller must attach the date on which every factor
was available.  That makes the availability check explicit and prevents a
current financial snapshot from silently leaking into historical backtests.
All returns are decimal returns (``0.01`` means one percent).
é    )Úannotations)Ú	dataclass)ÚAnyÚSequenceNT)Úfrozenc                  óD   — e Zd ZU dZded<   ded<   ded<   ded<   ded<   y)	ÚFactorBacktestResultzCOutputs from an independent single-factor cross-sectional backtest.úpd.DataFrameÚnormalized_panelÚgroup_returnsÚperiod_returnsÚicÚsummaryN©Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__© ó    ú9C:\FundamentalWorkbench\src\fundamental_mvp\factor_lab.pyr	   r	      s#   … áMà"Ó"ØÓØ Ó ØÓØÔr   r	   c                  ó:   — e Zd ZU dZded<   ded<   ded<   ded<   y	)
ÚWeightOptimizationResultzCWalk-forward weights and the strictly out-of-sample composite test.ú	pd.SeriesÚweightsr
   Úweight_historyÚ	oos_panelr	   Úoos_backtestNr   r   r   r   r   r      s   … áMàÓØ Ó ØÓØ&Ô&r   r   ©Úrebalance_dateÚfactorÚgroupÚgroup_returnÚmember_count©r!   r"   Úlong_returnÚshort_returnÚlong_short_gross_returnÚlong_turnoverÚshort_turnoverÚlong_short_turnoverÚlong_only_net_returnÚlong_short_net_returnÚ
long_countÚshort_count©r!   r"   r   Úrank_icr%   ©	r"   ÚperiodsÚmean_icÚmean_rank_icÚic_positive_rateÚmean_long_short_gross_returnÚmean_long_short_net_returnÚ cumulative_long_short_net_returnÚmean_long_only_net_returnc                óˆ   — |D �cg c]  }|| j                   vsŒ|‘Œ }}|rt        ddj                  |«      › �«      ‚y c c}w )Nu!   å› å­�é�¢æ�¿ç¼ºå°‘å¿…è¦�å­—æ®µï¼šz, )ÚcolumnsÚ
ValueErrorÚjoin)Úframer=   ÚcolumnÚmissings       r   Ú_require_columnsrC   6   sL   € Ù$+ÓK¡G˜&¨v¸U¿]¹]Ò/JŠv G€GÐKÙÜÐ<¸T¿Y¹YÀwÓ=OÐ<PÐQÓRÐRð ùò Ls   …?™?c                óž   — | dk  rt        d«      ‚|| k  rt        d«      ‚|\  }}d|cxk  r|cxk  rdk  st        d«      ‚ t        d«      ‚y )Né   u   quantiles å¿…é¡»è‡³å°‘ä¸º 2u!   min_assets ä¸�èƒ½å°�äºŽ quantilesç        ç      ð?u2   winsor_limits å¿…é¡»æ»¡è¶³ 0 <= lower < upper <= 1)r>   )Ú	quantilesÚwinsor_limitsÚ
min_assetsÚlowerÚuppers        r   Ú_validate_optionsrM   <   sf   € Ø�1‚}ÜÐ6Ó7Ð7Ø�IÒÜÐ<Ó=Ð=Ø �L€Eˆ5Ø�5Ô'˜5Ô' CÒ'ÜÐMÓNÐNð (ÜÐMÓNÐNð (r   r!   Úsource_codeÚavailable_dateÚforward_return)g{®Gáz„?g®Gáz®ï?©Úrebalance_colÚsecurity_colÚavailable_date_colÚforward_return_colrI   c               óÎ  — |st        d«      ‚t        d|d«       ||||g|¢}t        | |«       | j                  «       }t	        j
                  ||   d¬«      ||<   t	        j
                  ||   d¬«      ||<   ||   j                  «       j                  «       s!||   j                  «       j                  «       rt        d«      ‚||   j                  «       j                  «       rt        d«      ‚|j                  ||gd¬«      }	|	j                  «       rt        d	«      ‚||   ||   kD  }
|
j                  «       r@|j                  |
|||gf   j                  d
«      }t        d|j                  d«      › �«      ‚t	        j                  ||   d¬«      ||<   |D ]  }t	        j                  ||   d¬«      ||<   Œ! |\  }}|D �]û  }t	        j                  t        j                  |j                   t"        ¬«      }t	        j                  t        j                  |j                   t"        ¬«      }|j%                  |d¬«      j&                  j)                  «       D �]Q  \  }}|j                  ||f   }|t        j*                  |«         }|j,                  rŒ=|j/                  |j1                  |«      |j1                  |«      ¬«      }||j                  |<   |t        j*                  |«         }t#        |j3                  d¬«      «      }t5        |«      dkD  rRt        j*                  |«      r=|dkD  r8|t#        |j7                  «       «      z
  |z  |j                  |j                   <   �Œt5        |«      dk(  rd|j                  |j                   <   �Œ9d|j                  |j                   <   �ŒT |||› d�<   |||› d�<   �Œþ |j9                  ||g«      j;                  d¬«      S )a/  Validate PIT provenance and cross-sectionally winsorize and z-score factors.

    ``available_date`` must be the first tradable date for the information, not
    a report period or a data-download date.  Values that are unavailable on a
    rebalance date raise instead of being shifted implicitly.
    u   è‡³å°‘éœ€è¦�ä¸€ä¸ªå› å­�å­—æ®µrE   Úcoerce)ÚerrorsuF   rebalance_date ä¸Ž available_date ä¸�å¾—ä¸ºç©ºä¸”å¿…é¡»æ˜¯æœ‰æ•ˆæ—¥æœŸu   security æ ‡è¯†ä¸�å¾—ä¸ºç©ºF)Úkeepu9   æ¯�ä¸ªè°ƒä»“æ—¥æ¯�å�ªè¯�åˆ¸å�ªèƒ½æœ‰ä¸€è¡Œå› å­�è§‚æµ‹å€¼é   u?   æ£€æµ‹åˆ°æœªæ�¥å‡½æ•°ï¼šavailable_date æ™šäºŽ rebalance_dateï¼šÚrecords)ÚindexÚdtype©Úsort)rK   rL   r   ©Úddofé   rF   Ú_winsorizedÚ_zscoreT)Údrop)r>   rM   rC   ÚcopyÚpdÚto_datetimeÚisnaÚanyÚ
duplicatedÚlocÚheadÚto_dictÚ
to_numericÚSeriesÚnpÚnanr\   ÚfloatÚgroupbyÚgroupsÚitemsÚisfiniteÚemptyÚclipÚquantileÚstdÚlenÚmeanÚsort_valuesÚreset_index)ÚpanelÚfactor_columnsrR   rS   rT   rU   rI   Úrequiredr@   Ú	duplicateÚleakedÚexamplesr"   rK   rL   Ú
winsorizedÚzscoreÚ_r\   ÚvaluesÚvalidÚclippedÚusabler{   s                           r   Úprepare_factor_panelr�   F   sv  € ñ  ÜÐ9Ó:Ð:Ü�a˜¨Ô*Ø˜|Ð-?ÐASÐeÐVdÐe€HÜ�U˜HÔ%Ø�J‰J‹L€EÜŸ>™>¨%°Ñ*>ÀxÔP€Eˆ-ÑÜ "§¡¨uÐ5GÑ/HÐQYÔ Z€EÐ
ÑØˆ]Ñ× Ñ Ó"×&Ñ&Ô(¨EÐ2DÑ,E×,JÑ,JÓ,L×,PÑ,PÔ,RÜÐaÓbÐbØˆ\Ñ×ÑÓ!×%Ñ%Ô'ÜÐ6Ó7Ð7Ø× Ñ  -°Ð!>ÀUÐ ÓK€IØ‡}�}„ÜÐTÓUÐUØÐ%Ñ&¨¨}Ñ)=Ñ=€FØ‡z�z„|Ø—9‘9˜V lÐ4FÈÐ%VÐVÑW×\Ñ\Ð]^Ó_ˆÜÐZÐ[c×[kÑ[kÐluÓ[vÐZwÐxÓyÐyÜ "§¡¨eÐ4FÑ.GÐPXÔ Y€EÐ
ÑÛ ˆÜŸ™ e¨F¡m¸HÔEˆˆfŠð !ð !�L€Eˆ5Ü ˆÜ—Y‘YœrŸv™v¨U¯[©[ÄÔFˆ
Ü—‘œ2Ÿ6™6¨¯©¼EÔBˆØŸ™ m¸%˜Ó@×GÑG×MÑM×O‰HˆAˆuØ—Y‘Y˜u f˜}Ñ-ˆFØœ2Ÿ;™; vÓ.Ñ/ˆEØ�{Š{ØØ—k‘k¨¯©°uÓ(=ÀUÇ^Á^ÐTYÓEZ�kÓ[ˆGØ$+ˆJ�N‰N˜5Ñ!ØœRŸ[™[¨Ó1Ñ2ˆFÜ˜Ÿ
™
¨˜
Ó*Ó+ˆCÜ�6‹{˜QŠ¤2§;¡;¨sÔ#3¸¸aºØ,2´U¸6¿;¹;»=Ó5IÑ,IÈSÑ+P�—
‘
˜6Ÿ<™<Ó(Ü�V“ Ò!Ø+.�—
‘
˜6Ÿ<™<Ó(à+.�—
‘
˜6Ÿ<™<Ó(ð Pð )3ˆ��˜Ð$Ñ%Ø$*ˆ��˜Ð Ó!ð' !ð( ×Ñ˜m¨\Ð:Ó;×GÑGÈTÐGÓRÐRr   c                ó„   — | j                  d¬«      }t        j                  ||dd¬«      j                  t        «      dz   S )zCAssign stable cross-sectional buckets even where factor values tie.Úfirst©ÚmethodFre   )ÚqÚlabelsÚ
duplicatesrb   )Úrankrg   ÚqcutÚastypeÚint)r‰   rH   Úrankeds      r   Ú_quantile_groupsrš   …   s9   € à�[‰[ ˆ[Ó(€FÜ�7‰7�6˜Y¨uÀÔH×OÑOÔPSÓTÐWXÑXÐXr   c                ó    ‡ ‡‡‡— ‰€y‰ s‰sy‰ rdt        ‰ «      z  ndŠ‰rdt        ‰«      z  ndŠ‰ ‰z  }dt        ˆ ˆˆˆfd„|D «       «      z  S )NrG   rF   ç      à?c              3  óR   •K  — | ]  }t        |‰v r‰nd |‰v r‰nd z
  «      –— Œ  y­w)rF   N)Úabs)Ú.0ÚnameÚcurrentÚcurrent_weightÚpreviousÚprevious_weights     €€€€r   Ú	<genexpr>z_turnover.<locals>.<genexpr>“   s<   øè ø€ ð \ÙUZÈTô ¨D°G©O™.ÀØ-1°XÑ-=™/À3ñH÷ IÙUZùs   ƒ$')r|   Úsum)r¡   r£   Únamesr¢   r¤   s   `` @@r   Ú	_turnoverr¨   ‹   sh   û€ ØÐØÙ™8ØÙ+2�Sœ3˜w›<Ò'¸€NÙ-5�cœC ›MÒ)¸3€OØ�hÑ€EØ”ö \ÙUZó\ó \ñ \ð \r   c                ó   — t        j                  | |dœ«      j                  «       }t        |«      dk  s0|d   j	                  d¬«      dk(  s|d   j	                  d¬«      dk(  rt        d«      S t        |d   j                  |d   «      «      S )z1Correlation without an optional SciPy dependency.©ÚleftÚrightrE   r«   r   r`   r¬   rr   )rg   Ú	DataFrameÚdropnar|   r{   rs   Úcorr©r«   r¬   rŠ   s      r   Ú_pearson_icr±   —   s…   € ä�L‰L $°Ñ7Ó8×?Ñ?ÓA€EÜ
ˆ5ƒz�A‚~˜˜v™×*Ñ*°Ð*Ó2°aÒ7¸5À¹>×;MÑ;MÐSTÐ;MÓ;UÐYZÒ;ZÜ�U‹|ÐÜ��v‘×#Ñ# E¨'¡NÓ3Ó4Ð4r   c                ó²   — t        j                  | |dœ«      j                  «       }t        |d   j	                  d¬«      |d   j	                  d¬«      «      S )zFSpearman correlation computed as Pearson correlation of average ranks.rª   r«   Úaverager�   r¬   )rg   r­   r®   r±   r•   r°   s      r   Ú_rank_icr´   Ÿ   sP   € ä�L‰L $°Ñ7Ó8×?Ñ?ÓA€EÜ�u˜V‘}×)Ñ)°Ð)Ó;¸UÀ7¹^×=PÑ=PÐXaÐ=PÓ=bÓcÐcr   c           	     óâ   — t        | t        j                  t        ¬«      t        j                  t        ¬«      t        j                  t
        ¬«      t        j                  t        ¬«      ¬«      S )N©r=   ©r   r   r   r   r   )r	   rg   r­   Ú_GROUP_COLUMNSÚ_PERIOD_COLUMNSÚ_IC_COLUMNSÚ_SUMMARY_COLUMNS)r   s    r   Ú_empty_backtestr¼   ¥   sC   € ÜØ)Ü—l‘l¬>Ô:Ü—|‘|¬OÔ<Ü�<‰<¤Ô,Ü—‘Ô%5Ô6ôð r   rZ   rF   é
   ©	rR   rS   rT   rU   rH   Útransaction_cost_bpsÚ
long_shortrI   rJ   c       	        óX	  — t        ||	|
«       |dk  rt        d«      ‚t        | ||||||	¬«      }g }g }g }|dz  }|D �]•  }|› d�}d}d}|j                  |d¬«      D �]r  \  }}|j                  t        j                  ||   «      t        j                  ||   «      z  |||gf   j                  «       }t        |«      |
k  rŒgt        ||   |«      |d	<   |j                  d	d¬«      |   j                  d
dg«      }|j                  «       D ]=  \  }}|j                  ||t        |«      t        |d
   «      t        |d   «      dœ«       Œ? t        |j                  |d	   j!                  |«      |f   «      }t        |j                  |d	   j!                  d«      |f   «      }t        |j                  |d	   j!                  |«      |f   j#                  «       «      }t        |j                  |d	   j!                  d«      |f   j#                  «       «      }t%        ||«      }t%        ||«      }d||z   z  } ||z
  }!|j                  |||||!||| |||z  z
  |r|!| |z  z
  nt
        j&                  t        |«      t        |«      dœ«       |j                  ||t)        ||   ||   «      t+        ||   ||   «      t        |«      dœ«       ||}}�Œu �Œ˜ |st-        |«      S t/        j0                  |t2        ¬«      }"t/        j0                  |t4        ¬«      }#t/        j0                  |t6        ¬«      }$g }%|#j                  dd¬«      D �]6  \  }}&|$j                  |$d   j!                  |«         }'|r|&d   n|&d   }(|%j                  |t        t        |&«      «      t        |'d   j#                  «       «      t        |'d   j#                  «       «      t        |'d   j9                  d«      j#                  «       «      t        |&d   j#                  «       «      t        |&d   j#                  «       «      t        d|(j;                  «       z   j=                  «       dz
  «      t        |&d   j#                  «       «      dœ	«       �Œ9 t?        ||"|#|$t/        j0                  |%t@        ¬«      ¬«      S )al  Backtest each factor with PIT-safe cross-sectional portfolios.

    The top bucket is long and the bottom bucket is short.  Transaction costs
    are charged on one-way turnover at each rebalance, including the initial
    portfolio entry.  If no date has enough valid names, empty result frames
    are returned rather than a misleading zero-return backtest.
    r   u$   transaction_cost_bps ä¸�èƒ½ä¸ºè´Ÿæ•°rQ   g     ˆÃ@rd   NTr^   Ú_groupr}   Úsizer    rb   rœ   r&   r1   r¶   r"   r.   r-   r   r2   r)   rG   r3   r·   )!rM   r>   r�   rt   rl   rq   rw   rf   r|   rš   ÚaggÚiterrowsÚappendr˜   rs   ÚsetÚeqr}   r¨   rr   r±   r´   r¼   rg   r­   r¸   r¹   rº   Úgtr®   Úprodr	   r»   ))r€   r�   rR   rS   rT   rU   rH   r¿   rÀ   rI   rJ   Ú
normalizedÚ
group_rowsÚperiod_rowsÚic_rowsÚ	cost_rater"   Ú
zscore_colÚprevious_longÚprevious_shortr!   Úslice_rŒ   ÚgroupedÚbucketr‰   Úlong_membersÚshort_membersr'   r(   r*   r+   r,   Úgrossr   r   r   Ú	summariesr4   Ú	factor_icÚnets)                                            r   Úrun_factor_backtestrÜ   ¯   s¿  € ô* �i °
Ô;Ø˜aÒÜÐ?Ó@Ð@Ü%Øˆ~¨]ÈØ-ÐBTØ#ô€Jð
 (*€JØ(*€KØ$&€GØ$ xÑ/€Iä ˆØ�x˜wÐ'ˆ
Ø)-ˆØ*.ˆØ&0×&8Ñ&8¸ÈTÐ&8×&RÑ"ˆN˜FØ—Z‘ZÜ—‘˜F :Ñ.Ó/´"·+±+¸fÐEWÑ>XÓ2YÑYØ˜zÐ+=Ð>ð@ñ÷ ‰d‹fð ô �6‹{˜ZÒ'ØÜ/°°zÑ0BÀIÓNˆF�8ÑØ—n‘n X°D�nÓ9Ð:LÑM×QÑQÐSYÐ[aÐRbÓcˆGØ")×"2Ñ"2Ö"4‘�˜Ø×!Ñ!Ø&4ÀÔQTÐU[ÓQ\Ü$)¨&°©.Ó$9Ì3ÈvÐV\É~ÓK^ñ#õ ð #5ô
 ˜vŸz™z¨&°Ñ*:×*=Ñ*=¸iÓ*HÈ,Ð*VÑWÓXˆLÜ §
¡
¨6°(Ñ+;×+>Ñ+>¸qÓ+AÀ<Ð+OÑ PÓQˆMä §
¡
¨6°(Ñ+;×+>Ñ+>¸yÓ+IÐK]Ð+]Ñ ^× cÑ cÓ eÓfˆKÜ  §¡¨F°8Ñ,<×,?Ñ,?ÀÓ,BÐDVÐ,VÑ!W×!\Ñ!\Ó!^Ó_ˆLÜ% l°MÓBˆMÜ& }°nÓEˆNØ"%¨¸Ñ)GÑ"HÐØ ,Ñ.ˆEØ×ÑØ"0Ø Ø*Ø ,Ø+0Ø!.Ø"0Ø':Ø(3°mÀiÑ6OÑ(OÙT^¨Ð1DÀyÑ1PÒ)PÔdf×djÑdjÜ! ,Ó/Ü" =Ó1ñ ô ð �N‰NØ"0Ø Ü! &¨Ñ"4°fÐ=OÑ6PÓQÜ# F¨:Ñ$6¸Ð?QÑ8RÓSÜ # F£ñô ð -9¸-˜>ŠMòY 'Sð	 !ñd Ü˜zÓ*Ð*Ü—L‘L ´^ÔD€MÜ—\‘\ +´ÔG€NÜ	�‰�g¤{Ô	3€BØ€IØ)×1Ñ1°(ÀÐ1×F‰ˆ�Ø—F‘F˜2˜h™<Ÿ?™?¨6Ó2Ñ3ˆ	Ù2<ˆgÐ-Ò.À'ÐJ`ÑBaˆØ×ÑØÜœ3˜w›<Ó(Ü˜Y t™_×1Ñ1Ó3Ó4Ü! )¨IÑ"6×";Ñ";Ó"=Ó>Ü % i°	Ñ&:×&=Ñ&=¸aÓ&@×&EÑ&EÓ&GÓ HÜ,1°'Ð:SÑ2T×2YÑ2YÓ2[Ó,\Ü*/°Ð8OÑ0P×0UÑ0UÓ0WÓ*XÜ05°s¸S¿Z¹Z»\Ñ7I×6OÑ6OÓ6QÐTWÑ6WÓ0XÜ).¨wÐ7MÑ/N×/SÑ/SÓ/UÓ)Vñ

ö 
	ð Gô  Ø#Ø#Ø%ØÜ—‘˜YÔ0@ÔAôð r   rG   c                ó.  ‡ ‡— |‰ dz   k  rt        d«      ‚‰d‰ z  k  s‰dkD  rt        d«      ‚t        j                  j                  |«      }t        j                  ‰ d‰ z  «      g}‰dk\  r#|j                  ˆ fd„t        ‰ «      D «       «       t        |«      |k  r\|j                  t        j                  ‰ «      t        d|dz  «      ¬«      }|j                  ˆfd	„|D «       «       t        |«      |k  rŒ\t        j                  |d | t        ¬
«      S )Nrb   u+   candidate_count è‡³å°‘åº”ä¸ºå› å­�æ•°åŠ ä¸€rG   u4   max_weight å¿…é¡»ä¸�å°�äºŽç­‰æ�ƒæ�ƒé‡�ä¸”ä¸�å¤§äºŽ 1c              3  óN   •K  — | ]  }t        j                  ‰«      |   –— Œ y ­w)N)rq   Úeye)rŸ   r\   Úcounts     €r   r¥   z%_weight_candidates.<locals>.<genexpr>)  s   øè ø€ ÐI¹L°5œ"Ÿ&™& ›-¨Õ.¹Lùs   ƒ"%é@   rE   )rÃ   c              3  óP   •K  — | ]  }|j                  «       ‰d z   k  sŒ|–— Œ y­w)gê-�™—q=N)Úmax)rŸ   ÚrowÚ
max_weights     €r   r¥   z%_weight_candidates.<locals>.<genexpr>,  s$   øè ø€ ÐR© #°#·'±'³)¸zÈEÑ?QÓ2Qœ#©ùs   ƒ&Ÿ&©r]   )r>   rq   ÚrandomÚdefault_rngÚfullÚextendÚranger|   Ú	dirichletÚonesrã   Úasarrayrs   )rà   Úcandidate_countÚrandom_staterå   ÚrngÚ
candidatesÚbatchs   `  `   r   Ú_weight_candidatesrô      sô   ù€ à˜ ™Ò"ÜÐFÓGÐGØ�C˜%‘KÒ :°Ò#3ÜÐOÓPÐPÜ
�)‰)×
Ñ
 Ó
-€CÜ$&§G¡G¨E°3¸±;Ó$?Ð#@€JØ�SÒØ×ÑÓI¼EÀ%¼LÓIÔIÜ
ˆj‹/˜OÒ
+Ø—‘œbŸg™g e›n´3°r¸?ÈQÑ;NÓ3O�ÓPˆØ×ÑÓR©ÓRÔRô ˆj‹/˜OÓ
+ô �:‰:�jÐ!1 /Ð2¼%Ô@Ð@r   )Údate_slicesc               ó˜  — |D �
cg c]  }
|
› d�‘Œ	 }}
g }|D �]ô  }|	€6| j                   | |   j                  |«      g |¢|‘f   j                  «       }nD|	j                  t	        j
                  |«      t	        j                  «       «      }|j                  rŒ€|j                   t        j                  ||   «         }t        |«      |k  rŒ´||   j                  t        ¬«      |z  }||   }|dk(  r,t        t	        j                  ||j                  ¬«      |«      }n¾|dk(  r,t!        t	        j                  ||j                  ¬«      |«      }n�t#        t	        j                  ||j                  ¬«      |«      }t        |j                   |j                  |«         j%                  «       |j                   |j                  d«         j%                  «       z
  «      }t	        j&                  |«      s�ŒÛ|j)                  t        |«      «       �Œ÷ |r)t        t        j$                  |«      «      t        |«      fS t        d«      dfS c c}
w )	Nrd   ræ   r2   )r\   r   rb   rr   r   )rl   rÈ   r®   Úgetrg   Ú	Timestampr­   rx   rq   rw   r|   Úto_numpyrs   r´   rp   r\   r±   rš   r}   ÚnotnarÆ   )Úpreparedr�   r   ÚdatesrR   rU   rJ   rH   Ú	objectiverõ   r"   Úzscore_columnsr‰   r!   rÓ   ÚscoreÚreturnsÚvalueru   s                      r   Ú_score_weightsr  0  sä  € ñ 8FÓF±~¨V˜˜ Ò(°~€NÐFØ€FÜˆØÐØ—\‘\ (¨=Ñ"9×"<Ñ"<¸^Ó"LÐNsÐP^ÐNsÐ`rÐNsÐ"sÑt×{Ñ{Ó}‰Fà —_‘_¤R§\¡\°.Ó%AÄ2Ç<Á<Ã>ÓRˆFØ�|Š|ØØ—‘œBŸK™K¨Ð/AÑ(BÓCÑDˆÜˆv‹;˜Ò#ØØ�~Ñ&×/Ñ/´eÐ/Ó<¸wÑFˆØÐ+Ñ,ˆØ˜	Ò!ÜœRŸY™Y u°G·M±MÔBÀGÓL‰EØ˜$ÒÜ¤§	¡	¨%°w·}±}Ô EÀwÓO‰Eä%¤b§i¡i°¸W¿]¹]Ô&KÈYÓWˆFÜ˜'Ÿ+™+ f§i¡i°	Ó&:Ñ;×@Ñ@ÓBÀWÇ[Á[ÐQW×QZÑQZÐ[\ÓQ]ÑE^×EcÑEcÓEeÑeÓfˆEÜ�8‰8�EŽ?Ø�M‰Mœ% ›,Ö'ð)  ñ* 5;ŒE”"—'‘'˜&“/Ó"¤C¨£KÐ0ÐQÄÀuÃÈqÐ@QÐQùò/ Gs   …Ié   r2   é   é   )rR   rS   rT   rU   Útrain_periodsrý   rï   rå   rð   rH   r¿   rÀ   rI   rJ   c               óº  — t        |||«       |dk  rt        d«      ‚|dvrt        d«      ‚t        | ||||||¬«      }t        t	        j
                  ||   j                  «       j                  «       «      j                  «       «      }t        t        |«      ||
|	«      }|D �cg c]  }|› d�‘Œ	 }}g |¢|‘}|j                  |d¬«      D ��ci c],  \  }}t	        j                  |«      ||   j                  «       “Œ. }}}g }g }t        |t        |«      «      D �]%  }||   }|||z
  | }d	}t        j                   }d
}|D ]A  } t!        ||| |||||||¬«
      \  }!}"|"|k  st        j"                  |!«      sŒ6|!|kD  sŒ<| |!|"}}}ŒC |€Œo|j$                  ||   j'                  |«         j)                  «       }#|#|   j+                  t,        ¬«      |z  |#d<   |j/                  |#«       ||d
   |d   |||dœ}$|$j1                  t3        ||«      D ��%ci c]  \  }}%|t-        |%«      “Œ c}%}«       |j/                  |$«       �Œ( ddddddg|¢}&t	        j4                  ||&¬«      }'|sk|j6                  d
d
 j)                  «       }(t	        j
                  t,        ¬«      |(d<   t9        t	        j
                  t,        d¬«      |'|(t;        |(«      ¬«      S t	        j<                  |d¬«      })t?        |)dg|||||||||¬«      }*|'j6                  d   t        |«         jA                  t,        «      jC                  d«      }+t9        |+|'|)|*«      S c c}w c c}}w c c}%}w )a$  Find simplex weights using trailing data, then test only the next period.

    Every row in ``weight_history`` for date *t* was fitted with dates strictly
    before *t*.  Candidate weights are non-negative and sum to one; a fixed
    random seed makes the candidate search reproducible.
    rb   u   train_periods å¿…é¡»è‡³å°‘ä¸º 1>   r   r2   Úlong_short_returnu6   objective ä»…æ”¯æŒ� rank_icã€�ic æˆ– long_short_returnrQ   rd   Fr^   Nr   )rR   rU   rJ   rH   rý   rõ   ræ   Úcomposite_factoréÿÿÿÿ)r!   Útraining_startÚtraining_endÚtraining_periodsrý   Útraining_objectiver!   r  r  r  rý   r  r¶   Úweight)r]   r    )r   r   r   r   T)Úignore_indexr¾   )"rM   r>   r�   Úlistrg   rp   r®   Úuniquer~   rô   r|   rt   rø   rë   rq   Úinfr  rw   rl   rÈ   rf   rù   rs   rÆ   ÚupdateÚzipr­   Úilocr   r¼   ÚconcatrÜ   r—   Úrename),r€   r�   rR   rS   rT   rU   r  rý   rï   rå   rð   rH   r¿   rÀ   rI   rJ   rû   rü   rò   r"   rþ   Úscore_columnsr!   rÓ   rõ   Úhistory_rowsÚoos_rowsÚpositionÚtrain_datesÚbest_weightÚ
best_scoreÚbest_periodsr   rÿ   Úvalid_periodsÚoosÚrecordr  Úhistory_columnsÚhistoryÚempty_panelr   ÚbacktestÚlatests,                                               r   Úoptimize_weights_walk_forwardr)  W  s¯  € ô2 �i °
Ô;Ø�qÒÜÐ:Ó;Ð;ØÐ>Ñ>ÜÐQÓRÐRÜ#Øˆ~¨]ÈØ-ÐBTØ#ô€Hô
 ”—‘˜8 MÑ2×9Ñ9Ó;×BÑBÓDÓE×QÑQÓSÓT€EÜ#¤C¨Ó$7¸È,ÐXbÓc€JÙ7EÓF±~¨V˜˜ Ò(°~€NÐFØ9�nÐ9Ð&8Ð9€Mð '/×&6Ñ&6°}È5Ð&6Ô&Qôá&QÑ"ˆN˜Fô 	�‰�^Ó$ f¨]Ñ&;×&BÑ&BÓ&DÑDØ&Qð ñ ð *,€LØ#%€HÜ˜-¬¨U«×4ˆØ˜x™ˆØ˜H }Ñ4°XÐ>ˆØ)-ˆÜ—f‘f�Wˆ
ØˆÛ!ˆGÜ#1Ø˜.¨'°;ÈmØ#5À*Ø#¨yÀkô$Ñ ˆE�=ð
 ˜}Ò,´B·K±KÀÔ4FØØ�zÓ!Ø8?ÀÈ¨˜Z‘ð "ð ÐØØ�l‰l˜8 MÑ2×5Ñ5°nÓEÑF×KÑKÓMˆØ"% nÑ"5×">Ñ">ÄUÐ">Ó"KÈkÑ"YˆÐÑØ�‰˜Ôà,Ø)¨!™nØ'¨™OØ ,Ø"Ø",ñ"
ˆð 	�‰Ä3À~ÐWbÔCcÔdÑCc±°¸�vœu V›}Ñ,ÐCcÒdÔeØ×Ñ˜FÖ#ð= 5ð@ 	Ð*¨NÐ<NÐP[Øðà-ð€Oô �l‰l˜<°ÔA€GÙØ—m‘m A aÐ(×-Ñ-Ó/ˆÜ*,¯)©)¼%Ô*@ˆÐ&Ñ'Ü'Ü—I‘I¤E°Ô9Ø"Ø!Ü(¨Ó5ô	
ð 	
ô —	‘	˜(°Ô6€IÜ"ØÐ&Ð'°}ÐS_Ø-ÐBTØÐ2FÐS]Ø#°
ô	€Hð �\‰\˜"Ñœd >Ó2Ñ3×:Ñ:¼5ÓA×HÑHÈÓR€FÜ# F¨G°YÀÓIÐIùò{ GùóùóF es   Â%MÃ1MÈM)r@   r
   r=   úSequence[str]ÚreturnÚNone)rH   r˜   rI   útuple[float, float]rJ   r˜   r+  r,  )r€   r
   r�   r*  rR   ÚstrrS   r.  rT   r.  rU   r.  rI   r-  r+  r
   )r‰   r   rH   r˜   r+  r   )r¡   zset[Any]r£   zset[Any] | Noner+  rs   )r«   r   r¬   r   r+  rs   )r   r
   r+  r	   )r€   r
   r�   r*  rR   r.  rS   r.  rT   r.  rU   r.  rH   r˜   r¿   rs   rÀ   ÚboolrI   r-  rJ   r˜   r+  r	   )rG   )
rà   r˜   rï   r˜   rð   r˜   rå   rs   r+  ú
np.ndarray)rû   r
   r�   r*  r   r0  rü   zSequence[pd.Timestamp]rR   r.  rU   r.  rJ   r˜   rH   r˜   rý   r.  rõ   z'dict[pd.Timestamp, pd.DataFrame] | Noner+  ztuple[float, int])"r€   r
   r�   r*  rR   r.  rS   r.  rT   r.  rU   r.  r  r˜   rý   r.  rï   r˜   rå   rs   rð   r˜   rH   r˜   r¿   rs   rÀ   r/  rI   r-  rJ   r˜   r+  r   ) r   Ú
__future__r   Údataclassesr   Útypingr   r   Únumpyrq   Úpandasrg   r	   r   r¸   r¹   rº   r»   rC   rM   r�   rš   r¨   r±   r´   r¼   rÜ   rô   r  r)  Úbacktest_factorsÚwalk_forward_optimize_weightsr   r   r   Ú<module>r8     sW  ðñõ #å !ß  ã Û ñ �$Ô÷ð ó ðñ �$Ô÷'ð 'ó ð'ò W€ò€ò
 L€òÐ óSóOð *Ø%Ø.Ø.Ø)5ñ<SØð<Sà!ð<Sð ð	<Sð
 ð<Sð ð<Sð ð<Sð 'ð<Sð ó<Só~Yó	\ó5ódóð *Ø%Ø.Ø.ØØ"%ØØ)5ØñnØðnà!ðnð ð	nð
 ðnð ðnð ðnð ðnð  ðnð ðnð 'ðnð ðnð ónðd ,/ðAØ#(ðAØ3=óAð6 <@ñ$RØð$Rà!ð$Rð ð$Rð "ð	$Rð ð$Rð ð$Rð ð$Rð ð$Rð ð$Rð 9ð$Rð ó$RðV *Ø%Ø.Ø.ØØØØØØØ"%ØØ)5Øñ#bJØðbJà!ðbJð ð	bJð
 ðbJð ðbJð ðbJð ðbJð ðbJð ðbJð ðbJð ðbJð ðbJð  ðbJð ðbJð  'ð!bJð" ð#bJð$ ó%bJðL 'Ð Ø =Ñ r   