
    1i:                       d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	 ddl
mZm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             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"ddZ#	 	 	 	 	 	 	 	 	 	 	 	 ddZ$e G d de             Z% G d d      Z&y)a  
Synthetic option chain generation using the trained **XGBoost IV surface** model.

Provides :class:`SyntheticLoader` with the same ergonomics as
:class:`~RenTech.core.options_data_loader.IVolatilityLoader` (``iter_chain_dates``,
``get_chain_for_date``) while building quotes on demand via
:class:`SyntheticOptionChain.find_target_leg`.

On open positions, call :meth:`SyntheticLoader.attach_position_quotes` so
``find_contract_in_chain`` receives refreshed bid/ask/mid for MTM in the backtester.
    )annotationsN)	dataclassfield)Path)AnyIterator   )DEFAULT_MODEL_FILENAMEsynthetic_black_scholes)OptionChainOptionContractg?c                H    t        j                  |       j                         S )N)pd	Timestamp	normalize)tss    >/opt/rentech/trading_bot/RenTech/core/synthetic_data_loader.py	_norm_dayr   &   s    <<%%''    c                P   ddl m} d}t        t        |      |      }t        t        |      |      }t        |       t        |      }} t	        j
                  |      }t	        j                  | |z        |d|dz  z  z   |z  z   ||z  z  }t        |j                  |      dz
        S )u5   Black–Scholes delta for a European put (per share).r   norm-q=      ?r	         ?)scipy.statsr   maxfloatmathsqrtlogcdf	SKTrsigmar   epssqrt_td1s	            r   _bs_put_deltar,   *   s     
CE!HcAec"E8U1XqAYYq\F
((1q5/Quax/14
4	HB"#$$r   c                   ddl m} d}t        j                  ||      }t        j                  ||      }t        j                  |      }t        j
                  | |z        |d|dz  z  z   |z  z   ||z  z  }|j                  |      dz
  S )Nr   r   r   r   r	   r   )r   r   npmaximumr    r!   r"   r#   s	            r   _vector_put_deltar0   7   s}     !
C


1cAJJuc"EWWQZF
&&Q-1sUAX~-2
2uv~	FB88B<#r   c                   ddl m} d}t        j                  ||      }t        j                  ||      }t        j                  |      }t        j
                  | |z        |d|dz  z  z   |z  z   ||z  z  }|||z  z
  }	t        j                  | |z        }
|j                  |      }|| |z  |z  z  }| |z  |z  dz  }|  |z  |z  d|z  z  ||z  |
z  |j                  |	       z  z   }|dz  }|||fS )	z8Gamma, theta (per day), vega (per 1 vol point) for puts.r   r   r   r   r	         Y@       @     v@)	r   r   r.   r/   r    r!   exppdfr"   )r$   r%   r&   r'   r(   r   r)   r*   r+   d2discpdf1gammavegatheta_ythetas                   r   _vector_put_gamma_theta_vegar>   H   s	    !
C


1cAJJuc"EWWQZF
&&Q-1sUAX~-2
2uv~	FB	efn	B661"q&>D88B<DAI&'EHv&Db4i%3<01q54<$((B3-3OOGeOE%r   c                  | |z  }| |z  }|dk  rt        d      t        t        j                  ||z
  |z              dz   }t	        j
                  |||      }t	        j                  ||z        |z  }t	        j                  ||dkD           S )Nr   zstep must be positive   )
ValueErrorintr   ceilr.   linspaceroundunique)	spy_pricelo_pcthi_pctsteplohinstrikess           r   _build_strike_gridrO   a   s    	V	B	V	Bqy011DIIrBw$&'(1,Akk"b!$Ghhw~&-G99WWq[)**r   c                    ddl }t        |      } |j                  t        j                  |t        |      t        j                        t        j                  |t        |      t        j                        |j                  t        j                        t        j                  |t        |      t        j                        d      }| j                  |      j                  t        j                        }t        j                  |dd      S )z'Batch IV prediction from XGBoost model.r   Ndtype	vix_leveldte	moneynessis_call-C6?      @)
pandaslen	DataFramer.   fullr   float64astypepredictclip)	modelrT   rU   rV   rW   r   rM   Xivs	            r   _predict_iv_matrixre   m   s     IAE)$4BJJG771eCj

