
    pjBK                    8   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mZ ddl	Z
ddlZddlZ ee      j!                         j"                  d   Z ee      ej(                  vr"ej(                  j+                  d ee             edz  dz  dz  Zed	z  Zd
dddddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZdddd	 	 	 	 	 	 	 	 	 ddZdd
ddddddddd
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZdddd	 	 	 	 	 	 	 	 	 	 	 d dZd!dZedk(  r e        yy)"u  
**VIX-Regime Dynamic Fund Scale** — rules-based leverage adjustment.

Replaces the fixed ``--fund-scale`` on the stock-only book with a daily
scale that reads the *prior-day* smoothed VIX level (no lookahead):

  VIX < ``--vix-low``   (default 15)  →  scale = ``--scale-high`` (default 2.0×)
  VIX ≥ ``--vix-high``  (default 25)  →  scale = ``--scale-low``  (default 1.0×)
  Otherwise                            →  scale = ``--scale-mid``  (default 1.5×)

VIX is smoothed with a ``--smooth`` -day EMA (default 5) to reduce
day-to-day noise and avoid excessive switching at the thresholds.

Why VIX instead of CNN-LSTM for fund scaling:
  - VIX is a genuine real-time market signal (options-market consensus on SPY vol)
  - In calm regimes (VIX < 15): 2017, H2 2019, 2021 → take more leverage
  - In stressed regimes (VIX > 25): 2022, 2020 COVID, 2018 Q4 → reduce leverage
  - No training data required, no lookahead risk, fully interpretable

Impact on the stock-only book:
  2019: VIX < 15 for ~60% of the year → avg scale ≈ 1.78× → return lifted ~+3pp
  2022: VIX > 25 for ~55% of the year → avg scale ≈ 1.18× → loss reduced ~−0.5pp
  2017: VIX < 15 almost all year → avg scale ≈ 1.9× → big boost
  2018: VIX spikes in Q4 → scale drops to 1× in the crash, reducing Q4 losses

Usage::

    # 1. Generate scale CSV
    cd /Users/robzingale/trading_bot && PYTHONUNBUFFERED=1 \
      .venv/bin/python RenTech/strategy_stack/run_vix_dynamic_scale.py \
      --start 2016-01-04 --end 2025-12-31 \
      --out-prefix RenTech/data/logs/vix_dynamic_scale

    # 2. Apply to existing stock-only combine CSV
    .venv/bin/python RenTech/strategy_stack/run_vix_dynamic_scale.py \
      --apply-to-daily \
        RenTech/data/logs/stock_only_baseline_plus_stock_only_plus_sp500_dip_plus_tactical_aw_plus_tsmom_plus_johansen_etf_plus_vol_edge_plus_fund_plus_nav_q_mtm_daily.csv \
      --out-prefix RenTech/data/logs/vix_dynamic_scale

Outputs:
  ``*_scale.csv``     — date, vix_raw, vix_smooth, fund_scale, regime
  ``*_applied_daily.csv`` — date, ret_base, ret_dynamic, eq_base, eq_dynamic, fund_scale
  ``*_metrics.json``  — headline stats + yearly table
    )annotationsN)Path   RenTechdatalogsvix_dynamic_scale      .@      9@       @      ?      ?   T   vix_lowvix_high
scale_high	scale_mid	scale_lowsmoothspy_trend_gatespy_smac                  t        j                  |       }
t        j                  |      }|
t        j                  |	dz         z
  j                  d      }|j                  d      }|rddgndg}t	        j
                  |||dd      }t        |j                  t         j                        r|d	   }|d   }|r|d   nd
}n|j                  d
d
df   }d
}t        j                  |j                        j                  d
      |_        |j                         j                         }|r}|{t        j                  |j                        j                  d
      |_        |j                         j                         }|j                  |	|	dz        j!                         }||kD  }n!t        j"                  d|j                        }|j%                  |d      j!                         }|j'                  d      }|j'                  d      }|j'                  d      j)                  d      }|j                  |
