import%20marimo%0A%0A__generated_with%20%3D%20%220.21.1%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20from%20vectorbtpro%20import%20vbt%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20numpy%20as%20np%0A%0A%20%20%20%20return%20mo%2C%20np%2C%20pd%2C%20vbt%0A%0A%0A%40app.cell%0Adef%20_(vbt)%3A%0A%20%20%20%20data%20%3D%20vbt.HDFData.pull(%22data.h5%22)%0A%20%20%20%20data%20%3D%20data.ohlcv%0A%20%20%20%20return%20(data%2C)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20outer_border%20%3D%20%7B%22border%22%3A%20%221px%20solid%20darkgray%22%2C%20%22padding%22%3A%20%228px%22%2C%20%22border-radius%22%3A%20%228px%22%2C%20%22height%22%3A%22100%25%22%2C%20%22width%22%3A%22100%25%22%7D%0A%20%20%20%20inner_border%20%3D%20%7B%22border%22%3A%20%221px%20solid%20lightgray%22%2C%20%22padding%22%3A%20%228px%22%2C%20%22height%22%3A%22100%25%22%2C%20%22width%22%3A%22100%25%22%7D%0A%20%20%20%20return%20inner_border%2C%20outer_border%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Signal%20parameters%20and%20summary%20stats%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(inner_border%2C%20mo)%3A%0A%20%20%20%20signal_descr%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%22**Signal%20Rules**%22)%2C%20%0A%20%20%20%20%20%20%20%20mo.md(%22ENTRY%3A%20RSI%20crosses%20ABOVE%20entry_thr%20(oversold%20recovery)%22)%2C%20%0A%20%20%20%20%20%20%20%20mo.md(%22EXIT%3A%20%20RSI%20crosses%20BELOW%20exit_thr%20%20(overbought%20recovery)%22)%2C%0A%20%20%20%20%5D%2C%20align%3D%22center%22).style(%7B**inner_border%7D)%0A%20%20%20%20return%20(signal_descr%2C)%0A%0A%0A%40app.cell%0Adef%20_(inner_border%2C%20mo)%3A%0A%20%20%20%20%23%20prepare%20parameters%20and%20corresponding%20UI%20controls%20block%0A%20%20%20%20timeperiod%20%3D%20mo.ui.number(value%3D6%2C%20start%3D3%2C%20stop%3D60%2C%20step%3D1%2C%20label%3D%22timeperiod%22)%0A%20%20%20%20entry_thr%20%3D%20mo.ui.number(value%3D20%2C%20start%3D5%2C%20stop%3D50%2C%20step%3D1%2C%20label%3D%22entry_thr%22)%0A%20%20%20%20exit_thr%20%3D%20mo.ui.number(value%3D80%2C%20start%3D50%2C%20stop%3D95%2C%20step%3D1%2C%20label%3D%22exit_thr%22)%0A%20%20%20%20signal_params%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%22**Signal%20Parameters**%22)%2C%0A%20%20%20%20%20%20%20%20timeperiod%2C%20%0A%20%20%20%20%20%20%20%20entry_thr%2C%20%0A%20%20%20%20%20%20%20%20exit_thr%0A%20%20%20%20%5D%2C%20align%3D%22center%22%2C%20gap%3D1).style(%7B**inner_border%7D)%0A%20%20%20%20return%20entry_thr%2C%20exit_thr%2C%20signal_params%2C%20timeperiod%0A%0A%0A%40app.cell%0Adef%20_(data%2C%20entry_thr%2C%20exit_thr%2C%20timeperiod%2C%20vbt)%3A%0A%20%20%20%20%23%20prepare%20indicator%0A%20%20%20%20rsi%20%3D%20vbt.talib(%22RSI%22).run(%0A%20%20%20%20%20%20%20%20data.open%2C%20%23%20use%20open%20prices%20to%20avoid%20look-ahead%20bias%0A%20%20%20%20%20%20%20%20timeperiod%3Dtimeperiod.value%2C%20%0A%20%20%20%20%20%20%20%20hide_params%3DTrue%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20generate%20signals%0A%20%20%20%20entries%20%3D%20rsi.rsi.vbt.crossed_above(entry_thr.value)%0A%20%20%20%20exits%20%3D%20rsi.rsi.vbt.crossed_below(exit_thr.value)%0A%0A%20%20%20%20%23%20clean%20signals%0A%20%20%20%20clean_entries%2C%20clean_exits%20%3D%20entries.vbt.signals.clean(exits)%0A%0A%20%20%20%20%23%20run%20portfolio%20simulation%0A%20%20%20%20pf%20%3D%20vbt.Portfolio.from_signals(%0A%20%20%20%20%20%20%20%20data.close%2C%20%0A%20%20%20%20%20%20%20%20entries%3Dclean_entries%2C%20%0A%20%20%20%20%20%20%20%20exits%3Dclean_exits%0A%20%20%20%20)%0A%20%20%20%20return%20clean_entries%2C%20clean_exits%2C%20entries%2C%20pf%0A%0A%0A%40app.cell%0Adef%20_(inner_border%2C%20mo%2C%20pd%2C%20pf)%3A%0A%20%20%20%20stats_df%20%3D%20pd.DataFrame(columns%3D%5B%22duration%22%2C%20%22return%22%5D)%0A%20%20%20%20stats_df%5B%22duration%22%5D%20%3D%20pf.exit_trades.duration.readable%5B%22Value%22%5D.describe()%0A%20%20%20%20stats_df%5B%22return%22%5D%20%3D%20pf.exit_trades.readable%5B%22Return%22%5D.describe()%0A%20%20%20%20stats_block%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%22**Trade%20Stats**%22)%2C%0A%20%20%20%20%20%20%20%20mo.ui.table(stats_df%2C%20selection%3DNone)%0A%20%20%20%20%5D%2C%20align%3D%22center%22%2C%20gap%3D1).style(%7B**inner_border%7D)%0A%20%20%20%20return%20(stats_block%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20outer_border%2C%20signal_descr%2C%20signal_params%2C%20stats_block)%3A%0A%20%20%20%20signal_summary_block%20%3D%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20mo.vstack(%5Bsignal_descr%2C%20signal_params%5D%2C%20align%3D%22center%22)%2C%0A%20%20%20%20%20%20%20%20stats_block%0A%20%20%20%20%5D%2C%20widths%3D%5B1%2C2%5D%2C%20align%3D%22stretch%22).style(%7B**outer_border%7D)%0A%20%20%20%20return%20(signal_summary_block%2C)%0A%0A%0A%40app.cell%0Adef%20_(signal_summary_block)%3A%0A%20%20%20%20signal_summary_block%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Projections%20Bands%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(clean_entries%2C%20data%2C%20entries%2C%20vbt)%3A%0A%20%20%20%20%23%20calculate%20entry%20probability%20per%20symbol%0A%20%20%20%20entry_probs%20%3D%20entries%5B~data.close.isna()%5D.mean()%20%23%20use%20only%20indeces%20with%20valid%20data%0A%20%20%20%20%23%20generate%20random%20signals%0A%20%20%20%20rand_entries%20%3D%20vbt.pd_acc.signals.generate_random(%0A%20%20%20%20%20%20%20%20clean_entries.vbt.wrapper%2C%20%0A%20%20%20%20%20%20%20%20prob%3Dvbt.to_2d_pc_array(entry_probs.astype(float).values)%2C%20%0A%20%20%20%20%20%20%20%20seed%3D42%0A%20%20%20%20)%0A%20%20%20%20%23%20keep%20signals%20only%20at%20indices%20with%20valid%20data%0A%20%20%20%20rand_entries%5Bdata.close.isna()%5D%20%3D%20False%0A%20%20%20%20return%20(rand_entries%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20proj_length%20%3D%20mo.ui.number(value%3D45%2C%20start%3D5%2C%20stop%3D150%2C%20step%3D1%2C%20label%3D%22Projection%20length%20(bars)%22)%0A%20%20%20%20return%20(proj_length%2C)%0A%0A%0A%40app.cell%0Adef%20_(clean_entries%2C%20clean_exits%2C%20data%2C%20proj_length%2C%20rand_entries)%3A%0A%20%20%20%20entry_ranges%20%3D%20clean_entries.vbt.signals.delta_ranges(proj_length.value%2C%20close%3Ddata.close)%0A%20%20%20%20entry_ranges%20%3D%20entry_ranges.status_closed%0A%20%20%20%20rand_ranges%20%3D%20rand_entries.vbt.signals.delta_ranges(proj_length.value%2C%20close%3Ddata.close)%0A%20%20%20%20rand_ranges%20%3D%20rand_ranges.status_closed%0A%20%20%20%20exit_ranges%20%3D%20clean_exits.vbt.signals.delta_ranges(proj_length.value%2C%20close%3Ddata.close)%0A%20%20%20%20exit_ranges%20%3D%20exit_ranges.status_closed%0A%0A%20%20%20%20entry_projections%20%3D%20entry_ranges.get_projections()%0A%20%20%20%20rand_projections%20%3D%20rand_ranges.get_projections()%0A%20%20%20%20exit_projections%20%3D%20exit_ranges.get_projections()%0A%20%20%20%20return%20entry_projections%2C%20exit_projections%2C%20rand_projections%0A%0A%0A%40app.cell%0Adef%20_(entry_projections%2C%20exit_projections%2C%20rand_projections)%3A%0A%20%20%20%20proj_bands_fig%20%3D%20entry_projections.vbt.plot_projections(%0A%20%20%20%20%20%20%20%20plot_projections%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20plot_aux_middle%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20plot_fill%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20lower_trace_kwargs%3Ddict(name%3D%22Entry%22%2C%20legendgroup%3D%22entry%22%2C%20showlegend%3DTrue%2C%20line_color%3D%22%2300cc96%22)%2C%20%0A%20%20%20%20%20%20%20%20middle_trace_kwargs%3Ddict(legendgroup%3D%22entry%22%2C%20showlegend%3DFalse%2C%20line_color%3D%22%2300cc96%22)%2C%20%0A%20%20%20%20%20%20%20%20upper_trace_kwargs%3Ddict(legendgroup%3D%22entry%22%2C%20showlegend%3DFalse%2C%20line_color%3D%22%2300cc96%22)%0A%20%20%20%20)%0A%20%20%20%20proj_bands_fig%20%3D%20rand_projections.vbt.plot_projections(%0A%20%20%20%20%20%20%20%20plot_projections%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20plot_aux_middle%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20plot_fill%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20lower_trace_kwargs%3Ddict(name%3D%22Random%22%2C%20legendgroup%3D%22rand%22%2C%20showlegend%3DTrue%2C%20line_color%3D%22%23636efa%22)%2C%20%0A%20%20%20%20%20%20%20%20middle_trace_kwargs%3Ddict(legendgroup%3D%22rand%22%2C%20showlegend%3DFalse%2C%20line_color%3D%22%23636efa%22)%2C%20%0A%20%20%20%20%20%20%20%20upper_trace_kwargs%3Ddict(legendgroup%3D%22rand%22%2C%20showlegend%3DFalse%2C%20line_color%3D%22%23636efa%22)%2C%20%0A%20%20%20%20%20%20%20%20fig%3Dproj_bands_fig%0A%20%20%20%20)%0A%20%20%20%20proj_bands_fig%20%3D%20exit_projections.vbt.plot_projections(%0A%20%20%20%20%20%20%20%20plot_projections%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20plot_aux_middle%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20plot_fill%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20lower_trace_kwargs%3Ddict(name%3D%22Exit%22%2C%20legendgroup%3D%22exit%22%2C%20showlegend%3DTrue%2C%20line_color%3D%22%23ef553b%22)%2C%20%0A%20%20%20%20%20%20%20%20middle_trace_kwargs%3Ddict(legendgroup%3D%22exit%22%2C%20showlegend%3DFalse%2C%20line_color%3D%22%23ef553b%22)%2C%20%0A%20%20%20%20%20%20%20%20upper_trace_kwargs%3Ddict(legendgroup%3D%22exit%22%2C%20showlegend%3DFalse%2C%20line_color%3D%22%23ef553b%22)%2C%20%0A%20%20%20%20%20%20%20%20fig%3Dproj_bands_fig%0A%20%20%20%20)%0A%20%20%20%20proj_bands_fig.update_layout(%0A%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%20%0A%20%20%20%20%20%20%20%20width%3DNone%2C%20%0A%20%20%20%20%20%20%20%20autosize%3DTrue%2C%20%0A%20%20%20%20%20%20%20%20legend%3Ddict(%0A%20%20%20%20%20%20%20%20%20%20%20%20yanchor%3D%22bottom%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3D0.99%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xanchor%3D%22left%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D0.01%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20None%0A%20%20%20%20return%20(proj_bands_fig%2C)%0A%0A%0A%40app.cell%0Adef%20_(entry_projections%2C%20exit_projections%2C%20pd%2C%20rand_projections)%3A%0A%20%20%20%20final_stats_df%20%3D%20pd.DataFrame(columns%3D%5B%22entry%22%2C%20%22random%22%2C%20%22exit%22%5D)%0A%20%20%20%20final_stats_df%5B%22entry%22%5D%20%3D%20entry_projections.iloc%5B-1%5D.describe()%0A%20%20%20%20final_stats_df%5B%22random%22%5D%20%3D%20rand_projections.iloc%5B-1%5D.describe()%0A%20%20%20%20final_stats_df%5B%22exit%22%5D%20%3D%20exit_projections.iloc%5B-1%5D.describe()%0A%20%20%20%20return%20(final_stats_df%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20final_stats_df%2C%0A%20%20%20%20inner_border%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20outer_border%2C%0A%20%20%20%20proj_bands_fig%2C%0A%20%20%20%20proj_length%2C%0A)%3A%0A%20%20%20%20_descr%20%3D%20mo.md(%22%22%22%0A%20%20%20%20Fixed-window%20price%20projections%20starting%20from%20entry%2C%20exit%2C%20and%20random%20signals.%20%0A%20%20%20%20Bands%20show%20lower%2Fmiddle%2Fupper%20quantiles%20of%20normalized%20price%20paths%20over%20the%20next%20%0A%20%20%20%20N%20bars.%20A%20signal%20with%20edge%20should%20diverge%20visibly%20from%20the%20random%20baseline.%0A%20%20%20%20%22%22%22)%0A%0A%20%20%20%20proj_bands_block%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20_descr.center().style(%7B**inner_border%7D)%2C%0A%20%20%20%20%20%20%20%20proj_length.center().style(%7B**inner_border%7D)%2C%20%0A%20%20%20%20%20%20%20%20mo.ui.plotly(proj_bands_fig).style(%7B**inner_border%7D)%2C%20%0A%20%20%20%20%20%20%20%20mo.ui.table(final_stats_df%2C%20selection%3DNone).style(%7B**inner_border%7D)%0A%20%20%20%20%5D).style(%7B**outer_border%7D)%0A%20%20%20%20return%20(proj_bands_block%2C)%0A%0A%0A%40app.cell%0Adef%20_(proj_bands_block)%3A%0A%20%20%20%20proj_bands_block%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Shrink%20vs%20Stretch%20projections%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(clean_entries%2C%20clean_exits%2C%20data)%3A%0A%20%20%20%20%23%20trade%20ranges%3A%20entry-%3Eexit%20and%20exit-%3Eentry%0A%20%20%20%20entry_exit_ranges%20%3D%20clean_entries.vbt.signals.between_ranges(clean_exits%2C%20close%3Ddata.close).status_closed%0A%20%20%20%20exit_entry_ranges%20%3D%20clean_exits.vbt.signals.between_ranges(clean_entries%2C%20close%3Ddata.close).status_closed%0A%0A%20%20%20%20%23%20trade%20projections%0A%20%20%20%20entry_exit_projections%20%3D%20entry_exit_ranges.get_projections()%0A%20%20%20%20exit_entry_projections%20%3D%20exit_entry_ranges.get_projections()%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20entry_exit_projections%2C%0A%20%20%20%20%20%20%20%20entry_exit_ranges%2C%0A%20%20%20%20%20%20%20%20exit_entry_projections%2C%0A%20%20%20%20%20%20%20%20exit_entry_ranges%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(inner_border%2C%20mo)%3A%0A%20%20%20%20proj_period%20%3D%20mo.ui.number(value%3D7%2C%20start%3D5%2C%20stop%3D200%2C%20step%3D1%2C%20label%3D%22proj_period%22)%0A%20%20%20%20lower_qq%20%3D%20mo.ui.number(value%3D0.2%2C%20start%3D0.01%2C%20stop%3D0.5%2C%20step%3D0.01%2C%20label%3D%22lower_qq%22)%0A%20%20%20%20upper_qq%20%3D%20mo.ui.number(value%3D0.8%2C%20start%3D0.5%2C%20stop%3D0.99%2C%20step%3D0.01%2C%20label%3D%22upper_qq%22)%0A%20%20%20%20controls%20%3D%20mo.hstack(%5Bproj_period%2C%20lower_qq%2C%20upper_qq%5D).style(%7B**inner_border%7D)%0A%20%20%20%20return%20controls%2C%20lower_qq%2C%20proj_period%2C%20upper_qq%0A%0A%0A%40app.cell%0Adef%20_(entry_exit_ranges%2C%20exit_entry_ranges%2C%20proj_period)%3A%0A%20%20%20%20shrink_entry_proj%20%3D%20entry_exit_ranges.get_projections(proj_period%3Dproj_period.value)%0A%20%20%20%20stretch_entry_proj%20%3D%20entry_exit_ranges.get_projections(proj_period%3Dproj_period.value%2C%20extend%3DTrue)%0A%0A%20%20%20%20shrink_exit_proj%20%3D%20exit_entry_ranges.get_projections(proj_period%3Dproj_period.value)%0A%20%20%20%20stretch_exit_proj%20%3D%20exit_entry_ranges.get_projections(proj_period%3Dproj_period.value%2C%20extend%3DTrue)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20shrink_entry_proj%2C%0A%20%20%20%20%20%20%20%20shrink_exit_proj%2C%0A%20%20%20%20%20%20%20%20stretch_entry_proj%2C%0A%20%20%20%20%20%20%20%20stretch_exit_proj%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20vbt)%3A%0A%20%20%20%20def%20shrink_stretch_plot(shrink_proj%2C%20stretch_proj%2C%20lower_qq%2C%20upper_qq)%3A%0A%20%20%20%20%20%20%20%20fig%20%3D%20vbt.make_subplots(rows%3D1%2C%20cols%3D2%2C%20shared_yaxes%3DTrue%2C%20subplot_titles%3D%5B%22Shrink%22%2C%20%22Stretch%22%5D)%0A%20%20%20%20%20%20%20%20shrink_proj.vbt.plot_projections(%0A%20%20%20%20%20%20%20%20%20%20%20%20plot_lower%3Df%22Q%3D%7Blower_qq%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20plot_upper%3Df%22Q%3D%7Bupper_qq%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20plot_projections%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20add_trace_kwargs%3Ddict(row%3D1%2C%20col%3D1)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20fig%3Dfig%2C%20%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20stretch_proj.vbt.plot_projections(%0A%20%20%20%20%20%20%20%20%20%20%20%20plot_lower%3Df%22Q%3D%7Blower_qq%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20plot_upper%3Df%22Q%3D%7Bupper_qq%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20plot_projections%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20add_trace_kwargs%3Ddict(row%3D1%2C%20col%3D2)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20fig%3Dfig%2C%20%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20fig.update_layout(%0A%20%20%20%20%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3DNone%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20autosize%3DTrue%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20showlegend%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20yaxis_showticklabels%3DTrue%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20yaxis2_showticklabels%3DTrue%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20return%20mo.ui.plotly(fig)%0A%0A%20%20%20%20return%20(shrink_stretch_plot%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20inner_border%2C%0A%20%20%20%20lower_qq%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20shrink_entry_proj%2C%0A%20%20%20%20shrink_exit_proj%2C%0A%20%20%20%20shrink_stretch_plot%2C%0A%20%20%20%20stretch_entry_proj%2C%0A%20%20%20%20stretch_exit_proj%2C%0A%20%20%20%20upper_qq%2C%0A)%3A%0A%20%20%20%20tabs%20%3D%20mo.ui.tabs(%7B%0A%20%20%20%20%20%20%20%20%22entry%22%3A%20shrink_stretch_plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20shrink_entry_proj%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20stretch_entry_proj%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20lower_qq%3Dlower_qq.value%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20upper_qq%3Dupper_qq.value%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22exit%22%3A%20shrink_stretch_plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20shrink_exit_proj%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20stretch_exit_proj%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20lower_qq%3Dlower_qq.value%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20upper_qq%3Dupper_qq.value%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%7D).style(%7B**inner_border%7D)%0A%20%20%20%20return%20(tabs%2C)%0A%0A%0A%40app.cell%0Adef%20_(controls%2C%20inner_border%2C%20mo%2C%20outer_border%2C%20tabs)%3A%0A%20%20%20%20_descr%20%3D%20mo.md(%22%22%22%0A%20%20%20%20Price%20projections%20between%20consecutive%20entry%E2%86%92exit%20and%20exit%E2%86%92entry%20signals.%20%0A%20%20%20%20Shrink%20trims%20longer%20projections%20to%20proj_period%20bars.%20Stretch%20extends%20shorter%20%0A%20%20%20%20ones%20beyond%20the%20opposite%20signal%20%E2%80%94%20if%20the%20edge%20disappears%20after%20stretching%2C%20%0A%20%20%20%20exit%20timing%20is%20the%20source%20of%20alpha%2C%20not%20entry%20alone.%0A%20%20%20%20%22%22%22)%0A%0A%20%20%20%20shrink_stretch_block%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20_descr.center().style(%7B**inner_border%7D)%2C%0A%20%20%20%20%20%20%20%20controls%2C%20%0A%20%20%20%20%20%20%20%20tabs%0A%20%20%20%20%5D).style(%7B**outer_border%7D)%0A%20%20%20%20return%20(shrink_stretch_block%2C)%0A%0A%0A%40app.cell%0Adef%20_(shrink_stretch_block)%3A%0A%20%20%20%20shrink_stretch_block%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Individual%20Projections%20Sample%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20rand_btn%20%3D%20mo.ui.button(label%3D%22refresh%22)%0A%20%20%20%20return%20(rand_btn%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20np)%3A%0A%20%20%20%20def%20plot_rand_proj(proj%2C%20n%3D20)%3A%0A%20%20%20%20%20%20%20%20rand_cols%20%3D%20np.random.choice(proj.shape%5B1%5D%2C%20n%2C%20replace%3DFalse)%0A%20%20%20%20%20%20%20%20fig%20%3D%20proj.iloc%5B%3A%2C%20rand_cols%5D.vbt.plot_projections(plot_bands%3DFalse)%0A%20%20%20%20%20%20%20%20fig.update_layout(%0A%20%20%20%20%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3DNone%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20autosize%3DTrue%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20showlegend%3DFalse%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20return%20mo.ui.plotly(fig)%0A%0A%20%20%20%20return%20(plot_rand_proj%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20entry_exit_projections%2C%0A%20%20%20%20exit_entry_projections%2C%0A%20%20%20%20inner_border%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20outer_border%2C%0A%20%20%20%20plot_rand_proj%2C%0A%20%20%20%20rand_btn%2C%0A)%3A%0A%20%20%20%20rand_btn%0A%20%20%20%20_fig_en%20%3D%20plot_rand_proj(entry_exit_projections)%0A%20%20%20%20_fig_ex%20%3D%20plot_rand_proj(exit_entry_projections)%0A%0A%20%20%20%20_plots%20%3D%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20mo.vstack(%5Bmo.md(%22%23%23%23%23%20Entry%20%E2%86%92%20Exit%22).center()%2C%20_fig_en%5D).style(%7B**inner_border%7D)%2C%0A%20%20%20%20%20%20%20%20mo.vstack(%5Bmo.md(%22%23%23%23%23%20Exit%20%E2%86%92%20Entry%22).center()%2C%20_fig_ex%5D).style(%7B**inner_border%7D)%2C%0A%20%20%20%20%5D%2C%20widths%3D%22equal%22)%0A%0A%20%20%20%20_descr%20%3D%20mo.md(%0A%20%20%20%20%22%22%22%0A%20%20%20%20Random%20sample%20of%2020%20individual%20projections.%20Each%20line%20is%20one%20price%20path%2C%20normalized%20to%20start%20at%201.%0A%20%20%20%20Click%20refresh%20to%20resample%20%E2%80%94%20consistent%20patterns%20across%20samples%20indicate%20a%20robust%20signal.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%0A%20%20%20%20rand_block%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.center(_descr).style(%7B**inner_border%7D)%2C%20%0A%20%20%20%20%20%20%20%20rand_btn.center().style(%7B**inner_border%7D)%2C%20%0A%20%20%20%20%20%20%20%20_plots%0A%20%20%20%20%5D).style(%7B**outer_border%7D)%0A%20%20%20%20return%20(rand_block%2C)%0A%0A%0A%40app.cell%0Adef%20_(rand_block)%3A%0A%20%20%20%20rand_block%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Portfolio%20Simulation%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(clean_entries%2C%20clean_exits%2C%20data%2C%20vbt)%3A%0A%20%20%20%20pf_en%20%3D%20vbt.Portfolio.from_signals(%0A%20%20%20%20%20%20%20%20data.close%2C%20%0A%20%20%20%20%20%20%20%20entries%3Dclean_entries%2C%20%0A%20%20%20%20%20%20%20%20exits%3Dclean_exits%0A%20%20%20%20)%0A%0A%20%20%20%20pf_ex%20%3D%20vbt.Portfolio.from_signals(%0A%20%20%20%20%20%20%20%20data.close%2C%20%0A%20%20%20%20%20%20%20%20entries%3Dclean_exits%2C%20%0A%20%20%20%20%20%20%20%20exits%3Dclean_entries%0A%20%20%20%20)%0A%20%20%20%20return%20pf_en%2C%20pf_ex%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20outer_border%2C%20pd%2C%20pf_en%2C%20pf_ex)%3A%0A%20%20%20%20pf_stats_mean%20%3D%20pd.DataFrame(columns%3D%5B%22entry_exit%22%2C%20%22exit_entry%22%5D)%0A%20%20%20%20pf_stats_mean%5B%22entry_exit%22%5D%20%3D%20pf_en.stats()%0A%20%20%20%20pf_stats_mean%5B%22exit_entry%22%5D%20%3D%20pf_ex.stats()%0A%0A%20%20%20%20pf_stats_median%20%3D%20pd.DataFrame(columns%3D%5B%22entry_exit%22%2C%20%22exit_entry%22%5D)%0A%20%20%20%20pf_stats_median%5B%22entry_exit%22%5D%20%3D%20pf_en.stats(agg_func%3DNone).median()%0A%20%20%20%20pf_stats_median%5B%22exit_entry%22%5D%20%3D%20pf_ex.stats(agg_func%3DNone).median()%0A%0A%20%20%20%20mo.ui.tabs(%7B%0A%20%20%20%20%20%20%20%20%22mean%22%3A%20mo.ui.table(pf_stats_mean%2C%20pagination%3DFalse%2C%20selection%3DNone)%2C%0A%20%20%20%20%20%20%20%20%22median%22%3A%20mo.ui.table(pf_stats_median%2C%20pagination%3DFalse%2C%20selection%3DNone)%0A%20%20%20%20%7D).style(**outer_border)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%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
adea9a0d4d68118340f9dfcae6dbb704