
    pj?                        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ddlmZmZmZmZ ddlmZ ed   Zed	   Z ed
       G d d             Ze G d d             Zy)u   
Cross-sectional **MA slope momentum** — rank universe, hold top-N equal weight.

Monthly (or weekly) rebalance: score = dual EMA slope strength (stage-4 SPY winner
logic), pick the ``top_n`` names with highest ``rank_score``.
    )annotations)	dataclass)DictLiteralN)MaType
RankMetricSlopeMethodcompute_slope_rank_score)_align_panel_frames)monthlyweekly)none	atr_trail	pct_trailportfolio_ddT)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)<   y*)+MaSlopeCrossSectionalConfigemar   ma_type
   intfast_period2   slow_periodfast_lookback   slow_lookbackpctr	   slope_method        floatentry_slope_minTboolprice_above_madual_productr   rank_metricr   RebalanceFreq	rebalanceg{Gz?cash_annual_yield   bars_per_yearr   StopMode	stop_mode   
atr_periodg       @atr_multipliergQ?pct_trail_stopg?portfolio_dd_stopN)__name__
__module____qualname__r   __annotations__r   r   r   r   r    r#   r%   r'   r)   r*   r,   r.   r0   r1   r2   r3        K/opt/rentech/trading_bot/RenTech/strategy_stack/ma_slope_cross_sectional.pyr   r      s    GVKKM3M3 %L+% OU ND,K,(I}(#u#M3%Ix%JNE NE #u#r9   r   c                      e Zd ZU dZ e       Zded<   ddZddZ	 	 	 	 ddZ		 	 	 	 	 	 	 	 ddZ
	 	 	 	 	 	 	 	 ddZ	 dd	d
	 	 	 	 	 	 	 ddZ	 d	 	 	 	 	 ddZy)MaSlopeCrossSectionalz0Long-only top-N rotation by MA slope rank score.r   configc                   t        j                  |j                        j                         }| j                  j
                  dk(  rt        j                  d|      j                  |j                               j                         }t        |j                               dkD  rb|j                  |   j                  |j                  |j                  g      j                  d      }t        j                   |j                        S |j#                  d      j%                         j                  S | j                  j
                  dk(  rt        j                  d|      j                  |j                               j                         }t        |j                               dkD  r[|j                  |   j                  |j'                  d            j                  d      }t        j                   |j                        S |j#                  d      j%                         j                  S t)        d| j                  j
                        )	Nr      )indexg      ?BMEr   zW-FRIzunknown rebalance: )pdto_datetimer@   sort_valuesr=   r)   Seriesgroupby	normalizesumr"   medianlocyearmonthtailDatetimeIndexresamplelast	to_period
ValueError)selfscore_dfidxbars_per_dayrP   s        r:   _rebalance_indexz&MaSlopeCrossSectional._rebalance_index8   s   nnX^^,88:;;  I-99Qc2::3==?KOOQL\((*+c1||C(00#((CII1FGLLQO''

33$$U+002888;;  H,99Qc2::3==?KOOQL\((*+c1||C(00w1GHMMaP''

33$$W-224:::.t{{/D/D.GHIIr9   c                *    | j                  |      d   S )uC   Wide panel of ``rank_score`` per ticker (DatetimeIndex × tickers).r   )build_slope_panels)rS   equity_dicts     r:   build_score_panelz'MaSlopeCrossSectional.build_score_panelI   s    &&{3A66r9   c                   |st        d      | j                  }g }g }g }t        |j                               D ]`  \  }}||j                  sd|j
                  vr$t        j                  |j                        j                  d      }|d   j                  t        j                        }	||	_        t        |	j                         |j                  |j                   |j"                  |j$                  |j&                  |j(                  |j*                  |j,                  |j.                  
      }
|j1                  |
d   j3                  |             |j1                  |
d   j3                  |             |j1                  |
d   j3                  |             c |st        d      t5        |      \  }}t5        |      \  }}t5        |      \  }}|j                         |j                         |j                         fS )	z8Return (rank_score, fast_slope, slow_slope) wide panels.zequity_dict cannot be emptyNclose)	r   r   r   r   r   r    r#   r%   r'   
rank_score
fast_slope
slow_slopez"no valid tickers with close prices)rR   r=   sorteditemsemptycolumnsrB   rC   r@   tz_localizeastypenpfloat64r
   
