Lightweight quant-grade backtesting for price-action and factor strategies
Project description
Backtest price-action and quant strategies in Python — simple API, serious simulation.
pip install stolgo · Python 3.10+ · MIT License · bandl data & brokers
What is stolgo?
stolgo is a lightweight backtesting framework built for traders who think in price action — breakouts, consolidation, support/resistance, candle behaviour — not only indicator crossovers.
Most libraries optimise for MACD/RSI grids. stolgo optimises for clear rules on OHLCV, a deterministic event loop (fills, cash, positions), and reporting you can share (equity curve, drawdown, trade log, Plotly tearsheet).
Write your logic once. Point it at market data or a CSV. Get metrics and charts.
Why stolgo?
| Strength | What you get |
|---|---|
| Price-action first | Write pa.crosses_above(pa.resistance(21)) — readable rules, not indicator soup |
| Composable | Combine levels, relations, candles, and streaks with & | ~ .then() |
| Trade in one line | trade.long(ctx, rr=(1, 2), stop="candle_low") — stops, targets, sizing handled |
| Presets | pa.preset.consolidation_breakout(7) and friends — proven setups, zero wiring |
| Honest simulation | Event loop with configurable fill timing (next_open or close), commission, slippage |
| No look-ahead | ctx.data only exposes history up to the current bar; MTF levels align safely |
| Data your way | bandl for crypto/equity OHLCV, or load() for CSV/Parquet |
| Built-in analytics | Sharpe, drawdown, hit rate, profit factor, HTML tearsheet |
Installation
pip install stolgo
For live market data via bandl (recommended):
pip install stolgo bandl
Optional extras:
pip install "stolgo[numba]" # faster sweeps
pip install "stolgo[ui]" # local read-only backtest browser
See docs/UI.md for the local backtest UI, its read-only API, and security notes.
Price action in 30 seconds
This is the heart of stolgo. Describe a setup the way you'd say it out loud, attach a
risk/reward bracket, and backtest it. One import: import stolgo.pa as pa.
from datetime import datetime, timedelta, timezone
import stolgo.pa as pa
from stolgo import Backtest, Bandl, Context, Strategy, trade
# 1. Describe the edge in plain price-action language
entry = pa.crosses_above(pa.resistance(21)) # close breaks the 21-bar high
# 2. Trade it with a 1:2 risk/reward bracket, stop under the signal candle
class Breakout(Strategy):
bracket = None
def on_bar(self, ctx: Context) -> None:
if not ctx.position.flat: # in a trade → manage exit
if trade.bracket_hit(ctx, self.bracket): # stop or target touched
trade.close(ctx)
self.bracket = None
elif entry(ctx): # flat + breakout → enter
self.bracket = trade.long(ctx, rr=(1, 2), stop="candle_low", qty=0.05)
# 3. Run it on real data
end = datetime.now(timezone.utc)
df = Bandl().history("BTCUSDT", "1h", end - timedelta(days=365), end)
result = Backtest(Breakout(), df, fill_on="close").run()
print(result.summary())
result.report.to_html("tearsheet.html")
That's a complete, look-ahead-safe breakout backtest with stops, targets, fees, and a shareable tearsheet — no indicators required.
The price-action vocabulary
Everything below is a flat attribute on pa. Mix and match with & (and), | (or),
~ (not), and .then() (sequence) to build any setup.
Levels — a price line per bar
pa.resistance(20) pa.support(20) # rolling highs / lows
pa.range_high(7) pa.range_low(7) # consolidation box edges
pa.donchian_high(20) pa.donchian_low(20)
pa.vwap() pa.prev_day_high() pa.prev_day_low()
pa.swing_high(5) pa.swing_low(5) pa.pivot_point()
pa.level(42_000) # a fixed price
pa.resistance(21, tf="1d") # daily level on intraday bars (MTF)
Relations — turn a level into a true/false rule
pa.above(lvl) pa.below(lvl)
pa.crosses_above(lvl) pa.crosses_below(lvl) # breakouts / breakdowns
pa.rejected_at(lvl) pa.recovered_at(lvl) # failed break / reclaim
pa.near(lvl, pct=0.005) pa.touched(lvl)
Patterns — candles, streaks, structure, momentum
pa.bullish_engulfing() pa.bearish_engulfing() pa.hammer() pa.doji()
pa.streak.green(3) pa.streak.red(3) pa.first_red_day()
pa.consolidation(days=7) pa.breakout_up(7) pa.breakout_down(7)
pa.run_up(min_pct=2.0) pa.parabolic_up() pa.giant_uptrend()
Compose them into the setup you actually trade
# 7-day box, then close breaks the box high
entry = pa.consolidation(days=7) & pa.crosses_above(pa.range_high(7))
# three green candles, then a bearish engulfing → fade it
fade = pa.streak.green(3).then(pa.bearish_engulfing())
# break above 21-day daily resistance, but only near VWAP
intraday = pa.crosses_above(pa.resistance(21, tf="1d")) & pa.near(pa.vwap())
Trade it: risk/reward brackets (stolgo.trade)
stolgo.trade turns a signal into an order with a stop and target — no manual SL/TP math.
from stolgo import trade
trade.long(ctx, rr=(1, 2), stop="candle_low", qty=0.05) # target = 2× risk
trade.short(ctx, rr=(1, 3), stop="candle_high", qty=0.05)
trade.bracket_hit(ctx, bracket) # -> "stop", "target", or None on this bar
trade.close(ctx) # flatten (longs sell, shorts cover)
Size by fixed qty, or risk a fixed fraction of equity per trade with
size_risk_pct=0.01.
Presets: battle-tested setups in one line
Don't want to wire rules yourself? pa.preset.* returns ready-made rules.
import stolgo.pa as pa
pa.preset.scalp_green_fade(min_green=3) # fade a green run on a reversal candle
pa.preset.consolidation_breakout(days=7) # classic box breakout
pa.preset.breakout_intraday(days=7) # daily S/R, intraday trigger → (long, short)
pa.preset.failed_break_intraday(days=7) # fade failed breaks of daily S/R
pa.preset.parabolic_short(min_pct=2.0) # short the first red day after a parabola
See docs/PA.md for the full grammar, multi-timeframe levels, and every
rule. Runnable bots live under examples/pa/.
Not just price action
Prefer indicators or vector signals? The same engine runs those too.
from stolgo import Backtest, Context, Strategy, load
from stolgo.signals import sma
class Trend(Strategy):
def on_start(self, ctx: Context) -> None:
self._sma = sma(ctx.data.close, 50)
def on_bar(self, ctx: Context) -> None:
if ctx.position.flat and ctx.data.close[-1] > self._sma[ctx.i]:
ctx.buy(size_pct=0.25)
elif not ctx.position.flat and ctx.data.close[-1] < self._sma[ctx.i]:
ctx.close()
df = load("ohlcv.csv", symbol="BTCUSDT")
print(Backtest(Trend(), df, cash=100_000, commission=0.001).run().summary())
Data sources
stolgo does not lock you into one vendor. Use whichever fits your workflow.
bandl — Bandl
bandl aggregates OHLCV from crypto exchanges, equity feeds, and other providers. stolgo wraps it as Bandl: fetch, normalize columns, optional parquet cache, then backtest.
from stolgo import Bandl
df = Bandl(provider="crypto").history("BTCUSDT", "1d", start, end)
Use this for crypto (e.g. BTCUSDT), Indian/US equity, and any symbol bandl supports. Configure credentials per bandl’s docs when required.
Local files — load
For research archives, exports, or offline work:
from stolgo import load
df = load("data/btc_daily.parquet", symbol="BTCUSDT")
Supports .csv, .parquet, .pq. Timestamps are parsed to UTC; columns are normalized to open, high, low, close, volume.
Bring your own DataFrame
Already have pandas OHLCV? Pass it directly to Backtest as long as it has a UTC datetime index (or a timestamp column).
Public API (v0.2)
| Import | Purpose |
|---|---|
stolgo.pa |
Price-action toolkit: levels, relations, patterns, presets |
trade |
Risk/reward brackets: trade.long, trade.short, trade.bracket_hit, trade.close |
Backtest |
High-level runner → RunResult |
Strategy |
Base class: on_start, on_bar, on_fill, on_end |
Context |
Per-bar API: ctx.data, ctx.position, ctx.buy / ctx.close |
Bandl |
Market data via bandl (BandlDataSource alias) |
load |
Load CSV / Parquet |
RunResult |
metrics, trades, equity, report |
parameter_sweep |
Grid search over strategy parameters |
stolgo.signals |
sma, ema, atr, rsi, donchian, … |
Advanced: Engine, RunConfig, Pipeline (cross-sectional), stolgo.report.export_all.
Examples
Price action (stolgo.pa + trade) — start here:
| Script | Setup |
|---|---|
examples/pa/btc_21d_sr_intraday.py |
Break of 21-day daily support/resistance on intraday bars (long + short) |
examples/pa/btc_consolidation_pa.py |
7-day consolidation breakout with a 1:4 bracket |
examples/pa/scalp_green_fade.py |
Fade a green streak on a reversal candle |
examples/pa/intraday_breakout.py |
pa.preset.breakout_intraday long/short |
examples/pa/failed_break.py |
Fade failed breaks of daily S/R |
examples/pa/parabolic_short.py |
Short the first red day after a parabolic run |
Indicators & vector signals:
| Script | Demonstrates |
|---|---|
examples/trend_breakout_backtest.py |
SMA trend, signals, tearsheet |
examples/vector_momentum_backtest.py |
Vector entries / exits (no on_bar body) |
examples/parameter_sweep.py |
100-combo parameter sweep |
CLI:
stolgo my_strategy.py --class MyStrategy --data ohlcv.csv --output ./results
How it works (short)
OHLCV (Bandl / load / DataFrame)
↓
Backtest(strategy, data).run()
↓
Engine: for each bar → match fills → on_bar → risk → submit orders
↓
RunResult: equity, trades, metrics, Plotly tearsheet
Default fill model: signal on bar t → fill at bar t+1 open (fill_on="next_open"). Use fill_on="close" when you want same-bar close fills (e.g. breakout-on-close setups).
Legacy price-action helpers
The original stolgo modules (candlestick, breakout, trend, …) remain available for pattern checks on raw DataFrames. New projects should prefer the Strategy + Backtest API above.
from stolgo.candlestick import CandleStick
from stolgo.breakout import Breakout
cs = CandleStick()
is_engulfing = cs.is_bullish_engulfing(df)
Development
git clone https://github.com/stockalgo/stolgo.git
cd stolgo
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -q
Architecture notes: docs/HLD.md.
Contributing
Pull requests are welcome. For larger changes, open an issue first. Follow PEP 8, add tests for new behaviour, and keep the public API intuitive (prefer from stolgo import … over deep internal paths).
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stolgo-0.2.0.tar.gz.
File metadata
- Download URL: stolgo-0.2.0.tar.gz
- Upload date:
- Size: 65.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79bd1c53825a650120412ba5dc52b118c4e66150076e03e4907e5955bee893bb
|
|
| MD5 |
fea5f588d36644a195eaa19b3503ea25
|
|
| BLAKE2b-256 |
d466eac14d2283911d6c744b122b9689880ba425186bf96e25bf1b041898c326
|
Provenance
The following attestation bundles were made for stolgo-0.2.0.tar.gz:
Publisher:
publish.yml on stockalgo/stolgo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stolgo-0.2.0.tar.gz -
Subject digest:
79bd1c53825a650120412ba5dc52b118c4e66150076e03e4907e5955bee893bb - Sigstore transparency entry: 2093548289
- Sigstore integration time:
-
Permalink:
stockalgo/stolgo@2abb427df8dd10db75e7c3824776551f0f731b80 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/stockalgo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2abb427df8dd10db75e7c3824776551f0f731b80 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file stolgo-0.2.0-py3-none-any.whl.
File metadata
- Download URL: stolgo-0.2.0-py3-none-any.whl
- Upload date:
- Size: 74.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d6944597c890dc54c16c40a5f1ca1d957766604c967eff6bd06cef7c87cb72b
|
|
| MD5 |
94982dadf5fc9c5cbcb5a6e3e3603496
|
|
| BLAKE2b-256 |
56de880b543499b9ed10edc76b2d5628bf2e44a8892afbbeda649f400628b0b8
|
Provenance
The following attestation bundles were made for stolgo-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on stockalgo/stolgo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stolgo-0.2.0-py3-none-any.whl -
Subject digest:
9d6944597c890dc54c16c40a5f1ca1d957766604c967eff6bd06cef7c87cb72b - Sigstore transparency entry: 2093548520
- Sigstore integration time:
-
Permalink:
stockalgo/stolgo@2abb427df8dd10db75e7c3824776551f0f731b80 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/stockalgo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2abb427df8dd10db75e7c3824776551f0f731b80 -
Trigger Event:
workflow_dispatch
-
Statement type: