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

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# /// 

11 

12"""Experiment 1: Basic CTA strategy implementation using moving averages. 

13 

14This module demonstrates a simple trend-following strategy using exponential 

15moving averages with different lookback periods. 

16""" 

17 

18import marimo 

19 

20__generated_with = "0.23.1" 

21app = marimo.App() 

22 

23with app.setup: 

24 import sys 

25 from pathlib import Path 

26 

27 import marimo as mo 

28 import polars as pl 

29 from jquantstats import Portfolio 

30 

31 sys.path.insert(0, str(Path(__file__).parent)) 

32 

33 from preamble import date_col, load_prices 

34 

35 prices = load_prices(__file__) 

36 prices_only = prices.drop(date_col) 

37 

38 

39@app.cell(hide_code=True) 

40def _(): 

41 mo.md(r"""# CTA 1.0""") 

42 return 

43 

44 

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() 

49 

50 

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") 

55 

56 mo.vstack([fast, slow]) 

57 

58 return fast, slow 

59 

60 

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,) 

66 

67 

68@app.cell 

69def _(portfolio): 

70 print(portfolio.stats.sharpe()) 

71 

72 

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 

85 

86 

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 

97 

98 

99@app.cell 

100def _(portfolio): 

101 fig = portfolio.plots.snapshot() 

102 fig 

103 return 

104 

105 

106if __name__ == "__main__": 

107 app.run()