;"))"**5wwq%.

C		
	A 
q		 	 	,B772tS!!r   c                      e Zd ZU dZdZded<   dZded<   dZded<   dZd	ed
<   dZ	ded<   dZ
ded<   dZded<   eZded<   	 	 	 	 	 	 	 	 ddZy)SyntheticOptionChaina&  
    Lightweight chain: ``contracts`` may stay empty until
    :meth:`SyntheticLoader.attach_position_quotes` injects MTM rows, or until
    :meth:`find_target_leg` returns a single synthesized :class:`OptionContract`
    (without adding it to ``contracts`` unless you append it yourself).
            r   rG   rT   {Gz?risk_free_rateNr   rb   r   strike_stepffffff?moneyness_low皙?moneyness_high
spread_absc                   | j                   t        d      t        |      j                         j	                         dd }|dvrdt        |      j	                         v rdnd}|dk(  rdnd}t        | j                        }t        | j                        }t        | j                        }t        | j                        }	|	t        j                  t        |      	      z   }
t        |      d
z  }|dk  rt        d      t        || j                  | j                   | j"                        }||z  }t%        | j                   |t        |      ||      }t'        j(                  ||t&        j*                        }t'        j(                  ||t&        j*                        }t'        j,                  t/        |      |rdndt0              }t3        ||||||      }|rddlm} d}t'        j8                  ||      }t'        j8                  ||      }t'        j:                  |      }t'        j<                  ||z        |d|dz  z  z   |z  z   ||z  z  }|j?                  |      }ntA        |||||      }tC        |||||      \  }}}|rddlm} d}t'        j8                  ||      }t'        j8                  ||      }t'        j:                  |      }t'        j<                  ||z        |d|dz  z  z   |z  z   ||z  z  }|||z  z
  }t'        jD                  | |z        }|jG                  |      }|||z  |z  z  }||z  |z  dz  }| |z  |z  d|z  z  ||z  |z  |j?                  |      z  z
  d
z  }t        t'        jH                  t'        jJ                  |t        |      z
                    } t        ||          }!t        tM        ||    d            }"t        ||          }#t        ||          }$t        ||          }%t        ||          }&t        ||          }'t        | jN                        }(tM        |"|(z
  d      })tM        |"|(z   |)dz         }*tQ        |	|
|!||)|*|"|#|$|%|&|'      S )u   
        Search strikes from ``moneyness_low``–``moneyness_high`` × spot in ``strike_step``
        increments; predict IV; price with Black–Scholes; pick strike with delta closest to
        ``target_delta``.
        Nz,SyntheticOptionChain has no model reference.r@   CPrt   rs   r   rh   )daysr4   r   ztarget_dte must be >= 1)rH   rI   rJ   rQ   r   r   r   r	   r2   r3   rX   date
