
    pj)                        d Z ddlmZ ddlZddlmZ ddlmZ ddlm	Z	 ddl
mZmZ ddlZddlZddlmZ dd	lmZmZ dd
lmZmZ dZddZddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 ddZ G d d      Zy)u  
Load SPY option chains from ThetaData 15:45 ET monthly Parquet chunks (``build_theta_dataset``).

Schema: ``quote_datetime``, ``expiration``, ``strike``, ``right``, ``bid``, ``ask``,
``implied_vol``, ``delta``.

If ``implied_vol`` / ``delta`` are missing (e.g. ``--skip-greeks`` pulls), this loader
uses **Black–Scholes** with **SPY close** from the provided ``spy_df`` to infer IV from
mid and populate delta/gamma/theta/vega so :class:`~RenTech.core.options_data_loader.OptionChain`
targeting (e.g. VRP backtester) works.
    )annotationsN)date)Mapping)Path)AnyIterator)brentq)_bs_greeks_rowsynthetic_black_scholes)OptionChainOptionContractg{Gz?c                r   t        |       j                         }|j                         st        d|       t	        |j                  d            }|st        d|       d}d}|D ]  }t        j                  |dg      }|j                  r(t        |d         }|j                         }|j                  rS|j                         |j                         }	}||nt        ||      }||	nt        ||	      } ||t        d|       t        j                  |      t        j                  |      fS )zWMin/max **session date** (America/New_York calendar day) across ``spy_1545_*.parquet``.Not a directory: spy_1545_*.parquetzNo spy_1545_*.parquet under Nquote_datetimecolumnsz'No quote_datetime rows in chunks under )r   
expanduseris_dirNotADirectoryErrorsortedglobFileNotFoundErrorpdread_parquetempty_session_dates_seriesdropnaminmax
ValueError	Timestamp)
	theta_dirdpathslohiptsqabs
             </opt/rentech/trading_bot/RenTech/core/theta_chunks_loader.pytheta_chunks_date_boundsr.      s(   Y""$A88: #4QC!899166./0E">qc BCC"B"B 
-__Q)9(:;88!"%5"67HHJ77uuw1*Q#b!**Q#b!*
- 
zRZB1#FGG<<R\\"---    c                "   t        j                  | d      }|j                  j                  |j                  j	                  ddd      }n|j                  j                  d      }t        j                  |j                  j                        S )z_
    NY session **calendar date** as timezone-naive midnight (matches yfinance SPY index).
    F)utczAmerica/New_YorkNaTshift_forward)	ambiguousnonexistent)r   to_datetimedttztz_localize
tz_convertr   )r   qds     r-   r   r   9   sr     
E	2B	uuxxUU'  
 UU01>>"%%**%%r/   c                     t        j                         r dkD  rdkD  r
dkD  rdkD  syd fd}	 t        t        |ddd            S # t        $ r Y yw xY w)	z0Brentq IV in (1%, 300%) so BS price matches mid.r   g      ?c                    t        t        t        t        j                  | dd            rdnd            }|z
  S )Ng-C6?g      @CP)floatr   npclip)sigr(   KSTis_callmidrs     r-   errz_solve_iv_from_mid.<locals>.errU   sD    #1aE"''#tS"9:7CPS

 3wr/   g{Gz?g      @P   )maxiter)rC   r@   returnr@   )mathisfiniter@   r	   r!   )rH   rE   rD   rF   rI   rG   rJ   s   `````` r-   _solve_iv_from_midrP   I   sc     MM#37q1uQ1q5 VCsB788 s   A 	A! A!c                   t        j                  | j                  d            rt        | d         n
t        d      }t        j                  | j                  d            rt        | d         n
t        d      }t	        j
                  |      rt	        j
                  |      r
|dkD  r|dkD  sy d||z   z  }t        | j                  d| j                  dd                  j                         j                         }|j                  d	      rd	}n|j                  d
      rd
}n	|r|d d nd}|dvry |d	k(  }	t        j                  | d         j                         }
t        j                  |      j                         }t        |
|z
  j                        }t        t        |      dz  d      }t        | d         }t        |      }t	        j
                  |      r|dkD  rt	        j
                  |      r|dkD  sy | j                  d      }| j                  d      }t        j                  |      xr. t	        j
                  t        |            xr t        |      dkD  }t        j                  |      xr t	        j
                  t        |            }|rt        |      }nt        ||||||	      }t!        j"                  |gt         j$                        }t!        j"                  |gt         j$                        }t!        j"                  |gt         j$                        }t!        j"                  |gt         j$                        }t!        j"                  |	gt&              }t)        ||||||      \  }}}}|rt        |      nt        |d         }t        |d         }t        |d         } t        |d         }!t+        ||
