
    pj5H              
         d Z ddlmZ ddlmZ ddlmZ ddlZddl	Z
ed   Zed   Zed   Zed	   Zed
   Zed   Zed   Zed   Zd$dZdd	 	 	 	 	 	 	 d%dZ	 	 	 	 	 	 	 	 	 	 	 	 d&dZdddddddddd		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d'dZ ed       G d d             Ze G d d              Zd!dd"	 	 	 	 	 	 	 d(d#Zy))u  
Moving-average slope momentum engine.

Hypothesis: steeper positive (negative) MA slope → stronger trend → better
long (short) follow-through. Slope is measured over a lookback window on the
MA line itself (not raw price), which smooths noise vs price ROC.

Signal at bar *t* → position from *t+1* (no lookahead).

Stage 4 extensions: dual-timeframe confirmation, regime gates (SMA200 / VIX),
vol-scaled sizing, ATR chandelier exits.
    )annotations)	dataclass)LiteralN)smaema)pct
regressionannualized_pct)	long_only
long_short)
slope_flip
slope_halfprice_crossslope_or_priceatr_chandelier)singledual_timeframe)nonesma200vix_lt)binary	vol_scale)dual_product
dual_blendfastslowdual_minc                  | j                  t        j                        }|dk  rt        d      |dk(  r!|j	                  ||      j                         S |dk(  r"|j                  |d|      j                         S t        d|      )	Nr   zperiod must be > 0r   )windowmin_periodsr   F)spanadjustr    zunknown ma_type: )astypenpfloat64
ValueErrorrollingmeanewm)closema_typeperiodcs       B/opt/rentech/trading_bot/RenTech/strategy_stack/ma_slope_engine.py
compute_mar/       s    RZZ A{-..%yyFy;@@BB%uu&FuCHHJJ
(4
55    r   )methodc                  |dk  rt        d      | j                  t        j                        }|dk(  r7|j	                  |      }||z
  |j                  dt        j                        z  S |dk(  rH|j	                  |      }||z
  |j                  dt        j                        z  }|dt        |      z  z  S |dk(  r|j                  t        j                        }t        |      }t        j                  |t        j                  t        j                        }t        j                  |t        j                        }	|	j                         }
|	|
z
  d	z  j                         }|dk  r!t        j                  || j                   
      S t#        |dz
  |      D ]  }|||z
  dz   |dz    }t        j$                  t        j&                  |             r;|j                         }t)        |      dk  rZ|	|
z
  ||z
  z  j                         }||z  }||z  ||<    t        j                  || j                   
      S t        d|      )u5  
    Slope of the MA line over ``lookback`` bars.

    Methods
    -------
    pct
        (MA_t - MA_{t-lookback}) / MA_{t-lookback}
    annualized_pct
        pct * (252 / lookback) — comparable across lookbacks
    regression
        OLS slope of MA vs bar index, divided by MA level (dimensionless)
    r   zlookback must be > 0r           r
        o@r	   dtype   index   -q=zunknown slope method: )r&   r#   r$   r%   shiftreplacenanfloatto_numpylenfullaranger(   sumpdSeriesr9   rangeanyisfiniteabs)malookbackr1   mpastrawarrnoutxx_meanx_varir   y_meancovslopes                    r.   compute_ma_sloperZ   +   s   $ 1}/00
		"**Awwx DDLLbff555!!wwx 4x4<<RVV44eeHo-..jjrzzj*Hggarzz2IIhbjj1f*"'')A:99S11x!|Q' 		$AX)AE2Fvvr{{6**+[[]F6{U"J6F?388:C%KEV^CF		$ yyBHH--
-fZ8
99r0   c                   | j                  ||      j                  d      t        j                  d      z  }||j	                  dt        j
                        z  }|j                  ||      j                  d      S )Nr    r:   ddofr4   r3         ?)r'   stdr$   sqrtr=   r>   clipfillna)ret
