%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.12%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22marimo%3D%3D0.23.9%22%2C%0A%23%20%20%20%20%20%22numpy%3D%3D2.4.6%22%2C%0A%23%20%20%20%20%20%22plotly%3D%3D6.8.0%22%2C%0A%23%20%20%20%20%20%22polars%3D%3D1.41.2%22%2C%0A%23%20%20%20%20%20%22jquantstats%3D%3D0.9.6%22%2C%0A%23%20%20%20%20%20%22tinycta%3D%3D0.13.2%22%0A%23%20%5D%0A%23%20%2F%2F%2F%0A%0A%22%22%22Experiment%203%3A%20Advanced%20CTA%20strategy%20with%20price%20filtering%20and%20oscillators.%0A%0AThis%20module%20implements%20a%20more%20sophisticated%20trend-following%20strategy%20that%0Aincorporates%20price%20filtering%20to%20handle%20outliers%20and%20oscillators%20with%20proper%0Ascaling%20for%20more%20consistent%20signal%20generation%20across%20different%20assets.%0A%22%22%22%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.9%22%0Aapp%20%3D%20marimo.App()%0A%0Awith%20app.setup%3A%0A%20%20%20%20import%20sys%0A%20%20%20%20from%20pathlib%20import%20Path%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20from%20jquantstats%20import%20Portfolio%0A%20%20%20%20from%20tinycta.osc%20import%20osc%0A%20%20%20%20from%20tinycta.util%20import%20vol_adj%0A%0A%20%20%20%20sys.path.insert(0%2C%20str(Path(__file__).parent))%0A%0A%20%20%20%20from%20preamble%20import%20date_col%2C%20load_prices%0A%0A%20%20%20%20prices%20%3D%20load_prices(__file__)%0A%20%20%20%20prices_only%20%3D%20prices.drop(date_col)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20CTA%203.0%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20We%20use%20the%20system%3A%0A%20%20%20%20%24%24%5Cmathrm%7BCashPosition%7D%3D%5Cfrac%7Bf(%5Cmathrm%7BPrice%7D)%7D%7B%5Cmathrm%7BVolatility(Returns)%7D%7D%24%24%0A%0A%20%20%20%20This%20is%20very%20problematic%3A%0A%20%20%20%20*%20Prices%20may%20live%20on%20very%20different%20scales%2C%20hence%20trying%20to%20find%20a%0A%20%20%20%20more%20universal%20function%20%24f%24%20is%20almost%20impossible.%20The%20sign-function%20was%0A%20%20%20%20a%20good%20choice%20as%20the%20results%20don't%20depend%20on%20the%20scale%20of%20the%20argument.%0A%20%20%20%20*%20Price%20may%20come%20with%20all%20sorts%20of%20spikes%2Foutliers%2Fproblems.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20We%20need%20a%20simple%20price%20filter%20process%0A%20%20%20%20*%20We%20compute%20volatility-adjusted%20returns%2C%20filter%20them%20and%20compute%20prices%20from%20those%20returns.%0A%20%20%20%20*%20Don't%20call%20it%20Winsorizing%20in%20Switzerland.%20We%20apply%20Huber%20functions.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Oscillators%0A%20%20%20%20*%20All%20prices%20are%20now%20following%20a%20standard%20arithmetic%20Brownian%0A%20%20%20%20motion%20with%20std%20%241%24.%0A%20%20%20%20*%20What%20we%20want%20is%20the%20difference%20of%20two%20moving%20means%20(exponentially%20weighted)%0A%20%20%20%20to%20have%20a%20constant%20std%20regardless%20of%20the%20two%20lengths.%0A%20%20%20%20*%20An%20oscillator%20is%20the%20**scaled%20difference%20of%20two%20moving%20averages**.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.function%0Adef%20f(price%3A%20%22pl.Expr%22%2C%20slow%3D96%2C%20fast%3D32%2C%20vola%3D96%2C%20clip%3D3)%20-%3E%20%22pl.Expr%22%3A%0A%20%20%20%20%22%22%22Return%20the%20tanh%20oscillator%20of%20vol-adjusted%20cumulative%20price%2C%20divided%20by%20volatility.%22%22%22%0A%20%20%20%20price_adj%20%3D%20vol_adj(price%2C%20vola%3Dvola%2C%20clip%3Dclip%2C%20min_samples%3D300).cum_sum()%0A%20%20%20%20mu%20%3D%20osc(price_adj%2C%20fast%3Dfast%2C%20slow%3Dslow).tanh()%0A%20%20%20%20vol%20%3D%20price.pct_change().ewm_std(com%3Dslow%2C%20min_samples%3D300)%0A%20%20%20%20return%20mu%20%2F%20vol%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20fast%20%3D%20mo.ui.slider(4%2C%20192%2C%20step%3D4%2C%20value%3D32%2C%20label%3D%22Fast%20Moving%20Average%22)%0A%20%20%20%20slow%20%3D%20mo.ui.slider(4%2C%20192%2C%20step%3D4%2C%20value%3D96%2C%20label%3D%22Slow%20Moving%20Average%22)%0A%20%20%20%20vola%20%3D%20mo.ui.slider(4%2C%20192%2C%20step%3D4%2C%20value%3D32%2C%20label%3D%22Volatility%22)%0A%20%20%20%20winsor%20%3D%20mo.ui.slider(1.0%2C%206.0%2C%20step%3D0.1%2C%20value%3D4.2%2C%20label%3D%22Winsorizing%22)%0A%0A%20%20%20%20mo.vstack(%5Bfast%2C%20slow%2C%20vola%2C%20winsor%5D)%0A%20%20%20%20return%20fast%2C%20slow%2C%20vola%2C%20winsor%0A%0A%0A%40app.cell%0Adef%20_(fast%2C%20slow%2C%20vola%2C%20winsor)%3A%0A%20%20%20%20signals%20%3D%20prices_only.select(%0A%20%20%20%20%20%20%20%20(f(pl.all()%2C%20fast%3Dfast.value%2C%20slow%3Dslow.value%2C%20vola%3Dvola.value%2C%20clip%3Dwinsor.value)%20*%201e5)%0A%20%20%20%20%20%20%20%20.fill_nan(0.0)%0A%20%20%20%20%20%20%20%20.fill_null(0.0)%0A%20%20%20%20)%0A%20%20%20%20portfolio%20%3D%20Portfolio.from_cash_position(prices%3Dprices%2C%20cash_position%3Dsignals%2C%20aum%3D1e8)%0A%20%20%20%20return%20(portfolio%2C)%0A%0A%0A%40app.cell%0Adef%20_(portfolio)%3A%0A%20%20%20%20print(portfolio.stats.sharpe())%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(portfolio)%3A%0A%20%20%20%20fig%20%3D%20portfolio.plots.snapshot()%0A%20%20%20%20fig%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
3d8a9a527b1e7b1a22e037752517768f