Skip to content

Commit 8b39aad

Browse files
authored
Merge pull request #475 from coding-kitties/dev
Release: merge dev into main
2 parents 0022508 + 35cf936 commit 8b39aad

50 files changed

Lines changed: 7276 additions & 1139 deletions

Some content is hidden

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

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,4 @@ venv
161161
examples/tutorial/data
162162
examples/tutorial/backtest_results
163163
examples/tutorial/resources
164+
.data_cache/

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ This framework is built around the full loop: **create strategies → backtest t
7676
- 🎯 **Return Scenario Projections** — Good, average, bad & very bad year projections from backtest data
7777
- 📉 **Benchmark Comparison** — Beat-rate analysis vs Buy & Hold, DCA, risk-free & custom benchmarks
7878
- 📄 **One-Click HTML Report** — Self-contained file, no server, dark & light theme, shareable
79+
- 🌐 **Load External Data** — Fetch CSV, JSON, or Parquet from any URL with caching and auto-refresh
7980
- 🚀 **Build → Backtest → Deploy** — Local dev, cloud deploy (AWS / Azure), or monetize on Finterion
8081

8182
</details>
@@ -262,15 +263,17 @@ report.save("my_report.html")
262263

263264
| | |
264265
|---|---|
265-
| **Backtest Report Dashboard** | Self-contained HTML report with ranking tables, equity curves, metric charts, heatmaps, and strategy comparison |
266-
| **Event-Driven Backtesting** | Realistic, order-by-order simulation |
267-
| **Vectorized Backtesting** | Fast signal research and prototyping |
266+
| **[Backtest Report Dashboard](https://coding-kitties.github.io/investing-algorithm-framework/Getting%20Started/backtest-reports)** | Self-contained HTML report with ranking tables, equity curves, metric charts, heatmaps, and strategy comparison |
267+
| **[Event-Driven Backtesting](https://coding-kitties.github.io/investing-algorithm-framework/Getting%20Started/backtesting)** | Realistic, order-by-order simulation |
268+
| **[Vectorized Backtesting](https://coding-kitties.github.io/investing-algorithm-framework/Advanced%20Concepts/vector-backtesting)** | Fast signal research and prototyping |
268269
| **50+ Metrics** | CAGR, Sharpe, Sortino, max drawdown, win rate, profit factor, recovery factor, volatility, and more |
269-
| **Live Trading** | Connect to exchanges via CCXT for real-time execution |
270-
| **Portfolio Management** | Position tracking, trade management, persistence |
271-
| **Cloud Deployment** | Deploy to AWS Lambda, Azure Functions, or run as a web service |
272-
| **Market Data** | OHLCV, tickers, custom data — Polars and Pandas native |
273-
| **Extensible** | Custom data providers, order executors, and strategy classes |
270+
| **[Live Trading](https://coding-kitties.github.io/investing-algorithm-framework/Getting%20Started/application-setup)** | Connect to exchanges via CCXT for real-time execution |
271+
| **[Portfolio Management](https://coding-kitties.github.io/investing-algorithm-framework/Getting%20Started/portfolio-configuration)** | Position tracking, trade management, persistence |
272+
| **[Cloud Deployment](https://coding-kitties.github.io/investing-algorithm-framework/Getting%20Started/deployment)** | Deploy to AWS Lambda, Azure Functions, or run as a web service |
273+
| **[Market Data Providers](https://coding-kitties.github.io/investing-algorithm-framework/Advanced%20Concepts/custom-data-providers)** | Built-in providers for CCXT, Yahoo Finance, Alpha Vantage, and Polygon — or build your own |
274+
| **[Load External Data](https://coding-kitties.github.io/investing-algorithm-framework/Data/external-data)** | Fetch CSV, JSON, or Parquet from any URL with caching, date parsing, and pre/post-processing |
275+
| **[Strategies](https://coding-kitties.github.io/investing-algorithm-framework/Getting%20Started/strategies)** | OHLCV, tickers, custom data — Polars and Pandas native |
276+
| **[Extensible](https://coding-kitties.github.io/investing-algorithm-framework/Advanced%20Concepts/custom-data-providers)** | Custom data providers, order executors, and strategy classes |
274277

275278
</details>
276279

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
# Blotter
6+
7+
Learn how the Blotter system works and how to use slippage models, commission models, and custom order routing.
8+
9+
## Overview
10+
11+
The **Blotter** sits between your strategy and the order execution layer. Every order you create — whether via `create_limit_order()`, `create_market_order()`, or `batch_order()` — flows through the blotter before reaching the `OrderService`.
12+
13+
```
14+
Strategy (Context)
15+
16+
17+
┌────────┐
18+
│ Blotter │ ← slippage, commission, routing
19+
└────┬───┘
20+
21+
22+
OrderService → OrderExecutor → Exchange / Simulation
23+
```
24+
25+
The framework automatically selects a blotter if you don't set one:
26+
27+
| Mode | Default Blotter | Behavior |
28+
|-------------|----------------------|---------------------------------------------|
29+
| Live trading | `DefaultBlotter` | Pass-through — no slippage or commission |
30+
| Backtesting | `SimulationBlotter` | Configurable slippage and commission models |
31+
32+
You can override the default by calling `app.set_blotter(...)` with any `Blotter` subclass.
33+
34+
## Slippage Models
35+
36+
Slippage models determine how the execution price deviates from the intended order price. They are used by the `SimulationBlotter` during backtesting.
37+
38+
### NoSlippage (default)
39+
40+
Orders fill at the exact intended price.
41+
42+
```python
43+
from investing_algorithm_framework import SimulationBlotter, NoSlippage
44+
45+
app.set_blotter(SimulationBlotter(
46+
slippage_model=NoSlippage()
47+
))
48+
```
49+
50+
### PercentageSlippage
51+
52+
Buy orders fill at a slightly higher price, sell orders at a slightly lower price.
53+
54+
```python
55+
from investing_algorithm_framework import SimulationBlotter, PercentageSlippage
56+
57+
# 0.1% slippage
58+
app.set_blotter(SimulationBlotter(
59+
slippage_model=PercentageSlippage(percentage=0.001)
60+
))
61+
```
62+
63+
For a buy order at price `100.0` with `0.1%` slippage, the fill price becomes `100.10`. For a sell order, it becomes `99.90`.
64+
65+
### FixedSlippage
66+
67+
Adds or subtracts a fixed amount from the order price.
68+
69+
```python
70+
from investing_algorithm_framework import SimulationBlotter, FixedSlippage
71+
72+
# $0.05 slippage per order
73+
app.set_blotter(SimulationBlotter(
74+
slippage_model=FixedSlippage(amount=0.05)
75+
))
76+
```
77+
78+
### Custom Slippage Model
79+
80+
Create your own by extending `SlippageModel`:
81+
82+
```python
83+
from investing_algorithm_framework import SlippageModel
84+
85+
class VolumeWeightedSlippage(SlippageModel):
86+
def __init__(self, base_pct=0.001, volume_factor=0.0001):
87+
self.base_pct = base_pct
88+
self.volume_factor = volume_factor
89+
90+
def calculate_slippage(self, price, order_side, amount=None):
91+
pct = self.base_pct
92+
if amount is not None:
93+
pct += self.volume_factor * amount
94+
95+
if order_side == "BUY":
96+
return price * (1 + pct)
97+
return price * (1 - pct)
98+
```
99+
100+
## Commission Models
101+
102+
Commission models determine the fee charged for each trade. They are used by the `SimulationBlotter` during backtesting.
103+
104+
### NoCommission (default)
105+
106+
Zero fees on all trades.
107+
108+
```python
109+
from investing_algorithm_framework import SimulationBlotter, NoCommission
110+
111+
app.set_blotter(SimulationBlotter(
112+
commission_model=NoCommission()
113+
))
114+
```
115+
116+
### PercentageCommission
117+
118+
Fee is a percentage of the total trade value (`price × amount`).
119+
120+
```python
121+
from investing_algorithm_framework import SimulationBlotter, PercentageCommission
122+
123+
# 0.1% commission
124+
app.set_blotter(SimulationBlotter(
125+
commission_model=PercentageCommission(percentage=0.001)
126+
))
127+
```
128+
129+
### FixedCommission
130+
131+
A fixed fee per trade, regardless of trade size.
132+
133+
```python
134+
from investing_algorithm_framework import SimulationBlotter, FixedCommission
135+
136+
# $1.00 per trade
137+
app.set_blotter(SimulationBlotter(
138+
commission_model=FixedCommission(amount=1.0)
139+
))
140+
```
141+
142+
### Custom Commission Model
143+
144+
Create your own by extending `CommissionModel`:
145+
146+
```python
147+
from investing_algorithm_framework import CommissionModel
148+
149+
class TieredCommission(CommissionModel):
150+
def calculate_commission(self, price, amount, order_side):
151+
trade_value = price * amount
152+
if trade_value > 10000:
153+
return trade_value * 0.0005 # 0.05% for large trades
154+
return trade_value * 0.001 # 0.1% for small trades
155+
```
156+
157+
## SimulationBlotter
158+
159+
The `SimulationBlotter` applies slippage and commission models to every order and records each fill as a `Transaction`.
160+
161+
```python
162+
from investing_algorithm_framework import (
163+
SimulationBlotter,
164+
PercentageSlippage,
165+
PercentageCommission,
166+
)
167+
168+
app.set_blotter(SimulationBlotter(
169+
slippage_model=PercentageSlippage(0.001), # 0.1% slippage
170+
commission_model=PercentageCommission(0.001), # 0.1% commission
171+
))
172+
```
173+
174+
:::info Automatic Setup
175+
If you don't set a blotter and run a backtest, the framework automatically uses a `SimulationBlotter` with `NoSlippage` and `NoCommission`.
176+
:::
177+
178+
## Transactions
179+
180+
Every order placed through the blotter is recorded as a `Transaction`. Transactions provide an audit trail of all fills, including the actual execution price, slippage, and commission.
181+
182+
```python
183+
class MyStrategy(TradingStrategy):
184+
def run_strategy(self, algorithm, market_data):
185+
# Place some orders...
186+
self.create_limit_order(
187+
target_symbol="BTC", price=50000, amount=0.1
188+
)
189+
190+
# Get all transactions recorded by the blotter
191+
transactions = self.get_transactions()
192+
193+
for tx in transactions:
194+
print(f"{tx.symbol} {tx.order_side}: "
195+
f"price={tx.price}, amount={tx.amount}, "
196+
f"commission={tx.commission}, slippage={tx.slippage}")
197+
```
198+
199+
Each `Transaction` contains:
200+
201+
| Field | Description |
202+
|-------|-------------|
203+
| `order_id` | ID of the order |
204+
| `symbol` | Target symbol (e.g. `"BTC"`) |
205+
| `order_side` | `"BUY"` or `"SELL"` |
206+
| `price` | Actual fill price (after slippage) |
207+
| `amount` | Fill amount |
208+
| `cost` | Total cost (`price × amount`) |
209+
| `commission` | Commission charged |
210+
| `slippage` | Slippage amount (`abs(fill_price - intended_price)`) |
211+
| `timestamp` | UTC timestamp of the fill |
212+
213+
You can serialize a transaction with `tx.to_dict()`.
214+
215+
## Batch Orders
216+
217+
The `batch_order()` method lets you place multiple orders at once through the blotter:
218+
219+
```python
220+
class MyStrategy(TradingStrategy):
221+
def run_strategy(self, algorithm, market_data):
222+
orders = [
223+
{
224+
"target_symbol": "BTC",
225+
"order_side": "BUY",
226+
"price": 50000,
227+
"amount": 0.1,
228+
},
229+
{
230+
"target_symbol": "ETH",
231+
"order_side": "BUY",
232+
"price": 3000,
233+
"amount": 1.0,
234+
},
235+
]
236+
created_orders = self.batch_order(orders)
237+
```
238+
239+
The default implementation places orders sequentially. Override `batch_order()` in a custom blotter for atomic batch behavior or smart order routing.
240+
241+
## Custom Blotter
242+
243+
Create your own blotter by extending the `Blotter` class and implementing `place_order()` and `cancel_order()`:
244+
245+
```python
246+
from investing_algorithm_framework import Blotter
247+
248+
class SmartOrderRouter(Blotter):
249+
def place_order(self, order_data, context):
250+
"""
251+
Custom order routing logic.
252+
"""
253+
symbol = order_data.get("target_symbol")
254+
255+
# Example: route large orders differently
256+
amount = order_data.get("amount", 0)
257+
if amount > 100:
258+
# Split into smaller orders
259+
half = amount / 2
260+
order_data["amount"] = half
261+
order1 = context.order_service.create(order_data)
262+
order2 = context.order_service.create(order_data)
263+
return order1 # Return the first order
264+
265+
return context.order_service.create(order_data)
266+
267+
def cancel_order(self, order_id, context):
268+
"""
269+
Cancel a specific order.
270+
"""
271+
order = context.order_service.get(order_id)
272+
if order is None:
273+
raise Exception(f"Order {order_id} not found")
274+
275+
context.order_service.update(
276+
order_id, {"status": "CANCELED"}
277+
)
278+
return context.order_service.get(order_id)
279+
280+
# Register the custom blotter
281+
app.set_blotter(SmartOrderRouter())
282+
```
283+
284+
### Blotter API
285+
286+
| Method | Required | Description |
287+
|--------|----------|-------------|
288+
| `place_order(order_data, context)` | Yes | Place a single order. Must be implemented. |
289+
| `cancel_order(order_id, context)` | Yes | Cancel an order. Must be implemented. |
290+
| `batch_order(orders_data, context)` | No | Place multiple orders. Default calls `place_order()` sequentially. |
291+
| `get_open_orders(context, target_symbol)` | No | Get open orders. Default delegates to context. |
292+
| `get_transactions()` | No | Get recorded transactions. |
293+
| `record_transaction(transaction)` | No | Record a fill. |
294+
| `clear_transactions()` | No | Clear all recorded transactions. |
295+
| `prune_orders(context)` | No | Clean up stale orders. Default is a no-op. |

0 commit comments

Comments
 (0)