
    <yj =              	         S r SSKJr  SSKrSSKrSSKrSSKrSSKJr  SSKJ	r	  SSK
r
SSKr\R                  R                  \R                  R                  \R                  R                  \R                  R!                  \5      5      5      SS5      rSS jrSS	 jrSS
 jr  S   SS jjrSS jrSS jrSS jrSS jr    S         SS jjr          SS jr   S        S!S jjrS r\S:X  a  \" 5         gg)"u|  
Оптимизация MoE v12: удаление худших признаков и переобучение моделей.

Принцип:
    1. Загружаем существующее сохранение MoE (moe_v12_rr1x2.joblib)
    2. У XGBoost Long/Short берём `feature_importances_` первых n_base столбцов —
       это вклад каждого базового признака в финальную модель.
    3. Сортируем базовые признаки по сумме важности Long+Short (imp_total).
    4. Оставляем top-K% (по умолчанию 50%), с защитой min_features.
    5. Делаем бэкап оригинальной модели в backup_top<pct>_<date>/.
    6. Запускаем train_moe_for(ticker, feature_cols_override=top_features).
       Внутри train_moe_for monkey-patchим _get_feature_cols / _crypto_feature_cols —
       поэтому LSTM-эксперты и XGBoost обучаются только на выбранном подмножестве.
    7. Сохраняем финальную модель поверх старой, логируем before/after.

Запуск:
    python train_all_rr1x2.py --optimize-features 50 --ticker SBER
    python train_all_rr1x2.py --optimize-features 50             # 4 тикера по умолчанию
    python train_all_rr1x2.py --optimize-features 60 --min-features 50 --ticker ALL

