Skip to content

Commit 7ca3e85

Browse files
committed
docs: add Monte Carlo overfitting detection to README, update sidebars, remove batch_one examples
1 parent 21cb653 commit 7ca3e85

205 files changed

Lines changed: 2814 additions & 1877054 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
</h1>
44

55
<p align="center">
6-
<i align="center">Create trading strategies. Compare them side by side. Pick the best one and Deploy 🚀</i>
6+
<i align="center">The full quant workflow in one framework: build strategies, vector & event-driven backtest at scale, compare in a single dashboard, and deploy the winner 🚀</i>
77
</p>
88

99
<h4 align="center">
@@ -59,11 +59,11 @@
5959

6060
## Introduction
6161

62-
`Investing Algorithm Framework` is a Python framework for creating, backtesting, and deploying trading strategies.
62+
`Investing Algorithm Framework` is a Python framework that covers the entire quant workflow: define a strategy once, vector-backtest thousands of parameter variants to find promising signals, narrow down with a storage layer that ranks 10k+ results in milliseconds, validate the winners in a realistic event-driven simulation, compare everything in a single interactive HTML dashboard, and deploy the best performer live, all with the same `TradingStrategy` class, no code rewrites between stages.
6363

64-
Most quant frameworks stop at "here's your backtest result." You get a number, maybe a chart, and then you're on your own figuring out which strategy is actually better.
64+
Most quant frameworks stop at "here's your backtest result." You get a number, maybe a chart, and then you're on your own figuring out which strategy variant is actually better, whether the result is robust across time windows, and how to go from research to production. This framework closes that gap.
6565

