
    ,]jV                      U d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlZddlZej                   j#                  ej                   j%                  ej                   j'                  e      dd            Zeej                   vrej                   j-                  de       ddlmZ ddlmZ dd	lmZmZ dd
lmZ ddl m!Z! dZ"de#d<   d@dAdZ$dBdZ%ddddddddddddddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dCdZ&dd dDd!Z'	 	 	 	 	 	 dEd"Z(d#d$dFd%Z)d#d$dGd&Z*d#d$dHd'Z+dId(Z,dJd)Z-dKd*Z.	 	 	 	 	 	 	 	 	 	 dLd+Z/dMd,Z0d-d.	 	 	 	 	 	 	 	 	 dNd/Z1dOd0Z2	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dPd1Z3dQd2Z4dRd3Z5dSd4Z6dSd5Z7dSd6Z8dTd7Z9dd8d9	 	 	 	 	 	 	 	 	 dUd:Z:dVd;Z;dWd<Z<	 	 	 	 	 	 	 	 	 	 	 	 dXd=Z=dYd>Z>e?d?k(  r e>        yy)Zu1  
Tie together DataLoader, MomentumFilter, StatArbEngine, and StrategyOrchestrator.

Fetches:
  * Daily data for regime asset (default SPY)
  * Intraday data for traded asset (default QQQ or AAPL)

Backtest (vectorized, close-to-close on each bar):
  * ``standalone_ret`` = lagged ``micro_position`` * bar return
  * ``filtered_ret``   = lagged ``orchestrated_position`` * bar return

Pair mode (``--hedge-ticker``): causal OLS hedge, z-score on causal spread; bar
return is ``basket_ret`` (Y return minus lagged β times X return).

Plots cumulative compounded returns for both (requires matplotlib).

Run from repository root::

    .venv/bin/python RenTech/strategy_stack/main.py
    .venv/bin/python RenTech/strategy_stack/main.py --trade-ticker AAPL --regime-ticker SPY
    .venv/bin/python RenTech/strategy_stack/main.py --trade-ticker QQQ --hedge-ticker SPY
    .venv/bin/python RenTech/strategy_stack/main.py --mode hourly-swing-breakout --intraday-period 730d
    # default: macro ETFs (GLD, USO, TLT, DBC, UUP, …); use --hourly-swing-universe sp100 for equities
    )annotationsN)Path)Anyz..)
DataLoader)MomentumFilter)PairStatArbEngineStatArbEngine)StrategyOrchestrator)SwingEngine)GLDIAUSLVUSOBNOTLTIEFSHYDBCGSGUUPFXEFXYVXXSVXYztuple[str, ...]HOURLY_SWING_MACRO_ETF_TICKERSc                    | |   j                  d      j                  d      j                  t        j                        | |   j                  t        j                        z  S )z3pos_{t-1} * ret_t (no lookahead on same bar close).   r   )shiftfillnaastypenpfloat64)dfpos_colret_cols      7/opt/rentech/trading_bot/RenTech/strategy_stack/main.pyvectorized_strategy_returnsr'   H   sL    g;Q&&q)00<r'{?Q?QRTR\R\?]]]    c                    | j                  d      j                  t        j                        }d|z   j	                         dz
  S )zCompounded growth of 1.0.              ?)r   r    r!   r"   cumprod)simple_retsrs     r&   equity_curver/   M   s9    3&&rzz2A!G$$r(   SPYQQQ5y730d1h   ~                   @r*      )regime_tickertrade_tickerdaily_periodintraday_periodintraday_interval
sma_window
roc_periodaqr_lookbackaqr_skipz_windowz_entryz_exitregime_shift_sessionshedge_tickerhedge_windowmin_hedge_obsc                   t               }|j                  | |      }|j                  rt        d|        t	        |||      }|j                  |d         }|dv rdn|}|j                  |||      }|j                  rt        d| d	      |j                  |      }|r|j                  |||      }|j                  rt        d
|       |j                  |      }|dk  rd n|}t        |	|
|||      }|j                  ||      }|j                  }|dnd| }d|v r*t        d| d| d|d   dd|j                  d              |d   |d<   n5t        |	|
|      }|j                  |      }|d   j                         |d<   t        |      }|j                  ||d      }t!        |d      |d<   t!        |d      |d<   |S ) NperiodNo daily data for )rA   rC   rD   closer4   60mrR   intervalrN   zNo intraday data for u#    — try shorter period or intervalzNo intraday data for hedge r   )windowentry_zexit_zrJ   rK   	expandingzrolling pvalueu   📌 Pair causal OLS (z, min_train=z) | ADF p-value=.4fz  stationary@5%=is_stationary_5pct
basket_retret)rU   rV   rW   )rH   
trade_date)trade_date_colmicro_positionstandalone_retorchestrated_positionfiltered_ret)r   fetch_dailyemptyRuntimeErrorr   	transformfetch_intradayalign_to_trading_daysr   
adf_reportprintgetr	   
pct_changer
   merge_and_gater'   )r<   r=   r>   r?   r@   rA   rB   rC   rD   rE   rF   rG   rH   rI   rJ   rK   loaderdailymfdaily_regimeivintra_yintra_xhwpengintraarhmodeengorchs                                 r&   run_pipeliner}   S   s   & \F}\BE{{/?@@ 
:LS[	\B<<g/L#}4:KB##L2o#VG}}2<.@cdee**73G''r/'Z==!<\NKLL..w7!Q&TL '
 w0__!z"r>(|M? K!(|C00@H\A]@^` \*e8WVLg&W~002e6KLD|LQE9%AQRE
7?VWE.Lr(   	save_pathc                  	 dd l m} t        | d         }t        | d         }|j                  d      \  }}|j                  |j                  |j                  dd	
       |j                  |j                  |j                  dd	
       |j                  ddd       |j                  |       |j                  d       |j                  d       |j                  dd       |j!                          |r"|j#                  |d       t        d|        y |j%                          y # t        $ r t        dt        j
                         Y y w xY w)Nr   z;matplotlib not installed; skip plot. pip install matplotlib)filera   rc   )
      )figsizezStandalone mean-reversiong333333?)label	linewidthzMomentum-filteredr*   gray      ?)colorr   zCumulative return (compounded)best)locTg333333?)alpha   )dpiu   💾 Saved plot → )matplotlib.pyplotpyplotImportErrorrk   sysstderrr/   subplotsplotindexvaluesaxhline	set_title
set_ylabellegendgridtight_layoutsavefigshow)rx   titler   pltstafltfigaxs           r&   plot_curvesr      s!   '
 u-.
/C
u^,
-Cll7l+GCGGCIIszz)DPSGTGGCIIszz)<GLJJs&CJ0LLMM23II&IGGDGI3'$YK01
)  KRUR\R\]s   D+ +$EEc           	         ddddddfddddddfd	dd
dddfddddddfg}|D ]*  \  }} | j                   |fi |}|j                  r&||fc S  t        j                         dfS )u   
    Universe scan often returns zero rows: Holm–Bonferroni across ~5k pairs is strict,
    and Hurst / half-life filters remove many survivors. Retry with looser thresholds.
    u,   p<0.05 (Holm), Hurst<0.45, half-life 1–15dg?g?r+   g      .@)p_value_threshold	hurst_maxhalf_life_minhalf_life_maxzp<0.10 (Holm), same tradabilityg?u'   p<0.10, Hurst<0.55, half-life 0.5–40dg?r   g      D@u(   p<0.20, Hurst<0.60, half-life 0.5–120dg?g333333?g      ^@N)find_cointegrated_pairsre   pd	DataFrame)us	data_dictattemptsr   kwpairss         r&   %find_cointegrated_pairs_with_fallbackr      s     ;%)!!$!%		
 .%)!!$!%		
 6%)!!$!%		
 7%)!!$!&		
9%2HL   	r***9;;{{%<  <<>4r(        o@bars_per_yearc               (   |j                  d      j                  t        j                        }t	        |      dk  s|j                  d      dk  rt        d|  dt	        |       d       y	t        |j                               }t        |j                  d            }|d
kD  r$||z  t        j                  t        |            z  n
t        d      }t        d|z   j                         dz
        }t        d|  d|dd|dd|dd|d
       y	)z
    Simple mean/std Sharpe on a return series.

    Use ``bars_per_year=252`` for daily returns (default). For ~hourly intraday bars,
    pass e.g. ``252 * 6`` as a rough annualization factor.
    r*      r   ddof-q=  z: n=z (insufficient for Sharpe)Nr   nanr+   z: mean_bar=z.6fz  std=z  approx_sharpe*=.3fz  cum=rZ   )r   r    r!   r"   lenstdrk   floatmeansqrtprod)namer.   r   musdsharpecums          r&   	summarizer      s     	
RZZ(A
1vzQUUU]U*4&SVH$>?@	qvvxB	quu!u}	B8:QR"Wrwwu]344E%LF
q 3&
'C	BtfK3xvbX5FvclRXY\]`Xa
bcr(   c               ,   |j                  t        j                        }|j                  rt	        d|  d       yt        |j                  d         }d|z   j                         j                  d      j                  t        j                        }t        t        |            }|j                  t        j                        }||dkD     }||dk     }|rt        |dkD  j                               n
t        d	      }	|j                  rt        |j                               n
t        d	      }
|j                  rt        |j                               n
t        d	      }|rt        |j                               n
t        d	      }|d
kD  rt        |j                  d
            n
t        d	      }|dkD  r$||z  t        j                  t        |            z  n
t        d	      }t        j                   d|z         }t        j"                  j%                  |      }||z  dz
  }t        |      rt        |j'                               n
t        d	      }t	        d|  d|dd|dd|dz  dd	       t	        d| d|	dz  dd|
dz  dd|dz  dd	       y)z
    Stats from a compounded cumulative return column (e.g. ensemble_df['macro_cumulative_ret']).
    Daily returns are implied via pct_change of (1+cum). Discrete trade counts are not defined.
    r   z: empty seriesNr+   r*   dtyper   r   r   r   r   z: final_cum=rZ     approx_sharpe=r   z  max_drawdown=d   .2f%z       trading_days=z  daily_win_rate=z%  avg_win_day=z%  avg_loss_day=)r    r!   r"   re   rk   r   ilocrm   r   intr   to_numpyr   sizer   r   r,   maximum
accumulatemin)r   r   r   	final_cumrp   narrpos_daysneg_dayswrawalr   r   r   eqpeakddmdds                      r&   #_print_cumulative_curve_diagnosticsr      s*   
 **RZZ
 C