||||t        |      t        |      ||| |!      S )Nbidnanaskr         ?rightoption_type r>   r?      >   r>   r?   
expirationg     v@gg^1?strikeimplied_voldelta)dtype)r   rZ   r[   rW   rR   rT   rH   ivr]   gammathetavega)r   notnagetr@   rN   rO   strstripupper
startswithr"   	normalizeintdaysr    rP   rA   arrayfloat64boolr
   r   )"rI   as_of	spy_closer_raterR   rT   rH   optotrG   expas_ndterF   rD   rE   iv_rawd_rawhas_ivhas_dr_   S_arrK_arrT_arrsig_arris_call_arrdelta_bgamma_btheta_bvega_br]   r`   ra   rb   s"                                     r-   _row_to_contractr   c   s     XXaeeEl3%%/uCXXaeeEl3%%/uCMM#4==#5#'cAg
s
C
aeeGQUU="56
7
=
=
?
E
E
GC
~~c		S!W	CiG
,,q
'
1
1
3C<<((*D
sTz
 CE#J-AakAiAMM!Q4==+;AUU=!FEE'NEXXfT$--f">T5=STCTFHHUO;eEl ;E6]Q1fg>HHaS

+EHHaS

+EHHaS

+Ehht2::.G((G9D1K(6ueUFT[]h(i%GWgv!E%LuWQZ'8E'!*E'!*ED#J9 r/   c                  f    e Zd ZdZed	 	 	 	 	 	 	 d	dZd
dZddZddddd	 	 	 	 	 	 	 	 	 	 	 ddZy)ThetaChunksLoaderu  
    Point-in-time chains from ``RenTech/data/theta_chunks/spy_1545_YYYY_MM.parquet``.

    Parameters
    ----------
    theta_dir
        Directory containing monthly Parquet files.
    spy_df
        Panel with ``close`` (and same index convention as backtester) — used to infer
        IV/greeks when Theta rows omit them.
    r
        Risk-free rate for BS (default 4% continuous).
    )rI   c                   t        |      j                         | _        | j                  j                         st	        d| j                         || _        t        |      | _        d | _        d | _	        d | _
        y )Nr   )r   r   r#   r   r   spy_dfr@   rI   _dates_cache_spy_id_close_by_date)selfr#   r   rI   s       r-   __init__zThetaChunksLoader.__init__   si     i335~~$$&$'88H%IJJq7;#'8<r/   c           	        | j                   }| j                  t        |      k7  rft        |      | _        |j                         D ci c]5  \  }}t	        j
                  |      j                         t        |d         7 c}}| _        | j                  J t	        j
                  |      j                         j                         }|| j                  vrt        d| d      | j                  |   S c c}}w )zNResolve SPY close by **calendar date** (robust to tz-naive vs tz-aware index).closezNo SPY row for z
 in spy_df)r   r   iditerrowsr   r"   r   r@   r   ri   KeyError)r   ro   sdfixrowkeys         r-   
_spy_closezThetaChunksLoader._spy_close   s    kk<<2c7"c7DL  #||~#B R %%'s7|)<<#D ""...ll5!++-224d)))_SE<==""3''#s   :C6c              #    K   | j                   t               }t        | j                  j	                  d            D ]|  }t        j                  |dg      }|j                  r(t        |d         }|j                         j                         D ]&  }|j                  t        j                  |             ( ~ t        |      | _         | j                   E d {    y 7 w)Nr   r   r   )r   setr   r#   r   r   r   r   r   r   uniqueaddr"   )r   datesr(   dfr*   us         r-   iter_chain_datesz"ThetaChunksLoader.iter_chain_dates   s     $'*uEDNN//0DEF /__Q1A0BC88)"-=*>?**, /AIIbll1o.// !'uD$$$$s   CC#C!C#N)strike_pct_lostrike_pct_himin_dtemax_dtec                  t        j                  t        j                  |      j                               }|j                  |j                  }}| j
                  d|dd|ddz  }	|	j                         st        |g       S t        j                  |	      }
