Coverage for book/marimo/notebooks/Experiment1.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 1: Basic CTA strategy implementation using moving averages.
14This module demonstrates a simple trend-following strategy using exponential
15moving averages with different lookback periods.
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 1.0""")
42 return
45@app.function
46def f(price: "pl.Expr", fast: int = 32, slow: int = 96) -> "pl.Expr":
47 """Return the sign of the fast-minus-slow EWM crossover."""
48 return (price.ewm_mean(com=fast, min_samples=100) - price.ewm_mean(com=slow, min_samples=100)).sign()
51@app.cell
52def _():
53 fast = mo.ui.slider(4, 192, step=4, value=32, label="Fast moving average")
54 slow = mo.ui.slider(4, 192, step=4, value=96, label="Slow moving average")
56 mo.vstack([fast, slow])
58 return fast, slow
61@app.cell
62def _(fast, slow):
63 signals = prices_only.select(f(pl.all(), fast=fast.value, slow=slow.value).fill_null(0.0) * 5e6)
64 portfolio = Portfolio.from_cash_position(prices=prices, cash_position=signals, aum=1e8)
65 return (portfolio,)
68@app.cell
69def _(portfolio):
70 print(portfolio.stats.sharpe())
73@app.cell(hide_code=True)
74def _():
75 mo.md(
76 r"""
77 Results do not look terrible but...
78 * No concept of risk integrated.
79 * The size of each bet is constant regardless of the underlying asset.
80 * The system lost its mojo in 2009 and has never really recovered.
81 * The sign function is very expensive to trade as position changes are too extreme.
82 """
83 )
84 return
87@app.cell(hide_code=True)
88def _():
89 mo.md(
90 r"""
91 Such fundamental flaws are not addressed by **parameter-hacking**
92 or **pimp-my-trading-system** steps (remove the worst performing assets,
93 insane quantity of stop-loss limits, ...)
94 """
95 )
96 return
99@app.cell
100def _(portfolio):
101 fig = portfolio.plots.snapshot()
102 fig
103 return
106if __name__ == "__main__":
107 app.run()