sort_indexr   r   r   r   r   r    r#   r%   r'   appendrenamer   )rS   rZ   cfg	score_panfast_panslow_pantdfrU   r]   scoredrT   _fast_dfslow_dfs                  r:   rY   z(MaSlopeCrossSectional.build_slope_panelsM   s    :;;kk%'	$&$&K--/0 	<EArzRXX

)B..*66t<CwK&&rzz2EEK-  "OOOO!//!// -- # 3 3"11OOF VL188;<OOF<077:;OOF<077:;)	<* ABB))4!(2
(2
""$g&8&8&:G<N<N<PPPr9   c                   | j                  |      }|j                  |d      }|j                  t        j                        }|j
                  \  }}t        j                  ||ft        j                        }	t        |      D ]  }
||
   }t        j                  |      |dkD  z  }t        j                  |      d   }|j                  dk(  rKt        t        |      t        |j                              }||   }t        j                  | |dz
        d | }||   }t        j                  ||          }||   }dt        |      z  |	|
|f<    t!        j"                  |	||j$                        }|j&                  j)                         }|j                  |      j+                         j-                  d      j/                  d      }|||	fS )	Nffillmethoddtyper!   r   r?         ?r@   rd   )rW   reindexto_numpyrg   rh   shapezerosrangeisfinitenonzerosizeminr   argpartitionargsortr"   rB   	DataFramerd   r@   rD   rw   shiftfillna)rS   rT   ret_dftop_nbm_indexscore_m	score_arrMN	weights_mmscvalid	idx_validksub
pick_localchosenorderweights_m_dfmaster_index	weights_ds                         r:   _topn_weightsz#MaSlopeCrossSectional._topn_weightss   s    ((2""8G"<$$2::$6	1HHaV2::6	q 	2A1BKKOrCx0E

5)!,I~~"CJINN 34AY-C#q1u5bq9Jz*FJJJ/0EE]F#&q>Iai 	2 ||IXxGWGWX||//1 ((6<<>DDQGNNsS	(I--r9   c                   | j                   }|j                  }|dk(  r|S t        |j                        }g }g }g }	|D ]/  }
||
   }t	        j
                  |j                        j                  d      }|j                         j                         }||_        |d   j                  t        j                        }d|j                  v r"|d   j                  t        j                        n|}|j                  |j                  |
             |j                  |j                  |
             |	j                  |d   j                  t        j                        j                  |
             2 t        |      \  }}t        |      \  }}t        |	      \  }}|j                  j!                         }|j#                  |      j%                         }|j#                  |      j%                         }|j#                  |      j'                  d      }|j#                  |      j'                  d      j)                  t        j                        }|j)                  t        j                        }|j)                  t        j                        }|j)                  t        j                        }|j*                  \  }}|D ch c]%  }t	        j,                  |      j/                         ' }}t1        |j2                        t1        |j4                        z  }d}|dk(  rt        j6                  ||ft        j                        }t9        |      D ]  \  }}
||
   }||
   }|j;                  d	      } t	        j<                  ||z
  || z
  j?                         || z
  j?                         gd	
      jA                  d	
      }!|!jC                  |jD                  |jD                        jG                         j)                         |dd|f<    |j                         }"t        jH                  |t        jJ                        }#t        jH                  |t        jJ                        }$t        j6                  |tL              }%d}&d}'d}(d})tO        |      D ]  }*t	        j,                  ||*         j/                         }+|+|v r/d}&d}'d|%dd t        jJ                  |#dd t        jJ                  |$dd |dk(  r|'}&d}'|dk(  r|&rd|"|*<   stO        |      D ]  }||*|f   },|,dk  rd|"|*|f<   |+|v st        jP                  |#|         s||*|f   |#|<   ||*|f   |$|<   d|%|<   |%|   rd|"|*|f<   YtA        |$|   ||*|f         |$|<   d}-|dk(  r||*|f   |$|   d|jR                  z
  z  k  }-nB|dk(  r=|;|$|   |jT                  ||*|f   z  z
  }.t        jP                  |.      xr
 ||*|f   |.k  }-|,|"|*|f<   |-sd|%|<    |dk(  sm|"|*   ||*   z  jW                         tA        dd|"|*   jW                         z
        |z  z   }/|(d|/z   z  }(tA        |)|(      })|(|)z  dz
  |jX                   k  sd}' t	        jZ                  |"||      S c c}w )zEZero stopped-out names between rebalances; cash until next rebalance.r   Nr]   highretr!   rz   r   r?   axis)min_periodsFr|   r   r   r   Tr}   ).r=   r.   listrd   rB   rC   r@   re   ri   copyrf   rg   rh   rj   rk   r   rD   r~   rw   r   r   r   	TimestamprG   r"   r*   r,   r   	enumerater   concatabsmaxrollingr0   meanfullnanr$   r   r   r2   r1   rH   r3   r   )0rS   target_weightsrZ   r   rl   modetickers	close_panhigh_panret_panrp   rq   rU   dfxchclose_dfrs   high_dfr   mastertwc_arrh_arrr_arrn_dn_txreb_setdaily_rfatr_arrjprevtr_sw_outentry_pxpeak_pxstoppedport_blockedport_block_nexteqpeak_eqidttgthitstop_pxday_rs0                                                   r:   _apply_stopsz"MaSlopeCrossSectional._apply_stops   s    kk}}6>!!~--.%'	$&#% 		DAQB..*66t<C--/&&(CCIG##BJJ/A28CKK2GF""2::.QAQXXa[)OOAHHQK(NN3u:,,RZZ8??BC		D *)4!(2
