-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathquickstart.py
More file actions
70 lines (52 loc) · 2.39 KB
/
Copy pathquickstart.py
File metadata and controls
70 lines (52 loc) · 2.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"""End-to-end example: build an ensemble, backtest it, then tune it.
Run from the repo root with::
PYTHONPATH=src python examples/quickstart.py
It shows the three things you will do most often:
1. Blend several signals into one ensemble and backtest it.
2. Read the rich metrics off the result.
3. Grid-scan a parameter space and walk-forward validate the winner.
Part of AI Trading Bot by Viprasol Tech Private Limited (https://viprasol.com).
"""
from __future__ import annotations
import math
from collections.abc import Mapping
from ai_trading_bot.backtest.engine import run_backtest
from ai_trading_bot.config import BotSettings
from ai_trading_bot.models import Bar
from ai_trading_bot.optimize.scan import grid_scan, walk_forward
from ai_trading_bot.signals.base import Signal
from ai_trading_bot.signals.indicators import MovingAverageCrossover
def make_bars(n: int = 600) -> list[Bar]:
"""A deterministic trending-with-noise series so the example is reproducible."""
bars: list[Bar] = []
for i in range(n):
price = 100.0 + i * 0.2 + 8.0 * math.sin(i / 12.0)
bars.append(Bar(timestamp=i, open=price, high=price + 1.5, low=price - 1.5, close=price))
return bars
def main() -> None:
bars = make_bars()
# 1) Config-driven ensemble (MA + RSI + MACD by default) and a backtest.
settings = BotSettings(starting_cash=10_000.0)
result = run_backtest(
settings.symbol,
settings.build_ensemble(),
bars,
starting_cash=settings.starting_cash,
sizer=settings.build_sizer(),
)
print("Ensemble backtest")
for key, value in result.as_dict().items():
print(f" {key:>18}: {value:,.2f}")
# 2) Grid-scan an MA-crossover's windows, ranked by Sharpe.
def build_ma(params: Mapping[str, float]) -> Signal:
return MovingAverageCrossover(int(params["fast"]), int(params["slow"]))
grid = {"fast": [5, 10, 15], "slow": [30, 40, 50]}
ranked = grid_scan(settings.symbol, build_ma, bars, grid, metric="sharpe")
best = ranked[0]
print("\nBest grid params:", best.params, f"(sharpe={best.score:.2f})")
# 3) Walk-forward validation of that same grid.
wf = walk_forward(settings.symbol, build_ma, bars, grid, folds=4, metric="sharpe")
print(f"Walk-forward avg OOS sharpe: {wf.avg_out_of_sample:.2f}")
print(f"Walk-forward efficiency: {wf.efficiency:.2f}")
if __name__ == "__main__":
main()