k\  |j                  |k  z  }||   }||   }dfd}dfd}t        j*                  ||   |d      }t-        ||      D  cg c]  \  }}  |||        c} }|d<   t-        ||      D  cg c]  \  }}  |||        c} }|d<   |j/                         S c c} }w c c} }w )u  
    Download VIX (+ SPY if ``spy_trend_gate=True``) and return a daily
    DataFrame with columns: vix_raw, vix_smooth, fund_scale, regime.

    When ``spy_trend_gate=True`` (default / recommended):
      - Stressed scale (``scale_low``) triggers only when
        VIX ≥ ``vix_high`` **AND** SPY < SMA(``spy_sma``).
      - This prevents deleveraging during high-VIX *recovery* periods
        (e.g. 2020 COVID bounce) where reducing leverage is costly.

    All signals are lagged one day (prior-day values → today's scale).
    <   )daysz%Y-%m-%dz^VIXSPYTF)startendauto_adjustprogressCloseNr   r   )min_periodsindex)spanadjust   c                (    | k  rS | k\  r|sS S N )vix_vbullr   r   r   r   r   s     H/opt/rentech/trading_bot/RenTech/strategy_stack/run_vix_dynamic_scale.py_scalezbuild_vix_scale.<locals>._scale   s&    7?HT    c                "    | k  ry| k\  r|syy)Ncalmstressednormalr+   )r,   r-   r   r   s     r.   _regimez build_vix_scale.<locals>._regime   s    7?HTr0   )vix_raw
vix_smooth
fund_scaleregime)r,   floatr-   boolreturnr:   )r,   r:   r-   r;   r<   str)pd	Timestamp
DateOffsetstrftimeyfdownload
isinstancecolumns
MultiIndexilocto_datetimer%   tz_localize
sort_indexffillrollingmeanSeriesewmshiftfillna	DataFramezipdropna)!r   r   r   r   r   r   r   r   r   r   t0t1fetch_start	fetch_endtickersrawclosevixspysma_spy	spy_abover7   vix_prevvix_smooth_prevspy_above_prevmaskvix_sspy_abr/   r5   dfvbs!     `````                          r.   build_vix_scaleri   D   s|   2 
e	B	c	B7R<88BB:NK++j)I!/vuofXG
++g[i"&8C#++r}}-GFm,eEl$hhq!tnsyy)55d;CI
..

 
 
"C#/NN399-99$?	nn$$&++g7a<+@EEG7]	IId#))4	fU388:J YYq\H &&q)Oooa(//5NIIO		R0DT"ED!F  
tn 
B 25UF1CDAq!DB|25eV2DE$!Q1EBxL99; EEs   K'=K-r   r   r   c                   d\  }}}t        ||n|       }|dk  r|}t        ||n|||z  z        }t        ||n|||z  z        }	|||	fS )u  
    Return (high, mid, low) VIX scale tiers.

    When *scale_mid* is omitted, it defaults to *base_scale* (the book's fixed
    leverage). High/low tiers are proportional to the stock-only reference
    2.0× / 1.5× / 1.0× ladder unless overridden explicitly.
    )r   r   r   r   )r:   )

base_scaler   r   r   ref_highref_midref_lowmidhighlows
             r.   vix_scale_tiers_for_basers      so     "/Hgw
Y2	

CC
axz53(WBT;UVD
Y2	w?P8Q
RCc>r0   )
rl   r   r   r   r   r   r   r   r   pnl_colsc               8     j                          d j                  vr>d j                  vrt        d       d   j                         j	                  d       d<   t        ||||	      \  }}}t        ||||||||
||
      }|d   j                   j                        j                         j	                  |      }|d   j                   j                        j                         j	                  d	      }|d
kD  r|t        |      z  n|}| d<   | d<    d   j                  t               d<    d   |z   d<   t               }|r|j                   fd|D                j                  D ]7  }|j                  d      s|j                  d      r'|j!                  |       9 t#        |      D ]   } |   j                  t              |z   |<   " t        |      }|d d   z   j%                         z   d<   d j                  v r d    d<   d j                  v r7|r5 |D cg c]  }| j                  v s| c}   j'                  d       d<   |j)                  d      j+                         }dt        |      |||d||dt-        |
      t/        |      t-        |      t1        t        |j3                               d      |j5                         D ci c]  \  }}|t1        t        |      dz  d        c}}d	} |fS c c}w c c}}w )a	  
    Apply VIX-regime daily leverage to an in-memory combine *panel*.

    Scales ``daily_return_mtm`` by ``vix_scale / base_scale``, recomputes
    ``equity_mtm_usd``, and scales sleeve ``pnl_*`` columns by the same ratio
    so yearly tables stay consistent.
    daily_return_mtmequity_mtm_usdz.panel needs daily_return_mtm or equity_mtm_usdg        rj   r   r8   r9   r4   r   vix_fund_scale
vix_regimedaily_return_mtm_basec              3  @   K   | ]  }|j                   v s|  y wr*   )rE   ).0cpanels     r.   	<genexpr>z3apply_vix_dynamic_scale_to_panel.<locals>.<genexpr>   s     G1A4FQGs   pnl__baser   nav_usdpnl_best_ideas_mtmr(   )axisT	normalizerq   rp   rr   rr   rq      g      Y@)	r	   vix_base_scale
vix_scalesvix_thresholdsvix_smooth_emavix_spy_trend_gatevix_spy_smavix_mean_scalevix_regime_pct)copyrE   
ValueError
pct_changerQ   rs   ri   reindexr%   rK   r:   astypesetupdate
startswithendswithaddsortedcumprodsumvalue_countsto_dictintr;   roundrM   items)r~   r   r   capitalrl   r   r   r   r   r   r   r   r   rt   shsmslscale_df	vix_scaler9   ratiocols_to_scalecolref_capr}   regime_distkrg   metas   `                            r.    apply_vix_dynamic_scale_to_panelr      s)   0 JJLE.5==0MNN$)*:$;$F$F$H$O$OPS$T !)zY)JBB %H &..u{{;AACJJ2NIh''4::<CCHMF-7!^Ij))E'E
 E,%*+=%>%E%Ee%LE
!" %&= > FE
!eMGGG}} #>>&!#,,w*?c"# m$ 63Z&&u-5c
6 GnG%u5G/H)H(Q(Q(SSE
EMM! !12iu}},&+,W1ADVQ,W&X&\&\ '] '
"# %%%5==?K!
+!"R8")8<f+">27|inn&6 7;EPEVEVEXYTQ1eE!Hu$4a88Y
D $;! -X Zs   -LL$#L     j@rl   r   verbosec               
   t        j                  | dg      j                  d      }t        j                  |j                        j                  d      |_        d}|j                  D ]+  }d|j                         v s|j                         dk(  s)|} n |'|j                  D ]  }d|j                         v s|} n |t        d|        ||   j                  t              }|d   j                  |j                        j                         j                  |      }	||	|z  z  }
d|z   j                         z  }d|
z   j                         z  }t        j                  ||
|||	|d	   j                  |j                        j                         j                  d
      d      }dLfd} |||      } |||
      }g }}}|j!                  |j                  j"                        D ]y  \  }}t        d|z   j%                         dz
        dz  }|
j&                  |
j                  j"                  |k(     }t        d|z   j%                         dz
        dz  }|d	   j&                  |j                  j"                  |k(     }t)        |dk(  j+                         dz  d      }t)        |dk(  j+                         dz  d      }t)        t        |	j&                  |	j                  j"                  |k(     j+                               d      }|j-                  t/        |      t)        |d      t)        |d      t)        ||z
  d      |||d       |d|dz  z   z  }|d|dz  z   z  }| t        j                  |      }|rt1        dd        t1        d| j2                          t1        d        t1        d|dd|d   dd|d   dd|d   dd|d    d!
       t1        d"|d   dd|d   dd|d   dd|d    d!       t        |	j+                               }|d	   j5                  d#$      dz  }t1        d%|dd&       t1        d'|j7                  dd      d(d)|j7                  d
d      d(d*|j7                  dd      d(d+       t1        dd,d-d.d/d0d.d1d2d.d3d-d.d4d-d.d5d-d.d6d0       |D ]g  } d7}!| d8   d9k\  r| d:   d9k  rd;}!n| d8   dk  r
| d:   dk\  rd<}!t1        d.| d,    d.| d:   d=d>| d8   d?d>| d@   dAdB| dC   dDdE| dF   dGd>| dH   dId+|!        i ||t)        t        |	j+                               dJ      |dK}"||"fS )Mu   
    Rescale daily returns from an existing combine CSV using VIX-regime scales.

    Daily return at dynamic scale = base_daily_return × (dynamic_scale / base_scale).
    date)parse_datesNrv   r<   z No daily return column found in r8   r(   r9   r4   )ret_baseret_dynamiceq_base