yy4&'(chhrl#I3Y""$++C077

CECJA
..rzz.
*C37|H37|H$%a~~	 5<B#+==x}}	eElB#+==x}}	eElB !uzz|	uU|B%&Uuyyay 	!eB8:U
R"Wrwwu]344eF	C#I	B::  $D	dSB W%/%,C	
TF,yo-=fS\ Jc	#a	) 

qc!228C. ACxn$4R#XcN!	Er(   c                   ddddd}| j                  t        j                        } | j                  st	        |       dk  r|j                         S t        | j                  d         }t        j                  |      s|j                         S d| z   j                         j                  d      j                  t        j                        }t        t	        |            }|j                  t        j                        }t        |j                               }|dkD  rt        |j                  d	            n
t        d
      }|dkD  r$||z  t        j                  t        |            z  n
t        d
      }	t        j                   d|z         }
t        j"                  j%                  |
      }|
|z  dz
  }t        |j'                               }|t        |      z  }d}|dkD  r8|dkD  r3d|z   d|z  z  dz
  dz  }t        j                  |      rt        |      nd}t)        |dz  d      |t)        |d      ndt        j                  |	      rt)        t        |	      d      ndt        j                  |      rt)        |dz  d      dS ddS )z
    Stats from a compounded cumulative return series for investor_metrics.json.

    Percent fields are percentage points (e.g. 87.5 means +87.5% total return; -22.0 means -22% max DD).
    Ntotal_return_pctannualized_return_pctsharpe_approxmax_drawdown_pctr   r   r+   r*   r   r   r   r   r   gư>g            Y@   )r    r!   r"   re   r   copyr   r   isfiniterm   r   r   r   r   r   r   r,   r   r   r   round)r   r   re   r   rp   r   r   r   r   r   r   r   r   r   yearsannann_vs                    r&   _cumulative_series_metrics_dictr     s    !!% 	E **RZZ
 C
yyCHqLzz|chhrl#I;;y!zz|3Y""$++C077

CECJA
..rzz.
*C	uzz|	B%&Uuyyay 	!eB8:U
R"Wrwwu]344eF	C#I	B::  $D	dSB
/Cm$$ECt|	D(	/sU{3c9UB kk%0eEld!)e"3Q725/sAt46KK4GuV}a0T57[[5EE#+q1	  LP	 r(   c               v    d| i}|s|j                  d d d d d       |S |j                  t        |             |S )Nr   r   )updater   )r   r   enabledrows       r&   _investor_metrics_rowr   F  sI    !4.C

$()-!%$(		
 
JJ.s34Jr(   c           	        ddddddd}| j                  t        j                        } | j                  st	        |       dk  r|j                         S d| z   j                         j                  d      j                  t        j                        }t        t	        |            }|dk(  r|j                         S |j                  t        j                        }t        t        j                  t        j                  |      dkD              }||dkD     }||dk     }|j                  rt        |j                               d	z  n
t        d
      }|j                  rt        |j                               d	z  n
t        d
      }	|||rt        d	|z  |z  d      nddt        j                   |      rt        |d      ndt        j                   |	      rt        |	d      dS ddS )zKDaily PnL-style stats from a cumulative return leg (unweighted or blended).N)trading_daysdays_in_tradepct_time_in_trade_pct
num_tradesavg_win_day_pctavg_loss_day_pctr   r+   r*   r   r   g|=r   r   r   )r    r!   r"   re   r   r   rm   r   r   r   sumabsr   r   r   r   r   )
r   re   rp   r   r   nonzeroposnegr   r   s
             r&   _leg_activity_from_cumr  V  s    !% E **RZZ
 C
yyCHqLzz|3Y""$++C077

CECJAAvzz|
..rzz.
*C"&&u,-.G
cAg,C
cAg,C&)hhsxxz	U	"E%LB&)hhsxxz	U	"E%LB BCuw':A!>+-;;r?5Q<,.KKOE"aL  BF r(   c                F   d| j                  t        j                        j                  d      z   j	                         }|j
                  dk(  ryt        j                  j                  |      }||dz
  k  }dx}}|D ]  }|r|dz  }t        ||      }d} t        |      S )zSLongest streak of closes below the running high-water mark (approx. drawdown pain).r+   r*   r   r   r   )
r    r!   r"   r   r   r   r   r   maxr   )r   xr   
underwater
max_streakstreakus          r&    _longest_underwater_trading_daysr  w  s    	szz"**%,,S1	1;;=Avv{::  #DTE\!JJ aKFZ0JF z?r(   c               
   | j                  t        j                        j                  d      }|j                  t        j                        j	                  |j
                        j                  d      }t        t        |            }|dkD  rt        |j                  d            n
t        d      }t        j                  |      r$t        |t        j                  d      z  dz        n
t        d      }|rt        |j                               n
t        d      }	|dkD  r9t        j                  |      r$t        |	|z  t        j                  d      z        n
t        d      }
|j                  t        j                        }|rt        |d	kD  j                         dz        n
t        d      }	 t        j                  |j
                        }t        j                   ||
      j#                         }d|z   j%                  d      j'                         dz
  }t        |      rt        |d	kD  j                         dz        n
t        d      }t+        |      }|j-                  d      }|j-                  d      }|j-                  d      }t/        |      }t1        dt        t3        |dz                    }| d}|rd}d}nE|j                  t        j                        }|j                  t        j                        }|dkD  rWt        j                  |d      dkD  r=t        j                  |d      dkD  r#t        t        j4                  ||      d         }nt        d      }|d	k  }t        t        j6                  |            dkD  rm||   }||   }t        j                  |d      dkD  r=t        j                  |d      dkD  r#t        t        j4                  ||      d         }nt        d      }nt        d      }|t3        |d      ndt        j                  |      rt3        |d      ndt        j                  |
      rt3        |