|
j                  rt        |g       S t        |
d         }t        j                  |d	      j                  j                  |j                         k(  }|
j                  |   }
|
j                  rt        |g       S d
|
j                  v r|
d
   n|
j                  dd      }|j!                  t"              j"                  j%                         j"                  j'                         j"                  d   }|
j                  |j)                  ddg         }
|
j                  rt        |g       S 	 | j+                  |      }t        j.                  |
d   d	      }t1        |j3                  d      dk        r|dz  }|
j5                         }
||
d<   |dn
t7        |      }|dn
t7        |      }|t7        |      z  }|t7        |      z  }|
j                  |
d   |k\  |
d   |k  z     }
||t        j                  |
d   d	      j                  j9                         }||z
  j                  j:                  }|t=        |      nd}|t=        |      nd}|
j                  ||k\  ||k  z     }
t?        |
j                        }g }|
jA                  dd      D ]V  }tC        tE        |            D ci c]  }||   ||    }}tG        |||| jH                        }|F|jK                  |       X t        ||      S # t,        $ r t        |g       cY S w xY wc c}w )u  
        Parameters
        ----------
        strike_pct_lo, strike_pct_hi
            If set, keep strikes in ``[strike_pct_lo * S, strike_pct_hi * S]`` (else legacy 0.50–1.10).
        min_dte, max_dte
            If set, drop rows whose calendar DTE vs ``ts`` is outside ``[min_dte, max_dte]``
            (inclusive). Evaluated after strike scaling, before per-row BS work.
        	spy_1545_04d_02dz.parquet)ro   	contractsr   coerce)errorsrV   rW   rX   r   r>   r?   r[   T)skipna   g      $@NrU   g?rZ   i 6ei ʚ;F)indexname)&r   r"   r   yearmonthr#   is_filer   r   r   r   r6   r7   locr   rd   astypere   rg   rf   isinr   r   
to_numericrn   r    copyr@   ri   rk   rj   list
itertuplesrangelenr   rI   append)r   target_dater   r   r   r   r)   ympathr   r;   maskrcolruprp   r[   pct_lopct_hilo_khi_krt   rv   lo_dhi_dcolsr   tupir   cs                                  r-   get_chain_for_datez$ThetaChunksLoader.get_chain_for_date   s_   $ \\",,{388:;ww1~~)Ac7!Ac7( CC||~R266__T"88R266"2&6#78~~b255::bggiGVVD\88R266 &3r'{r9Rkk#""((*..446::1=VVCHHc3Z()88R266	7+I r(|H=

$
'#-.d]FWWY8&.E-4H&.E-4Hi((i((VVR\T)bld.BCD'"5..L!1(CFFPPRC8--$$C#*#63w<FD#*#63w<ED56BBJJ*,	==u4=8 	$C,1#d),<=q47CF?=C= b)TVV<A}  #		$ y99A  	7R266	76 >s   N% O%N>=N>)r#   
str | Pathr   zpd.DataFramerI   r@   rM   None)ro   pd.TimestamprM   r@   )rM   zIterator[pd.Timestamp])r   r   r   float | Noner   r   r   
int | Noner   r   rM   r   )	__name__
__module____qualname____doc__
_DEFAULT_Rr   r   r   r    r/   r-   r   r      s    & == =
 = 
= (%" '+&*""L:!L: $	L:
 $L: L: L: 
L:r/   r   )r#   r   rM   z!tuple[pd.Timestamp, pd.Timestamp])r   	pd.SeriesrM   r   )rH   r@   rE   r@   rD   r@   rF   r@   rI   r@   rG   rn   rM   r@   )
rI   zMapping[str, Any] | pd.Seriesro   r   rp   r@   rq   r@   rM   zOptionContract | None)r   
__future__r   rN   datetimer   collections.abcr   pathlibr   typingr   r   numpyrA   pandasr   scipy.optimizer	   RenTech.core.iv_surface_modelr
   r    RenTech.core.options_data_loaderr   r   r   r.   r   rP   r   r   r   r/   r-   <module>r      s   
 #   #      ! Q H
.4& 	  	
   4C$CC C 	C
 CLG: G:r/   