
    B!xj)Q              
          S r SSKrSSKrSSKrSSKrSSKr\R                  R                  S\R                  R                  \R                  R                  \R                  R                  \5      5      5      5        S rS rS rS rS rS rS	 rS
 rS rS rS rS rS rS rS rS rS rS rS rS r S r!S r"S r#S r$S r%S r&S r'S r(S r)S  r*S! r+S" r,S# r-S$ r.g)%u+  
Тесты для улучшений MoE v13 (audit 2026-08-03).

Покрывает:
  - Этап 0.1: SL validation в TradeManager._open_new_trade
  - Этап 0.2: cleanup_stale_signals в utils/sync_signals.py
  - Этап 0.3: Auto-disable (_auto_disable_check) в TradeManager
  - Этап 1.1: max_bars=48 в MARKET_TARGET_CONFIGS
  - Этап 1.2: crypto RR 1:1.5 (SL=3, TP=4.5)
  - Этап 2.1: threshold sweep [0.50, 0.85] шаг 0.01
  - Этап 4.1: per-ticker risk overrides (SBER, GAZP, VTBR, NVTK)
  - Этап 4.4: MOEX session filter
    Nc                      SSK Jn   U S   S   S:X  d   SU S   S    35       eU S   S   S:X  d   eU S   S   S	:X  d   eg
)uV   max_bars должен быть 48 для moex/forex (синхрон с TIME_STOP=48h).r   MARKET_TARGET_CONFIGSmoexmax_bars0   u,   moex max_bars должен быть 48, got forexcryptoH   N)configr   r   s    tests/test_v13_improvements.py&test_market_target_configs_max_bars_48r      sm    , (4: c
67LV7TU_7`6abc: )*5;;; *:6"<<<    c                      SSK Jn   U S   nUS   S:X  d   eUS   S:X  d   eUS   US   -  n[        US-
  5      S	:  d
   S
U 35       eg)uP   Crypto: SL=4.0×ATR, TP=6.0×ATR (RR 1:1.5). v12.9: расширены с 3/4.5.r   r   r
   atr_mult_sl      @atr_mult_tp      @g      ?{Gz?u)   crypto RR должен быть 1.5, got N)r   r   abs)r   r
   rrs      r   test_crypto_rr_1_1_5r   %   sl    ,"8,F- C'''- C'''		!6	6BrCx=4Q#LRD!QQr   c                      SSK Jn Jn  S H;  nU " U5      nUS   S:X  d   U SUS    35       eUS   S:X  a  M.   U S	US    35       e   g
)uV   SBER/GAZP/VTBR/NVTK получают узкий SL=2.5/TP=5 (RR 1:2 сохранён).r   )get_target_configTICKER_RISK_OVERRIDES)SBERGAZPVTBRNVTKMTSSZCASHr         @u#    SL должен быть 2.5, got r         @u#    TP должен быть 5.0, got N)r   r   r   )r   r   tickercfgs       r   test_per_ticker_risk_overridesr&   /   s|    ?C'=!S( 	Oh9#m:L9MN	O(=!S( 	Oh9#m:L9MN	O(	 Dr   c                      SSK Jn   S H;  nU " U5      nUS   S:X  d   U SUS    35       eUS   S:X  a  M.   U S	US    35       e   g
)uF   ASTR/PHOR (100% winrate) сохраняют default SL=4/TP=8 (v12.9).r   )r   )ASTRPHORr   r   u:    SL должен остаться 4.0 (звезда), got r          @u:    TP должен остаться 8.0 (звезда), got N)r   r   )r   r$   r%   s      r   +test_per_ticker_risk_overrides_keep_winnersr+   :   s    ("'=!S( 	fhPQTUbQcPde	f(=!S( 	fhPQTUbQcPde	f(	 #r   c                  |    SSK Jn   U " S5      u  pnUS:X  d   eUS:X  d   eU " S5      u  pnUS:X  d   eUS:X  d   eg	)
uN   get_risk_params возвращает per-ticker значения, не global.r   )get_risk_paramsr   r"   r#   r(   r   r*   N)r   r-   )r-   sltpmax_atr_s        r   'test_get_risk_params_returns_per_tickerr2   E   sQ    &%f-OBG9999'IBA9999r   c                  f    SSK Jn   U S   SL d   eU S   S:X  d   eU S   S:X  d   eU S	   S
:X  d   eg )Nr   TICKER_AUTO_DISABLEenabledT
min_trades
   min_window_pnl_pctg333333ÿcooldown_hours   )r   r5   r4   s    r   test_auto_disable_config_valuesr<   T   sS    *y)T111|,22234===/0C777r   c                  v    SSK Jn Jn  U " S5      SL d   eU " S5      SL d   e[        U[        5      (       d   eg )Nr   is_ticker_blacklistedTICKER_BLACKLISTr   Fr(   )r   r?   r@   
isinstancesetr>   s     r   test_blacklist_checkrC   \   s?    > (E111 (E111&,,,,r   c                  H    SSK Jn Jn  [        U 5      (       d   eUS:X  d   eg )Nr   cleanup_stale_signalsSTALE_SIGNAL_THRESHOLD_SECi`T  )utils.sync_signalsrF   rG   callablerE   s     r   !test_cleanup_stale_signals_existsrJ   h   s%    T)****%111r   c                  @    SSK Jn   [        U S5      (       d   S5       eg)uD   TradeManager должен иметь метод _auto_disable_check.r   TradeManager_auto_disable_checkz(TradeManager._auto_disable_check missingN)trade.managerrM   hasattrrL   s    r   )test_trade_manager_has_auto_disable_checkrQ   s   s#    *<!677 3237r   c                  n    SSK n SSKJn  U R                  UR                  5      nSU;   d   eSU;   d   eg)uR   _open_new_trade должен содержать SL MULT MISMATCH проверку.r   NrL   zSL MULT MISMATCHactual_sl_multinspectrO   rM   	getsource_open_new_traderU   rM   srcs      r   $test_trade_manager_has_sl_validationrZ   z   s:    *


