%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.11%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22marimo%3D%3D0.20.4%22%2C%0A%23%20%20%20%20%20%22jquantstats%22%2C%0A%23%20%20%20%20%20%22numpy%3E%3D2.0.0%22%2C%0A%23%20%20%20%20%20%22polars%3E%3D1.0.0%22%2C%0A%23%20%20%20%20%20%22plotly%3E%3D6.0.0%22%2C%0A%23%20%20%20%20%20%22jinja2%3E%3D3.1.0%22%2C%0A%23%20%5D%0A%23%20%5Btool.uv.sources%5D%0A%23%20jquantstats%20%3D%20%7B%20path%20%3D%20%22..%2F..%2F..%22%2C%20editable%20%3D%20true%20%7D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.20.4%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0Awith%20app.setup%3A%0A%20%20%20%20from%20datetime%20import%20date%2C%20timedelta%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%20%20%20%20import%20polars%20as%20pl%0A%0A%20%20%20%20from%20jquantstats%20import%20Portfolio%0A%0A%0A%40app.cell%0Adef%20cell_intro()%3A%0A%20%20%20%20%22%22%22Render%20the%20portfolio%20construction%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%20%F0%9F%8F%97%EF%B8%8F%20jquantstats%20%E2%80%94%20Portfolio%20Construction%20%26%20Attribution%0A%0A%20%20%20%20%20%20%20%20This%20notebook%20compares%20the%20**three%20Portfolio%20constructors**%20and%20then%20digs%20into%0A%20%20%20%20%20%20%20%20**tilt%2Ftiming%20attribution**%2C%20**turnover%20analysis**%2C%20and%20**cost%20sensitivity**.%0A%0A%20%20%20%20%20%20%20%20%7C%20Constructor%20%7C%20Inputs%20%7C%20Use%20case%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%20%60from_cash_position%60%20%7C%20Cash%20%24%20value%20per%20asset%20%7C%20Direct%20exposure%20control%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60from_position%60%20%7C%20Unit%20quantity%20per%20asset%20%7C%20Shares%20%2F%20contracts%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60from_risk_position%60%20%7C%20Risk%20units%20%2B%20EWMA%20vol%20%7C%20Vol-targeting%20strategies%20%7C%0A%0A%20%20%20%20%20%20%20%20**Data%3A**%20500-day%20synthetic%203-asset%20portfolio%20(assets%20A%2C%20B%2C%20C).%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_data()%3A%0A%20%20%20%20%22%22%22Generate%20synthetic%20price%20and%20position%20data%20for%20three%20assets.%22%22%22%0A%20%20%20%20n%20%3D%20500%0A%20%20%20%20start%20%3D%20date(2021%2C%201%2C%204)%0A%20%20%20%20dates%20%3D%20pl.date_range(start%3Dstart%2C%20end%3Dstart%20%2B%20timedelta(days%3Dn%20-%201)%2C%20interval%3D%221d%22%2C%20eager%3DTrue).cast(pl.Date)%0A%0A%20%20%20%20rng%20%3D%20np.random.default_rng(7)%0A%0A%20%20%20%20%23%20Asset%20A%3A%20uptrend%20%2B%20moderate%20noise%0A%20%20%20%20price_a%20%3D%20pl.Series(%0A%20%20%20%20%20%20%20%20%5B100.0%20*%20np.exp(0.0004%20*%20i%20%2B%200.012%20*%20rng.standard_normal())%20for%20i%20in%20range(n)%5D%2C%0A%20%20%20%20%20%20%20%20dtype%3Dpl.Float64%2C%0A%20%20%20%20)%0A%20%20%20%20%23%20Asset%20B%3A%20mean-reverting%0A%20%20%20%20price_b%20%3D%20pl.Series(%0A%20%20%20%20%20%20%20%20%5B50.0%20%2B%208.0%20*%20np.sin(0.04%20*%20i)%20%2B%201.5%20*%20rng.standard_normal()%20for%20i%20in%20range(n)%5D%2C%0A%20%20%20%20%20%20%20%20dtype%3Dpl.Float64%2C%0A%20%20%20%20)%0A%20%20%20%20%23%20Asset%20C%3A%20high-volatility%20uptrend%0A%20%20%20%20price_c%20%3D%20pl.Series(%0A%20%20%20%20%20%20%20%20%5B200.0%20*%20np.exp(0.0002%20*%20i%20%2B%200.025%20*%20rng.standard_normal())%20for%20i%20in%20range(n)%5D%2C%0A%20%20%20%20%20%20%20%20dtype%3Dpl.Float64%2C%0A%20%20%20%20)%0A%0A%20%20%20%20prices%20%3D%20pl.DataFrame(%7B%22date%22%3A%20dates%2C%20%22A%22%3A%20price_a%2C%20%22B%22%3A%20price_b%2C%20%22C%22%3A%20price_c%7D)%0A%0A%20%20%20%20%23%20Cash%20positions%3A%20time-varying%20exposure%0A%20%20%20%20cash_a%20%3D%20pl.Series(%5B10_000.0%20%2B%2020.0%20*%20i%20for%20i%20in%20range(n)%5D%2C%20dtype%3Dpl.Float64)%0A%20%20%20%20cash_b%20%3D%20pl.Series(%5B8_000.0%20%2B%20float(i%20%25%2010)%20*%20500.0%20for%20i%20in%20range(n)%5D%2C%20dtype%3Dpl.Float64)%0A%20%20%20%20cash_c%20%3D%20pl.Series(%5B5_000.0%20-%20float(i%20%25%2025)%20*%20100.0%20for%20i%20in%20range(n)%5D%2C%20dtype%3Dpl.Float64)%0A%20%20%20%20cash_position%20%3D%20pl.DataFrame(%7B%22date%22%3A%20dates%2C%20%22A%22%3A%20cash_a%2C%20%22B%22%3A%20cash_b%2C%20%22C%22%3A%20cash_c%7D)%0A%0A%20%20%20%20%23%20Unit%20positions%20(shares)%3A%20derived%20from%20cash%20%2F%20price%0A%20%20%20%20unit_a%20%3D%20cash_a%20%2F%20price_a%0A%20%20%20%20unit_b%20%3D%20cash_b%20%2F%20price_b%0A%20%20%20%20unit_c%20%3D%20cash_c%20%2F%20price_c%0A%20%20%20%20unit_position%20%3D%20pl.DataFrame(%7B%22date%22%3A%20dates%2C%20%22A%22%3A%20unit_a%2C%20%22B%22%3A%20unit_b%2C%20%22C%22%3A%20unit_c%7D)%0A%0A%20%20%20%20%23%20Risk%20positions%3A%20same%20%24%20units%20but%20constructor%20will%20de-vol%20them%0A%20%20%20%20risk_position%20%3D%20cash_position.clone()%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20**Synthetic%20data%20generated**%20%E2%9C%85%0A%0A%20%20%20%20%20%20%20%20-%20Assets%3A%20A%2C%20B%2C%20C%0A%20%20%20%20%20%20%20%20-%20Trading%20days%3A%20%7Bn%7D%0A%20%20%20%20%20%20%20%20-%20Date%20range%3A%20%60%7Bstart%7D%60%20%E2%86%92%20%60%7B(start%20%2B%20timedelta(days%3Dn%20-%201))%7D%60%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20cash_position%2C%20prices%2C%20risk_position%2C%20unit_position%0A%0A%0A%40app.cell%0Adef%20cell_constructors_header()%3A%0A%20%20%20%20%22%22%22Constructors%20section%20header.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20---%0A%20%20%20%20%20%20%20%20%23%23%201%20%C2%B7%20Three%20constructors%20%E2%80%94%20side-by-side%20comparison%0A%0A%20%20%20%20%20%20%20%20We%20build%20the%20same%20strategy%20three%20ways%20and%20compare%20the%20resulting%20NAV%20curves.%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_build_portfolios(cash_position%2C%20prices%2C%20risk_position%2C%20unit_position)%3A%0A%20%20%20%20%22%22%22Build%20three%20portfolios%20using%20different%20constructors.%22%22%22%0A%20%20%20%20pf_cash%20%3D%20Portfolio.from_cash_position(%0A%20%20%20%20%20%20%20%20prices%3Dprices%2C%0A%20%20%20%20%20%20%20%20cash_position%3Dcash_position%2C%0A%20%20%20%20%20%20%20%20aum%3D1_000_000.0%2C%0A%20%20%20%20%20%20%20%20cost_bps%3D2.0%2C%0A%20%20%20%20)%0A%20%20%20%20pf_unit%20%3D%20Portfolio.from_position(%0A%20%20%20%20%20%20%20%20prices%3Dprices%2C%0A%20%20%20%20%20%20%20%20position%3Dunit_position%2C%0A%20%20%20%20%20%20%20%20aum%3D1_000_000.0%2C%0A%20%20%20%20%20%20%20%20cost_bps%3D2.0%2C%0A%20%20%20%20)%0A%20%20%20%20pf_risk%20%3D%20Portfolio.from_risk_position(%0A%20%20%20%20%20%20%20%20prices%3Dprices%2C%0A%20%20%20%20%20%20%20%20risk_position%3Drisk_position%2C%0A%20%20%20%20%20%20%20%20aum%3D1_000_000.0%2C%0A%20%20%20%20%20%20%20%20vola%3D32%2C%0A%20%20%20%20%20%20%20%20cost_bps%3D2.0%2C%0A%20%20%20%20)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%7C%20Constructor%20%7C%20Description%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%60from_cash_position%60%20%7C%20Raw%20%24%20exposure%20%E2%80%94%20NAV%20driven%20directly%20by%20price%20P%26L%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60from_position%60%20%7C%20Unit%20positions%20%C3%97%20price%20%E2%80%94%20equivalent%20when%20initialised%20from%20cash%2Fprice%20%7C%0A%20%20%20%20%20%20%20%20%7C%20%60from_risk_position%60%20%7C%20Risk%20units%20%C3%B7%20EWMA%20vol%20%E2%80%94%20position%20sizes%20shrink%20in%20volatile%20regimes%20%7C%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20pf_cash%2C%20pf_risk%2C%20pf_unit%0A%0A%0A%40app.cell%0Adef%20cell_nav_comparison(pf_cash%2C%20pf_risk%2C%20pf_unit)%3A%0A%20%20%20%20%22%22%22Plot%20NAV%20curves%20for%20all%20three%20portfolios.%22%22%22%0A%20%20%20%20fig%20%3D%20go.Figure()%0A%0A%20%20%20%20for%20_label%2C%20_pf%20in%20%5B%0A%20%20%20%20%20%20%20%20(%22from_cash_position%22%2C%20pf_cash)%2C%0A%20%20%20%20%20%20%20%20(%22from_position%22%2C%20pf_unit)%2C%0A%20%20%20%20%20%20%20%20(%22from_risk_position%22%2C%20pf_risk)%2C%0A%20%20%20%20%5D%3A%0A%20%20%20%20%20%20%20%20_nav%20%3D%20_pf.nav_accumulated%0A%20%20%20%20%20%20%20%20_date_col%20%3D%20_nav.columns%5B0%5D%0A%20%20%20%20%20%20%20%20_nav_col%20%3D%20%22NAV_accumulated%22%0A%20%20%20%20%20%20%20%20fig.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%3D_nav%5B_date_col%5D.to_list()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3D_nav%5B_nav_col%5D.to_list()%2C%0A%20%20%20%20%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%20%20%20%20%20name%3D_label%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20fig.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22NAV%20Comparison%20%E2%80%94%20Three%20Portfolio%20Constructors%22%2C%0A%20%20%20%20%20%20%20%20xaxis_title%3D%22Date%22%2C%0A%20%20%20%20%20%20%20%20yaxis_title%3D%22Cumulative%20NAV%20(%24)%22%2C%0A%20%20%20%20%20%20%20%20height%3D440%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(fig%2C)%0A%0A%0A%40app.cell%0Adef%20cell_nav_comparison_show(fig)%3A%0A%20%20%20%20%22%22%22Display%20NAV%20comparison%20chart.%22%22%22%0A%20%20%20%20fig%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_constructor_stats(pf_cash%2C%20pf_risk%2C%20pf_unit)%3A%0A%20%20%20%20%22%22%22Compare%20Sharpe%20and%20max%20drawdown%20across%20the%20three%20portfolios.%22%22%22%0A%20%20%20%20_rows%20%3D%20%5B%5D%0A%20%20%20%20for%20_label%2C%20_pf%20in%20%5B%0A%20%20%20%20%20%20%20%20(%22from_cash_position%22%2C%20pf_cash)%2C%0A%20%20%20%20%20%20%20%20(%22from_position%22%2C%20pf_unit)%2C%0A%20%20%20%20%20%20%20%20(%22from_risk_position%22%2C%20pf_risk)%2C%0A%20%20%20%20%5D%3A%0A%20%20%20%20%20%20%20%20_sharpe%20%3D%20_pf.stats.sharpe()%0A%20%20%20%20%20%20%20%20_max_dd%20%3D%20_pf.stats.max_drawdown()%0A%20%20%20%20%20%20%20%20_cagr%20%3D%20_pf.stats.cagr()%0A%20%20%20%20%20%20%20%20_rows.append(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Constructor%22%3A%20_label%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Sharpe%22%3A%20round(next(iter(_sharpe.values()))%2C%203)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Max%20Drawdown%22%3A%20round(next(iter(_max_dd.values()))%2C%204)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22CAGR%22%3A%20round(next(iter(_cagr.values()))%2C%204)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20mo.plain_text(str(pl.DataFrame(_rows)))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_attribution_header()%3A%0A%20%20%20%20%22%22%22Attribution%20section%20header.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20---%0A%20%20%20%20%20%20%20%20%23%23%202%20%C2%B7%20Tilt%20%2F%20timing%20attribution%0A%0A%20%20%20%20%20%20%20%20Brinson-style%20attribution%20decomposes%20NAV%20into%20two%20components%3A%0A%0A%20%20%20%20%20%20%20%20-%20**Tilt**%20%E2%80%94%20NAV%20from%20the%20*time-average*%20position%20(what%20you%20would%20earn%0A%20%20%20%20%20%20%20%20%20%20holding%20constant%20weights%20throughout%20the%20period)%0A%20%20%20%20%20%20%20%20-%20**Timing**%20%E2%80%94%20NAV%20from%20position%20*deviations*%20around%20the%20average%0A%20%20%20%20%20%20%20%20%20%20(the%20return%20from%20varying%20weights%20over%20time)%0A%0A%20%20%20%20%20%20%20%20A%20timing%20contribution%20near%20zero%20means%20the%20variation%20in%20weights%20added%0A%20%20%20%20%20%20%20%20little%20value%20beyond%20a%20simple%20buy-and-hold.%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_attribution(pf_cash)%3A%0A%20%20%20%20%22%22%22Compute%20tilt%2Ftiming%20decomposition.%22%22%22%0A%20%20%20%20decomp%20%3D%20pf_cash.tilt_timing_decomp%0A%20%20%20%20return%20(decomp%2C)%0A%0A%0A%40app.cell%0Adef%20cell_attribution_chart(decomp)%3A%0A%20%20%20%20%22%22%22Plot%20tilt%2Ftiming%20NAV%20decomposition.%22%22%22%0A%20%20%20%20_fig%20%3D%20go.Figure()%0A%20%20%20%20_date_col%20%3D%20decomp.columns%5B0%5D%0A%0A%20%20%20%20for%20_col%20in%20%5B%22portfolio%22%2C%20%22tilt%22%2C%20%22timing%22%5D%3A%0A%20%20%20%20%20%20%20%20if%20_col%20in%20decomp.columns%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_fig.add_trace(%0A%20%20%20%20%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%20%20%20%20%20x%3Ddecomp%5B_date_col%5D.to_list()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3Ddecomp%5B_col%5D.to_list()%2C%0A%20%20%20%20%20%20%20%20%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%20%20%20%20%20%20%20%20%20name%3D_col.capitalize()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20_fig.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Tilt%20%2F%20Timing%20Attribution%20%E2%80%94%20Cumulative%20NAV%22%2C%0A%20%20%20%20%20%20%20%20xaxis_title%3D%22Date%22%2C%0A%20%20%20%20%20%20%20%20yaxis_title%3D%22Cumulative%20NAV%20(%24)%22%2C%0A%20%20%20%20%20%20%20%20height%3D440%2C%0A%20%20%20%20)%0A%20%20%20%20_fig%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_turnover_header()%3A%0A%20%20%20%20%22%22%22Turnover%20section%20header.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20---%0A%20%20%20%20%20%20%20%20%23%23%203%20%C2%B7%20Turnover%20analysis%0A%0A%20%20%20%20%20%20%20%20Turnover%20measures%20how%20actively%20positions%20change.%20%20High%20turnover%20magnifies%0A%20%20%20%20%20%20%20%20the%20impact%20of%20trading%20costs%20on%20realised%20returns.%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_turnover(pf_cash)%3A%0A%20%20%20%20%22%22%22Compute%20turnover%20statistics.%22%22%22%0A%20%20%20%20to_summary%20%3D%20pf_cash.turnover_summary()%0A%20%20%20%20to_daily%20%3D%20pf_cash.turnover%0A%20%20%20%20return%20to_daily%2C%20to_summary%0A%0A%0A%40app.cell%0Adef%20cell_turnover_summary(to_summary)%3A%0A%20%20%20%20%22%22%22Display%20turnover%20summary.%22%22%22%0A%20%20%20%20mo.md(%22%23%23%23%20Turnover%20summary%20statistics%22)%0A%20%20%20%20mo.plain_text(str(to_summary))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_turnover_chart(to_daily)%3A%0A%20%20%20%20%22%22%22Plot%20daily%20turnover.%22%22%22%0A%20%20%20%20_date_col%20%3D%20to_daily.columns%5B0%5D%0A%20%20%20%20_fig%20%3D%20go.Figure()%0A%20%20%20%20for%20_col%20in%20to_daily.columns%5B1%3A%5D%3A%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%3Dto_daily%5B_date_col%5D.to_list()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20y%3Dto_daily%5B_col%5D.to_list()%2C%0A%20%20%20%20%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%20%20%20%20%20name%3D_col%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20opacity%3D0.7%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20_fig.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Daily%20Turnover%20per%20Asset%22%2C%0A%20%20%20%20%20%20%20%20xaxis_title%3D%22Date%22%2C%0A%20%20%20%20%20%20%20%20yaxis_title%3D%22Turnover%20(%24%20change%20in%20position)%22%2C%0A%20%20%20%20%20%20%20%20height%3D400%2C%0A%20%20%20%20)%0A%20%20%20%20_fig%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_cost_header()%3A%0A%20%20%20%20%22%22%22Cost%20sensitivity%20section%20header.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20---%0A%20%20%20%20%20%20%20%20%23%23%204%20%C2%B7%20Trading%20cost%20sensitivity%0A%0A%20%20%20%20%20%20%20%20The%20%60trading_cost_impact_plot%60%20sweeps%20one-way%20trading%20costs%20from%200%20to%0A%20%20%20%20%20%20%20%20%60max_bps%60%20and%20shows%20how%20the%20Sharpe%20ratio%20degrades.%20%20This%20helps%20identify%0A%20%20%20%20%20%20%20%20the%20**break-even%20cost**%20%E2%80%94%20the%20maximum%20fee%20the%20strategy%20can%20absorb.%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_cost_chart(pf_cash)%3A%0A%20%20%20%20%22%22%22Plot%20trading%20cost%20impact.%22%22%22%0A%20%20%20%20fig_cost%20%3D%20pf_cash.plots.trading_cost_impact_plot(max_bps%3D30)%0A%20%20%20%20return%20(fig_cost%2C)%0A%0A%0A%40app.cell%0Adef%20cell_cost_chart_show(fig_cost)%3A%0A%20%20%20%20%22%22%22Display%20cost%20impact%20chart.%22%22%22%0A%20%20%20%20fig_cost%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_smooth_header()%3A%0A%20%20%20%20%22%22%22Smoothed%20holdings%20section%20header.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20---%0A%20%20%20%20%20%20%20%20%23%23%205%20%C2%B7%20Smoothed%20holdings%20performance%0A%0A%20%20%20%20%20%20%20%20Position%20smoothing%20(rolling-mean%20entry)%20reduces%20turnover%20at%20the%20cost%20of%0A%20%20%20%20%20%20%20%20slower%20signal%20response.%20%20The%20plot%20compares%20NAV%20curves%20for%20different%0A%20%20%20%20%20%20%20%20smoothing%20windows%20(0%20%3D%20no%20smoothing%2C%205%20%3D%205-day%20average).%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_smooth_chart(pf_cash)%3A%0A%20%20%20%20%22%22%22Plot%20smoothed%20holdings%20performance.%22%22%22%0A%20%20%20%20fig_smooth%20%3D%20pf_cash.plots.smoothed_holdings_performance_plot(windows%3D%5B0%2C%201%2C%203%2C%205%2C%2010%5D)%0A%20%20%20%20return%20(fig_smooth%2C)%0A%0A%0A%40app.cell%0Adef%20cell_smooth_chart_show(fig_smooth)%3A%0A%20%20%20%20%22%22%22Display%20smoothed%20holdings%20figure.%22%22%22%0A%20%20%20%20fig_smooth%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_lag_header()%3A%0A%20%20%20%20%22%22%22Execution%20lag%20section%20header.%22%22%22%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20---%0A%20%20%20%20%20%20%20%20%23%23%206%20%C2%B7%20Execution%20lag%20analysis%0A%0A%20%20%20%20%20%20%20%20Signals%20often%20have%20a%20delay%20between%20generation%20and%20execution.%20%20The%0A%20%20%20%20%20%20%20%20lead%2Flag%20IR%20plot%20shows%20how%20much%20the%20Sharpe%20ratio%20degrades%20as%20we%20shift%0A%20%20%20%20%20%20%20%20positions%20by%201%E2%80%9310%20days.%20%20A%20steep%20drop%20indicates%20the%20strategy%20is%0A%20%20%20%20%20%20%20%20highly%20sensitive%20to%20execution%20timing.%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_lag_chart(pf_cash)%3A%0A%20%20%20%20%22%22%22Plot%20lead%2Flag%20information%20ratio.%22%22%22%0A%20%20%20%20fig_lag%20%3D%20pf_cash.plots.lead_lag_ir_plot(start%3D0%2C%20end%3D10)%0A%20%20%20%20return%20(fig_lag%2C)%0A%0A%0A%40app.cell%0Adef%20cell_lag_chart_show(fig_lag)%3A%0A%20%20%20%20%22%22%22Display%20lead%2Flag%20IR%20chart.%22%22%22%0A%20%20%20%20fig_lag%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20cell_lagged_perf(pf_cash)%3A%0A%20%20%20%20%22%22%22Plot%20NAV%20curves%20at%20multiple%20execution%20lags.%22%22%22%0A%20%20%20%20fig_lp%20%3D%20pf_cash.plots.lagged_performance_plot(lags%3D%5B0%2C%201%2C%202%2C%205%5D)%0A%20%20%20%20return%20(fig_lp%2C)%0A%0A%0A%40app.cell%0Adef%20cell_lagged_perf_show(fig_lp)%3A%0A%20%20%20%20%22%22%22Display%20lagged%20performance%20chart.%22%22%22%0A%20%20%20%20fig_lp%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
ef5368f07505d3c615853190b033c344