66-
This framework is built around the full loop: **create strategies → vector backtest for signals analysis → compare them in a single report → event backtest the most promising strategies → deploy the winner.** It generates a self-contained HTML dashboard that lets you rank, filter, and visually compare every strategy you've tested — all in one view, no notebooks required.
66+
> **Want to see this in practice?** Check out the [`examples/tutorial/`](examples/tutorial/README.md): a series of runnable notebooks that walk you through every stage: defining a strategy, visualizing its signals, sweeping parameters across rolling windows, detecting overfitting with Monte Carlo permutation tests, filtering and ranking with the storage layer, and deploying the winner.
6767
6868
<details open>
6969
<summary>
@@ -87,11 +87,91 @@ This framework is built around the full loop: **create strategies → vector bac
8787
- 🗄️ **[Tiered Backtest Storage Layer](examples/storage_layer_demo/README.md)** — Manage thousands of `.iafbt` bundles with a Tier-1 SQLite index (sub-100 ms ranks/filters over 10k+ backtests), a swappable `BacktestStore` protocol (`LocalDirStore`, `LocalTieredStore`), content-addressed Tier-3 OHLCV deduplication, and a CLI (`iaf index` / `iaf list` / `iaf rank` / `iaf migrate-store`) that plugs straight into the HTML dashboard.
8888
- 🌐 **[Load External Data](https://coding-kitties.github.io/investing-algorithm-framework/Data/external-data)** — Fetch CSV, JSON, or Parquet from any URL with caching and auto-refresh
8989
-**[Per-Market Deposit Schedules & Portfolio Sync](https://coding-kitties.github.io/investing-algorithm-framework/Advanced%20Concepts/portfolio-sync)** — Declare recurring or one-shot external cash flows on a market with `deposit_schedule=` / `auto_sync=True`. Backtests simulate the deposits; live mode reconciles with the broker — same `context.sync_portfolio()` API in both modes.
90-
- �📝 **[Record Custom Variables](https://coding-kitties.github.io/investing-algorithm-framework/Advanced%20Concepts/recording-variables)** — Track any indicator or metric during backtests with `context.record()`
90+
- 📝 **[Record Custom Variables](https://coding-kitties.github.io/investing-algorithm-framework/Advanced%20Concepts/recording-variables)** — Track any indicator or metric during backtests with `context.record()`
91+
- ⏱️ **Signal Cooldowns**: Throttle whipsaw with declarative `CooldownRule`s: per-symbol or portfolio-wide, side-aware (`trigger="sell"`, `blocks="buy"`), enforced identically by the vector and event-driven engines
9192
- 🚀 **[Build → Backtest → Deploy](https://coding-kitties.github.io/investing-algorithm-framework/Getting%20Started/application-setup)** — Local dev, cloud deploy (AWS / Azure), or monetize on Finterion
9293

9394
</details>
9495

96+
<details open>
97+
<summary>
98+
<strong>Strategy Definition</strong>
99+
</summary> <br>
100+
101+
Declare **what data** your strategy needs and **when to buy or sell** as a `TradingStrategy` subclass — the framework wires up data loading, signal evaluation, order execution, position management, and reporting around it. The same class runs unchanged in vector backtests, event-driven backtests, paper trading and live.
102+
103+
Risk and execution behaviour are expressed as **declarative rule lists** rather than ad-hoc code paths, so the engines can enforce them identically across modes:
104+
105+
- **`position_sizes`**: [`PositionSize`](https://coding-kitties.github.io/investing-algorithm-framework/Risk%20Rules/position-size) per symbol (fixed amount or percentage of portfolio).
106+
- **`stop_losses`** / **`take_profits`**: [`StopLossRule`](https://coding-kitties.github.io/investing-algorithm-framework/Risk%20Rules/stop-loss-rule) / [`TakeProfitRule`](https://coding-kitties.github.io/investing-algorithm-framework/Risk%20Rules/take-profit-rule) with fixed or trailing thresholds and partial-exit `sell_percentage`.
107+
- **`scaling_rules`**: [`ScalingRule`](https://coding-kitties.github.io/investing-algorithm-framework/Risk%20Rules/scaling-rule) for pyramiding (`scale_in_percentage=[…]`, `max_entries`, per-symbol `cooldown_in_bars`).
108+
- **`cooldowns`**: [`CooldownRule`](https://coding-kitties.github.io/investing-algorithm-framework/Risk%20Rules/cooldown-rule) to throttle whipsaw — per-symbol or portfolio-wide, side-aware (e.g. `trigger="sell", blocks="buy", bars=12`). Enforced bar-for-bar in both the vector and event-driven engines.
109+
- **`trading_costs`**: [`TradingCost`](https://coding-kitties.github.io/investing-algorithm-framework/Risk%20Rules/trading-cost) per symbol (fees, slippage, fixed costs).
110+
111+
```python
112+
from investing_algorithm_framework import (
113+
TradingStrategy,
114+
PositionSize,
115+
ScalingRule,
116+
StopLossRule,
117+
TakeProfitRule,
118+
CooldownRule,
119+
TradingCost,
120+
)
121+
122+
123+
class MyStrategy(TradingStrategy):
124+
symbols = ["BTC", "ETH"]
125+
126+
position_sizes = [
127+
PositionSize(symbol="BTC", percentage_of_portfolio=20),
128+
PositionSize(symbol="ETH", percentage_of_portfolio=20),
129+
]
130+
131+
stop_losses = [
132+
StopLossRule(symbol="BTC", percentage_threshold=5, trailing=True),
133+
StopLossRule(symbol="ETH", percentage_threshold=5, trailing=True),
134+
]
135+
136+
take_profits = [
137+
TakeProfitRule(
138+
symbol="BTC", percentage_threshold=10, sell_percentage=50,
139+
),
140+
TakeProfitRule(
141+
symbol="ETH", percentage_threshold=10, sell_percentage=50,
142+
),
143+
]
144+
145+
scaling_rules = [
146+
ScalingRule(
147+
symbol="BTC", max_entries=3, scale_in_percentage=[50, 25],
148+
),
149+
ScalingRule(
150+
symbol="ETH", max_entries=3, scale_in_percentage=[50, 25],
151+
),
152+
]
153+
154+
cooldowns = [
155+
CooldownRule(symbol="BTC", trigger="sell", blocks="buy", bars=12),
156+
CooldownRule(trigger="any", blocks="any", bars=2),
157+
]
158+
159+
trading_costs = [
160+
TradingCost(symbol="BTC", fee_percentage=0.1),
161+
TradingCost(symbol="ETH", fee_percentage=0.1),
162+
]
163+
164+
def generate_buy_signals(self, data):
165+
...
166+
167+
def generate_sell_signals(self, data):
168+
...
169+
```
170+
171+
[Strategy docs](https://coding-kitties.github.io/investing-algorithm-framework/Getting%20Started/strategies)
172+
173+
</details>
174+
95175
<details open>
96176
<summary>
97177
<strong>Backtesting Engines</strong>
@@ -355,7 +435,7 @@ from pyindicators import ema, rsi, crossover, crossunder
355435

356436
from investing_algorithm_framework import (
357437
TradingStrategy, DataSource, TimeUnit, DataType,
358-
PositionSize, ScalingRule, StopLossRule,
438+
PositionSize, ScalingRule, StopLossRule, CooldownRule,
359439
)
360440

361441

@@ -408,6 +488,18 @@ class RSIEMACrossoverStrategy(TradingStrategy):
408488
sell_percentage=100, trailing=True,
409489
),
410490
]
491+
# Signal throttling: after a stop-out / sell, block re-entries on
492+
# the same symbol for 12 bars, plus a portfolio-wide breather of
493+
# 2 bars after any order to avoid same-bar pile-ups.
494+
cooldowns = [
495+
CooldownRule(
496+
symbol="BTC", trigger="sell", blocks="buy", bars=12,
497+
),
498+
CooldownRule(
499+
symbol="ETH", trigger="sell", blocks="buy", bars=12,
500+
),
501+
CooldownRule(trigger="any", blocks="any", bars=2),
502+
]
411503

412504
def generate_buy_signals(
413505
self, data: Dict[str, Any]
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
sidebar_position: 6
3+
---
4+
5+
# CooldownRule
6+
7+
`CooldownRule` is a declarative, side-aware signal-throttling primitive. After a triggering order fills, the rule suppresses certain *new* signals for a configured number of bars — per symbol, or across the whole portfolio.
8+
9+
It is the symmetric, side-aware sibling of [`ScalingRule.cooldown_in_bars`](./scaling-rule.md), which only models a single both-sides, symbol-scoped cooldown that is restarted by *any* order. `CooldownRule` lets you say things like *"after a stop-out on BTC, don't re-enter BTC for 12 bars"* or *"after any order anywhere, throttle the whole portfolio for 2 bars"*.
10+
11+
```python
12+
from investing_algorithm_framework import CooldownRule
13+
```
14+
15+
## Signature
16+
17+
```python
18+
CooldownRule(
19+
*,
20+
symbol: str | None = None,
21+
trigger: str = "any", # "buy" | "sell" | "any"
22+
blocks: str = "any", # "buy" | "sell" | "any"
23+
bars: int = 0,
24+
)
25+
```
26+
27+
| Parameter | Type | Default | Description |
28+
|---|---|---|---|
29+
| `symbol` | `str \| None` | `None` | Target symbol the rule applies to. `None` makes the rule **portfolio-scoped**: any qualifying order on any symbol restarts it, and any qualifying signal on any symbol is suppressed. |
30+
| `trigger` | `str` | `"any"` | Order side that **starts** the cooldown timer. One of `"buy"`, `"sell"`, `"any"`. |
31+
| `blocks` | `str` | `"any"` | Signal side this cooldown **suppresses**. One of `"buy"`, `"sell"`, `"any"`. |
32+
| `bars` | `int` | `0` | Number of bars the cooldown lasts. `0` is a valid no-op (handy for tests / disabled rules). Must be `>= 0`. |
33+
34+
All arguments are keyword-only. Both `trigger` and `blocks` accept the strings shown above as well as `CooldownTrigger` / `CooldownBlocks` enum members.
35+
36+
## Window Semantics
37+
38+
The bar on which the order fills is treated as bar 0 of the cooldown. A rule with `bars=N` therefore **blocks the next `N` bars** and **allows the `(N+1)`th bar**:
39+
40+
```text
41+
bar … fill +1 +2 +3 +4
42+
┃ ┃ ┃ ┃
43+
bars=3: block block block allow
44+
```
45+
46+
Formally, the rule blocks signals when `bar_index − last_event < bars`.
47+
48+
## Examples
49+
50+
### Re-entry breather
51+
52+
Don't re-buy `BTC` for 12 bars after a sell:
53+
54+
```python
55+
cooldowns = [
56+
CooldownRule(
57+
symbol="BTC", trigger="sell", blocks="buy", bars=12,
58+
),
59+
]
60+
```
61+
62+
### Both-sides freeze
63+
64+
Freeze all trading on `ETH` for 4 bars after any order:
65+
66+
```python
67+
cooldowns = [
68+
CooldownRule(
69+
symbol="ETH", trigger="any", blocks="any", bars=4,
70+
),
71+
]
72+
```
73+
74+
### Portfolio-wide throttle
75+
76+
After any order on any symbol, suppress every signal across the portfolio for 2 bars (kills same-bar pile-ups):
77+
78+
```python
79+
cooldowns = [
80+
CooldownRule(trigger="any", blocks="any", bars=2),
81+
]
82+
```
83+
84+
### Stacking rules
85+
86+
Multiple rules can coexist — the engine evaluates them all and the **most restrictive** active rule wins:
87+
88+
```python
89+
cooldowns = [
90+
# Symbol-specific re-entry guard.
91+
CooldownRule(symbol="BTC", trigger="sell", blocks="buy", bars=12),
92+
# Short global breather after any order.
93+
CooldownRule(trigger="any", blocks="any", bars=2),
94+
]
95+
```
96+
97+
## Where It's Enforced
98+
99+
Identical semantics in both backtest engines and live trading:
100+
101+
- **Vector backtest engine** — a single `CooldownTracker` is shared across symbols (so portfolio-scoped rules work). Suppressed signals are recorded in `signal_events` with `reason="in_cooldown_rule"` for inspection.
102+
- **Event-driven backtest engine** and **live runtime** — the `TradingStrategy` increments an internal bar counter every `run_strategy()` call, gates buy / sell / scale-out at the relevant phase, and records every fill in the tracker.
103+
104+
## Interaction With Other Rules
105+
106+
- **`ScalingRule.cooldown_in_bars`** — both can coexist. `cooldown_in_bars` remains a fast both-sides per-symbol shortcut; `CooldownRule` is the way to express side- or portfolio-aware throttling. The more restrictive active rule wins.
107+
- **`StopLossRule` / `TakeProfitRule`** — their fills count as `sell` events for cooldown bookkeeping. A typical pattern is `trigger="sell", blocks="buy"` so a stop-out doesn't immediately invite a re-entry on the next bar.
108+
- **Scale-ins and scale-outs** — count as `buy` and `sell` events respectively for cooldown bookkeeping.
109+
110+
## See Also
111+
112+
- [`ScalingRule`](./scaling-rule.md)
113+
- [`StopLossRule`](./stop-loss-rule.md)
114+
- [Risk Rules Overview](./overview.md)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
sidebar_position: 1
3+
---
4+
5+
# Risk Rules Overview
6+
7+
The framework lets you express risk and execution behaviour as **declarative rule lists** on a `TradingStrategy` rather than as ad-hoc code paths. The vector backtest engine, the event-driven backtest engine and the live trading runtime all read the same rule objects, so a strategy behaves identically across all three modes.
8+
9+
```python
10+
from investing_algorithm_framework import (
11+
TradingStrategy,
12+
PositionSize,
13+
StopLossRule,
14+
TakeProfitRule,
15+
ScalingRule,
16+
CooldownRule,
17+
TradingCost,
18+
)
19+
20+
21+
class MyStrategy(TradingStrategy):
22+
symbols = ["BTC", "ETH"]
23+
24+
position_sizes = [
25+
PositionSize(symbol="BTC", percentage_of_portfolio=20),
26+
PositionSize(symbol="ETH", percentage_of_portfolio=20),
27+
]
28+
stop_losses = [
29+
StopLossRule(
30+
symbol="BTC", percentage_threshold=5,
31+
sell_percentage=100, trailing=True,
32+
),
33+
]
34+
take_profits = [
35+
TakeProfitRule(
36+
symbol="BTC", percentage_threshold=10,
37+
sell_percentage=50, trailing=False,
38+
),
39+
]
40+
scaling_rules = [
41+
ScalingRule(
42+
symbol="BTC", max_entries=3,
43+
scale_in_percentage=[50, 25],
44+
),
45+
]
46+
cooldowns = [
47+
CooldownRule(
48+
symbol="BTC", trigger="sell", blocks="buy", bars=12,
49+
),
50+
CooldownRule(trigger="any", blocks="any", bars=2),
51+
]
52+
trading_costs = [
53+
TradingCost(
54+
symbol="BTC", fee_percentage=0.1,
55+
slippage_percentage=0.05,
56+
),
57+
]
58+
```
59+
60+
## The Rule Catalogue
61+
62+
| Attribute | Class | Purpose |
63+
|---|---|---|
64+
| `position_sizes` | [`PositionSize`](./position-size.md) | How much capital to allocate per symbol — fixed amount or percentage of portfolio. |
65+
| `stop_losses` | [`StopLossRule`](./stop-loss-rule.md) | Bar-end exit when price drops a fixed or trailing percentage from entry / peak. |
66+
| `take_profits` | [`TakeProfitRule`](./take-profit-rule.md) | Bar-end exit when price rises a fixed or trailing percentage from entry / peak. |
67+
| `scaling_rules` | [`ScalingRule`](./scaling-rule.md) | Pyramid into winners and partially close — `max_entries`, `scale_in_percentage`, `scale_out_percentage`, optional `max_position_percentage` cap. |
68+
| `cooldowns` | [`CooldownRule`](./cooldown-rule.md) | Side-aware, per-symbol or portfolio-wide signal throttling after fills. |
69+
| `trading_costs` | [`TradingCost`](./trading-cost.md) | Per-symbol fees and slippage applied during fill simulation. |
70+
71+
## Where Rules Are Enforced
72+
73+
| Rule | Vector backtest | Event-driven backtest | Live trading |
74+
|---|---|---|---|
75+
| `PositionSize` ||||
76+
| `StopLossRule` ||||
77+
| `TakeProfitRule` ||||
78+
| `ScalingRule` ||||
79+
| `CooldownRule` ||||
80+
| `TradingCost` (fees + slippage) ||| n/a — broker reports actual cost |
81+
82+
## Resolution Order
83+
84+
When more than one rule could fire at the same bar, the engine evaluates them in a deterministic order:
85+
86+
1. **Stop loss** (highest priority — defensive exit).
87+
2. **Take profit**.
88+
3. **Sell signal** from `generate_sell_signals()`.
89+
4. **Scale-out signal** (only if no full sell fired).
90+
5. **Buy signal** from `generate_buy_signals()` — gated by `CooldownRule` and `ScalingRule.cooldown_in_bars`.
91+
6. **Scale-in signal** — gated by `ScalingRule.max_entries` and `max_position_percentage`.
92+
93+
Within each step, `TradingCost` is applied to the fill price, and `PositionSize` (or the relevant `scale_in_percentage`) determines the order amount.
94+
95+
## See Also
96+
97+
- [`PositionSize`](./position-size.md)
98+
- [`StopLossRule`](./stop-loss-rule.md)
99+
- [`TakeProfitRule`](./take-profit-rule.md)
100+
- [`ScalingRule`](./scaling-rule.md)
101+
- [`CooldownRule`](./cooldown-rule.md)
102+
- [`TradingCost`](./trading-cost.md)

0 commit comments

Comments
 (0)