
    z4tjzY                        S SK rS SKrS SKJr    S&S\R                  S\R                  S\R                  S\R                  S\S\S	\S
\S\\R                     S\\R                     S\R                  4S jjr	   S'S\R                  S\S\S	\S\R                  4
S jjr    S(S\R                  S\S\S	\S\S\R                  4S jjrS\R                  S\R                  S\R                  S\R                  S\S\S\S\\R                  \R                  \R                  \R                  4   4S jr     S)S\R                  S\S\S	\S\S\S\R                  4S jjrS\R                  S\R                  S\S\S\4
S jrS\R                  S\R                  S\S\S\4
S jr S*S\R                  S\S\R                  4S jjrS\R                  S\R                  S\S\S\\\4   4
S jrS\R                  S\R                  S\S\S\\\4   4
S jr/ S QrS!r  S+S\R                  S"\\   S-  S#\\\\4   -  S\R                  4S$ jjrS\R                  S\R6                  4S% jrg),    N)Optionalcloseshighslows
atr_valuesatr_mult_slatr_mult_tpmax_bars	direction	sl_levels	tp_levelsreturnc
                    [        U 5      n
[        R                  " U
[        R                  S9nUS:  [        R                  " U5      ) -  nUb  U	b  UnU	nO#US:X  a  XU-  -   nXU-  -
  nOXU-  -
  nXU-  -   n[        U
S-
  5       H|  nX   (       d  M  X   nX   n[        XU-   S-   5      n[        US-   U5       HB  nUS:X  a  UU   U:  nUU   U:*  nOUU   U:*  nUU   U:  nU(       a  SX'     Mn  U(       d  MA    Mz     M~     U$ )u  Outcome scan: TP/SL hit detection within max_bars forward bars.

Outcome: 1 = TP hit first, 0 = SL hit first, 0 = no hit / unresolved.

Если sl_levels/tp_levels переданы — использует их вместо ATR-based расчёта.

Args:
    sl_levels: Кастомные SL уровни (для swing-based стратегии).
    tp_levels: Кастомные TP уровни.
dtype绽|=   )lennpzerosint32isnanrangemin)r   r   r   r   r   r	   r
   r   r   r   noutcomesvalidsltpitp_levelsl_levellimitjtp_hitsl_hits                         features/targets.py_scan_outcomer(      s9   , 	FAxx*H%RXXj%9$9:E !6>{22B{22B{22B{22B1q5\x55A8|a'(q1ue$AA~qX-aH,aH,qX-v % * O    dfc                 j   U R                  5       n SU R                  ;  a  [        S5      eU S   R                  nU S   R                  nU S   R                  nU S   R                  n[	        U 5      n[
        R                  " U[
        R                  S9n	[        US-
  5       H  n
[
        R                  " Xz   5      (       d  Xz   S:X  a  M*  XJ   nXU
   U-  -   nXU
   U-  -
  n[        XU-   S-   5      n[        U
S-   U5       H!  nX_   U:  a  SX'     Mt  Xo   U::  d  M  S	X'     M     M     XS
'   U $ )Natrz7add_atr() must be called before compute_trade_outcome()CloseHighLowr   r   r      outcome)copycolumns
ValueErrorvaluesr   r   r   r   r   r   r   )r*   r   r	   r
   r   r   r   r   r   r   r    entryr   r   r#   r$   s                   r'   compute_trade_outcomer7   E   s4    
BBJJRSS[FvJEe9DE!!JBAxx*H1q5\88JM""jmq&8	][00][00A8|a'(q1ue$Ax2~w"} %   yMIr)   disable_shortc                    U R                  5       n SU R                  ;  a  [        S5      eU S   R                  U S   R                  U S   R                  U S   R                  4n[	        / UQUPUPUP7SS06U S'   U(       d  [	        / UQUPUPUP7SS	06U S
'   [	        / UQUPUPSP7SS06U S'   U(       d  [	        / UQUPUPSP7SS	06U S'   U $ )u   Вычисляет SL/TP outcomes (long и опционально short).

Args:
    disable_short: Если True — outcome_short НЕ вычисляется
                   (для MOEX-тикеров, где short-selling недоступен).
r,   z7add_atr() must be called before compute_dual_outcomes()r-   r.   r/   r   r   outcome_longoutcome_short   outcome_long_fastoutcome_short_fast)r2   r3   r4   r5   r(   )r*   r   r	   r
   r8   arrs         r'   compute_dual_outcomesrA   k   s    
BBJJRSS 	7BvJ--r%y/?/?
5	C ']][]+]x][\]B~+cSc+c{cHc`bc? ,\S\+\{\B\Z[\B#0#b##b{#bK#bQS#b_a#b Ir)   swing_lookbackc                    [        U 5      n[        R                  " U5      n[        R                  " U5      n	[        R                  " U5      n
[        R                  " U5      n[        U5       GH  n[        R                  " X<   5      (       d  X<   S:  a  M+  X<   nX   n[        SX-
  5      nX:  a  [        R                  " X/US-    5      OXS-  -
  nX:  a  [        R
                  " XUS-    5      OXS-  -   nXU-  -
  n[        UU5      nXS-  -
  nXS-  -
  n[        R                  " UUU5      X'   XU-  -   X'   XU-  -   n[        UU5      nXS-  -   nXS-  -   n[        R                  " UUU5      X'   XU-  -
  X'   GM     XX4$ )uN  
Вычисляет SL на основе свингов (support/resistance) и ATR-based TP.

Для long:
    swing_sl = min(low[i-swing_lookback : i])  — поддержка
    atr_sl = close - atr * atr_mult_sl         — ATR-minimal distance
    SL = min(swing_sl, atr_sl)                 — более консервативный (ближе)
    TP = close + atr * atr_mult_tp

Для short:
    swing_sl = max(high[i-swing_lookback : i]) — сопротивление
    atr_sl = close + atr * atr_mult_sl
    SL = max(swing_sl, atr_sl)
    TP = close - atr * atr_mult_tp

Гарантии:
    - SL расстояние ≥ 0.5×ATR (не слишком близко)
    - SL расстояние ≤ 5.0×ATR (не слишком далеко)

Returns:
    (sl_long, tp_long, sl_short, tp_short) — np.ndarray уровней.
r   r   r   r0   g      @      ?)r   r   r   r   r   maxr   clip)r   r   r   r   rB   r   r	   r   sl_longtp_longsl_shorttp_shortr    r,   r6   lookback_startswing_supportswing_resistatr_slcandidate_slmin_slmax_slatr_sl_shortcandidate_sl_shortmin_sl_shortmax_sl_shorts                             r'   _compute_swing_sl_levelsrV      s   : 	FAhhqkGhhqkGxx{Hxx{H1X88JM""jme&;m	 Q 23<=<Nt1Q378TYbc\cTc<=<Nrvve1Q378TYbc\cTc {** =&1s"s"WW\66:
;..
 [00 |<Sy(Sy(gg0,MK//; > X//r)   c                    U R                  5       n SU R                  ;  a  [        S5      eU S   R                  nU S   R                  nU S   R                  nU S   R                  n	[	        XgXXAU5      u  pp[        XgXXUSXS9
U S'   U(       d  [        XgXXUS	XS9
U S
'   [        XgXXSSXS9
U S'   U(       d  [        XgXXSS	XS9
U S'   XS'   XS'   U(       d  XS'   XS'   U $ )u  
Dual-Outcome таргеты со Swing-based SL вместо чисто ATR-based.

SL размещается на ближайшем уровне поддержки/сопротивления
(минимум/максимум за последние swing_lookback баров до входа),
что делает стоп-лосс рыночно-обоснованным.

TP остаётся ATR-based (close + atr_mult_tp × ATR).

Args:
    df: DataFrame с колонкой 'atr'.
    atr_mult_sl: Множитель ATR для fallback SL.
    atr_mult_tp: Множитель ATR для TP.
    max_bars: Максимум баров для сканирования forward.
    swing_lookback: Размер окна для поиска support/resistance.
    disable_short: Если True — outcome_short НЕ вычисляется
                   (для MOEX-тикеров, где short-selling недоступен).

Returns:
    DataFrame с колонками outcome_long/outcome_short + _fast.
r,   z=add_atr() must be called before compute_swing_dual_outcomes()r-   r.   r/   r   )r   r   r   r:   r;   r<   r=   r>   r?   swing_sl_longswing_tp_longswing_sl_shortswing_tp_short)r2   r3   r4   r5   rV   r(   )r*   r   r	   r
   rB   r8   r   r   r   r   rG   rH   rI   rJ   s                 r'   compute_swing_dual_outcomesr\      s0   : 
BBJJXYY[FvJEe9DE!!J+Ct[,(Gh 't(aB~
 +4h"
? ,t"B
 #04bB$
  "!''Ir)   highlowstartendc                    [         R                  " XU 5      nU[         R                  " XU 5      -   nXR-
  nUS-
  U-
  n[        Xg5      nUS:  a+  XX-
  n	XX-   S-   n
X   [         R                  " X	U
 5      :  a  U$ [	        US-   US-
  5       Ho  nX   XS-
     :  d  M  X   XS-      :  d  M!  X-
  nUS-
  U-
  n[        X5      nUS:  d  M@  X-
  n	X-   S-   n
X   [         R                  " X	U
 5      :  d  Mk  X   s  $    U$ )u  
Ищет ближайший валидный пик в окне [start, end).

Критерии пика:
  1. High[j] — локальный максимум (High[j] >= High[j-1] и High[j] >= High[j+1])
  2. Пик центрирован: в симметричном окне [j-r : j+r], где r = min(j-start, end-1-j),
     нет High выше, чем High[j] (т.е. j — истинная вершина на этом участке графика)
  3. Выбирается первый (ближайший к start) валидный пик

Если валидный пик не найден — возвращает глобальный максимум окна (фолбэк).
r   r0   )r   rE   argmaxr   r   r]   r^   r_   r`   
global_maxrb   	left_dist
right_distradiuscheck_lcheck_rr$   left_dright_drs                  r'   _find_valid_peak_highrm   %  s   $ 3(JRYYt#//F Iq6!J'F{//A%<266$w"788 519cAg&7dq5k!dg!e&<YFAgkGF$AAv%%!)7bffT'%:;;7N ' r)   c                    [         R                  " XU 5      nU[         R                  " XU 5      -   nXR-
  nUS-
  U-
  n[        Xg5      nUS:  a+  XX-
  n	XX-   S-   n
X   [         R                  " XU
 5      ::  a  U$ [        US-   US-
  5       Ho  nX   XS-
     ::  d  M  X   XS-      ::  d  M!  X-
  nUS-
  U-
  n[        X5      nUS:  d  M@  X-
  n	X-   S-   n
X   [         R                  " XU
 5      ::  d  Mk  X   s  $    U$ )uy  
Ищет ближайшую валидную впадину в окне [start, end).

Критерии впадины:
  1. Low[j] — локальный минимум (Low[j] <= Low[j-1] и Low[j] <= Low[j+1])
  2. Впадина центрирована: в симметричном окне [j-r : j+r], где r = min(j-start, end-1-j),
     нет Low ниже, чем Low[j]
  3. Выбирается первая (ближайшая к start) валидная впадина

Если валидная впадина не найдена — возвращает глобальный минимум окна (фолбэк).
r   r0   )r   r   argminr   r]   r^   r_   r`   
global_minro   re   rf   rg   rh   ri   r$   rj   rk   rl   s                  r'   _find_valid_trough_lowrr   U  s   $ #'JRYYs~..FIq6!J'F{//A%;"&&W!566 519cAg&6SQZCFca%j$8YFAgkGF$AAv%%!)6RVVC$8996M ' r)   horizonc                    U R                  5       n U S   R                  nU S   R                  nU S   R                  n[        U 5      n[        R                  " U[        R
                  [        R                  S9n[        R                  " U[        R
                  [        R                  S9n[        XQ-
  5       H@  nUS-   n	US-   U-   n
[        X4X5      n[        X4X5      nXU   -  S-
  Xh'   XU   -  S-
  Xx'   MB     X`S'   XpS'   U $ )	u  
Для каждой свечи находит ближайший ВАЛИДНЫЙ пик и впадину
в следующих `horizon` барах.

Пик/впадина считаются валидными, если:
  - Это локальный экстремум (выше/ниже соседей)
  - Центрирован: в симметричном окне [pos-r : pos+r] нет точек выше/ниже

Возвращает регрессионные цели:
  pct_to_peak:    High[пик] / Close[i] - 1  — потенциал роста (+, %)
  pct_to_trough:  Low[впадина] / Close[i] - 1  — потенциал падения (-, %)
r-   r.   r/   r   r         ?pct_to_peakpct_to_trough)
r2   r5   r   r   fullnanfloat64r   rm   rr   )r*   rs   closer]   r^   r   rv   rw   r    r_   r`   	peak_high
trough_lows                r'   compute_peak_trough_targetsr~     s      
BwKEf:D
U)

CBA''!RVV2::6KGGArvvRZZ8M1;A!ego)$U@	+DuB
"1X-3%a036   $}'Ir)   c                 l   [        [        R                  " XU 5      5      n[        U[        R                  " XU 5      -   5      nXR-
  nUS-
  U-
  n[        Xg5      nUS:  a6  XX-
  n	XX-   S-   n
X   [        R                  " X	U
 5      :  a  [        U5      U4$ [        US-   US-
  5       Hz  nX   XS-
     :  d  M  X   XS-      :  d  M!  X-
  nUS-
  U-
  n[        X5      nUS:  d  M@  X-
  n	X-   S-   n
X   [        R                  " X	U
 5      :  d  Mk  [        X   5      U4s  $    [        U5      U4$ )u]   
Как _find_valid_peak_high, но возвращает (значение, позиция).
r   r0   )floatr   rE   intrb   r   r   rc   s                  r'   _find_valid_peak_with_posr     sB    rvvdo./J4c?334FIq6!J'F{//A%<266$w"788$f,,519cAg&7dq5k!dg!e&<YFAgkGF$AAv%%!)7bffT'%:;; >1,, ' f$$r)   c                 l   [        [        R                  " XU 5      5      n[        U[        R                  " XU 5      -   5      nXR-
  nUS-
  U-
  n[        Xg5      nUS:  a6  XX-
  n	XX-   S-   n
X   [        R                  " XU
 5      ::  a  [        U5      U4$ [        US-   US-
  5       Hz  nX   XS-
     ::  d  M  X   XS-      ::  d  M!  X-
  nUS-
  U-
  n[        X5      nUS:  d  M@  X-
  n	X-   S-   n
X   [        R                  " XU
 5      ::  d  Mk  [        X   5      U4s  $    [        U5      U4$ )u^   
Как _find_valid_trough_low, но возвращает (значение, позиция).
r   r0   )r   r   r   r   ro   r   rp   s                  r'   _find_valid_trough_with_posr     sB    rvvcn-.J3S>223FIq6!J'F{//A%;"&&W!566$f,,519cAg&6SQZCFca%j$8YFAgkGF$AAv%%!)6RVVC$899 =!++ ' f$$r)   )
   r=   <      horizonsmin_atr_movec                 P   U R                  5       n Uc  [        nSU R                  ;   a  U S   R                  nO[        R
                  " U S   U S   -
  [        R
                  " [        R                  " U S   U S   R                  S5      -
  5      [        R                  " U S   U S   R                  S5      -
  5      5      5      nUR                  [        5      R                  5       U S   -  R                  S5      R                  nU S   R                  nU S   R                  nU S   R                  n[        U 5      n[        U[        5      (       a  Un	OU V
s0 s H  oU_M     n	n
U GH/  n
U	R                  U
S5      n[        R                   " U[        R"                  [        R$                  S9n[        R                   " U[        R"                  [        R$                  S9n[        R                   " U[        R"                  [        R$                  S9n[        R                   " U[        R"                  [        R$                  S9n[        R                   " US	[        R&                  S9n[        R                   " US	[        R&                  S9n[        R(                  " U[        R*                  S9n[-        X-
  5       H  nUS-   nUS-   U
-   n[/        XgUU5      u  nn[1        XgUU5      u  nnUUU   -  S
-
  nUUU   -  S
-
  nUUU'   UUU'   [3        UU   S5      nUU-  UU'   UU-  UU'   UU-
  UU'   UU-
  UU'   UUU-  :  d  [        U5      UU-  :  a  S
OSUU'   M     XSU
 3'   XSU
 3'   XSU
 3'   XSU
 3'   UU SU
 3'   UU SU
 3'   UU SU
 3'   GM2     U $ s  sn
f )u  
Улучшенные регрессионные цели для MoERegression v2.

Для каждого бара i и каждого горизонта h:
  - pct_to_peak_h:    % до пика в окне h баров (всегда ≥ 0)
  - pct_to_trough_h:  % до впадины в окне h баров (всегда ≤ 0)
  - peak_norm_h:      pct_to_peak_h / atr_pct (в единицах ATR)
  - trough_norm_h:    pct_to_trough_h / atr_pct (в единицах ATR)
  - bars_to_peak_h:   кол-во баров до достижения пика
  - bars_to_trough_h: кол-во баров до достижения впадины
  - move_exists_h:    1 если есть движение > min_atr_move × ATR

ATR-нормализация переводит цели в единую шкалу «сколько ATR», что
позволяет модели одинаково хорошо работать в разных режимах волатильности.

Время до экстремума (bars_to_*) позволяет модели различать
«скоро будет пик» (через 3 бара) и «когда-нибудь будет пик» (через 55).

Параметры:
  min_atr_move: множитель ATR для move_exists.
                 Если float — применяется ко всем горизонтам.
                 Если dict — {horizon: threshold} для каждого горизонта.
atr_pctr.   r/   r-   r   r   rD   r   r;   ru   gư>g        pct_to_peak_pct_to_trough_
peak_norm_trough_norm_bars_to_peak_bars_to_trough_move_exists_)r2   REGRESSION_HORIZONSr3   r5   r   maximumabsshiftrollingDEFAULT_ATR_PERIODmeanfillnar   
isinstancedictgetrx   ry   rz   r   r   float32r   r   r   rE   )r*   r   r   r   trr{   r]   r^   r   move_thresholdshthrpct_peak
pct_trough	peak_normtrough_norm	bars_peakbars_troughmove_existsr    r_   r`   p_valp_post_valt_pospeak_pct
trough_pctatr_is                                r'   #compute_enhanced_regression_targetsr     sx   8 
B& BJJY-&&ZZvJE"JJr&zBwK$5$5a$889r%y2g;#4#4Q#778
 ::016682g;FNNqQXXwKEf:D
U)

CBA ,%%&4<=Hql?H=!!!S)771bffBJJ7WWQbjj9
GGArvvRZZ8	ggarzz:GGAr2	gga2884hhq

3quAEEa%!)C4TsKLE56t%MLE5uQx'#-Hq)C/J"HQK&JqM 
D)E#e+IaL'%/KN !19IaL"QYKN &.e%;s:QTW\Q\?\ScfKN/ 2 "*\!#-^A3 (Zs!,\!"+]1#$/_QC !!,\!S V I[ >s   N#c                 D    U S   R                  5       R                  5       $ )Nr1   )value_counts
sort_index)r*   s    r'   outcome_distributionr   S  s    i=%%'2244r)   )NN)      ?      @d   )r   r   r   F)r   g      @      F)r   )NrD   )numpyr   pandaspdtypingr   ndarrayr   r   r(   	DataFramer7   boolrA   tuplerV   r\   rm   rr   r~   r   r   r   r   listr   r   Seriesr    r)   r'   <module>r      s      '+&*<JJ<::< **< 

	<
 < < < < 

#< 

#< ZZ<B 	#
## # 	#
 \\#P 
  	
  \\JB0JJB0!zzB013B0

B0 B0 	B0
 B0 2::rzz2::rzz9:B0N L
LL L 	L
 L L \\L`-
**-	- - 
	-
 -`+
**+	+ + 
	+
 +` %
%% \\%X%
**%::%.1%8;%
5#:%:%
**%::%.1%8;%
5#:%: #  
 "&-0c
c3i$c $sEz**c \\	cL5R\\ 5bii 5r)   