L88
9C$$$s"""r   c                  ~    SSK n SSKJn  U R                  UR                  5      nSU;   d   eSU;   d   eSU;   d   eg)uE   _reentry_allowed должен содержать MOEX session filter.r   NrL   off_sessionzget_market(ticker)r   )rU   rO   rM   rV   _reentry_allowedrX   s      r   %test_trade_manager_has_session_filterr^      sF    *


L99
:CC3&&&S==r   c                  n    SSK n SSKJn  U R                  UR                  5      nSU;   d   eSU;   d   eg)uU   Ladder tiers должны масштабироваться под per-ticker tp_mult.r   NrL   zscale = tp_mult / 6.0scaled_tp_multrT   rX   s      r   +test_trade_manager_has_dynamic_ladder_scalera      s:    *


L88
9C"c)))s"""r   c                      [        [        R                  R                  [        R                  R	                  [
        5      SSS5      5      R                  5       n SU ;   d   S5       eSU ;   d   S5       eg)	uS   find_best_thr должен использовать sweep [0.50, 0.85] шаг 0.01...modelsmoe.pyznp.arange(0.50, 0.86, 0.01)uA   threshold sweep должен быть np.arange(0.50, 0.86, 0.01)z	0.95, 0.0z&no-trade protection (thr=0.95) missingNopenospathjoindirname__file__readrY   s    r   !test_moe_threshold_sweep_extendedro      se    
rww||BGGOOH5tXxP
Q
V
V
XC(C/ LKL/
 #GGGr   c                      [        [        R                  R                  [        R                  R	                  [
        5      SSS5      5      R                  5       n SU ;   d	  SU ;   d   egg)u7   clip порогов должен быть [0.50, 0.85].rc   rd   re   znp.clip(best_thr, 0.50, 0.85)zclip(best_thr, 0.50, 0.85)Nrf   rn   s    r   test_moe_threshold_clip_50_85rq      sW    
rww||BGGOOH5tXxP
Q
V
V
XC*c1'3./ /. 2r   c                  b    SSK Jn Jn  U " S5      u  p#nU" S5      nX%S   :X  d   eX5S   :X  d   eg)u`   SBER: get_risk_params и get_target_config должны давать одинаковые SL/TP.r   )r-   r   r   r   r   N)r   r-   r   )r-   r   sl_ptp_pr1   r%   s         r   #test_sber_risk_pipeline_consistencyru      s?    9#F+MD
F
#C}%%%%}%%%%r   c                      SSK Jn Jn  U " S5      S   nUS-  n[        US-
  5      S:  d   eUS   nUS	   S
   U-  nXRS-   ::  d   SU SU S35       eg)u   Ladder scale для SBER (tp_mult=5.0) должен быть 5/6 = 0.833.

Tier 1: 3.0 * 0.833 = 2.5×ATR
Tier 2: 4.5 * 0.833 = 3.75×ATR
Tier 3: 6.0 * 0.833 = 5.0×ATR (= tp_mult SBER, OK)
r   )r   RISK_CONFIGr   r   r   g?r   ladder_tiers      zscaled tier3 (u*   ) должен быть ≤ SBER tp_mult ()N)r   r   rw   r   )r   rw   sber_tpscaleladderscaled_tier3s         r   test_ladder_scale_for_sberr      s     6'6GcMEuw$&&&(F!9Q<%'LT>) \
&PQXPYYZ[\)r   c                  ~    SSK n SSKJn  U R                  UR                  5      nSU;   d   eSU;   d   eSU;   d   eg)uo   Ратчет-защита присутствует в _update_ladder_sl_after_tp: SL не ухудшается.r   NrL   zmax(old_sl, new_sl)zmin(old_sl, new_sl)	moved_any)rU   rO   rM   rV   _update_ladder_sl_after_tprX   s      r    test_ladder_sl_after_ratchet_srcr      sH    *