Альтернативно (как модуль):
    from analysis.optimize_features import run_optimization
    results = run_optimization(['SBER','GAZP'], keep_pct=0.5, min_features=40)
    )annotationsN)datetime)Anymodelssavedc                "    U R                  5       $ )N)lowertickers    ;/home/ai/projects/AI_Strategy/analysis/optimize_features.py_ticker_pathnamer   -   s    <<>    c                b    [         R                  R                  [        [	        U 5       S35      $ )Nz_moe_v12_rr1x2.joblib)ospathjoinSAVE_DIRr   r
   s    r   _model_pathr   1   s&    77<<%5f%=$>>S"TUUr   c                j   U R                  S5      nU R                  S5      nU S   nU S   nUR                  SU nUSLnU(       a  UR                  SU O[        R                  " U5      n0 n[	        USU 5       H3  u  p[        XY   5      [        Xy   5      [        XY   Xy   -   5      S.X'   M5     U$ )u   Извлекает важность базовых признаков из сохранённой модели.

Returns:
    {feature_name: {'imp_long': float, 'imp_short': float, 'imp_total': float}}
rf_longrf_shortn_base_featuresfeature_colsN)imp_long	imp_short	imp_total)getfeature_importances_npzeros	enumeratefloat)r   r   r   n_baser   r   	has_shortr   outinames              r   _fetch_base_importancesr(   5   s     ii	"Gyy$H$%F(L++GV4H$I:C--gv6RXIYI
C\'623hk*y|,x{Y\9:
	 4 Jr   c                F  ^ [        U 5      m[        TR                  5       S SS9n[        U5      n[	        [        XA-  5      U5      nXT:  a  UnUSU  VVs/ s H  u  pgUPM	     nnn[        U4S jU 5       5      n	[        S TR                  5        5       5      n
U	[	        U
S5      -  nUUUUXE-
  SU-
  UX5S  VVs/ s H  u  pgUPM	     snnUSS	  VVs/ s H  u  pgUPM	     snnX5US	-     VVs/ s H  u  pgUPM	     snnS
.
nX4$ s  snnf s  snnf s  snnf s  snnf )uG  Возвращает список top-K% базовых признаков по imp_total.

Args:
    saved: joblib-загруженная MoE.
    keep_pct: Доля признаков для сохранения (0.5 = 50%).
    min_features: Минимум признаков (failsafe).

Returns:
    (top_features, stats)
c                    U S   S   $ )N   r    )xs    r   <lambda>%select_top_features.<locals>.<lambda>[   s    QqT+=Nr   T)keyreverseNc              3  4   >#    U  H  nTU   S    v   M     g7fr   Nr,   ).0fimportancess     r   	<genexpr>&select_top_features.<locals>.<genexpr>a   s     E1;q>+.s   c              3  *   #    U  H	  oS    v   M     g7fr3   r,   )r4   vs     r   r7   r8   b   s     A,@qkN,@s   g|=g      ?   )
n_total_basen_keep_basekeep_pctcoverage_impremoved_countremoved_imp_sharetop_featuresremoved_features
top_5_kepttop_5_dropped)r(   sorteditemslenmaxintsumvalues)r   r>   min_featuressorted_featsn_totaln_keepr'   _rB   kept_imp	total_impcoveragestatsr6   s                @r   select_top_featuresrV   N   s@    *%0K+++-3NX\]L,GW'(,7F(4Wf(=>(=WTD(=L>EEEHAK,>,>,@AAI#i//H  ) 8^$1=g1FG1FgdT1FG+7+;<+;t+;<.:&1*.MN.M74$.MNE % ? H<Ns   D DD4Dc                4   [        U 5      n[        R                  R                  [        U5      n[        R
                  " USS9  [        R                  R                  U[        R                  R                  U5      5      n[        R                  " X$5        U$ )uJ   Копирует модель в backup_dir/{ticker}_moe_v12_rr1x2.joblib.T)exist_ok)	r   r   r   r   r   makedirsbasenameshutilcopy2)r   
backup_dirsrcdst_dirdsts        r   _backup_modelra   u   sb    
f
Cggll8Z0GKK$'
'',,w 0 0 5
6C
LLJr   c           	     &   [         R                  R                  [        U[         R                  R	                  [        U 5      5      5      n[        U 5      n[         R                  R                  U5      (       d  g[        R                  " X#5        g)ut   Восстанавливает модель из бэкапа (если оптимизация не удалась).FT)	r   r   r   r   rZ   r   isfiler[   r\   )r   r]   r^   r`   s       r   _restore_from_backuprd      s[    
'',,xRWW-=-=k&>Q-R
SC
f
C77>>#
LLr   c           
     r   SSK Jn  U R                  S5      =(       d    0 n[        U R                  SS5      5      U(       a  [        UR                  SS5      5      OSU(       a  [        UR                  SS5      5      OS[	        U R                  SS5      5      [        U R                  S	/ 5      5      US
.$ )u^   Извлекает metrics из сохранённой модели для сравнения.r   )get_target_configoptimal_thresholdsval_acc        longshortr   r   )rh   long_thshort_thr   total_feature_size
thresholds)configrf   r   r"   rJ   rH   )r   rf   ths      r   _get_metricsrr      s    (	'	(	.BB9c23135,-35E"&&#./3uyy):A>?!%))NB"?@ r   c                    SSS.$ )u  Грубая оценка Net PnL Long по OOF threshold на val части (по архивным данным модели).

Возвращает {'net_long': float, 'net_short': float, 'long_thr': float, 'short_thr': float}.
Если не получилось — все 0.0.
ri   )net_long	net_shortr,   )r   r   s     r   _estimate_net_pnl_on_valrv      s     #..r   c           	        SSK Jn  [        U 5      n[        R                  R                  U5      (       d	  SU SU 3S.$ U(       a  [        SU  S35        [        R                  " U5      n[        U5      n[        XqUS9u  pU(       aR  [        S	U
S
    SU
S    SU
S    S35        [        SU
S   S 35        [        SU
S    35        [        SU
S    35        Uc8  [        R                  " 5       R                  S5      nS[        US-  5       SU 3n[        X5      nU(       a  [        SU 35        U(       a  [        S[!        U	5       S35         U" X	S9nU(       a  UR-                  S"5      S#:w  a$  U(       a  [        S$5        [%        X5        SU S%US&.$ [        R                  " [        U 5      5      n[        U5      nUS'   US'   -
  nS(nUU:  nU(       dA  U(       a  [        S)U* S S*U* S+ S,35        [%        X5        S-U S.U* S 3US/.[/        XUX5      E$ S#U US0.[/        XUX5      E$ ! ["         aM  nU(       a  [        S U S!35        [%        X5        SSKoR)                  5         SU [+        U5      S.s SnA$ SnAff = f)1uK   Запускает оптимизацию для одного тикера.r   )train_moe_forerrorzmodel not found: statusr   ry   z
[uR   ] Загрузка текущей модели для анализа importance...)r>   rM   u     ➤ Base features: r<       → r=   u    (удалено r@   )u:     ➤ Сохранённая важность (coverage): r?   .1%u$     ➤ Top-5 оставленных: rD   u!     ➤ Top-5 удалённых:  rE   N%Y-%m-%d_%H-%M
backup_topd   rQ   u     ➤ Бэкап: u=     ➤ Переобучение MoE с feature_cols_override (u    признаков)...)feature_cols_overrideu     ✗ train_moe_for упал: u,   . Восстанавливаю бэкап...r{   okuT     ✗ train_moe_for вернул не ok. Восстанавливаю бэкап...ztrain_moe_for returned non-ok)r{   r   ry   train_resultrh   gQu$     ⚠ val_acc снизился на z (>z.0%u-   ). Восстанавливаю бэкап...rejectedzval_acc decreased by )r{   r   reasonr]   )r{   r   r]   )train_all_rr1x2rx   r   r   r   rc   printjoblibloadrr   rV   r   nowstrftimerJ   ra   rH   	Exceptionrd   	traceback	print_excstrr   _interop_metrics)r   r>   rM   r]   verboserx   r   	saved_oldmetrics_beforerB   selection_statsdatebackup_pathresulter   	saved_newmetrics_afterva_deltaaccept_thresholdaccepts                        r   _run_optimization_singler      s    .vD77>>$!V@QRVQW>XYYF8mnoD!I!),N$7	EQ%S!L %on&E%Fe /00A/RaBbAccdf 	gJ?[iKjknJopq4_\5R4STU1//2R1STU ||~&&'78!#hn"5!6av>
3K";-01 McR^N_M``wxyFvJ VZZ)T1hjV0!V>] &( 	( K/0I +M Y'.*CCH))F8(3sL\K\]`Ja b? @ AV0 -xi_=$	

 v}o^
 	
   6=/
Z	 M  F21#5abcV0--/!Vc!fEEFs   H3 3
J
=AJ?J
J
c                x    US   US   US   US   US   US   US   US   US   US   US   US   US   US	   S
.$ )u;   Сводка метрик before/after для отчёта.rn   r   rh   rl   rm   r?   r@   rD   rE   )n_features_beforen_features_aftern_base_beforen_base_afterval_acc_beforeval_acc_afterlong_th_beforelong_th_aftershort_th_beforeshort_th_afterr?   r@   rD   rE   r,   )r   beforeafterr   r   s        r   r   r      s     $$89!"67 12/0 +y) +y)!*-
+'7(9%l3(9 r   c           	        SSK Jn  U" 5         [        R                  " 5       R	                  S5      nS[        US-  5       SU 3n/ nU  H^  n[        R                  " 5       n	 [        XUUUS9n
[        [        R                  " 5       U	-
  S-  S	5      U
S
'   UR                  U
5        M`     U$ ! [         a=  nSSKnUR                  5         UR                  SU[        U5      S.5         SnAM  SnAff = f)u  Параллельно-последовательная оптимизация для списка тикеров.

Args:
    tickers: список тикеров (SBER, GAZP, ...)
    keep_pct: доля признаков для сохранения (0.5 = 50%)
    min_features: минимально допустимое количество признаков
    verbose: печатать прогресс

Returns:
    Список dict с результатами per-ticker
r   )seed_everythingr   r   r   rQ   )r>   rM   r]   r   <   r+   elapsed_minNry   rz   )models.expertsr   r   r   r   rJ   timer   roundappendr   r   r   r   )tickersr>   rM   r   r   r   r]   outputr   t0rr   r   s                r   run_optimizationr     s      /<<>""#34Dc(S.12!D6:JFYY[
	R(5A3=079A  %diikB&6"%<a@AmMM!  M	  	R!MMWQPQQ	Rs   #AB..
C582C00C5c                 ,   SS K n [        R                  " SS9nUR                  S[        SSS9  UR                  S[
        S	S
S9  UR                  S[        SS9  UR                  5       nUR                  R                  S5       Vs/ s H7  o3R                  5       (       d  M  UR                  5       R                  5       PM9     nn[        XBR                  UR                  SS9n[        S5        [        SS SSS SSS SSS SSS SSS 35        [        S5        U H  nUR!                  S5      S:X  aL  [        US    S S!US"   S# S$US%   S& S'US(   S# S$US)   S& S'US*   S+ S,US-   S+ S.US/   S0 S1US2   S0 S335        Md  UR!                  S5      S4:X  aB  [        US    S S!US"   S# S$US%   S& S5US*   S+ S6US-   S+ S7UR!                  S8S95       35        M  [        UR!                  S S:5      S S;UR!                  S<S:5       35        M     g s  snf )=Nr   z.Optimize MoE models: keep top-N% base features)descriptionz	--tickerszSBER,GAZP,BITCOIN,EURUSDz6Tickers; comma-separated (default: 4 tickers analyzed))typedefaulthelpz
--keep-pct      ?uC   Доля признаков для сохранения (0.5 = 50%)z--min-features(   )r   r   ,T)r>   rM   r   u4   
=== Результаты оптимизации ===u
   Тикерz>8 wasz>20r   rh   zLong thz>16Verdictz>10zd----------------------------------------------------------------------------------------------------r{   r   r   z | r   3dz (r   2dz	 base) | r   r   r   r~   r|   r   u    | L≥r   z.2fu   →r   z | ACCEPTEDr   z base) | reverted | z vs z | REJECTED: r    ?z
 | ERROR: ry   )sysargparseArgumentParseradd_argumentr   r"   rJ   
parse_argsr   splitstripupperr   r>   rM   r   r   )r   parserargstr   resultsr   s          r   mainr   6  s   $$1abF
#7QU  W
5#b  d
(sB?D*.,,*<*<S*AO*AQWWY qwwy *AGOw-1->->(,.G 

AB	\"QuSk5+QyoQyQToUVW`adVe
fg	)55?d"Qx[$C*=(>r'B"QEWXZD[ \+,R01^3DR2H I)*3/uQ5G4L M-.s33q7I#6N O  !
 UU8_
*Qx[$C*=(>r'B"QEWXZD[ \""#$4"5c!:$q?QRU>V W!!"x!3 46 7 QUU8C(,JquuWS7I6JKL  Ps   H#"H__main__)r   r   returnr   )r   dictr   zdict[str, dict])r   r   )r   r   r>   r"   rM   rJ   r   ztuple[list[str], dict])r   r   r]   r   r   r   )r   r   r]   r   r   bool)r   r   r   r   )r   r   r   r   r   r   )r   r   NT)r   r   r>   r"   rM   rJ   r]   z
str | Noner   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   )r   r   T)
r   z	list[str]r>   r"   rM   rJ   r   r   r   z
list[dict]) __doc__
__future__r   r   r   r[   r   r   typingr   r   numpyr   r   r   dirnameabspath__file__r   r   r   r(   rV   ra   rd   rr   rv   r   r   r   r   __name__r,   r   r   <module>r      st  0 #  	       77<<8Q(RS '+V2 8;-/$'*$4J$N/ 14247;.2	R(-R,/R +5R (,	R 8<	Rj!*.&* $( -10 (+)+%)#$##&# ## /9#RMB zF r   