d      ndt        j                  |      rt3        |d      ndt        j                  |      rt3        |d      nd||t        j                  |      rt3        |d	      nddt        j                  |      rt3        |d	      |dS d|dS # t(        $ r t        d      }Y w xY w)zMOne column of 'Strategy statistics' (blended or SPY) for investor HTML table.r*   r   r   r   r   r   r   r   r   )r   r+   MEr   r   r   g      5@z Monthsr   )r   r   r   N)average_annual_return_pctstandard_deviation_pctsharpe_ratiocorrelation_sp500down_correlation_sp500biggest_drawdown_pctbiggest_drawdown_durationwin_rate_pctshort_long_vol_breakdownpct_positive_monthsr   )r    r!   r"   r   reindexr   r   r   r   r   r   r   r   r   r   to_datetimeSeries
sort_indexresampler   	Exceptionr   rl   r  r
  r   corrcoefr  )rp   r   	spy_dailyis_benchmarkdspy_br   r   ann_vol_pctr   r   r   win_ratedis_mmonthlypos_mfinr   r   totalu_daysdur_mdd_durcorrdcorrabmaskaabbs                                  r&   _strategy_stat_sider:    s    	RZZ '',ARZZ(009@@EECFA!"Qquu!u}	E%LB8:B%RWWU^+e34USX\Kqvvx5<B02U
r{{2U27RWWU^+,TYZ_T`F
**2::*
&C23ucAg^^%-.uH^^AGG$ii2&1139&&t,113c97:7|w{((*U23u *#
.C
''$
%C
'')
*CGG&'E-c2F3uVd]+,-EwgFJJRZZJ(NNN,q5RVVAA&.266!!3Du3LQ*401D<D1urvvd|q 4B4Bvvbq!E)bffRa.@5.Hbkk"b1$78e%LE 7:oU3]4;=;;{;S%Q"7Y],.KK,?fa(T/1{{4/@U4^d57[[5G%q/T #%+.0kk(.Ch*$(24++e2DuUA!  KO! C  es   5BS4 4TTc                R   t        | t              r| j                  d      dk7  ry| j                  d      }| j                  d      }| j                  d      }t        |t              r%t        |t              rt        |t              r|dk  ryd|z  |z  }d|z  |z  }|d	d
|d	dS )zKRough exposure-style split if AQR diagnostics present (not true vol split).enginecross_sectional_momentumNdays_with_long_exposuredays_with_short_exposurer   r   r   z.0fz% | r   )
isinstancedictrl   r   )ls_diagdldstdp_longp_shorts         r&   _short_long_vol_breakdown_lsrH    s    gt$H(=A[([	.	/B	/	0B	^	$Bb#jS&9BPSATXZ^_X_RZ"_Fbj2oGS\gc]!,,r(   i  )
max_pointsc                  t        |       }|dk(  rg g g dS t        dt        t        j                  |t        |      z                    }t        j                  d||t              }|d   |dz
  k7  r,t        j                  t        j                  ||dz
              }g }g }g }	|D ]  }
|j                  t        j                  | |
         j                  d             |j                  t        t        ||
         dz  d             |9t        |      |
kD  r+|	j                  t        t        ||
         dz  d             |	j                  d	        |||	dS )
zJJSON-friendly series for HTML chart (cumulative return in percent points).r   )datesensemble_cumulative_pctspy_cumulative_pctr   r   r   %Y-%m-%dr      N)r   r
  r   r!   ceilr   arangeuniqueappendr   	Timestampstrftimer   )idxensemble_cumspy_cumrI  r   stepselrK  ens_pctspy_pctis              r&   _downsample_equity_curver^    s=    	CAAvRTUUq#bgga%
"33456D
))Aq$c
*C
2w!a%ii		#q1u-.E"$G"$G !R\\#a&)22:>?uU<?3e;Q?@3w<!#3NN5wqz!2U!:A>?NN4 ! #*% r(   c                2   t        | t              r6| j                         D ci c]  \  }}t        |      t	        |       c}}S t        | t
              r| D cg c]  }t	        |       c}S t        | t              r | | k7  s| t        d      t        d      fv ry| S t        | t        j                  f      r+t        |       }||k7  s|t        d      t        d      fv ry|S t        | t        j                  f      rt        |       S | S c c}}w c c}w )z8Make structure safe for json.dump(..., allow_nan=False).infz-infN)r@  rA  itemsstr_json_sanitizelistr   r!   floatingintegerr   )objkvr  s       r&   rc  rc    s    #t69iikBdaAq))BB#t+./aq!//#u#:uuV} ==
#~&#J6Q5<v77#

}%3xJ C/s   DDc                  |j                   }		 t        j                  |	d         }
t        j                  |	d         }|j                  d      }|
j                  d       d|j                  d       dt	        |	       d}t        d	|d
   d      t        d|d   d      t        d|d   d      t        d|d   |      t        d|d   |      g}|r |j                  t        d|d   d             t        |j                        }t        |j                        }|rt        |j                        nd}|rt        |j                        nd}|rt        |j                        nd}||z   |z   |z   |z   }|dkD  r|nd}d|||||dt        d|z  |z  d      t        d|z  |z  d      t        d|z  |z  d      t        d|z  |z  d      t        d|z  |z  d      dd}dd d!dd"|fdd#|fg}|r|j                  d$       t        |      D ]n  \  }}|t	        |      k\  r nZ||   \  }}}||j                   v r<||   j#                  t$        j&                        }|j)                  t+        |             |dk(  rd%|d&<   d%|d'<   n3|r1|rt        t        |d(   |         d)      nd%|d&<   |r|d'   |   nd%|d'<   |dk(  st-        |t.              s|j1                  d*      d+k(  s|j1                  d,      }|j1                  d-      }|!|t3        |      t3        |      z   |d.<   d/|d0<   |j1                  d1      }|j1                  d2      } t-        |t2              sSt-        | t2              se||d1<   | |d2<   q |d
   j#                  t$        j&                        j5                         }!|Dt	        |      t	        |      k(  r-|j#                  t$        j&                        j5                         nd%}"t7        |j                   |!|"      }#d%}$	 |ot	        |j                         t	        |j                         k(  rC|d
   j#                  t$        j&                        }%d|%z   j9                         j;                  d      j#                  t$        j&                        }&|j#                  t$        j&                        }'d|'z   j9                         j;                  d      j#                  t$        j&                        }(|(j=                  |&j                         j;                  d      j#                  t$        j&                        }(t?        |&|%|(d34      })t?        |(|'|(d4      }*tA        |      }+|+r
|+|)d5<   d6|)d7<   |d8d9|)|*d:}$|t	        |      dk\  rtC        |      nd%d%d%d%d;},|d   j1                  d<      }-|,j1                  d<      }.d%}/t-        |-t2        t        f      r7t-        |.t2        t        f      r!t        t        |-      t        |.      z
  d)      }/tE        tG        |d=d>      xs d>      jI                         }0|0sd?}0d@|0||dA|/||#|$|dBdC|,d<   |,dD   |,dE   |,dF   dGdHdI|jJ                   dJ|jL                   dK|jN                   dLdMdNdOdPgdQ}1tQ        |       jS                         }2|2jT                  jW                  ddR       tY        |1      }3|2j[                  dSdTU      5 }4t]        j^                  |3|4dd3V       |4ja                  dW       d%d%d%       tc        dX|2        y%# t
        $ r d}d}Y w xY w# t
        $ r d%}$Y w xY w# 1 sw Y   >xY w)YzMWrite investor_metrics JSON for HTML/PDF reports (curves, weights, activity).r   r   z	%B %d, %YrN  u    — z (z trading days)u   —z Blended strategy (full ensemble)ensemble_cumulative_retT)r   z&Tactical All Weather core (macro book)macro_cumulative_retz%Market-neutral equity momentum sleevels_cumulative_retz*Defensive Treasury tilt (risk-off overlay)defensive_cumulative_retzSector rotation sleevesector_cumulative_ret#Buy-the-dip sleeve (unweighted leg)ls_short_cumulative_retr*   r   r+   zModel weights multiply each sleeve's daily return before summing to the blended ensemble. Shares sum to 100% of the weight budget shown (gross exposure can exceed 100% capital).)
macro_bookls_alphadefensive_tltsector_rotationbuy_the_dipr   r   )descriptionweights_rawweight_share_pct)rk  NT)rl  rr  T)rm  rs  Trt  ru  )rq  rv  TNmodel_weightry  rx  r   r<  r=  approx_long_leg_tradesapprox_short_leg_tradesr   zOAQR L/S: sum of approx. completed round-turns per leg; not broker ticket count.num_trades_noter>  r?  F)r%  r  z{Share of days with long vs short exposure on the L/S sleeve (illustrative); not a true short/long volatility decomposition.short_long_vol_breakdown_notezBlended strategyzS&P 500 (SPY))period_bannerblended_column_labelbenchmark_column_labelblended	benchmarkr   r   investor_strategy_title <Multi-sleeve systematic macro & equity (research simulation)zytotal_return_pct and max_drawdown_pct are percentage points (e.g. 87.5 = +87.5% cumulative; -22.3 = -22.3% max drawdown).zUSD (nominal)u   S&P 500 — SPY buy & holdzlUnmanaged U.S. large-cap equity via SPY, buy-and-hold over the same trading days as the ensemble simulation.r   r   r   zUses daily `ret` from the research panel (aligned to ensemble dates); ensure your data source is total-return consistent if you rely on these figures.)r   rw  r   r   r   r   reinvestment_notez,Auto-exported from ensemble: --daily-period z, L/S universe z	, engine .zeIncludes equity_curve (downsampled), portfolio_blend, and per-row activity stats for the HTML report.uQ   Sharpe ≈ mean/vol × sqrt(252) on daily returns implied from cumulative curves.zASleeves disabled in this run are shown as null metrics in `rows`.z6Hypothetical / simulated; not actual investor results.)_unitsstrategy_titleas_ofbacktest_periodcurrency&ensemble_vs_benchmark_total_return_pctportfolio_blendr/   strategy_statisticsrowsr  	footnotes)parentsexist_okwzutf-8)encoding)indent	allow_nan
u#   
  Wrote investor metrics JSON → )2r   r   rT  rU  r   r"  r   rS  r   weight_macroweight_alphaweight_defensiveweight_sectorweight_ls_shortr   	enumeratecolumnsr    r!   r"   r   r  r@  rA  rl   r   r   r^  rm   r   r  r:  rH  r   rb  getattrstripr>   ls_universeensemble_ls_enginer   
expanduserparentmkdirrc  openjsondumpwriterk   )5out_pathensemble_dfuse_sector_sleeveuse_def_sleeveuse_dip_sleevespy_bnh_cumargsensemble_managerls_diagnosticsrV  i0i1r  
period_lblr  wmwawdwswlsw_sumr  	cum_specsr]  r   colwkey	sleeve_oncum_sltstrC  rD  ens_arrspy_arrr/   r  	ens_cum_sens_dspy_c_sspy_d
strat_side
bench_sideslbm_statsens_trb_trexcessr   payloadpathsafefs5                                                        r&   &_export_ensemble_investor_metrics_jsonr    s    

C\\#a&!\\#b'"K(J/0bkk*6M5NbQTUXQYPZZhi
 	.12	

 	4./	

 	3+,	

 	823"	

 	$/0%	
+"D6 !556	
 
,,	-B	,,	-B5C00	1B2C--	.B5C% 00
1CGbL2#EU]EEf !
  
U 2A6ebj50!4"52:#5q9$URZ%%7; u!4a8
O, 	04/	#_nE	 "35FG5I IJD/ 93I(|T9+%%%$++BJJ7EJJ-e456"&C&*C"#[d%om.LT.R(SUV"WjnC=F 23D9D "# 6j6!!(+/II#''(@A#''(AB>bn(+B#b'(9C%i )* $''(AB#''(BCb#&:b#+>57C1268C2399< 34;;BJJGPPRG "s;'73{;K'K 	2::&//1 
 ,K,=,=wPL15#"s;+<+<'=[EVEVAW'W#$=>EEbjjQI9_00299#>EEbjjQE!((4G7]..077<CCBJJOEMM%++.55c:AA"**ME,UIuSXYJ,UGUQUVJ-n=B9;
56F :;
 ",(:*9%'# "s;'71'< 	(4 !%%)! $	
  !W[[+,F<<*+DF&3,'Jtc5\,JuV}uT{2A67<BCIIKENL  %#28*$20; !)); <%-.E%F%o6 (); <c
  ;4;L;L:M N ,,-Yt7N7N6OqRs_OD
;%GN >$$&DKKdT2'"D	3	) Q		$!u5	 
0
78c  
Z  #"#B s1   A3\, E2]  *+],\=<\= ]]]c                   t        j                  | j                  t        j                        d      j                         }|j                  ryt        j                  |j                        |_        |j                  j                   |j                  j                  d      |_        |j                         }d|z   j                  d      j                         dz
  }|j                  ryt        j                  |j                  t        j                        |j                  j                   |j                  j"                  d      }|j%                  dd	d
d      }|j'                  t)        t+        dd                  }t+        dd      D cg c]  }t,        j.                  |    c}|_        |dz  }t3        d| d       t3        |j5                  dd              yc c}w )uX   
    Print calendar-month compound returns (%) in a year × month grid (Jan–Dec).
    T)r   Nr+   r  r   )r]   yearmonthr  r  r]   first)r   r  r   aggfuncr      r  r   r  u     — monthly returns by year (%)u
       —   c                
    | dS )Nz7.2f )r  s    r&   <lambda>z0_print_monthly_returns_by_year.<locals>.<lambda>  s
    QtH r(   )na_repfloat_format)r   r  r    r!   r"   dropnare   r  r   tztz_localizer   r!  r   r   r   r  r  pivot_tabler  rd  rangecalendar
month_abbrr  rk   	to_string)	daily_retr   r.   r,  r#   pivotm	pivot_pcts           r&   _print_monthly_returns_by_yearr    sx    			)""2::.T:AACAwwnnQWW%AGwwzz''%%d+	AQw  &++-3G}}	##"**#5MM&&]]((	

B NNPWNXEMM$uQ|"4M5E5:1b\BX((+BEMI	Bug5
67	)

\@U

VW	 Cs   G'c                    t        |       j                         j                         }|dv ry|j                  d      r	 t	        |dd       S y# t
        $ r Y yw xY w)zIInterpret e.g. '5y', '10y', 'max' as lookback years for universe_scanner.)r
  r   allg      >@yNr         $@)rb  r  lowerendswithr   r"  )rN   ss     r&   _parse_daily_period_yearsr    sb    F!!#A%%zz#	3B=    		s   A 	AAc                   | j                   r| S d| j                  v rdnd| j                  v rdnd}|s!t        dt        | j                               | |   j	                  t
        j                        }| j                         }d|j                  vr||d<   |j                         |d<   |j                  dd      j                         |d<   |j                  d	      |j                  d
      z  dz
  |d<   |S )zFNormalize close, ret, sma_200, aqr_mom for ensemble / ls-equity modes.rP   Closer  z)Expected 'close' or 'Close' column, got: r]   r5   rU   min_periodssma_200r8   r7   r+   aqr_mom)re   r  KeyErrorrd  r    r!   r"   r   rm   rollingr   r   )r#   	price_colrP   outs       r&    _compute_daily_backtest_featuresr  &  s    	xx	"bjj0'RZZBWw]_IB4

CSBTUVVyM  ,E
'')Cckk!G!!#CJ]]#3]?DDFC	N[[_u{{3'77#=C	NJr(   c                    d| j                   vrt        d      | j                         }d|j                   vr3|d   j                  t        j
                        j                         |d<   |S )zfEnsure ``ret`` for :class:`BuyTheDipSleeve` on ML OHLCV panels (trend SMA built from close in-engine).rP   zclose requiredr]   )r  r  r   r    r!   r"   rm   )r#   r  s     r&   _ensure_ml_panel_for_dipr  7  s[    bjj '((
'')CCKK\((4??AE
Jr(   c           	     `   | j                         }t        |j                  t        j                        r |j                  j                  d      |_        |j                  |j                  D ci c]  }|t        |      j                          c}      }d|j                  v r%|d   j                  t        j                        |d<   d|j                  vrt        d      dD ]  }||j                  vst        d| d       |g d	   j                  t        j                        S c c}w )
zELowercase OHLCV; ``close`` prefers Adj Close when present (yfinance).r   r  z	adj closerP   z&ML path requires Close/close in OHLCV.)highvolumezML path requires z
 in OHLCV.)rP   r  r  )r   r@  r  r   
MultiIndex	droplevelrenamerb  r  r    r!   r"   r  )r#   r  cs      r&   _normalize_ohlcv_for_mlr  A  s    
'')C#++r}}-kk++A.
**EAaQ/E*
FCckk!;'..rzz:Gckk!?@@ >CKK.qc<==> *+222::>> Fs   .!D+c                   ddl m} ddl} |       }|d   j                  t              j                         }|r|dkD  r|dt        |       }||d   j                  t              j                  |         }t        t        |d   j                  t              |d   j                  t                          }|D ]  }|j                  |d        t        |       }	t        dt        |       d       |j                  |d	|	
      }
i }|
j                         D ]#  \  }}||j                   r	 t#        |      ||<   % ||fS # t$        $ r Y 5w xY w)zTDownload S&P 500 (Wikipedia) daily OHLCV + sector map for :mod:`ml_momentum_engine`.r   %fetch_sp500_constituents_with_sectorsNtickersectorUnknown   🔍 Downloading u     S&P 500 names (daily OHLCV) …1d	timeframelookback_years))RenTech.strategy_stack.ml_momentum_enginer  universe_scannerr    rb  tolistr   isinrA  zip
setdefaultr  rk   r   download_and_cache_datara  re   r  r"  )r>   max_tickersr  r   tabtickerssub
sector_maptr   rawequity_dictr#   s                r&   _load_sp500_ml_panelr'  Q  s]   _!
/
1C(m""3'..0G{Q,C,-
c(m""3',,W5
6Cc#h-..s3S]5I5I#5NOPJ ,a+, &l3E	c'l^+K
LM

$
$ % C
 ,.K 2:	4R8KN	 
""  		s   0E	EEF)r  refresh_cachec                  ddl }t        |       }|dk(  r|j                  d      }d}n^|dk(  rKddlm}  |       }	|	d   j                  t              j                         }|r|dkD  r|dt        |       }d	}nt        d
|      t        d| dt        |       d       |j                  |d||rdnd      }
i }|
j                         D ]u  \  }}||j                  r|j                         }t!        j"                  |j$                        j'                  d      |_        |j)                         }t+        |      ||<   w |S )zm
    Daily panels with ``close``, ``ret``, ``sma_200``, ``aqr_mom`` for :class:`CrossSectionalMomentum`.
    r   Nsp100universeS&P 100sp500r  r  S&P 500z%universe must be sp100 or sp500, got r  z equity universe (u    names) …r  r*   g      8@)r  r  max_age_hours)r  r  get_sp100_tickersr  r  r    rb  r  r   
ValueErrorrk   r   r  ra  re   r   r   r  r   r  r   r  )r>   r,  r  r(  r   r   r!  r   r  r   
equity_rawr&  r$  r#   df2s                  r&   _load_aqr_equity_dictr5  r  sS    "%l3E7&&&8	W	c35h-&&s+224;?0K 01G@MNN	eW$6s7|nK
PQ++*c	 , J ,.K!!# ?2:ggiNN399-99$?	nn9#>A? r(   c                    ddl m}  |       }t        t        |d   j	                  t
              |d   j	                  t
                          }| D ci c]  }||j                  |d       c}S c c}w )zRGICS sector labels from Wikipedia S&P 500 table; missing tickers map to 'Unknown'.r   r  r  r  r  )r  r  rA  r  r    rb  rl   )r!  r  r   wikir$  s        r&   _wiki_sector_map_for_tickersr8    s`    _
/
1CCM((-s8}/C/CC/HIJD/67!Atxx9%%777s   A0c                    ddl m} t               }i }|D ]>  }|j                  ||       }|j                  rt        d| d      t        |      ||<   @ |S )za11 SPDR sector ETFs with ``close``, ``ret``, ``sma_200``, ``aqr_mom`` (same as macro/L/S panels).r   )SPDR_SECTOR_TICKERSrM   zNo daily data for sector ETF z (check period / ticker))-RenTech.strategy_stack.multi_strategy_managerr:  r   rd   re   rf   r  )r>   r:  rC  r  r$  rp   s         r&   _load_sector_etf_dictr<    sd    Q	B#%C  9q6;;!>qcAYZ[[1%8A	9
 Jr(   c               :   ddl }|j                         rN|j                  d      D cg c]1  }|j                         s|j                         j                         3 }}d}nz|dk(  rt	        t
              }d}nc|dk(  r|j                  d      }d	}nI|d
k(  r6ddlm}	  |	       }
|
d   j                  t              j                         }d}nt        d|      |r|dkD  r|dt        |       }|dv rdn|}t               }i }t        d| dt!        |       d| d|  d	       |D ]L  }	 |j#                  |||       }||j&                  r'	 |j)                  |      }t!        |      dk  rH|||<   N |S c c}w # t$        $ r Y aw xY w# t$        $ r Y pw xY w)z
    Hourly OHLCV panels with ``trade_date`` for :mod:`swing_breakout_hourly`.
    Yahoo limits intraday history; prefer ``730d`` for 60m bars.
    r   N,zcustom list
macro-etfszDmacro ETFs (FX, oil, gold/silver, Treasuries, commodities, VXX/SVXY)r*  r+  r-  r.  r  r  r/  zNuniverse must be macro-etfs, sp100, sp500, or use --hourly-swing-tickers; got rQ   rR   u   🔍 Downloading hourly (z) for z names (z, u   ) …rS   2   )r  r  splitupperrd  r   r1  r  r  r    rb  r  r2  r   r   rk   r   rh   r"  re   ri   )r?   rT   r,  r  tickers_csvr   r  r!  r   r  r   rs   rC  r  r$  r%  hs                    r&   _load_hourly_swing_universerE    s    ".9.?.?.DR	1779??$RR	\	!56V	W	&&&8	W	c35h-&&s+224\]e\hi
 	
 {Q,C,-m+B	B#%C	%bTG~XeWBN__d
ef 	##A?#KC ;#))	((-A q6B;A JQ S:  		  		s.   E: E:-E?F?	F
F	FFc                 O   t        j                  t              } | j                  dddd       | j                  dd	       | j                  d
d	       | j                  dd	       | j                  dd	       | j                  ddd       | j                  dt        d       | j                  dt        d       | j                  dt        d       | j                  dt        d       | j                  dt
        d       | j                  dt
        d        | j                  d!t        d"d#$       | j                  d%d&d'(       | j                  d)t        d"d*$       | j                  d+t        d,d-$       | j                  d.t        dd/$       | j                  d0t        d1d2$       | j                  d3t
        d4d5$       | j                  d6t        d7d8$       | j                  d9t        d:d;$       | j                  d<d=d>?       | j                  d@t        d&dA$       | j                  dBt        dCdD$       | j                  dEt
        dFdG$       | j                  dHd&dI(       | j                  dJd&dK(       | j                  dLdMdN(       | j                  dOdPdQdRS       | j                  dTd=dU?       | j                  dVt        dWdX$       | j                  dYt
        dZd[$       | j                  d\d=d]?       | j                  d^t
        d_d`$       | j                  dat        ddb$       | j                  dct
        ddd$       | j                  det
        dfdg$       | j                  dht
        didj$       | j                  dkd=dl?       | j                  dmt
        d_dn$       | j                  dot        d7dp$       | j                  dqt
        drds$       | j                  dtt        d7du$       | j                  dvt        d7dw$       | j                  dxdydzd{S       | j                  d|t        dd}$       | j                  d~d=d?       | j                  dd=d?       | j                  ddddS       | j                  dt
        dd$       | j                  dt        dd$       | j                  dt        dd$       | j                  ddddS       | j                  dt        d"d$       | j                  ddddS       | j                  dt        d"d$       | j                  ddddS       | j                  dt        d&d$       | j                  dt        d"d$       | j                  dt        d1d$       | j                  dt        d1d$       | j                  dt        dd$       | j                  dt
        did$       | j                  dt        dd$       | j                  dd=d?       | j                  dd=d?       | j                  dd=d?       | j                  ddddS       | j                  dt        dd$       | j                  dt
        d d$       | j                  dd=d?       | j                  ddddS       | j                  ddddìS       | j                  dd&dŬ(       | j                  dd&dǬ(       | j                  dt        ddɬ$       | j                  dt        dCdˬ$       | j                  dt
        ddά$       | j                  dd&dЬ(       | j                         }t        j                  t               |j                  dk(  rdt        dѫ       |j                  j                         xs d }t        |j                   |j"                  |j$                  |j&                  |j(                  |j*                  |j,                  |j.                  |j0                  |j2                  |j4                  |j6                  ||j8                  |j:                  ҫ      }t        dt=        |      dԛd|rdndכ        d}t?        d|d   |۫       t?        d|d   |۫       |rd| nd&}|j"                   | d|j(                   d|j                    d}	 tA        |||jB                  xs d        y |j                  dk(  r_t        d       tG               jI                  |j"                  |j$                        }|jJ                  rtM        d|j"                         tO        |jP                  |jR                  |jT                  |jV                        }|jY                  |      }	|	d   j[                  t\        j^                        ja                         |	d<   tc        |	dd      }
|
|	d<   |
|	d<   t?        d|
       d|j"                   d|jP                   d|jR                   d|jT                  dd|jV                   
}	 tA        |	||jB                  xs d        y |j                  dk(  rd"dl2m3}m4}m5} tm        |jo                               }t        d|        i }|D ]  }tG               jI                  ||j$                        }|jJ                  sd|jp                  vrtM        d|       |d   j[                  t\        j^                        }|js                         }|ja                         |d<   |ju                  dd      jw                         |d<   |jy                  d      |jy                  d      z  diz
  |d<   |||<     |       }|j{                  |d      }t?        d|d          di|d   z   ja                         j}                  d       }di|d   z   ja                         j}                  d       }t?        d|       t?        d |       t        |d   d       d}	  |||jB                  xs d        y |j                  dk(  rd"dl@mA}mB}mC} t        d       t        |j$                  |j                  |j                        }|stM        d      tG               jI                  d|j$                        }|jJ                  rtM        d	      t        |      }|j                  r"t        t        |jo                                     nd }|j                  rt        d
        |       }i }|j                  |d|d||      }  ||       | j}                  d       j[                  t\        j^                        }!t        di|!z   j                         diz
        }"t=        |!      dkD  rt        |!j                  d            nd }#t        |!jw                               }$|#dkD  r|$|#z  t]        j                  d      z  nt        d      }%t        d|"dd|%d       t        | d       	  || |jB                  xs d        y |j                  dk(  rd"dl@mO}&mB} t        d       t        |j$                  |j                  |j                        }|stM        d      tG               jI                  d|j$                        }|jJ                  rtM        d      t        |      } |&t	        |j                        t        |j                        t	        |j                        t	        |j                        t	        |j                        t	        |j                        t        |j                        t        |j                        t        |j                         t        |j                        t        |j                        t        |j                              }'|'j                  |t	        |j                        |d      }(|(j}                  d       j[                  t\        j^                        }!t        di|!z   j                         diz
        }"t=        |!      dkD  rt        |!j                  d            nd }#t        |!jw                               }$|#dkD  r|$|#z  t]        j                  d      z  nt        d      }%t        d|"dd|%d       t        |(d       	  ||(|jB                  xs d        y |j                  dk(  r7d"d l@mB} d"d!l_m`})ma}* t        |j                        r!t        |j                        rt        d"      t        d#       t        |j&                  |j(                  t        |j                        t	        |j                        t        |j                        $      }+t=        |+      dWk  rtM        d%t=        |+       d&       |)t	        |j                        t	        |j                        t	        |j                        t        |j                        t	        |j                        t        |j                         t        |j                         t        |j                        '      }, |*|+|,t        |j                        t	        |j                        t        |j                        d()      \  }-}.|-j}                  d       j[                  t\        j^                        }!d*}/t?        d+|!|/۫       t        |!d,       	  ||!|jB                  xs d        y |j                  d-k(  rd"d.lrms}0 d"d l@mB} t        d/       t        |j$                  |j                        \  }}t=        |      d0k  rtM        d1t=        |       d2       |0|j$                  |j                  |j                  3      }1|1j                  ||d|j$                  |j                  |j                  4      } | j}                  d       j[                  t\        j^                        }!t        di|!z   j                         diz
        }"t=        |!      dkD  rt        |!j                  d            nd }#t        |!jw                               }$|#dkD  r|$|#z  t]        j                  d      z  nt        d      }%t        d5|"dd|%d       t        | d6       	  || |jB                  xs d        y |j                  d7k(  rQd"d8l@mx}2my}3 t        d9       t        |j$                        }4 |2       }5|5j                  |4|j                  d:      }6|6j}                  d       j[                  t\        j^                        }!t        di|!z   j                         diz
        }"t=        |!      dkD  rt        |!j                  d            nd }#t        |!jw                               }$|#dkD  r|$|#z  t]        j                  d      z  nt        d      }%t        d;|"dd|%d       t        |6d<       	  |3|6|jB                  xs d        y |j                  d=k(  rd"d>l@m|}7m}}8 t        d?       tG               }9|9jI                  d|j$                        }:|9jI                  d@|j$                        };|:jJ                  s|;jJ                  rtM        dA      t        |:      }t        |;      }< |7t	        |j                        t        |j                        t        |j                         t        |j                        B      }=|=j                  ||<d      }>|>j}                  d       j[                  t\        j^                        }!t        di|!z   j                         diz
        }"t=        |!      dkD  rt        |!j                  d            nd }#t        |!jw                               }$|#dkD  r|$|#z  t]        j                  d      z  nt        d      }%t        dC|"dd|%d       t        |>dD       	  |8|>|jB                  xs d        y |j                  dEk(  rd"dFl@mO}&m|}7m}?mx}2m}@mC} t        dG       g dH}AtG               }Bi }C|AD ]H  }BjI                  ||j$                        }	|	jJ                  rtM        dI|       t        |	      C|<   J t        |j                        xr t        |j
                        d kD  }Dd }E|DrNt        dJ       t        |j$                        }4 |2       j                  |4t	        |j                        d:      }Et        |j                        xr t        |j                        d kD  }Fd }G|Frt        dK        |7t	        |j                        t        |j                        t        |j                         t        |j                        B      }=|=j                  Cd   |Cd@   d      }GDrt        |j
                        nd }HFrt        |j                        nd }It        |j                        xr t        |j                        d kD  }J|Jrt        |j                        nd }K |?HI|KL      }Ld }Md }d }Nd }O|j                  dMk(  rd"d.lrms}0 t        |j$                  |j                        \  }N}Pt=        |N      d0k  rtM        dNt=        N       dO      t        dP        |0|j$                  |j                  |j                  3      }1|1j                  NPd|j$                  |j                  |j                  4      }Mnut        |j$                  |j                  |j                        }|stM        dQ      |j                  r.t        t        |jo                                     }Ot        d
       d }QJrNt        dR        |&t	        |j                        t        |j                        t	        |j                        t	        |j                        t	        |j                        t	        |j                        t        |j                        t        |j                        t        |j                         t        |j                        S
      }'Cd   }RN/Nj                         D ci c]  \  }}|t        |       }S}}n||}SntM        dT      |'j                  St	        |j                        Rd      }Qg }TDrTj                  dU       FrTj                  dV       JrTj                  dW       dXj                  T      }Ut        dY|UrdXU nd&z   dZz          i }VM&Lj                  Cd MEGQ[      t        d\       n"Lj                  C|OEGQV]       ||V       dfd^}W |Wd_d`        |Wdadb       Dr Wdcdd       Fr Wdedf       Jr Wdgdh        Wdidj       d }XCj!                  d      }Y|YYjJ                  sdYjp                  v rj"                  }ZYd   j%                  |Z      j[                  t\        j^                        j}                  d       }[di|[z   j'                         diz
  }Xt        dk       t)        dl|X       t        |Xj*                  dm         dnz  }\t        dj   j[                  t\        j^                        j*                  dm         dnz  }]t        do|\dpdq|]dpdr|]|\z
  dpds       t        t-        |dtd&      xs d&      j                         }^|^rt/        ^DFJX|LVu	       dj   j[                  t\        j^                        }_di|_z   ja                         j}                  d       j[                  t\        j^                        }`t        |`dv        @       y |j                  dwk(  rt        dx       d"d l}at        |j$                        j                         j3                         }bdy}c|bj5                  dz      r	 t        bd dm       }ct8        j:                  d d  }d	 dd"   d{dd|d}d~t        c      ddg	t8        _        aj=                          |dt8        _        y |j                  dk(  rXt        d       d"d l}ad"d lmc m}e d"dl2m4}m5} t        |jD                        j                         }ft        |jF                        j                         }gt        |f      t        |g      z  rt        d      d"d l}hfrgrfgfg}it        d|f d|g d       ndt        d|jJ                   d       t        |j$                        j                         j3                         }bdy}c|bj5                  dz      r	 t        bd dm       }cajM                  d      }j|ajO                  |jd}c      }tQ        |a|      \  }k}l|kjS                  dd(      }k|kjJ                  rtM        d      lrt        dl d       tU        dt	        |jJ                              }mkjW                  |m      }n|njY                         D o!cg c]#  \  }o}!t        |!d         t        |!d         f% }i}o}!i }pg }qhjI                  idd      D ]w  \  }r}s|r d|s }t	 t        dt d       t        d       ej[                  |j                   rs|j$                  |j&                  |j(                        }u|ej]                  |uj^                  d      \  }v}w|eja                  |uddWd d|j,                  |j.                  |j6                        \  }x}o|xjJ                  rtM        d      xjS                  dd      j*                  d"   }yt        |yd         }zt        |yd         }{t	        |yd         }|t	        |yd         }}||}~ejc                  ||      }t        d|| d|zdd|{dd|} d|~ 
       t        |j                   r|j$                  |j&                  |j(                  |}|j,                  |j.                  |||z|{|j6                  s|~|ҫ      }wj"                  je                         }|jf                  |j"                  |k\     js                         }|jJ                  sdjp                  vrtM        d      t?        dt d   dج۫       |dg   js                         p|t<   z pstM        d      t        d        |       }|j{                  pt	        |jh                        t        |jj                              }d}t?        d|d   |۫       t?        d|d   |۫       t        d        |||jl                  xs d        y tM        d|j                         # tD        $ r Y y w xY w# tD        $ r Y y w xY w# tD        $ r Y y w xY w# tD        $ r Y y w xY w# tD        $ r Y y w xY w# tD        $ r Y y w xY w# tD        $ r Y y w xY w# tD        $ r Y y w xY w# tD        $ r Y y w xY wc c}}w # t6        $ r dy}cY zw xY w# dt8        _        w xY w# t6        $ r dy}cY w xY wc c}!}ow # t6        $ r0}qj                  t       t        d|t d        Y d }~Ad }~ww xY w(  N)rw  z--moderun)rG  scanpipelineswingswing-sector	ls-equityls-equity-dip	ml-equitysector-etfsdefensive-mrensemblehourly-swing-breakoutu  Execution mode: run (single backtest), scan (find pairs only), pipeline (scan+optimize+OOS backtest), swing (daily CRSI pullback), swing-sector (Tactical All Weather macro book only), ls-equity (monthly AQR L/S; see --ls-universe), ls-equity-dip (RSI dip in SMA200 uptrend, fixed hold; see --ensemble-dip-*), ml-equity (S&P 500 XGBoost walk-forward L/S only), sector-etfs (SPDR sector ETF momentum rotation only), defensive-mr (SPY return panic z-score → TLT tilt; standalone), ensemble (macro + L/S + optional sleeves; see flags), hourly-swing-breakout (ETFs: fractal swing breakouts on hourly bars; --hourly-swing-blend equal|inv_vol|winners_equal; see --hourly-swing-*).)defaultchoiceshelpz--regime-tickerr0   )rS  z--trade-tickerr1   z--daily-periodr2   z--intraday-periodr3   z
--intervalr4   )r4   rR   15m30m)rS  rT  z--smar5   )typerS  z--aqr-lookbackr7   z
--aqr-skipr8   z
--z-windowr9   z	--z-entryr:   z--z-exitr*   z--regime-shift-daysr   zLag regime by N calendar days)rX  rS  rU  z--hedge-tickerr  z[If set, pair stat-arb: causal OLS hedge, z on spread, PnL on basket_ret (needs statsmodels))rS  rU  z--hedge-windowzEHedge refit: 0 = expanding OLS on all past bars; N>0 = rolling N barsz--min-hedge-obsr;   z>Minimum prior bars required before first causal spread / hedgez--trend-smazSwingEngine: trend SMA windowz--rsi-windowr   zSwingEngine: RSI windowz--rsi-thresholdr  z SwingEngine: RSI entry thresholdz
--exit-smar   zSwingEngine: exit SMA windowz--inverse-sp500-tickerSHzhTickers for --mode swing-sector: inverse S&P 500 ETF to include alongside the sector ETFs (default: SH).z--disable-inverse-sp500
store_truezDDisable inclusion of the inverse S&P 500 ETF in --mode swing-sector.)actionrU  z--sector-tickersznComma-separated sector ETF tickers for --mode swing-sector. If empty, uses default 11 SPDR Select Sector ETFs.z--rp-vol-windowx   zeRisk parity volatility window (bars) for --mode swing-sector (inverse-vol weights, shifted by 1 bar).z--rp-kill-switch-max-ddg    ezPer-asset drawdown kill switch max dd threshold for --mode swing-sector. Set very negative to effectively disable (default: -1e9).z--save-plotzPNG path (empty = plt.show())z--export-investor-metrics-jsonzensemble only: append to your full ensemble command (same flags as a normal run); writes metrics JSON for scripts/build_investor_strategy_pdf.py (e.g. docs/investor_metrics.json). The literal word '...' is not valid here.z--investor-strategy-titler  zPensemble: strategy_title in the JSON produced by --export-investor-metrics-json.z--ensemble-ls-engine)aqmlr]  uU   ensemble: L/S sleeve — aq (AQR rank + regime/vol) or ml (XGBoost on S&P 500 OHLCV).)rT  rS  rU  z--ensemble-sector-sleevezcensemble: add SPDR sector ETF sleeve (top-k by 12-1 momentum); weight via --ensemble-sector-weight.z--ensemble-sector-top-k   zXensemble + sector-etfs: hold this many sector ETFs each month (equal weight among them).z--ensemble-sector-weightg333333?zfensemble: blend weight on sector rotation daily returns (0 disables even if --ensemble-sector-sleeve).z--ensemble-defensive-mruY   ensemble: add defensive mean-reversion sleeve (SPY daily-return z → TLT when panicked).z--ensemble-defensive-weightgQ?zbensemble: blend weight on defensive MR daily returns (0 disables even if --ensemble-defensive-mr).z--defensive-z-windowz=defensive-mr / ensemble: rolling days for SPY return z-score.z--defensive-entry-zzXdefensive-mr / ensemble: activate TLT tilt when SPY return z <= -this (positive number).z--defensive-exit-zr   u`   defensive-mr / ensemble: clear tilt when SPY return z >= -this (e.g. 0.5 → exit at z >= -0.5).z--defensive-tlt-weightr+   zYdefensive-mr / ensemble: TLT weight within the sleeve when active (remainder cash at rf).z--ensemble-dip-sleevezZensemble: add **buy-the-dip** sleeve (RSI washout in SMA uptrend, ATR-ranked, fixed hold).z--ensemble-dip-weightzXensemble: blend weight on buy-the-dip sleeve (0 disables even if --ensemble-dip-sleeve).z--ensemble-dip-rsi-periodz8ensemble / ls-equity-dip: RSI period (Wilder smoothing).z--ensemble-dip-rsi-maxg      4@zJensemble / ls-equity-dip: enter when RSI is strictly below this (e.g. 20).z--ensemble-dip-hold-dayszZensemble / ls-equity-dip: hold each new entry for this many trading days (close-to-close).z--ensemble-dip-atr-periodzWensemble / ls-equity-dip: ATR period for normalized ATR (ATR/price) when ranking picks.z--ensemble-dip-rank-by)atr_normrsir`  zXensemble / ls-equity-dip: rank new signals by normalized ATR (desc) or deeper RSI (asc).z--ensemble-dip-trend-smazFensemble / ls-equity-dip: SMA for dip-in-uptrend filter (close > SMA).z --ensemble-dip-no-uptrend-filterzOBuy dips even when close is below the trend SMA (default: require close > SMA).z--ensemble-dip-spy-bull-onlyz@Only hold dip basket when SPY is above its 200d SMA at week-end.z--ensemble-dip-signal-mode)ra  pct_dropra  zSensemble / ls-equity-dip: 'rsi' washout or 'pct_drop' (prior-day return threshold).z--ensemble-dip-pct-drop-mingQ?z\ensemble / ls-equity-dip: with pct_drop, enter when prior daily return <= -this (e.g. 0.03).z--ensemble-dip-top-nr   zOensemble / ls-equity-dip: max new entries per day (among simultaneous signals).z--ensemble-dip-vol-windowz`ensemble / ls-equity-dip: trailing return vol window for inverse-vol weights among active names.z--ensemble-dip-weighting)equalinv_volrc  zLensemble / ls-equity-dip: equal weight across picks or inverse trailing vol.z--ml-max-tickerszVml-equity / ensemble+ml: max names from the S&P 500 list (0 = all; smaller is faster).z--ls-universe)r*  r.  r*  zVls-equity / ensemble+aq: cross-sectional universe (S&P 100 or S&P 500 from Wikipedia).z--ls-max-tickerszGWith --ls-universe sp500: cap names (0 = full list). Ignored for sp100.z--hourly-swing-universe)r?  r*  r.  r?  zhourly-swing-breakout: universe when --hourly-swing-tickers is empty. macro-etfs = FX/oil/gold/silver/Treasuries/commodities + VXX & inverse ST VIX (SVXY); see HOURLY_SWING_MACRO_ETF_TICKERS in main.py.z--hourly-swing-tickerszShourly-swing-breakout: comma-separated tickers (overrides --hourly-swing-universe).z--hourly-swing-max-tickersz]hourly-swing-breakout: max symbols to load from universe (0 = all; cap for sp500/sp100 only).z--hourly-swing-fractal-leftzThourly-swing-breakout: fractal swing bars to the left of the pivot (Williams-style).z--hourly-swing-fractal-rightzXhourly-swing-breakout: fractal swing bars to the right (confirmation lag; no lookahead).z--hourly-swing-daily-atr   z]hourly-swing-breakout: Wilder daily ATR period (from daily OHLC aggregated from hourly bars).z--hourly-swing-min-atr-multu]   hourly-swing-breakout: min swing vs prior opposite swing ≥ this × prior-session daily ATR.z--hourly-swing-hold-barszQhourly-swing-breakout: exit after this many hourly bars in the trade (time stop).z--hourly-swing-long-onlyz?hourly-swing-breakout: only trade upside breaks of swing highs.z--hourly-swing-short-onlyz@hourly-swing-breakout: only trade downside breaks of swing lows.z--hourly-swing-close-confirmzOhourly-swing-breakout: require close through the swing level (not only a wick).z--hourly-swing-blend)rc  rd  winners_equalzZhourly-swing-breakout: portfolio blend (equal | inverse underlying vol | IS winners-only).z--hourly-swing-vol-windowzOhourly-swing-breakout: inv_vol trailing std window on underlying hourly closes.z--hourly-swing-winner-epsilonzahourly-swing-breakout: winners_equal keeps tickers with full-sample strat cum > this (in-sample).z--ls-sector-neutrala[  ls-equity / ensemble+aq: split top_n long/short seats across GICS sectors (Wikipedia), ranking by momentum within each sector. Often lowers backtests vs default pool-wide rank because it adds weaker-trend names in sluggish sectors and trims the strongest names in leading sectors (classic momentum is intentionally cross-sectionally concentrated).z--ml-target)r  r/  r  zXML sleeve: train on excess fwd return vs SPY (excess) or total stock fwd return (total).z--ml-weighting)rd  rc  rd  zUML sleeve: within long/short legs use inverse 20d vol (inv_vol) or equal 1/N (equal).z--pair-yzRPipeline only: trade leg ticker; use with --pair-x to skip the cointegration scan.z--pair-xzRPipeline only: hedge leg ticker; use with --pair-y to skip the cointegration scan.z--portfolio-top-kz[Pipeline: number of cointegrated pairs to optimize independently for portfolio aggregation.z--portfolio-vol-windowzPPortfolio sizing: rolling volatility window (bars) used for inverse-vol weights.z--portfolio-max-ddgzLPortfolio kill-switch: max drawdown threshold per pair (e.g. -0.05 for -5%).z--portfolio-save-plotz1PNG path for portfolio plot (empty = plt.show()).u    📥 Loading data (yfinance) …)r<   r=   r>   r?   r@   rA   rC   rD   rE   rF   rG   rH   rI   rJ   rK   u   📌 Intraday bars: r>  z	 | mode: zpair spreadzsingle closeg     @zStandalone MRra   r   Filteredrc   z vs  z MR vs momentum(z
)-filtered)r   r   rJ  u   🧭 Loading daily data …rM   rO   )	trend_sma
rsi_windowrsi_thresholdexit_smarP   r]   r`   )r%   zSwing CRSI PullbackzSwing CRSI Pullback (z) | trend_sma=z, rsi_window=z, rsi_thresh=gz, exit_sma=rK  )BASE_WEIGHTSPortfolioManagerplot_portfoliou,   🧭 Tactical All Weather baseline tickers: r  r  r  g{Gz?)cash_annual_yieldzTactical All Weatherportfolio_bar_ret!static_all_weather_cumulative_retspy_cumulative_retzStatic All Weather (baseline)zSPY buy & holdz%Tactical All Weather (total strategy)z0Tactical All Weather vs Static All Weather + SPYr~   rL  )CrossSectionalMomentumplot_ls_standalone$print_cross_sectional_ls_diagnosticsu1   🚀 Running equity L/S momentum (standalone) …)r,  r  z4No equity universe data loaded; cannot run ls-equityz8No daily data for SPY (required for L/S regime throttle)z7  L/S: sector-balanced seat allocation (Wikipedia GICS))top_nspy_dfrq  r#  diagnosticsr   r   r   r   r   z%  L/S equity (standalone): final_cum=rZ   r   r   z'L/S equity (standalone, total strategy)rM  )BuyTheDipSleeverv  uK   📉 Buy-the-dip sleeve (RSI dip + SMA uptrend, fixed hold, standalone) …z8No equity universe data loaded; cannot run ls-equity-dipz4No daily data for SPY (required for regime throttle))
rsi_periodrsi_max
atr_periodhold_trading_days
vol_windowsma_trend_window	weightingrank_bydip_in_uptrendonly_when_spy_bullsignal_modepct_drop_min)rx  ry  rq  z&  Buy-the-dip (standalone): final_cum=z(Buy-the-dip (standalone, total strategy)rR  )rv  )SwingBreakoutHourlyConfiggenerate_portfolio_returnszGUse at most one of --hourly-swing-long-only / --hourly-swing-short-onlyu@   📈 Hourly swing breakout (ETF universe, research backtest) …)r,  r  rC  z"Too few tickers with hourly data (zb). Try --hourly-swing-max-tickers, --intraday-period 730d, or --hourly-swing-tickers AAPL,MSFT,...)fractal_leftfractal_rightdaily_atr_periodmin_atr_mult	hold_bars	long_side
short_sideclose_confirms_breakoutT)blendr  winner_epsilonverboseg     @z$Hourly swing breakout (equal-weight)z*Hourly swing breakout (compounded monthly)rN  )MLMomentumEngineu<   🤖 ML momentum (XGB walk-forward) — requires xgboost …r@  z"Too few tickers after OHLCV load (z)). Check data or increase --daily-period.)data_periodtarget_moder  )r#  rq  r  r  r  z(  ML L/S equity (standalone): final_cum=zML L/S equity (total strategy)rO  )SectorETFRotationplot_sector_rotation_standaloneu=   📊 Sector ETF rotation (11 SPDR sectors, 12-1 momentum) …)top_krq  z,  Sector ETF sleeve (standalone): final_cum=z$Sector ETF rotation (total strategy)rP  )DefensiveMeanReversionSleeveplot_defensive_mr_standaloneuC   🛡 Defensive mean reversion (SPY return z-score → TLT tilt) …r   z,Need SPY and TLT daily data for defensive-mr)rE   rV   rW   tlt_weight_when_activez'  Defensive MR (standalone): final_cum=z$Defensive MR sleeve (total strategy)rQ  )r{  r  EnsembleManagerr  plot_ensemblerw  u(   🚀 Running multi-strategy ensemble …)r0   r   r   r   r   zNo daily data for macro ticker u9   📊 Building sector ETF sleeve (SPDR top-k momentum) …u<   🛡 Building defensive MR sleeve (SPY return z → TLT) …)r  r  r  r^  zToo few tickers for ML sleeve (z5). Try --ml-max-tickers 150 or longer --daily-period.u4   ⚖️ Building ML L/S sleeve (XGB walk-forward) …z5No equity universe data loaded; cannot build ensembleuH   📉 Building buy-the-dip sleeve (RSI dip + SMA uptrend, fixed hold) …)
r|  r}  r~  r  r  r  r  r  r  r  z4buy-the-dip sleeve requires a loaded equity universezsector ETFszdefensive MRzbuy-the-dipz + u   ⚖️ Blending macro + L/Su    …)r&  ls_daily_retsector_daily_retdefensive_daily_retls_short_daily_retzZ  L/S sleeve (ML): AQR-style rebalance/trade diagnostics are not produced for this engine.)r#  r  r  r  r  c                    |j                   vrt        d| d      |   j                  t        j                        }t        | |       y )NzMissing z in ensemble output)r  r  r    r!   r"   r   )r   cum_colr   r  s      r&   _print_curve_statsz main.<locals>._print_curve_stats  sI    k111'2EFGGg&--bjj9C/c:r(   z
Macro bookrl  zL/S bookrm  z"Sector ETF sleeve (unweighted leg)ro  z$Defensive MR sleeve (unweighted leg)rn  rp  rq  Ensemblerk  z@
  --- Benchmark (same calendar as ensemble): SPY buy & hold ---zSPY (S&P 500 ETF)r   r   uA     investor_metrics.json hints — benchmark.total_return_pct ≈ r   u?     (percent over window); rows[0] ensemble total_return_pct ≈ u   ; excess vs SPY ≈ z ppexport_investor_metrics_json)r  r  r  r  r  r  r  r  zEnsemble (total strategy)rH  u:   🔍 Scanning Universe for Tradable Cointegrated Pairs …g      @r  z
--universez--timeframer  z--lookback-yearsz	--coint-pz0.05rI  u%   🚀 Starting End-to-End Pipeline …)ro  rp  z6Pipeline: pass both --pair-y and --pair-x, or neither.u,   ✅ Using fixed pairs (skip universe scan): z (Y) vs z (X)u>   🔍 Step 1: Scanning Universe (S&P 100) … (looking for top z pairs)r+  r  Adjusted_P_Value)	ascendingzUniverse scan returned no tradable pairs (even with relaxed coint + tradability thresholds). Re-run with an explicit pair, e.g. `--pair-y QQQ --pair-x SPY`.z   (coint scan: )Ticker_ATicker_BPairspair)descunit_z

=== Pair z ===u   ⚙️ Loading bundle …)r<   r=   rI   r>   r?   r@   gffffff?)
train_fracF)r  r  heatmap_pathplot_heatmaprC   rD   rH   z$Optimizer returned empty grid result	is_sharperF   rG   statarb_window
mom_windowu%   ✅ Optimal params (train): z_window=z
, z_entry=z	, z_exit=z, mom_window=z, hedge_window=z'OOS slice empty or missing filtered_retzOOS Filtered u   ⚠️ Skipping z due to error: z-No pair results available to build portfolio.uD   
🚀 Building portfolio (inverse-vol risk parity + kill switch) …)r  max_dd_thresholdzPortfolio (kill switch)zEqual Weight (no kill switch)equal_weight_bar_retu5   📈 Plotting portfolio vs equal weight benchmark …zUnknown --mode: )r   rb  r  rb  returnNone)argparseArgumentParser__doc__add_argumentr   r   rb  
parse_argsoschdir
_REPO_ROOTmoderk   rI   r  r}   r<   r=   r>   r?   rT   smarC   rD   rE   rF   rG   regime_shift_daysrJ   rK   r   r   r   	save_plotr   r   rd   re   rf   r   ri  rj  rk  rl  rg   r    r!   r"   rm   r'   -RenTech.strategy_stack.portfolio_risk_managerrn  ro  rp  rd  keysr  r   r  r   r   build_portfolior   r  r;  ru  rv  rw  r5  r  ls_max_tickersr  ls_sector_neutralr8  setgenerate_ls_returnsr   r   r   r{  ensemble_dip_rsi_periodensemble_dip_rsi_maxensemble_dip_atr_periodensemble_dip_hold_daysensemble_dip_vol_windowensemble_dip_trend_smaensemble_dip_weightingensemble_dip_rank_byboolensemble_dip_no_uptrend_filterensemble_dip_spy_bull_onlyensemble_dip_signal_modeensemble_dip_pct_drop_mingenerate_returnsensemble_dip_top_n,RenTech.strategy_stack.swing_breakout_hourlyr  r  hourly_swing_long_onlyhourly_swing_short_onlyr2  rE  hourly_swing_universehourly_swing_max_tickershourly_swing_tickershourly_swing_fractal_lefthourly_swing_fractal_righthourly_swing_daily_atrhourly_swing_min_atr_multhourly_swing_hold_barshourly_swing_close_confirmhourly_swing_blendhourly_swing_vol_windowhourly_swing_winner_epsilonr  r  r'  ml_max_tickers	ml_targetml_weightingr  r  r<  ensemble_sector_top_kr  r  defensive_z_windowdefensive_entry_zdefensive_exit_zdefensive_tlt_weightr  r  ensemble_sector_sleeveensemble_sector_weightensemble_defensive_mrensemble_defensive_weightensemble_dip_sleeveensemble_dip_weightr  ra  r  rS  joinbuild_ensemblerl   r   r  r,   r   r   r  r  r  r  r  r"  r   argvmain RenTech.strategy_stack.optimizerstrategy_stack	optimizerpair_ypair_xtqdmportfolio_top_kr1  r  r   sort_valuesr
  headiterrowsload_intraday_bundletime_ordered_train_test_splitrt   run_optimizationmin_hedge_obs_for_windowr   r   portfolio_vol_windowportfolio_max_ddportfolio_save_plot)pr  hedgerx   _hrhtr   daily_inr<  rp   	swing_retrn  ro  rp  baseline_tickersr   r$  rP   r#   pmportfolio_dfstatic_bar_retspy_bar_retru  rv  rw  r&  r$  ry  r#  	ls_enginerB  ls_dailyr.   r   r   r   r   r{  dip_eng	dip_dailyr  r  panelscfgportcountsbars_pyr  r{   r  r  etf_dictsec_eng	sec_dailyr  r  rC  r  tlt_dtlt_dfd_engd_dailyr  r  macro_tickersdata_loader
