import%20marimo%0A%0A__generated_with%20%3D%20%220.18.4%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0Awith%20app.setup%3A%0A%20%20%20%20from%20pathlib%20import%20Path%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%0A%20%20%20%20from%20qsmile%20import%20(%0A%20%20%20%20%20%20%20%20OptionChain%2C%0A%20%20%20%20%20%20%20%20SVIModel%2C%0A%20%20%20%20%20%20%20%20XCoord%2C%0A%20%20%20%20%20%20%20%20YCoord%2C%0A%20%20%20%20%20%20%20%20fit%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_intro()%3A%0A%20%20%20%20%22%22%22Render%20the%20notebook%20introduction.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Option%20Chain%20Pipeline%20%E2%80%94%20Real%20SPX%20Data%0A%0A%20%20%20%20%20%20%20%20This%20notebook%20demonstrates%20the%20**qsmile**%20option%20chain%20pipeline%20using%0A%20%20%20%20%20%20%20%20**real%20S%26P%20500%20(SPX)%20option%20chain%20data**%20loaded%20from%20parquet%3A%0A%0A%20%20%20%20%20%20%20%201.%20**%60OptionChain%60**%20%E2%80%94%20real%20bid%2Fask%20option%20prices%2C%20with%20forward%20%26%20discount%0A%20%20%20%20%20%20%20%20%20%20%20factor%20calibrated%20automatically%20from%20put-call%20parity%0A%20%20%20%20%20%20%20%202.%20**%60SmileData%60**%20%E2%80%94%20unified%20coordinate-labelled%20container%20with%0A%20%20%20%20%20%20%20%20%20%20%20%60.transform(x%2C%20y)%60%20to%20freely%20move%20between%20strike%2Fmoneyness%2Flog-moneyness%2Fstandardised%0A%20%20%20%20%20%20%20%20%20%20%20X-coordinates%20and%20price%2Fvol%2Fvariance%2Ftotal-variance%20Y-coordinates%0A%20%20%20%20%20%20%20%203.%20**%60fit%60**%20%E2%80%94%20SVI%20parametric%20smile%20fit%0A%0A%20%20%20%20%20%20%20%20%24%24%0A%20%20%20%20%20%20%20%20%5Ctext%7BPrices%7D%20%5Cxrightarrow%7B%5Ctexttt%7Bto%5C_smile%5C_data()%7D%7D%20%5Ctext%7BSmileData%7D%0A%20%20%20%20%20%20%20%20%5Cxrightarrow%7B%5Ctexttt%7Btransform()%7D%7D%20%5Ctext%7Bany%20coords%7D%0A%20%20%20%20%20%20%20%20%5Cquad%5Cbig%7C%5Cquad%0A%20%20%20%20%20%20%20%20%5Ctext%7BSmileData%7D%20%5Cxrightarrow%7B%5Ctexttt%7Bfit()%7D%7D%20%5Ctext%7BSVI%7D%0A%20%20%20%20%20%20%20%20%24%24%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_market_intro()%3A%0A%20%20%20%20%22%22%22Introduce%20the%20market%20data%20section.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Stage%200%20%E2%80%94%20Real%20SPX%20Market%20Data%0A%0A%20%20%20%20%20%20%20%20We%20load%20a%20real%20S%26P%20500%20(SPX)%20option%20chain%20fetched%20from%20Yahoo%20Finance%0A%20%20%20%20%20%20%20%20and%20saved%20as%20parquet.%20The%20chain%20includes%20bid%2Fask%20prices%2C%20volume%2C%20and%0A%20%20%20%20%20%20%20%20open%20interest%20for%20both%20calls%20and%20puts%20at%20a%20~3-month%20expiry.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_market_data()%3A%0A%20%20%20%20%22%22%22Load%20real%20SPX%20option%20chain%20from%20parquet.%22%22%22%0A%20%20%20%20_root%20%3D%20Path(__file__).resolve().parent.parent.parent.parent%0A%20%20%20%20_pq%20%3D%20sorted(_root.glob(%22parquet%2Fchains%2F*.parquet%22))%5B-1%5D%0A%20%20%20%20df_raw%20%3D%20pd.read_parquet(_pq)%0A%0A%20%20%20%20expiry%20%3D%20float(df_raw%5B%22daysToExpiry%22%5D.iloc%5B0%5D)%20%2F%20365.0%0A%0A%20%20%20%20%23%20Pivot%20calls%2Fputs%20onto%20common%20strikes%0A%20%20%20%20_cols%20%3D%20%5B%22strike%22%2C%20%22bid%22%2C%20%22ask%22%2C%20%22volume%22%2C%20%22openInterest%22%5D%0A%20%20%20%20calls%20%3D%20df_raw%5Bdf_raw%5B%22optionType%22%5D%20%3D%3D%20%22call%22%5D%5B_cols%5D.set_index(%22strike%22)%0A%20%20%20%20puts%20%3D%20df_raw%5Bdf_raw%5B%22optionType%22%5D%20%3D%3D%20%22put%22%5D%5B_cols%5D.set_index(%22strike%22)%0A%20%20%20%20merged%20%3D%20calls.join(puts%2C%20lsuffix%3D%22_call%22%2C%20rsuffix%3D%22_put%22%2C%20how%3D%22inner%22).sort_index()%0A%0A%20%20%20%20strikes%20%3D%20merged.index.values.astype(np.float64)%0A%20%20%20%20call_bid%20%3D%20merged%5B%22bid_call%22%5D.values.astype(np.float64)%0A%20%20%20%20call_ask%20%3D%20merged%5B%22ask_call%22%5D.values.astype(np.float64)%0A%20%20%20%20put_bid%20%3D%20merged%5B%22bid_put%22%5D.values.astype(np.float64)%0A%20%20%20%20put_ask%20%3D%20merged%5B%22ask_put%22%5D.values.astype(np.float64)%0A%20%20%20%20return%20call_ask%2C%20call_bid%2C%20expiry%2C%20put_ask%2C%20put_bid%2C%20strikes%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_prices_intro()%3A%0A%20%20%20%20%22%22%22Introduce%20the%20prices%20stage.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Stage%201%20%E2%80%94%20%60OptionChain%60%0A%0A%20%20%20%20%20%20%20%20Construct%20an%20%60OptionChain%60%20from%20the%20real%20SPX%20bid%2Fask%20prices.%0A%20%20%20%20%20%20%20%20The%20forward%20and%20discount%20factor%20are%20**calibrated%20from%20put-call%20parity**%0A%20%20%20%20%20%20%20%20using%20quasi-delta%20weighted%20least%20squares.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_prices(call_ask%2C%20call_bid%2C%20expiry%2C%20put_ask%2C%20put_bid%2C%20strikes)%3A%0A%20%20%20%20%22%22%22Build%20OptionChain%20%E2%80%94%20forward%2FDF%20calibrated%20automatically.%22%22%22%0A%20%20%20%20raw_prices%20%3D%20OptionChain(%0A%20%20%20%20%20%20%20%20strikes%3Dstrikes%2C%0A%20%20%20%20%20%20%20%20call_bid%3Dcall_bid%2C%0A%20%20%20%20%20%20%20%20call_ask%3Dcall_ask%2C%0A%20%20%20%20%20%20%20%20put_bid%3Dput_bid%2C%0A%20%20%20%20%20%20%20%20put_ask%3Dput_ask%2C%0A%20%20%20%20%20%20%20%20expiry%3Dexpiry%2C%0A%20%20%20%20%20%20%20%20%23%20forward%20and%20discount_factor%20omitted%20%E2%86%92%20calibrated%20from%20put-call%20parity%0A%20%20%20%20)%0A%20%20%20%20prices%20%3D%20raw_prices.denoise()%0A%20%20%20%20return%20(prices%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_prices_result(prices)%3A%0A%20%20%20%20%22%22%22Display%20calibrated%20forward%2FDF%20from%20put-call%20parity.%22%22%22%0A%20%20%20%20df_prices%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Strike%22%3A%20prices.strikes%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Call%20Bid%22%3A%20prices.call_bid.round(2)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Call%20Ask%22%3A%20prices.call_ask.round(2)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Put%20Bid%22%3A%20prices.put_bid.round(2)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Put%20Ask%22%3A%20prices.put_ask.round(2)%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20mo.vstack(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%22%23%23%23%20Calibrated%20Forward%20%26%20Discount%20Factor%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%7C%20Quantity%20%7C%20Calibrated%20%7C%0A%20%20%20%20%7C----------%7C%3A---------%3A%7C%0A%20%20%20%20%7C%20Forward%20%20%7C%20%7Bprices.forward%3A.2f%7D%20%7C%0A%20%20%20%20%7C%20Discount%20Factor%20%7C%20%7Bprices.discount_factor%3A.6f%7D%20%7C%0A%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%22%23%23%23%20Price%20Data%20(sample)%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20print(df_prices.head(20))%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_prices_plot(prices)%3A%0A%20%20%20%20%22%22%22Plot%20bid%2Fask%20prices.%22%22%22%0A%20%20%20%20_fig%20%3D%20go.Figure()%0A%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dprices.strikes%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dprices.call_mid%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22markers%2Blines%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20error_y%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22type%22%3A%20%22data%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22symmetric%22%3A%20False%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22array%22%3A%20(prices.call_ask%20-%20prices.call_mid).tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22arrayminus%22%3A%20(prices.call_mid%20-%20prices.call_bid).tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Calls%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20marker%3D%7B%22color%22%3A%20%22%232196F3%22%7D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dprices.strikes%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dprices.put_mid%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22markers%2Blines%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20error_y%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22type%22%3A%20%22data%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22symmetric%22%3A%20False%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22array%22%3A%20(prices.put_ask%20-%20prices.put_mid).tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22arrayminus%22%3A%20(prices.put_mid%20-%20prices.put_bid).tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Puts%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20marker%3D%7B%22color%22%3A%20%22%23E74C3C%22%7D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20_fig.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Stage%201%3A%20Bid%2FAsk%20Option%20Prices%22%2C%0A%20%20%20%20%20%20%20%20xaxis_title%3D%22Strike%22%2C%0A%20%20%20%20%20%20%20%20yaxis_title%3D%22Price%22%2C%0A%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%0A%20%20%20%20%20%20%20%20height%3D420%2C%0A%20%20%20%20%20%20%20%20legend%3D%7B%22x%22%3A%200.02%2C%20%22y%22%3A%200.98%7D%2C%0A%20%20%20%20)%0A%20%20%20%20mo.ui.plotly(_fig)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_divider_1()%3A%0A%20%20%20%20%22%22%22Section%20divider.%22%22%22%0A%20%20%20%20mo.md(r%22%22%22---%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_transforms_intro()%3A%0A%20%20%20%20%22%22%22Introduce%20the%20SmileData%20coordinate%20transforms%20section.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Stage%202%20%E2%80%94%20%60SmileData%60%20%26%20Coordinate%20Transforms%0A%0A%20%20%20%20%20%20%20%20Call%20%60prices.to_smile_data()%60%20to%20create%20a%20%60SmileData%60%20container%20in%0A%20%20%20%20%20%20%20%20**(FixedStrike%2C%20Price)**%20coordinates.%20From%20there%2C%20%60.transform(x%2C%20y)%60%0A%20%20%20%20%20%20%20%20moves%20freely%20between%20any%20combination%20of%20X%20and%20Y%20coordinates%3A%0A%0A%20%20%20%20%20%20%20%20%7C%20X-coordinate%20%7C%20Definition%20%7C%0A%20%20%20%20%20%20%20%20%7C--------------%7C------------%7C%0A%20%20%20%20%20%20%20%20%7C%20%60FixedStrike%60%20%7C%20%24K%24%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60MoneynessStrike%60%20%7C%20%24K%20%2F%20F%24%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60LogMoneynessStrike%60%20%7C%20%24%5Cln(K%20%2F%20F)%24%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60StandardisedStrike%60%20%7C%20%24%5Cln(K%20%2F%20F)%20%2F%20(%5Csigma_%7B%5Cmathrm%7BATM%7D%7D%20%5Csqrt%7BT%7D)%24%20%7C%0A%0A%20%20%20%20%20%20%20%20%7C%20Y-coordinate%20%7C%20Definition%20%7C%0A%20%20%20%20%20%20%20%20%7C--------------%7C------------%7C%0A%20%20%20%20%20%20%20%20%7C%20%60Price%60%20%7C%20Black76%20option%20price%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60Volatility%60%20%7C%20%24%5Csigma%24%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60Variance%60%20%7C%20%24%5Csigma%5E2%24%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60TotalVariance%60%20%7C%20%24%5Csigma%5E2%20T%24%20%7C%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_smile_data(prices)%3A%0A%20%20%20%20%22%22%22Create%20SmileData%20from%20OptionChain%20and%20transform%20to%20vols.%22%22%22%0A%20%20%20%20sd_prices%20%3D%20prices.to_smile_data()%0A%20%20%20%20sd%20%3D%20sd_prices.transform(XCoord.FixedStrike%2C%20YCoord.Volatility)%0A%20%20%20%20return%20(sd%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_transforms_table(sd)%3A%0A%20%20%20%20%22%22%22Show%20the%20original%20data%20and%20several%20coordinate%20views.%22%22%22%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22(FixedStrike%2C%20Vol)%22%3A%20sd%2C%0A%20%20%20%20%20%20%20%20%22(Moneyness%2C%20Vol)%22%3A%20sd.transform(XCoord.MoneynessStrike%2C%20YCoord.Volatility)%2C%0A%20%20%20%20%20%20%20%20%22(LogMoneyness%2C%20TotalVar)%22%3A%20sd.transform(XCoord.LogMoneynessStrike%2C%20YCoord.TotalVariance)%2C%0A%20%20%20%20%20%20%20%20%22(Standardised%2C%20TotalVar)%22%3A%20sd.transform(XCoord.StandardisedStrike%2C%20YCoord.TotalVariance)%2C%0A%20%20%20%20%20%20%20%20%22(FixedStrike%2C%20Price)%22%3A%20sd.transform(XCoord.FixedStrike%2C%20YCoord.Price)%2C%0A%20%20%20%20%7D%0A%20%20%20%20%23%20tables%20%3D%20%5B%5D%0A%20%20%20%20%23%20for%20label%2C%20v%20in%20views.items()%3A%0A%20%20%20%20%23%20%20%20%20%20df%20%3D%20pd.DataFrame(%0A%20%20%20%20%23%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%23%20%20%20%20%20%20%20%20%20%20%20%20%20%22X%22%3A%20v.x.round(4)%2C%0A%20%20%20%20%23%20%20%20%20%20%20%20%20%20%20%20%20%20%22Y%20Bid%22%3A%20v.y_bid.round(6)%2C%0A%20%20%20%20%23%20%20%20%20%20%20%20%20%20%20%20%20%20%22Y%20Mid%22%3A%20v.y_mid.round(6)%2C%0A%20%20%20%20%23%20%20%20%20%20%20%20%20%20%20%20%20%20%22Y%20Ask%22%3A%20v.y_ask.round(6)%2C%0A%20%20%20%20%23%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%23%20%20%20%20%20)%0A%20%20%20%20%23%20%20%20%20%20tables.append(mo.md(f%22**%7Blabel%7D**%20%E2%80%94%20%60x_coord%3D%7Bv.x_coord.name%7D%60%2C%20%60y_coord%3D%7Bv.y_coord.name%7D%60%22))%0A%20%20%20%20%23%20%20%20%20%20tables.append(df)%0A%20%20%20%20%23%20mo.vstack(tables)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_transforms_plot(sd)%3A%0A%20%20%20%20%22%22%22Plot%20the%20same%20smile%20in%20four%20coordinate%20systems.%22%22%22%0A%20%20%20%20coords%20%3D%20%5B%0A%20%20%20%20%20%20%20%20(%22FixedStrike%20%2F%20Volatility%22%2C%20XCoord.FixedStrike%2C%20YCoord.Volatility%2C%20%22Strike%22%2C%20%22%CF%83%22)%2C%0A%20%20%20%20%20%20%20%20(%22MoneynessStrike%20%2F%20Volatility%22%2C%20XCoord.MoneynessStrike%2C%20YCoord.Volatility%2C%20%22K%2FF%22%2C%20%22%CF%83%22)%2C%0A%20%20%20%20%20%20%20%20(%22LogMoneynessStrike%20%2F%20TotalVariance%22%2C%20XCoord.LogMoneynessStrike%2C%20YCoord.TotalVariance%2C%20%22ln(K%2FF)%22%2C%20%22%CF%83%C2%B2T%22)%2C%0A%20%20%20%20%20%20%20%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22StandardisedStrike%20%2F%20TotalVariance%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20XCoord.StandardisedStrike%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20YCoord.TotalVariance%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22k%CC%83%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%CF%83%C2%B2T%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D%0A%20%20%20%20from%20plotly.subplots%20import%20make_subplots%0A%0A%20%20%20%20_fig%20%3D%20make_subplots(rows%3D2%2C%20cols%3D2%2C%20subplot_titles%3D%5Bc%5B0%5D%20for%20c%20in%20coords%5D)%0A%20%20%20%20colors%20%3D%20%5B%22%232196F3%22%2C%20%22%23E74C3C%22%2C%20%22%232FA4A9%22%2C%20%22%239B59B6%22%5D%0A%20%20%20%20for%20idx%2C%20(title%2C%20xc%2C%20yc%2C%20xlabel%2C%20ylabel)%20in%20enumerate(coords)%3A%0A%20%20%20%20%20%20%20%20view%20%3D%20sd.transform(xc%2C%20yc)%0A%20%20%20%20%20%20%20%20row%2C%20col%20%3D%20divmod(idx%2C%202)%0A%20%20%20%20%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3Dview.x%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3Dview.y_mid%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22markers%2Blines%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20marker%3D%7B%22color%22%3A%20colors%5Bidx%5D%2C%20%22size%22%3A%206%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20line%3D%7B%22color%22%3A%20colors%5Bidx%5D%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3Dtitle%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20showlegend%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20row%3Drow%20%2B%201%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20col%3Dcol%20%2B%201%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_fig.update_xaxes(title_text%3Dxlabel%2C%20row%3Drow%20%2B%201%2C%20col%3Dcol%20%2B%201)%0A%20%20%20%20%20%20%20%20_fig.update_yaxes(title_text%3Dylabel%2C%20row%3Drow%20%2B%201%2C%20col%3Dcol%20%2B%201)%0A%20%20%20%20_fig.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Same%20Smile%20%E2%80%94%20Four%20Coordinate%20Systems%22%2C%0A%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%0A%20%20%20%20%20%20%20%20height%3D650%2C%0A%20%20%20%20)%0A%20%20%20%20mo.ui.plotly(_fig)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_roundtrip(sd)%3A%0A%20%20%20%20%22%22%22Demonstrate%20round-trip%20fidelity.%22%22%22%0A%20%20%20%20%23%20Go%20FixedStrike%2FVol%20%E2%86%92%20Standardised%2FTotalVar%20%E2%86%92%20back%0A%20%20%20%20there%20%3D%20sd.transform(XCoord.StandardisedStrike%2C%20YCoord.TotalVariance)%0A%20%20%20%20back%20%3D%20there.transform(XCoord.FixedStrike%2C%20YCoord.Volatility)%0A%20%20%20%20max_x_err%20%3D%20float(np.max(np.abs(back.x%20-%20sd.x)))%0A%20%20%20%20max_y_err%20%3D%20float(np.max(np.abs(back.y_mid%20-%20sd.y_mid)))%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%23%23%23%20Round-Trip%20Fidelity%0A%0A%20%20%20%20Transform%20**FixedStrike%20%2F%20Volatility%20%E2%86%92%20StandardisedStrike%20%2F%20TotalVariance%20%E2%86%92%20back**%3A%0A%0A%20%20%20%20%7C%20Metric%20%7C%20Value%20%7C%0A%20%20%20%20%7C--------%7C-------%7C%0A%20%20%20%20%7C%20Max%20X%20error%20(strikes)%20%7C%20%7Bmax_x_err%3A.2e%7D%20%7C%0A%20%20%20%20%7C%20Max%20Y%20error%20(mid%20vol)%20%7C%20%7Bmax_y_err%3A.2e%7D%20%7C%0A%0A%20%20%20%20Round-trip%20is%20exact%20to%20within%20floating-point%20precision.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_divider_3()%3A%0A%20%20%20%20%22%22%22Section%20divider.%22%22%22%0A%20%20%20%20mo.md(r%22%22%22---%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_svi_intro()%3A%0A%20%20%20%20%22%22%22Introduce%20the%20SVI%20fitting%20stage.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Stage%203%20%E2%80%94%20SVI%20Fit%0A%0A%20%20%20%20%20%20%20%20We%20pass%20the%20%60SmileData%60%20(in%20volatility%20coordinates)%20directly%20to%20%60fit%60%2C%0A%20%20%20%20%20%20%20%20which%20transforms%20to%20log-moneyness%20%2F%20total-variance%20internally.%0A%20%20%20%20%20%20%20%20The%20fitted%20SVI%20curve%20is%20overlaid%20on%20the%20market%20data.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_svi_fit(sd)%3A%0A%20%20%20%20%22%22%22Fit%20SVI%20directly%20from%20SmileData.%22%22%22%0A%20%20%20%20result%20%3D%20fit(sd%2C%20SVIModel)%0A%20%20%20%20p%20%3D%20result.params%0A%20%20%20%20return%20p%2C%20result%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_svi_params(p%2C%20result)%3A%0A%20%20%20%20%22%22%22Display%20fitted%20SVI%20parameters.%22%22%22%0A%20%20%20%20mo.vstack(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%22%23%23%23%20Fitted%20SVI%20Parameters%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%7C%20Parameter%20%7C%20Value%20%7C%20Description%20%7C%0A%20%20%20%20%7C-----------%7C-------%7C-------------%7C%0A%20%20%20%20%7C%20%24a%24%20%7C%20%7Bp.a%3A.6f%7D%20%7C%20Vertical%20translation%20%7C%0A%20%20%20%20%7C%20%24b%24%20%7C%20%7Bp.b%3A.6f%7D%20%7C%20Wing%20slope%20%7C%0A%20%20%20%20%7C%20%24%5C%5Crho%24%20%7C%20%7Bp.rho%3A.6f%7D%20%7C%20Skew%20(correlation)%20%7C%0A%20%20%20%20%7C%20%24m%24%20%7C%20%7Bp.m%3A.6f%7D%20%7C%20Horizontal%20shift%20%7C%0A%20%20%20%20%7C%20%24%5C%5Csigma%24%20%7C%20%7Bp.sigma%3A.6f%7D%20%7C%20ATM%20curvature%20%7C%0A%0A%20%20%20%20**RMSE%3A**%20%7Bresult.rmse%3A.2e%7D%20%26nbsp%3B%26nbsp%3B%20**Converged%3A**%20%7B%22Yes%22%20if%20result.success%20else%20%22No%22%7D%0A%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_svi_plot(expiry%2C%20result%2C%20sd)%3A%0A%20%20%20%20%22%22%22Plot%20market%20vols%20with%20SVI%20fitted%20curve%20in%20strike%20space.%22%22%22%0A%20%20%20%20_fwd%20%3D%20sd.metadata.forward%0A%20%20%20%20_strikes_fine%20%3D%20np.linspace(sd.x.min()%20*%200.95%2C%20sd.x.max()%20*%201.05%2C%20200)%0A%20%20%20%20_k_fine%20%3D%20np.log(_strikes_fine%20%2F%20_fwd)%0A%20%20%20%20_iv_fitted%20%3D%20result.params.implied_vol(_k_fine%2C%20expiry)%0A%0A%20%20%20%20_fig%20%3D%20go.Figure()%0A%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dsd.x%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dsd.y_mid%20*%20100%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22markers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20error_y%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22type%22%3A%20%22data%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22symmetric%22%3A%20False%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22array%22%3A%20((sd.y_ask%20-%20sd.y_mid)%20*%20100).tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22arrayminus%22%3A%20((sd.y_mid%20-%20sd.y_bid)%20*%20100).tolist()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20marker%3D%7B%22size%22%3A%209%2C%20%22color%22%3A%20%22%23E74C3C%22%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Market%20(bid%2Fask)%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20_fig.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D_strikes_fine%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3D_iv_fitted%20*%20100%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22lines%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line%3D%7B%22color%22%3A%20%22%232FA4A9%22%2C%20%22width%22%3A%202.5%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22SVI%20Fit%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20_fig.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Market%20Implied%20Vols%20vs%20SVI%20Fit%22%2C%0A%20%20%20%20%20%20%20%20xaxis_title%3D%22Strike%22%2C%0A%20%20%20%20%20%20%20%20yaxis_title%3D%22Implied%20Volatility%20(%25)%22%2C%0A%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%0A%20%20%20%20%20%20%20%20height%3D450%2C%0A%20%20%20%20%20%20%20%20legend%3D%7B%22x%22%3A%200.02%2C%20%22y%22%3A%200.98%7D%2C%0A%20%20%20%20)%0A%20%20%20%20mo.ui.plotly(_fig)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_divider_4()%3A%0A%20%20%20%20%22%22%22Section%20divider.%22%22%22%0A%20%20%20%20mo.md(r%22%22%22---%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20cell_summary()%3A%0A%20%20%20%20%22%22%22Render%20the%20conclusion.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Summary%0A%0A%20%20%20%20%20%20%20%20This%20notebook%20demonstrated%20the%20**qsmile**%20option%20chain%20pipeline%3A%0A%0A%20%20%20%20%20%20%20%20%7C%20Step%20%7C%20Class%20%7C%20Method%20%7C%0A%20%20%20%20%20%20%20%20%7C------%7C-------%7C--------%7C%0A%20%20%20%20%20%20%20%20%7C%20Raw%20prices%20with%20bid%2Fask%20%7C%20%60OptionChain%60%20%7C%20*constructor*%20%E2%80%94%20auto-calibrates%20F%2C%20D%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%E2%86%92%20Any%20coordinate%20system%20%7C%20%60SmileData%60%20%7C%20%60.to_smile_data().transform(x%2C%20y)%60%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%E2%86%92%20SVI%20smile%20fit%20%7C%20%60SmileResult%60%20%7C%20%60fit(sd%2C%20model)%60%20%7C%0A%0A%20%20%20%20%20%20%20%20The%20**coordinate%20transform%20framework**%20lets%20you%20freely%20move%20between%0A%20%20%20%20%20%20%20%20any%20combination%20of%20X-coordinates%20(Strike%2C%20Moneyness%2C%20Log-Moneyness%2C%0A%20%20%20%20%20%20%20%20Standardised)%20and%20Y-coordinates%20(Price%2C%20Volatility%2C%20Variance%2C%20Total%0A%20%20%20%20%20%20%20%20Variance)%20via%20composable%2C%20invertible%20maps.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
f5914d8fb20cce12a08928a22cd78354