eq_dynamicr8   r9   c           	        t        |      }|dz  }t        | j                  d         z  d|z  z  dz
  }t        | | j                         z  dz
  j	                               dz  }t        |j                         |j                  d      z  t        j                  d      z        }t        |dz  d      t        |d      t        |d      t        t        | j                  d         d      d	S )
N   r(   d   )ddofr   r   r   )cagr_pctsharpe
max_dd_pct
end_equity)
lenr:   rG   cummaxminrM   stdmathsqrtr   )eqretnyrscagrddr   r   s          r.   _statsz#apply_dynamic_scale.<locals>._stats7  s    H#gbggbk"W,!c':Q>b299;&*//12S8SXXZ#''q'/1DIIcNBCdSj!,b!A,bggbk 2A6	
 	
r0   r   r2   r   r3   r   )yearreturn_base_pctreturn_dynamic_pctdelta_pp	avg_scalepct_calmpct_stressed
zF======================================================================u   VIX-Regime Dynamic Scale — z  Base (fixed z.1fu
   ×): CAGR r   z
%  Sharpe r   .2fz  MaxDD r   z%  End $r   z,.0fz  Dynamic VIX:     CAGR Tr   z
  Mean dynamic scale:    ×z  Regime days: calm=.0f
%  normal=%  stressed=%r   z>6z  basez>7dynamicz>8deltaavg_sczcalm%zstress% r   
   r   u    ✓ FLOOR FIXEDu    ✗z+6.1fz%  z+7.1fr   z+5.1fzpp  r   z5.2fu   ×  r   z5.0fr   z6.0fr   )r   r   
mean_scaleyearly)r   	pd.Seriesr   r   r<   dict)r>   read_csv	set_indexrH   r%   rI   rE   lowerr   r   r:   r   rK   rQ   r   rR   groupbyr   prodlocr   rM   appendr   printnamer   get)#combine_daily_csvr   rl   r   r   dailyret_colr   base_ret	scale_serdynamic_retr   r   resultr   
stats_basestats_dynamicyr_rowseq_beq_dyrgret_bdyn_gret_d	regime_yrcalm_pct
stress_pctr   yr_dfr   r   rowflagr   s#      `                               r.   apply_dynamic_scaler  	  s$    KK)x@JJ6RE..-99$?EK G}} ,		?Q0QG == 	C399;&	 ;<M;NOPPW~$$U+H&..u{{;AACJJ:VIi*45KAL1133GAO4466J\\"! )11%++>DDFMMhW F
 7H-J:{3M G'$D!!(.."5"56  Aq1ullnq()C/ 1 1 6 6" <=q5y&&(1,-38$(():):b)@A	9.446<a@I399;cA1E
%	ioo.B.Bb.H I N N PQSTU	G$UA"'q/eemQ/" &
 	 	ECKECK% & LL!E6(m-.?.D.D-EFGz#. / ,S1 2"8,S1 2!,/4 5 .t4	6 	7
 	 #J/4 5%h/4 5$\237 8#L1$7	9 	: 9>>+,
X&33d3CcI(C(8;<$[__VA%>s$C D#3C8 9%//*Q7<A? 	@ 	6"+Rr{"YrN"Bxm2gb\Ib>K 	L 	8CD'(B.37H3IB3N))*Q.37H3IQ3NBs6{m2*+E2#-.u5S_U+4%d+4_T*#(.av7 8	8  E).."23Q7	D 4<r0   c                 >	   t        j                  t        j                  d      d         } | j	                  dd       | j	                  dd       | j	                  d	t
        d
d       | j	                  dt
        dd       | j	                  dt
        dd       | j	                  dt
        dd       | j	                  dt
        d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	                  d't
        d()       | j	                  d*t        t        )       | j                         }|j                   }t        d+|rd,nd- d.d/0       t        |j                  |j                  |j                  |j                   |j"                  |j$                  |j&                  |j(                  ||j*                  1
      }t        |j,                        }|j.                  j1                  d/d/2       t        | d3      }|j3                  d45      j5                  |d67       t        d8| d/0       |d9   j7                  d/:      d;z  }t        |d<   j9                               }t        d=|j;                  d>d      d?d@|j;                  dAd      d?dB|j;                  dCd      d?dD|dEdF	d/0       |j                  |j                   dG|j"                  |j$                  |j&                  dH|j(                  |j=                         D 	ci c]  \  }}	|t?        |	dI       c}	}t?        |dJ      dK}
|j@                  r|j@                  jC                         rtE        |j@                  ||jF                  |jH                  d/L      \  }}t        | dM      }|j3                  d45      j5                  |d67       t        dN| d/0       |
jK                  |       t        | dO      }tM        |dP      5 }tO        jP                  |
|dQtR        R       d d d        t        dS| d/0       y c c}	}w # 1 sw Y    xY w)TNz

r   )descriptionz--startz
2016-01-04)defaultz--endz
2025-12-31z	--vix-lowr
   u*   VIX below this → high scale (default 15))typer  helpz
--vix-highr   u)   VIX above this → low scale (default 25)z--scale-highr   z&Scale when VIX < vix-low (default 2.0)z--scale-midr   u3   Scale when vix-low ≤ VIX < vix-high (default 1.5)z--scale-lowr   u)   Scale when VIX ≥ vix-high (default 1.0)z--smoothr   z&EMA smoothing days for VIX (default 5)z--no-spy-gate
store_truez3Disable SPY>SMA200 trend gate (pure VIX thresholds))actionr  z	--spy-smar   z+SPY SMA window for trend gate (default 200)z--apply-to-dailyz,Existing combine daily CSV to apply scale toz--base-scalez2Fixed scale used for the combine CSV (default 1.5)z	--capitalr   )r  r  z--out-prefixzDownloading VIX z+ SPY trend gatez(no trend gate)u    …T)flushr   )parentsexist_okz
_scale.csvr   )namesFr$   u   Scale CSV → r9   r   r   r8   u(   Regime distribution (2016–2025): calm=r2   r   r   r4   r   r3   u   %  → mean scale r   r   r   r   r(   r   )r   scales
smooth_ema
regime_pctr   r   z_applied_daily.csvu   
Applied CSV → z_metrics.jsonwr   )indentr  u   Metrics → )*argparseArgumentParser__doc__splitadd_argumentr:   r   r   DEFAULT_OUT_PREFIX
parse_argsno_spy_gater   ri   r   r   r   r   r   r   r   r   r   
out_prefixparentmkdirreset_indexto_csvr   rM   r   r   r   apply_to_dailyis_filer  rl   r   r   openjsondumpr=   )apargsspy_gater   r$  
scale_pathr   r   r   rg   r   	result_df
apply_metaapplied_pathmetrics_pathfhs                   r.   mainr7    sX   		 	 W]]6-B1-E	FBOOI|O4OOG|O4OOKE  GOOLD  FOONA  COOMN  POOMD  FOOJA  COOOLN  POOKF  HOO&T4G  IOONM  OOOK	OBOON7IOJ==?D###H	8/ARSSW
X`de

DHH??....{{
H doo&JD48J/0Jv&--j-F	N:,
't48$11D1ACGK.3356J	 //&+C0 1OOHQ/4 5!ooj3C8 9&s+2	/ 7;	< #',,F??4>>$..Ykk2=2C2C2EF$!Qq%1+~FJ*D t22::< 3LL!
	: zl*<=>F+22<u2M"<.1>J:,m45L	lC	  3B		$1c23	L
't4) G$3 3s   +RRR__main__)r   r=   r   r=   r   r:   r   r:   r   r:   r   r:   r   r:   r   r   r   r;   r   r   r<   pd.DataFrame)
rl   r:   r   float | Noner   r:  r   r:  r<   ztuple[float, float, float])r~   r9  r   r=   r   r=   r   r:   rl   r:   r   r:   r   r:   r   r:  r   r:  r   r:  r   r   r   r;   r   r   rt   zlist[str] | Noner<   tuple[pd.DataFrame, dict])r   r   r   r9  rl   r:   r   r:   r   r;   r<   r;  )r<   None)r  
__future__r   r  r,  r   syspathlibr   numpynppandasr>   yfinancerB   __file__resolver  _REPOr=   pathinsertLOGSr!  ri   rs   r   r  r7  __name__r+   r0   r.   <module>rK     s  +X #    
    X ((+u:SXXHHOOAs5z"y6!F*//  RR	R 	R
 R R R R R R R Rp  $""  	
   : #""!%TT T 
	T
 T T T T T T T T T T T  !Tz BBB 	B
 B B BNQ5h zF r0   