expirationstrikeoption_typebidaskmidrd   deltar:   r=   r;   ))rb   rA   strstripupperr   rG   rT   rj   r   as_ofr   	TimedeltarB   rO   rm   ro   rk   re   r.   	full_liker^   r]   r[   objectr   r   r   r/   r    r!   r"   r0   r>   r5   r6   argminabsr   rp   r   )+self
target_dtetarget_deltarz   otrW   r$   vixr'   r   rx   r&   rN   rV   iv_hatS_arrT_arrot_arrmidsr   r)   Tasar*   r+   deltasgamthver7   r8   r9   ir%   r}   rd   degath_ve_spr{   r|   s+                                              r   find_target_legz$SyntheticOptionChain.find_target_leg   s    ::KLL##%++-bq1Zs;/5577SBs#$..!DNN#$%%&$**%R\\s:??
*%6677$d((1D1D4K[K[
 aK	#DJJU:5F	SZ[Warzz:Warzz:Wgs3fM&uguaP(CE3'BFC(BWWR[F&&)Qr1u_,BBrF{SBXXb\F&uguaHF25'5!VTR(CE3'BFC(BWWR[F&&)Qr1u_,BBrF{SBb6k!B661"r'?D88B<D%"*v-.C$,'50B&4-"$f5Gd8JTXXVX\8YY]bbB		"&&%*=!=>?@'!*CQ%&6!96!93q6]BqElBqEl4??##(C #(C$J'!
 	
r   )r   rB   r   r   rz   r   returnr   )__name__
__module____qualname____doc__rG   __annotations__rT   rj   rb   rk   rm   ro   DEFAULT_SYNTHETIC_SPREADrp   r    r   r   rg   rg      s     IuIu NE E3KM5 NE 0J0]
]
 ]
 	]

 
]
r   rg   c                  j    e Zd ZdZddddded	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZdd	Zdd
Z	 	 	 	 	 	 ddZy)SyntheticLoaderu  
    Drop-in replacement for :class:`~RenTech.core.options_data_loader.IVolatilityLoader`
    when backtesting with **model-generated** surfaces.

    Parameters
    ----------
    spy_vix
        Daily index (normalized dates) with at least ``close`` (SPY) and ``vix_close``.
    model_path
        Path to ``spy_iv_surface.joblib`` (default: next to ``iv_surface_model.py``).
    risk_free_rate
        Continuous rate passed to Black–Scholes (default 4%).
    strike_step
        Strike grid step ($) for :meth:`SyntheticOptionChain.find_target_leg`.
    moneyness_low, moneyness_high
        Strike search bounds as fractions of spot (default 0.70–1.10).
    spread_abs
        Half-spread around synthetic mid: ``bid = mid - spread_abs``, ``ask = mid + spread_abs``.
    Nri   r   rl   rn   )
model_pathrj   rk   rm   ro   rp   c               d   |j                         | _        t        j                  | j                  j                        j                         | j                  _        | j                  | j                  j                  j                  d          j                         | _        dD ](  }|| j                  j                  vst        d|       |t        |      n-t        t              j                         j                  t        z  }	|	j                         st!        d|	       	 dd l}
|
j'                  |	      | _        t+        |      | _        t+        |      | _        t+        |      | _        t+        |      | _        t+        |      | _        y # t$        $ r}t%        d      |d }~ww xY w)Nlast)keep)close	vix_closezspy_vix must include column zIV model not found: r   zpip install joblib)copy_dfr   to_datetimeindexr   
duplicated
sort_indexcolumnsrA   r   __file__resolveparentr
   is_fileFileNotFoundErrorjoblibImportErrorload_modelr   _r_strike_step_m_lo_m_hi_spread_abs)r   spy_vixr   rj   rk   rm   ro   rp   colpathr   es               r   __init__zSyntheticLoader.__init__  s^    <<>7AAC88TXX^^66F6CCDOOQ) 	IC$((*** #?w!GHH	I $.#9tJtH~?U?U?W?^?^aw?w||~#&:4&$ABB	; kk$''!+.=)
>*
 ,  	;23:	;s   *F 	F/F**F/c              #     K   | j                   j                  D ]"  }t        t        j                  |             $ yw)zJAll calendar dates in the SPY/VIX panel (same convention as loader dates).N)r   r   r   r   r   )r   ds     r   iter_chain_datesz SyntheticLoader.iter_chain_dates+  s0      	-ABLLO,,	-s   =?c                   t        t        j                  |            }|| j                  j                  vrt        d|j                                | j                  j                  |   }t        |d         }t        |d         }t        |g ||| j                  | j                  | j                  | j                  | j                  | j                  
      S )z
        Return a :class:`SyntheticOptionChain` with **empty** ``contracts`` and spot/VIX
        for ``target_date``. Quotes are produced in :meth:`find_target_leg` or
        :meth:`attach_position_quotes`.
        zNo SPY/VIX row for r   r   )
r   	contractsrG   rT   rj   rb   rk   rm   ro   rp   )r   r   r   r   r   KeyErrorrw   locr   rg   r   r   r   r   r   r   )r   target_dater   rowspyr   s         r   get_chain_for_datez"SyntheticLoader.get_chain_for_date0  s     r||K01TXX^^#0<==hhll2CL!C$%#77++))**::''
 	
r   c                ^   |g |_         yt        |dd      }|sg |_         yt        |j                        }t	        |j
                        }t	        |j                        }t	        |j                        }|j                  }t	        |j                        }	g }