'0	%%113##F+113//&)//1'..s3##F+2237@@rzz@R!!

!3  rzz 2bjj188S8@A12<<?,,.AA../%8I8I2JJ;hhSz<G!'* k1QKAJwwqzyy!a%!d()9AH>>;K!LSTUYY_`Ya $S^^ X ] ] _ h h j1k 	773'''#rvv&((3d+s 2	+AfQi(224BW}$"'"
 ffVV
~%."'~%,a3Z &Ah!8"%E!Q$K=HQK(@"'1+HQK!&q!tGAJ!&GAJ1:"%E!Q$K U1a4[9
;&1+
cC<N<N6N(OOC[(W-@%aj3+=+=1+MMG++g.H5A;3HC!ad!%GAJ3&8 ~%qE!H,113c#sU1X\\^?S6TW_6__cEk!gr*<#%#*?*?)??&*Oe2	+h ||EAAU Bs   *ZT)verbosec               n   |dk  rt        d      t        |j                               }g }|D ]  }||   }||j                  sd|j                  vr%t        j                  |j                        j                  d       }|j                         }	||	_        |j                  |	d   j                  t        j                        j                  |              |st        d      t        |      \  }
}| j!                  |      \  }}}|j#                  |
j                        }|
j                  j%                         }|
j#                  |      }
|
j'                  d      j                  t        j                        }| j)                  ||
|      \  }}}| j+                  |||      }|j#                  |      j'                  d      }t-        | j.                  j0                        t-        | j.                  j2                        z  }|j5                  t        j                        }|j5                  t        j                        }||z  j7                  d	      }|j7                  d	      }|t        j8                  dd
|z
        |z  z   }|rt;        |      }|rNt-        t        j<                  t?        |      D cg c]  }t        j6                  ||   dkD         c}            nd}|j#                  |d      jA                         j7                  d	      }t;        |      rt-        |j=                               nd}tC        d| d| j.                  jD                   d| j.                  jF                   d|dd|ddd       t        jH                  ||dt        j                        S c c}w )Nr   top_n must be > 0r   zno return series in equity_dict)rd   r!   rz   r?   r   r|   rw   rx   z  MA slope top-z	: metric=z	 | rebal=u    | avg held ≈ z.1fu    | avg eligible ≈ z.0fz names/rebalT)flushma_slope_topn_ret)r@   namer{   )%rR   ra   keysrc   rd   rB   rC   r@   re   r   rj   rf   rg   rh   rk   r   rY   r~   rD   r   r   r   r"   r=   r*   r,   r   rH   maximumlenr   r   notnaprintr'   r)   rE   )rS   rZ   r   r   r   r   rp   rq   rU   r   r   rs   rT   r   
