Coverage for book/marimo/notebooks/Experiment2.py: 100%
42 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-31 10:05 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-31 10:05 +0000
1# /// script
2# requires-python = ">=3.12"
3# dependencies = [
4# "marimo==0.23.15",
5# "numpy==2.4.6",
6# "plotly==6.9.0",
7# "polars==1.43.1",
8# "jquantstats==0.9.7"
9# ]
10# ///
12"""Experiment 2: Improved CTA strategy with volatility scaling.
14This module enhances the basic trend-following strategy by incorporating
15volatility scaling to adjust position sizes based on market conditions.
16"""
18import marimo
20__generated_with = "0.23.1"
21app = marimo.App()
23with app.setup:
24 import sys
25 from pathlib import Path
27 import marimo as mo
28 import polars as pl
29 from jquantstats import Portfolio
31 sys.path.insert(0, str(Path(__file__).parent))
33 from preamble import date_col, load_prices
35 prices = load_prices(__file__)
36 prices_only = prices.drop(date_col)
39@app.cell(hide_code=True)
40def _():
41 mo.md(r"""# CTA 2.0""")
42 return
45@app.function
46def f(price: "pl.Expr", fast: int = 32, slow: int = 96, volatility: int = 32) -> "pl.Expr":
47 """Return the volatility-scaled EWM crossover signal."""
48 return (
49 price.ewm_mean(com=fast, min_samples=300) - price.ewm_mean(com=slow, min_samples=300)
50 ).sign() / price.pct_change().ewm_std(com=volatility, min_samples=300)
53@app.cell
54def _():
55 fast = mo.ui.slider(4, 192, step=4, value=32, label="Fast Moving Average")
56 slow = mo.ui.slider(4, 192, step=4, value=96, label="Slow Moving Average")
57 vola = mo.ui.slider(4, 192, step=4, value=32, label="Volatility")
59 mo.vstack([fast, slow, vola])
61 return fast, slow, vola
64@app.cell
65def _(fast, slow, vola):
66 signals = prices_only.select(
67 f(pl.all(), fast=fast.value, slow=slow.value, volatility=vola.value).fill_null(0.0) * 1e5
68 )
69 portfolio = Portfolio.from_cash_position(prices=prices, cash_position=signals, aum=1e8)
70 return (portfolio,)
73@app.cell
74def _(portfolio):
75 print(portfolio.stats.sharpe())
78@app.cell(hide_code=True)
79def _():
80 mo.md(
81 r"""
82 * This is a **univariate** trading system, we map the (real) price of an asset to its (cash)position
83 * Only 3 **free parameters** used here.
84 * Scaling the bet-size by volatility has improved the situation.
85 """
86 )
87 return
90@app.cell(hide_code=True)
91def _():
92 mo.md(
93 r"""
94 Results do not look terrible but...
95 * No concept of risk integrated
97 Often hedge funds outsource the risk management to some board or committee
98 and develop machinery for more systematic **parameter-hacking**.
99 """
100 )
101 return
104@app.cell
105def _(portfolio):
106 portfolio.plots.snapshot()
107 return
110if __name__ == "__main__":
111 app.run()