target_volr   floorcaprealizedscales          r.   compute_vol_scalerj   b   sh     {{6v{6:::BRWWU^SH))#rvv66E::eS!((--r0   r   
   2      r3   Tr   )	r+   fast_periodslow_periodfast_lookbackslow_lookbackslope_methodentry_slope_minprice_above_marank_metricc       	           | j                  t        j                        }
t        |
||      }t        |
||      }t	        |||      }t	        |||      }||kD  ||kD  z  }|r||
|kD  z  }|	dk(  r||z  }nH|	dk(  r	||z   dz  }n:|	dk(  r|}n2|	dk(  r|}n*|	dk(  rt        j
                  ||      }nt        d	|	      |j                  |      }t        j                  |||||d
|
j                        S )u   
    Cross-sectional ranking score from fast/slow MA slopes (stage-4 SPY logic).

    Returns ``fast_slope``, ``slow_slope``, ``rank_score``. Ineligible names → NaN score.
    r+   r,   rL   r1   r   r         ?r   r   r   zunknown rank_metric: )fast_maslow_ma
fast_slope
slow_slope
rank_scorer8   )r#   r$   r%   r/   rZ   minimumr&   whererE   	DataFramer9   )r*   r+   rn   ro   rp   rq   rr   rs   rt   ru   r-   rz   r{   r|   r}   eligiblescores                    r.   compute_slope_rank_scorer   o   s   $ 	RZZ AGK@GGK@G!'M,WJ!'M,WJ_,o1MNHq7{+n$Z'		$j(C/					
	"

:z20@AAKK!E<<$$	
 gg	 	r0   )frozenc                     e Zd ZU dZded<   dZded<   dZded<   d	Zd