macro_dictr  
sector_retr  defensive_retw_secw_defr  
w_ls_shortens	ls_series	equity_mlaq_sector_mapsector_map_mlls_short_retspy_meq_dip
blend_bits	blend_msgr  r  r  	spy_panelrV  spy_rsp_pctr[  export_jsonens_cum	ens_dailyr   dpr   
saved_argvopt	pair_y_in	pair_x_inr  
candidatesr!  r   coint_labelrh  	pairs_topr  pair_resultsskippedr  r  	pair_namebundletrain_ytest_yis_resr   z_entry_opt
z_exit_optz_window_optmom_window_opthedge_window_optmin_hedge_obs_opt
intra_full
test_start	intra_ooser  s                                                                                                                                       @r&   r  r    s&   G4ANN
\%  : NN$eN4NN#UN3NN#TN2NN&N7NN<7RNSNN7cN2NN##sN;NN<c2N6NN<c2N6NN;UCN8NN:E3N7NN(sADcNdNNj  
 NNT	   NNM	   NN=sC>]N^NN>Q=VNWNN$5$EgNhNN<c1;YNZNN w	   NN!S  
 NN}	   NNt	   NN!H   NN="3RNSNN([	   NN#N_  
 NNd	   NN"r  
 NN!g	   NN"u	   NN!h  
 NN%q	   NNL	   NNg	   NNo	   NN h	   NNi  
 NNg	   NN#G	   NN Y	   NN"i	   NN#f	   NN #g	   NN"U	   NN*^  
 NN&O  
 NN$#b	   NN%k	   NN^	   NN#o	   NN"$[	   NNe	   NN"e	   NNV	   NN!0S   NN b	   NN$l	   NN%c	   NN&g	   NN"l	   NN%l	   NN"`	   NN"N  
 NN#O  
 NN&^  
 NN5i	   NN#^	   NN'p	   NNb	  	 NN#g	   NN$d	   NNa  
 NNa  
 NNj	   NN _	   NN[	   NN@  
 <<>DHHZyyE01!!'')1T,,**** 00"mmxx**]]]]LL;;"&"8"8**,,
$ 	"3u:a.	5-Vd9ef	
 /5)9#:#N*eN33G$tE7^"$$%bT4==/9I$J\J\I]]gh	Udnn6LM 	yyG+,<++D,=,=dFWFW+X>>!3D4E4E3FGHHnn,,]]	
   *W~,,RZZ8CCEe/7GQVW	 #, )n'3 $D$5$5#6 7(doo5F G,,Q/{4==/K 	
	Udnn6LM 	yyN"pp 1 1 34<=M<NOP-/	! 	A!|//$:K:K/LH~~0@0@!@"%7s#;<<W%,,RZZ8EB((*BuI!MM#MFKKMByM!KKOekk#.>>DByMIaL	 )))t)L(,7J*KL-P QQ]]_ffgjk\*>??KKMTTUXY1>B"K0&|4G'HJqrB	<4>>3ITJ 	yyK	
 	
 	AB+%%++
 UVVL,,U4;L;L,M	??YZZ1)< FJE[E[([-=-=-?)@Aae 	 !!KL*,	00"! 1 
 	-W5OOC ''

337..*S01	%(VaZU155a5=!SQVVX/1EzRWWU^+uU|5i_DTU[\_T`ab&x1Z[	x4>>3ITJ 	yyO#ee[\+%%++

 YZZL,,U4;L;L,M	??UVV1)<!4778$3344778!$"="=>4778 !<!<=$556112#D$G$GHH#D$C$CDD99:t==>
 ,,d--."	 - 
	 S!((437..*S01	%(VaZU155a5=!SQVVX/1EzRWWU^+uU|6yoEUV\]`Uabc&y2\]	yDNN4JdK 	yy++T	
 	

 ++,d6R6R1SfggPQ,  MM334D99:D556
 v;?4S[M Br r 
 (T;;<d==> !<!<=t==>$556t;;<< ; ;<<$()H)H$I	
 2d--.4778 !A!AB
f KK##BJJ/8!7S&q*VW	qDNN,BdC 	yyKNTLM"6t7H7H$J]J]"^Z{b 4S5E4FFop  ))''

 **!"))'' + 
 OOC ''

337..*S01	%(VaZU155a5=!SQVVX/1EzRWWU^+uU|83GWX^_bWcde&x1QR	x4>>3ITJ 	yyM!ttMN():):;#%,,,," - 
	
 S!((437..*S01	%(VaZU155a5=!SQVVX/1EzRWWU^+uU|<YsOK[\bcf[ghi&y2XY	+IAWSWX 	yyN"||ST\uT->->?uT->->?;;%++MNN1%81%8,001$001../#()B)B#C	
 ((4(PNN3&&rzz237..*S01	%(VaZU155a5=!SQVVX/1EzRWWU^+uU|7	#FVW]^aVbcd&w0VW	(DNN<RdS 	yyJ	
 	
 	
 	89 < l.0
 	DA++Ad6G6G+HE{{"%DQC#HII<UCJqM		D !!<!<=j%HcHcBdgjBj'+
MN,T->->?H*,==$445"& > J d889ieDDbDb>cfi>i*.PQ0T445d445T223',T-F-F'G	E "22:e3DjQVFWko2pM6Gd112S9Gd445Sd667aE$BZBZ<[^a<a8FU4334C
EE[ef&*	6:48	/3""d*R';D<M<MtObOb'c$I}9~""5c)n5EEz{  HI" -- NN++C
 //("& -- NN++ 0 I 0!!)) //K "#Z[[%% <SAQAQAS=T UOP)-\]%t;;<d778t;;<"%d&A&A"Bt;;<!$T%@%@!Ad99:D556#'(K(K#LL#'(G(G#HG u%E$GPGXYea!5b99YY($"#YZZ"33$112"&	 4 L 
m,n-m,JJz*	+IYK/@SUVY__`! ,, &!+$1#/ - K l ,,(!+$1#/- - K 1@	; 	<)?@:':;CE\]EGabDF_`:'@A )-NN5)	 UiFWFW=W##Ce$,,S188DKKCPE;//1C7KUV/0C[Q;++B/058FK(ABII"**UZZ[]^_bggGSTZ[^S_ `PPWX[} ]%%,v%5c$:#? '$(FKQrRXXZ2'"3--'!$-
 78??

K7]..077<CCBJJO	&y2MNk"yyFJK% ""#))+113;;sb"g
 XXa[
	"1"E

CH GGI!CHyyJ56%66bb$**,	$**,		?T)_,UVV 2;Y1G0HJ@8T]S^^bcdRSWSgSgRhhopqT&&'--/557BE{{3 !"Sb'NE **G*<G22$ 3 I
 "Gr9!UE;%%&8D%IE{{"3 
 (Q78As4//01A

1IOXOaOaOcdtq!3q}-s1Z=/ABdJd 13"ii
viN M	NFF!(!F8,IKI;d341211"&"4"4!'!'!%!2!2$($8$8&*mm 2  #&"C"CNN" #D #  00"!%!&!%!2!2!]]*.*@*@ 1 		 <<&'MNN))+)GLLQO#DO4"4>2
"4(8#9:!$T,%7!8#/ $'$@$@$N!  ,~ZAiPZ[\~ ^""0!1AQ@RT *"&"4"4!'!%!2!2$($8$8&*mm-!%!2!2!]])'%*.*@*@!'!1"3
$ $\\--/
&NN:+;+;z+IJOOQ	??nI<M<M&M&'PQQM)5y7P`kl*3^4D*E*J*J*LY'QM	^ NOOUV))4445"4#8#89 * 
 +\:M-N^ab1<@V3WgjkEF..6$	
 	
)$))5
66i  		F  		N  		j  		d  		j  		L  		0  		>  		d Z`  & "CH6 !  E , e`  y)(?1#FGsE  3B[$ "B[3 B\ *B\ C7B\  L?B\/ TB\> Y9B] bB] uB]+C8B]1 D8B^ J	B^ M(B^*N4I(B^0[$	B[0[/B[0[3	B[?[>B[?\	B\\B\\	B\\B\\ 	B\,\+B\,\/	B\;\:B\;\>	B]
]	B]
]	B]]B]]	B](]'B](]1B^^B^^B^^B^'^&B^'^0
B_*^:%B_%_%B_*__main__)r]   )r#   pd.DataFramer$   rb  r%   rb  r  	pd.Series)r-   r_  r  r_  )"r<   rb  r=   rb  r>   rb  r?   rb  r@   rb  rA   r   rB   r   rC   r   rD   r   rE   r   rF   r   rG   r   rH   r   rI   
str | NonerJ   r   rK   r   r  r^  )rx   r^  r   rb  r   r`  r  r  )r   r   r   dict[str, pd.DataFrame]r  ztuple[pd.DataFrame, str | None])r   rb  r.   r_  r   r   r  r  )r   rb  r   r_  r   r   r  r  )r   r_  r   r   r  zdict[str, float | None])r   rb  r   r_  r   r  r  dict[str, Any])r   r_  r  rb  )r   r_  r  r   )
rp   r_  r   r_  r$  r_  r%  r  r  rb  )rB  rb  r  r`  )
rV  zpd.IndexrW  z
np.ndarrayrX  znp.ndarray | NonerI  r   r  rb  )rg  r   r  r   )r  rb  r  r^  r  r  r  r  r  r  r  zpd.Series | Noner  zargparse.Namespacer  r   r  rb  r  r  )r  r_  r   rb  r  r  )rN   rb  r  r   )r#   r^  r  r^  )r>   rb  r  r   r  z.tuple[dict[str, pd.DataFrame], dict[str, str]])
r>   rb  r,  rb  r  r   r(  r  r  ra  )r!  zset[str]r  zdict[str, str])r>   rb  r  ra  )r?   rb  rT   rb  r,  rb  r  r   rC  rb  r  ra  )r  r  )@r  
__future__r   r  r  r  r  r   pathlibr   typingr   numpyr!   pandasr   r  abspathr  dirname__file__r  insert"RenTech.strategy_stack.data_loaderr   &RenTech.strategy_stack.momentum_filterr   %RenTech.strategy_stack.statarb_enginer   r	   ,RenTech.strategy_stack.strategy_orchestratorr
   #RenTech.strategy_stack.swing_enginer   r   __annotations__r'   r/   r}   r   r   r   r   r   r   r  r  r:  rH  r^  rc  r  r  r  r  r  r  r'  r5  r8  r<  rE  r  __name__r  r(   r&   <module>rs     s  2 #    	 
     WW__RWW\\"''//(*CT4PQ
SXXHHOOAz" 9 A R M ;3  &^
% !!!"##CC C 	C
 C C C C C C C C C C C  !C" #C$ %CL MQ 42 2 &2  %2 j BG d$ ^c  F OT &R B"CC	C C
 C CL-( 	 
  @(d9d9 d9 	d9
 d9 d9 "d9 d9 d9 #d9 
d9NX@"? #J ** * 	*
 * *Z8777 	7
 7 7 7tV7r$ zF r(   