LCC
DC C''' C'''#r   c                     SSK n [        R                  R                  [        R                  R                  [        R                  R	                  [
        5      5      5      n/ SQnU HU  n[        R                  R                  X5      n[        U5       nU R                  UR                  5       5        SSS5        MW     g! , (       d  f       Mi  = f)ux   Все изменённые файлы должны парситься без синтаксических ошибок.r   N)z	config.pyztrade/manager.pyzutils/sync_signals.py
monitor.pyzmodels/moe.pyzfeatures/technical.pyzfeatures/feature_cols.pyzfeatures/mtf_context.py)
astrh   ri   rk   abspathrl   rj   rg   parserm   )r   basefilesfri   fps         r   test_all_modified_files_parser      s~    77??277??277??8+DEFD	E ww||D$$Z2IIbggi  Z Zs    C
C	c                  X    SSK Jn   U " [        R                  " / SQ5      5      S:X  d   eg)uK   Hurst на короткой серии возвращает 0.5 (fallback).r   	_rs_hurst)      ?g       @g      @      ?N)features.technicalr   nparrayr   s    r   #test_hurst_random_walk_short_seriesr      s!    ,RXXo./3666r   c            
         SSK Jn   [        R                  R	                  S5        Sn[        R
                  " [        R                  R                  U5      S-  S-   5      S-   n[        R                  " X"S-   US-
  [        R                  R                  SS	U5      S
.5      nU " U5      nUS   R                  S   nUS:  d
   SU 35       eUS   R                  S   S:X  d   eUS   R                  S   S:  d   eg)u2   Trending series (random walk + drift) → H > 0.5.r   )	add_hurst*     g333333?g?d   rz     CloseHighLowVolume	hurst_100r   u0   Trending Hurst должен быть > 0.5, got is_trendingr   hurst_confidenceN)r   r   r   randomseedcumsumrandnpd	DataFramerandintiloc)r   ntrenddfhs        r   test_hurst_trending_seriesr      s    ,IINN2AIIbiiooa(3.45;E		%!)))##Cq1 