ed<   dZded<   dZ	ded<   dZ
ded<   dZded<   dZded<   dZded<   dZded<   dZded <   dZd!ed"<   dZd#ed$<   d%Zd&ed'<   d(Zded)<   dZded*<   d+Zded,<   d-Zd.ed/<   d0Zded1<   d2Zded3<   d4Zded5<   d6Zded7<   d8Zded9<   d:Zded;<   d<Zded=<   d?d>Zy)@MaSlopeConfigr   MaTyper+   rl   int	ma_periodrk   slope_lookbackr   SlopeMethodrr   g{Gz?r?   rs   Nzfloat | Noneexit_slope_maxTboolrt   r   	Direction	directionr   ExitMode	exit_moder   min_hold_daysr   
SignalModesignal_modeslow_ma_periodzMaType | Noneslow_ma_typez
int | Noneslow_slope_lookbackr   
RegimeGateregime_gate   regime_sma_windowregime_exit_on_breakg      9@vix_capr   
SizingModesizing_modeg333333?re   ry   vol_scale_floorg      ?vol_scale_cap   
vol_window   
atr_period   atr_high_lookbackg      @atr_multiplierc                   | j                   | j                   nd}| j                  rdnd}| j                   | j                   d| j                   d| j
                   d| j                  dd| d| j                   d| d| j                   }| j                  d	k(  r | j                  d
k(  r| j                  dk(  r|S | j                  xs | j                  }| j                  | j                  n| j                  }|g}| j                  dk(  r#|j                  d| | j                   d|        | j                  d
k7  rG| j                  }|dk(  rd| j                   d}n|dk(  rd| j"                   }|j                  |       | j                  dk(  r|j                  d| j$                  d       | j                  dk(  r|j                  d| j&                  d       dj)                  |      S )Nautopmanopf_lb__eng_exr   r   r   r   dual_r   vixr   r   r   atrr   vol)r   rt   r+   r   r   rr   rs   r   r   r   r   r   r   r   appendr   r   r   r   re   join)selfexit_tagpfbase	slow_typeslow_lbpartsrgs           r.   slugzMaSlopeConfig.slug   s   *.*=*=*I4&&v))Uv||nT^^,C0C0C/DAdFWFWEX$&&q)XJa7GqAdnnM]_ 	 x'D,<,<,F4K[K[_gKgK%%5	.2.F.F.R$**X\XkXk//LL5D,?,?+@G9MNv%!!BX~4<<*+x41123LL>>--LL3t221567{*LL3tq123xxr0   )returnstr)__name__
__module____qualname__r+   __annotations__r   r   rr   rs   r   rt   r   r   r   r   r   r   r   r   r   r   r   r   re   r   r   r   r   r   r   r    r0   r.   r   r      s   GVIsNC %L+%!OU!#'NL'ND&Iy&&Ix&M3&K&NC"&L-&&**$K$ s !%$%GU&K&J OU M5JJsNEr0   r   c                      e Zd ZU dZ e       Zded<   ddZe	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd       Z	e	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd       Z
dddd	Zy
)MaSlopeEnginezPLong-only / long-short MA-slope trend follower with optional dual-TF and sizing.r   configc                   |j                   rt        d      d|j                  vrt        d      | j                  }|j                         j                         }|d   j                  t        j                        }d|j                  v r1|d   j                  t        j                        j                  d      n|j                         j                  d      }t        ||j                  |j                        }t        ||j                   |j"                        }t%        j&                  t        j(                  |j*                        }t%        j&                  t        j(                  |j*                        }	|j,                  d	k(  rn|j.                  xs |j                  }
|j0                  |j0                  n|j                   }t        ||
|j2                        }t        |||j"                        }	|j4                  }||j6                  d
v rdn|j8                  dz  }||j8                  kD  }||j8                   k  }|j:                  r|||kD  z  }|||k  z  }|j,                  d	k(  r%||	|j8                  kD  z  }||	|j8                   k  z  }t%        j&                  d|j*                        }t%        j&                  d|j*                        }|j<                  dk(  rQ|j?                  |j@                  |j@                        jC                         }||kD  }|jD                  r||k  }||d<   nt|j<                  dk(  red|j                  vrt        d      |d   j                  t        j                        }||jF                  k  }|jD                  r||jF                  k\  }||z  }||z  }||k  }|| kD  }||k  }||kD  }|j6                  dk(  r|}|}n|j6                  dk(  r&||j8                  dz  k  }||j8                   dz  kD  }nZ|j6                  dk(  r|}|}nF|j6                  dk(  r||z  }||z  }n,|j6                  dk(  r|}|}nt        d|j6                        |jD                  r|j<                  dk7  r
||z  }||z  }|j6                  dk(  rd|j                  v r"|d   j                  t        j                        n|}| jI                  |||||d|j                  v r"|d   j                  t        j                        n|||jJ                  |jL                  |jN                  |jP                  |jR                        }n+| jU                  |||||jJ                  |jL                        }|j                  t        j                        }|jV                  dk(  r>tY        ||jZ                  |j\                  |j^                  |j`                         }||z  }n|}||d!<   ||d"<   |j,                  d	k(  r
||d#<   |	|d$<   ||d%<   ||d&<   ||d'<   ||d(<   ||d)<   ||d*<   |jc                  d+      j                  d      |d,<   |S )-Nzdaily is emptyr*   zdaily must contain 'close'rd   r3   rw   rx   r8   r   )r   r   r   ry   TFr   r\   
regime_smar   r   z9regime_gate=vix_lt requires a 'vix' column on daily framer   r   r   r   r   zunknown exit_mode: r   lowhigh)
long_entry	long_exitshort_entry
short_exitr*   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   )re   r   rf   rg   rK   ma_slopema_slowma_slow_sloper   r   r   r   target_positionposition_weightr:   position)2emptyr&   columnsKeyErrorr   
sort_indexcopyr#   r$   r%   rc   
pct_changer/   r+   r   rZ   r   rr   rE   rF   r>   r9   r   r   r   r   r   r   rs   rt   r   r'   r   r(   r   r   _atr_chandelier_state_machiner   r   r   r   r   _position_state_machiner   rj   re   r   r   r   r<   )r   dailycfgdfr*   rd   rK   rY   r{   r}   r   r   exit_threshr   r   	regime_okregime_exitsma_regr   long_exit_slopeshort_exit_slopelong_exit_priceshort_exit_pricer   r   low_coltargettarget_fri   weights                                 r.   	transformzMaSlopeEngine.transform   s   ;;-..%--'788kk$$&7""2::. 

" uIRZZ(//4!!#**3/ 	 s{{3==I c.@.@IYIYZ))BFF"((3YYrvvRXX6
??..((7CKKI141H1H1Tc--Z]ZlZlG 	#BTBTUG)'GCL\L\]J((!$2d!d#jmj}j}  AD  kDKS000
s2222#urz2J%4K??..#zC4G4G'GHJ%s7J7J6J)JKKIId"((3	iiRXX6??h&mmC$9$9sG\G\m]bbdGI''#go&B|__(BJJ&Z[[U)""2::.Cckk)I''!S[[0)+
!I-+- K</"* 2:==L('I)J]]l* 3 3c 99I#"5"5!5!;;J]]m+'I)J]]..'/9I),<<J]]..'I)J23==2CDEE##6(A!K/I#k1J==,,6;rzz6Ibi&&rzz2uG77%#'%6<

6JRZ&&rzz2PU--!//>>"%"7"7"11 8 F 11%#'%--!// 2 F ==,??k)%>>~~))%%E %FF4:??..#ByM",B%<#;'=%< ( &a//4:	r0   c                   | j                   }t        j                  t        |      t        j                        }d}d}	t        t        |            D ]  }
|dk(  ra|dk(  rt        | j                  |
         rd}d}	n|dk(  rt        | j                  |
         rd}d}	ntt        |j                  |
         r\d}d}	nW|dk(  r'|	dz  }	|	|k\  rHt        |j                  |
         r0d}d}	n+|dk(  r&|	dz  }	|	|k\  rt        |j                  |
         rd}d}	|||
<    t        j                  ||t        j                        S )Nr5   r   r   r:   r   r9   r6   )
r9   r$   zerosrA   int8rG   r   ilocrE   rF   )r   r   r   r   r   r   idxposstateholdrV   s              r.   r   z%MaSlopeEngine._position_state_machineo  s7    hhs3xrww/s3x 	Az+Z__Q5G0HED,.JOOA./ ! k..q12 " !	=(T)..2C-DED"	=(T*//!2D-EEDCF-	. yyCrww77r0   c           
     d   |j                  d      }t        j                  ||z
  ||z
  j                         ||z
  j                         gd      j	                  d      }|j                  |	|	      j                         }|j                  |
d      j	                         }|j                  }t        j                  t        |      t        j                        }d}d}d}t        t        |            D ]  }|dk(  r|dk(  rIt        | j                  |         r1d}d}t        |j                  |   ||j                  |   z  z
        }n6|dk(  r0t        | j                  |         r0d}d}t        |j                  |   ||j                  |   z  z
        }nt        |j                  |         rd	}d}n|dk(  r|dz  }t        j                   |j                  |         r5t	        |t        |j                  |   ||j                  |   z  z
              }t        |j                  |         |k  }||k\  rJt        |j                  |         s|r0d}d}n+|d	k(  r&|dz  }||k\  rt        |j                  |         rd}d}|||<    t        j"                  ||t        j                  
      S )Nr:   )axisr\   r5   r   r3   r   r   r   r   )r<   rE   concatrJ   maxr'   r(   r9   r$   r   rA   r   rG   r   r   r?   rI   rF   )r   r   r   r   r*   r   r   r   r   r   r   r   
prev_closetrr   hhr   r   r   r   stoprV   chandelier_hits                          r.   r   z+MaSlopeEngine._atr_chandelier_state_machine  sr     [[^
YYs

"'')z!&&(
 
 #1#+ 	 jjj<AAC\\+\;??Akkhhs3xrww/s3x 	Az+Z__Q5G0HED nsxx{.J!JKD,.JOOA./ ! $RWWQZ.388A;2N%NOk..q12 " !	;;sxx{+tU2771:RS8T+T%UVD!&uzz!}!5!<=(d9>>!3D.EED"	=(T*//!2D-EEDCF7	8 yyCrww77r0   rd   )ret_colc                  ||j                   vrB|d   j                  t        j                        }|j	                         j                  d      }n1||   j                  t        j                        j                  d      }| j                  |      }|d   j                  t        j                        }||z  j                  d      S )zMDaily strategy returns from ``ret_col`` (close-to-close) and lagged position.r*   r3   r   strategy_ret)r   r#   r$   r%   r   rc   r   rename)r   r   r  r*   rd   framer   s          r.   backtest_returnszMaSlopeEngine.backtest_returns  s    %--''N))"**5E""$++C0C.''

3::3?Cu%J&&rzz2c	!!.11r0   N)r   pd.DataFramer   r  )r   	pd.Seriesr   r  r   r  r   r  r   r   r   r   r   r  )r   r  r   r  r   r  r   r  r*   r  r   r  r   r  r   r   r   r   r   r   r   r   r   r?   r   r  )r   r  r  r   r   r  )r   r   r   __doc__r   r   r   r   staticmethodr   r   r
  r   r0   r.   r   r      s   Z)OFM+HT $8$8 $8 	$8
 $8 $8 $8 
$8 $8L <8<8 <8 	<8
 <8 <8 <8 <8 <8 <8 <8 <8 <8 
<8 <8| GL 	2r0   r   g     j@)capitalr   c          	     l   | j                  t        j                        j                         } t	        |       dk  ri S |d| z   j                         z  }t	        |       dz  }t        |j                  d         }||z  dz
  }|dkD  r||z  d|z  z  dz
  n
t        d      }t        ||j                         z  dz
  j                               }t        | j                  d            }	|	d	kD  r2t        | j                         |	z  t        j                  d      z        n
t        d      }
|@t        |j                  | j                        j                         j                               n
t        d      }t        t	        |             |d
z  |d
z  |d
z  |
|	t        j                  d      z  d
z  ||dS )Nr7   r_   r4   r   r   r>   r:   r]   r;   g      Y@)n_daystotal_return_pctcagr_pct
max_dd_pctsharpevol_ann_pct
end_equityinvested_frac)r#   r$   r%   dropnarA   cumprodr?   r   cummaxminr`   r(   ra   reindexr9   rJ   )rr  r   eqyearsendtotcagrddsdr  investeds               r.   metrics_from_returnsr'    s{    	
##%A
1vz		C!G$$&	&BFUNE

C
-#
C5:QYC'MsU{+c1E%LD	RYY[ 3&++-	.B	quu!u}	B685jU1668b=2775>12eElF@H@TuX%%agg.22499;<Z_`eZfHA-%K5L5jBGGEN*U2!	 	r0   )r*   r  r+   r   r,   r   r   r  )rK   r  rL   r   r1   r   r   r  )rd   r  re   r?   r   r   rf   r?   rg   r?   r   r  )r*   r  r+   r   rn   r   ro   r   rp   r   rq   r   rr   r   rs   r?   rt   r   ru   
RankMetricr   r  )r  r  r  r?   r   zpd.Series | Noner   zdict[str, float])r  
__future__r   dataclassesr   typingr   numpyr$   pandasrE   r   r   r   r   r   r   r   r(  r/   rZ   rj   r   r   r   r'  r   r0   r.   <module>r.     s   # !   		;<-.	`a/0
/0
*+
MN
6  	4:4: 4: 	4:
 4:n
.	
. 
. 	
.
 
. 

. 
.   % ,33 3 	3
 3 3 3 3 3 3 3 3l $6 6 6r ~2 ~2 ~2H !%	  	
 r0   