Skip to main content

Lightweight quant-grade backtesting for price-action and factor strategies

Project description

stolgo

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.

bandlBandl

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

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stolgo-0.2.0.tar.gz (65.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stolgo-0.2.0-py3-none-any.whl (74.8 kB view details)

Uploaded Python 3

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

Hashes for stolgo-0.2.0.tar.gz
Algorithm Hash digest
SHA256 79bd1c53825a650120412ba5dc52b118c4e66150076e03e4907e5955bee893bb
MD5 fea5f588d36644a195eaa19b3503ea25
BLAKE2b-256 d466eac14d2283911d6c744b122b9689880ba425186bf96e25bf1b041898c326

See more details on using hashes here.

Provenance

The following attestation bundles were made for stolgo-0.2.0.tar.gz:

Publisher: publish.yml on stockalgo/stolgo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for stolgo-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9d6944597c890dc54c16c40a5f1ca1d957766604c967eff6bd06cef7c87cb72b
MD5 94982dadf5fc9c5cbcb5a6e3e3603496
BLAKE2b-256 56de880b543499b9ed10edc76b2d5628bf2e44a8892afbbeda649f400628b0b8

See more details on using hashes here.

Provenance

The following attestation bundles were made for stolgo-0.2.0-py3-none-any.whl:

Publisher: publish.yml on stockalgo/stolgo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page