B 
2B
;R As7JFqcJJ7m!!"%,,, !&&r*Q...r   c                      SSK Jn   [        R                  R	                  S5        [        S5       H:  nU " [        R                  R                  S5      5      nSUs=::  a	  S::  a  M7   e   e   g)u   Hurst всегда в [0, 1].r   r         g        r   N)r   r   r   r   r   ranger   )r   r1   r   s      r   test_hurst_in_ranger     sO    ,IINN11Xbiiooc*+a3 r   c                      SSK Jn   [        R                  " / SQ/ SQ/ SQ/ SQS.5      nU " U5      nUS   R                  S	   S
:X  d   eUS   R                  S	   S:  d   eg)u-   VWAP: цена растёт → above_vwap=1.r   )add_vwap_features)r   e   f   g   h   )r   r   r   r   i   )c   r   r   r   r   )r   r   i,  i  r   r   
above_vwapr   r   vwap_dist_pctN)r   r   r   r   r   )r   r   s     r   !test_vwap_above_when_price_risingr     sl    4	**)*	 
B 
2	Bl  $+++o##B'!+++r   c                  H   SSK Jn   [        R                  R	                  S5        SnS[        R
                  " [        R                  R                  U5      S-  5      -   n[        R                  " [        R                  " U5      S-  S-   S/U-  S	/U-  X"S
-   US
-
  U[        R                  R                  SSU5      S.5      nU " U5      n/ SQnU HK  nXSR                  ;   d
   U S35       eX5   R                  5       R                  5       (       d  MC   U S35       e   g)u9   engineer_features добавляет все v13 features.r   )engineer_features   r   r   i  i Sez
2026.01.01z10:00rz   r   )	timestampDateTimeOpenr   r   r   r   r   r   is_mean_revertingr   vwap_24r   r   z missingz all NaNN)r   r   r   r   r   r   r   r   r   aranger   columnsisnaall)r   r   pricesr   expectedcs         r   #test_engineer_features_has_v13_colsr     s    4IINN1A299RYY__Q/#566F	YYq\D(:5"WIM
6A:299#4#4S$#B	 
B 
2	B:HJJ.1#X.5::<##%%5!H~5% r   c                  P    SSK Jn   SU ;   d   eSU ;   d   eSU ;   d   eSU ;   d   eg)uA   MTF_CONTEXT_COLS содержит новые divergence-фичи.r   MTF_CONTEXT_COLSmtf_h1_d1_rsi_divmtf_h1_d1_divergence_flagmtf_h1_w1_rsi_divmtf_trend_confluence_3tfN)features.mtf_contextr   r   s    r   test_mtf_context_cols_extendedr   3  sA    5"2222&*::::"2222%)9999r   c                  @    SSK Jn   S H  nX;   a  M
   U S35       e   g)u1   EXTRA_FEATURE_COLS включает Hurst + VWAP.r   )EXTRA_FEATURE_COLSr   z not in EXTRA_FEATURE_COLSN)features.feature_colsr   )r   r   s     r   'test_feature_cols_registry_includes_v13r   <  s*    88&H1#-G(HH&8r   c                  N    SSK Jn   U " 5       nS H  nX!;   a  M
   U S35       e   g)u4   clean_base_features() включает v13 features.r   )clean_base_features)r   r   z not in clean_base_features()N)r   r   )r   colsr   s      r   %test_clean_base_features_includes_v13r   D  s.    9 D+y=QC<==y ,r   c                      [        [        R                  R                  [        R                  R	                  [
        5      SSS5      5      R                  5       n SU ;   d   eSU ;   a  SU ;   d   eSU ;   d   eg)	u^   MONOTONE_FEATURES dict присутствует и содержит ключевые фичи.rc   rd   re   MONOTONE_FEATURESz'rsi':z-1z'hurst_100':Nrf   rn   s    r   test_monotone_features_dictr   P  sd    
rww||BGGOOH5tXxP
Q
V
V
XC#%%%s?ts{**S   r   c                      [        [        R                  R                  [        R                  R	                  [
        5      SSS5      5      R                  5       n SU ;   d   eSU ;   d   eSU ;   d   eg)u|   MONOTONE_FEATURES dict сохранён в moe.py (функция отключена до
обновления XGBoost API).rc   rd   re   r   _build_monotone_constraintsmonotone_constraintsNrf   rn   s    r   (test_monotone_constraints_applied_to_xgbr   Z  sa     rww||BGGOOH5tXxP
Q
V
V
XC#%%%(C///!S(((r   c            
         SSK n U R                  R                  S[        R                  R	                  [        R                  R	                  [        R                  R                  [        5      5      5      5        SSKJnJ	n  / SQnU" US5      n[        U[        5      (       d   S[        U5       35       eUS   S:X  d   eUS	   S:X  d   eUS
   S:X  d   eUS   S:X  d   eUS   S:X  d   eSU;  d   eU" US5      n[        U5      S:X  d   eUSS U:X  d   eUS   R                  S5      (       d   eUS   R                  S5      (       d   eg)u4   Тест функции _build_monotone_constraints.r   N)r   _build_feature_names_list)rsiatr_pct
choppinessr   adxunknown   u    Должен быть dict, got r   r   r   r   r   rz   r   r   r8      expert_	   )sysri   insertrh   rk   r   rl   
models.moer   r   rA   dicttypelen
startswith)r   r   r   test_featuresconstraintsnamess         r   (test_build_monotone_constraints_functionr  d  sP   HHOOArwwrwwrwwx7P'QRSQSM-mQ?Kk4((`,LTR]M^L_*``(u###y!R'''|$***{#q(((u"""K''' &mR8Eu:!9%%%8y))))8y))))r   c                      [        [        R                  R                  [        R                  R	                  [
        5      SSS5      5      R                  5       n SU ;   d   eSU ;   d   eSU ;   a  SU ;   d   eSU ;   a  S	U ;   d   eS
U ;   d   eg)uH   db/queries.py содержит signal_log CREATE SQL и insert function.rc   dbz
queries.pyz%CREATE TABLE IF NOT EXISTS signal_logzdef insert_signal_logp_longp_shortthreshold_longthreshold_shortflat_filterNrf   rn   s    r   test_signal_log_table_sqlr    s    
rww||BGGOOH5tT<P
Q
V
V
XC2c999"c)))s?yC///s"'8C'???Cr   c                      [        [        R                  R                  [        R                  R	                  [
        5      SS5      5      R                  5       n SU ;   d   eSU ;   d   eg)u;   monitor.py должен вызывать insert_signal_log.rc   r   insert_signal_log
signal_logNrf   rn   s    r   test_monitor_calls_signal_logr    sO    
rww||BGGOOH5t\J
K
P
P
RC#%%%3r   c                      [        [        R                  R                  [        R                  R	                  [
        5      SSS5      5      R                  5       n SU ;   d   eSU ;   d   eg)uA   Notifier должен иметь метод send_ticker_disabled.rc   utilsznotifier.pyzdef send_ticker_disabledzAuto-DisabledNrf   rn   s    r   "test_notifier_send_ticker_disabledr    sQ    
rww||BGGOOH5tWmT
U
Z
Z
\C%,,,c!!!r   c                      [        [        R                  R                  [        R                  R	                  [
        5      SS5      5      R                  5       n SU ;   d   eSU ;   d   eSU ;   d   eg)u:   main.py должен иметь --daily-report subcommand.rc   zmain.pyz'--daily-report'daily_report_entry_daily_reportNrf   rn   s    r   !test_main_daily_report_subcommandr    s]    
rww||BGGOOH5tYG
H
M
M
OC$$$S    C'''r   c                      [        [        R                  R                  [        R                  R	                  [
        5      SSS5      5      R                  5       n SU ;   d   eSU ;   d   eSU ;   d   eSU ;   d   eg)	uC   analysis/pipeline.py должен иметь generate_daily_report.rc   analysiszpipeline.pyzdef generate_daily_reporttrades_closedtrades_openzSanity checksNrf   rn   s    r   #test_analysis_pipeline_daily_reportr    sm    
rww||BGGOOH5tZW
X
]
]
_C&#---c!!!Cc!!!r   )/__doc__rh   r   numpyr   pandasr   pytestri   r   rk   r   rl   r   r   r&   r+   r2   r<   rC   rJ   rQ   rZ   r^   ra   ro   rq   ru   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r   r   r   <module>r!     s       277??277??277??83L#MN O=ROf8-23##H/&\$!07/",6(:I>!)*: "("r   