|D ]  }t        |j                        }t        dt        ||z
  j                              }|dk  rt        dd      }d}n|dz  }t	        |      }t	        |j                        }t        |j                         j#                         j%                         dd }|d	vrd
}|dk(  rdnd}||z  }t	        t'        j(                  |j+                  t-        j.                  |g|g|g|gd            d   dd            }t	        t1        ||||||            }t        |d      }|dk(  rddlm} d}t        ||      }t        ||      }t7        j8                  |      }t7        j:                  ||z        |d|dz  z  z   |z  z   ||z  z  }t	        |j=                  |            }|||z  z
  }t7        j>                  | |z        }|jA                  |      }|||z  |z  z  } ||z  |z  dz  }!| |z  |z  d|z  z  ||z  |z  |j=                  |      z  z
  dz  }"ntC        |||||      }tE        t'        jF                  |g      t'        jF                  |g      t'        jF                  |g      |t'        jF                  |g            \  }#}$}%t	        |#d         } t	        |$d         }"t	        |%d         }!t        ||	z
  d      }&t        ||	z   |&dz         }'|
jI                  tK        |||||&|'|||| |"|!              |
|_         y)z
        Populate ``chain.contracts`` with fresh synthetic quotes for each leg in ``position``
        (if any), using **remaining** calendar DTE and today's SPY/VIX so MTM matches
        ``find_contract_in_chain`` in the backtester.
        Nlegsr   r@   gư>gg6qf?r   r4   rr   rt   rs   rh   rS   rX   rY   r   r   r   r	   r2   r3   rv   )&r   getattrr   r   r   rG   rT   rj   rb   rp   rx   r   rB   ru   ry   r   rz   r   r   r.   ra   r`   r   r\   r   r   r   r   r    r!   r"   r5   r6   r,   r>   arrayappendr   )(r   chainpositionr   r   r$   r   r'   rb   r   outlegr5   dte_remr&   dte_featr%   r   rW   mr   r}   r   r)   r   r   r*   r+   r   r7   r8   r9   r   r   r   garrtarrvarrr{   r|   s(                                           r   attach_position_quotesz&SyntheticLoader.attach_position_quotesI  s     EOx. EO%++&%//"EOO$%&&'5##$$& L	CCNN+C!S#+!3!345G{k*eO >cjj!AS__%++-335bq9B#3YcCGAAMM.1U(0z./S,39		 	 F  /1aFBGHCc3-CSy,C[%2hhq1uoS2q5[B(>>2;O488B<("v+%xxR(xx|QVf_-$h'50b4i"nf5AtxxPR|8SSW\\"1aAv6#?HHaSM288QC="((A3-BHHfXDV$ dD 47^47^47^cBh$CcBhd
+CJJ" "{L	\ r   )r   zpd.DataFramer   zstr | Path | Nonerj   r   rk   r   rm   r   ro   r   rp   r   r   None)r   zIterator[pd.Timestamp])r   pd.Timestampr   rg   )r   rg   r   z
Any | Noner   r   )	r   r   r   r   r   r   r   r   r   r   r   r   r   r      s    0 )- $ # $4-- &	-
 - - - - - 
-@-

2i#i i 
	ir   r   )r   zpd.Timestamp | objectr   r   )r$   r   r%   r   r&   r   r'   r   r(   r   r   r   )r$   
np.ndarrayr%   r   r&   r   r'   r   r(   r   r   r   )r$   r   r%   r   r&   r   r'   r   r(   r   r   z)tuple[np.ndarray, np.ndarray, np.ndarray])
rG   r   rH   r   rI   r   rJ   r   r   r   )rb   r   rT   r   rU   r   rV   r   rW   r   r   r   )'r   
__future__r   r   sysdataclassesr   r   pathlibr   typingr   r   numpyr.   rZ   r   r   r   parents
_REPO_ROOTr   r   insertRenTech.core.iv_surface_modelr
   r    RenTech.core.options_data_loaderr   r   r   r   r,   r0   r>   rO   re   rg   r   r   r   r   <module>r      sk  
 #  
 (     (^##%--a0
z?#(("HHOOAs:' I   (
%  	
  "  	
  /2	+""" 
" 	"
 " ". n
; n
 n
b| |r   