ret_filledr   r   r   r   wrcorewsumoutr   r   avg_keligavg_eligs                                r:   generate_returnsz&MaSlopeCrossSectional.generate_returns  s+    A:011))+,#% 	DAQBzRXXbjj)@..*66t<C'')CCINN3u:,,RZZ8??BC	D >??'0	00=!Q##FNN#;||//1-]]3'..rzz:
)-););Hfe)T&	8Y%%ihG	%%l3::3?	667%@Y@Y:ZZRZZ0bjj1A{{{"uu!u}RZZS4Z08;;HATUE"''U1X"N266)A,*:#;"NOP[^E##HW#=CCEIIqIQD-0YuTYY[)CH!%	$++2I2I1J K..//?c{ K$$,S>? 	 yyL7JRTR\R\]] #Os   >"N2c                d   |dk  rt        d      | j                  |      \  }}}t        |j                        }| j	                  |      }|j                  |d      }|j                  |d      }	|j                  |d      }
|j                  t        j                        }|	j                  t        j                        }|
j                  t        j                        }|j                  j                         }|j                  \  }}|dk  rt        j                         S g }t               }t        |      D ]  }t        j                   ||         j#                         }||   }t        j$                  |      |dkD  z  }t        j&                  |      d   }|j(                  dk(  rt               }|t+        t-        |      t-        |j(                              }||   }t        j.                  | |dz
        d | }||   }t        j0                  ||          }||   }|D ch c]  }|t-        |          }}||z
  }||z
  } |||kD     }!t3        |!      r&t        j                   |!d         j#                         n|}"|dz   |k  r)t        j                   ||dz            j#                         n%t        j                   |d         j#                         }#t5        |d	      D ]  \  }$}|t-        |         }%|j7                  |j9                  d
      |"j9                  d
      |#j9                  d
      |%dt;        |      z  t;        ||         t;        |||f         t;        |||f         t-        |$      |%|v dt-        |      d        t=        |       D ]q  }%|j7                  |j9                  d
      |"j9                  d
      |#j9                  d
      |%dt;        d      t;        d      t;        d      dddt-        |      d       s |} t        j                  |      S c c}w )Nr   r   rw   rx   rz   r?   r!   )startz%Y-%m-%dr|   F)signal_dateeffective_dateperiod_end_datetickerweightr^   r_   r`   rankis_new_entryis_exit_from_priorr   r   T)rR   rY   r   rd   rW   r~   r   rg   rh   r@   rD   r   rB   r   setr   r   rG   r   r   r   r   r   r   r   r   r   rj   strftimer"   ra   )&rS   rZ   r   rT   rt   ru   r   r   r   fast_mslow_mr   fast_arrslow_arrr   r   r   rows	prev_heldr   	signal_dtr   r   r   r   r   r   r   r   r   heldentriesexitsaftereffective_dt
period_endrank_itkrs&                                         r:   generate_rebalance_logz,MaSlopeCrossSectional.generate_rebalance_log=  s   
 A:011%)%<%<[%I"'7x''(((2""8G"<':':$$2::$6	???4???4~~1131q5<<>!!e	q <	AXa[1;;=I1BKKOrCx0E

5)!,I~~"E	CJINN 34AY-C#q1u5bq9Jz*FJJJ/0EE]F-34GCFO4D4Y&G$E 	!9:EADU2<<a1;;=QZL=>UQYXa!e_-779BLLYefhYiLjLtLtLv  'vQ7 	c!fo'0'9'9*'E*6*?*?
*K+5+>+>z+J"%"%a.&+BqEl&+HQTN&;&+HQTN&; #F(+w.3!$Q$ e} '0'9'9*'E*6*?*?
*K+5+>+>z+J"%"%&+El&+El&+El !(-.2!$Q" Iy<	| ||D!!_ 5s   +P-N)rT   pd.DataFramereturnpd.DatetimeIndex)rZ   Dict[str, pd.DataFrame]r  r  )rZ   r  r  z/tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame])rT   r  r   r  r   r   r  z1tuple[pd.DataFrame, pd.DatetimeIndex, np.ndarray])r   r  rZ   r  r   r  r  r  )r   )rZ   r  r   r   r   r$   r  z	pd.Series)rZ   r  r   r   r  r  )r4   r5   r6   __doc__r   r=   r7   rW   r[   rY   r   r   r   r  r8   r9   r:   r<   r<   2   s   :*E*GF'GJ"7$Q2$Q	8$QL.. . 	.
 
;.>qB$qB -qB #	qB
 
qBl 6^
 6^,6^ 6^
 6^ 
6^v V",V" V" 
	V"r9   r<   )r  
__future__r   dataclassesr   typingr   r   numpyrg   pandasrB   &RenTech.strategy_stack.ma_slope_enginer   r   r	   r
   -RenTech.strategy_stack.multi_strategy_managerr   r(   r-   r   r<   r8   r9   r:   <module>r     s{    # !      N+,CD $$ $ $* `" `" `"r9   