Skip to main content

High-performance C++20 backtesting engine with Python interface

Project description

QuantCore

PyPI Build Coverage C++ License

High-performance backtesting engine for trading strategies, written in C++20 with a Python research interface.


Overview

QuantCore is an event-driven backtester built around an enhanced version of my limit order book simulator. It processes market events chronologically through a priority queue, ensuring no look-ahead bias and no unrealistic assumptions about fill prices.

The C++ core handles all the performance-critical work: event dispatch, order matching, position tracking, and execution simulation. Python sits on top via pybind11 bindings and handles strategy development, parameter optimization, and visualization.

Market Data → EventQueue → Strategy → Signal → OrderBook → Fill → Portfolio

Both bar data and tick data are supported. The engine is bar-agnostic internally - ticks become MarketDataEvent objects like bars do, so strategies work unchanged across both data types.


Quick Start

Bar data

# pip install quantcore
import quantcore as qc

class MyStrategy(qc.Strategy):
    def on_data(self, event):
        if not self.has_position(event.symbol):
            self.generate_signal(event.symbol, qc.SignalType.BUY, 1.0, event.timestamp_ns)

results = qc.run_backtest(
    strategy=MyStrategy(),
    data={'AAPL': qc.load_csv_data('data/aapl.csv', 'AAPL')},
    initial_capital=100_000.0,
)
print(results)

Tick data

results = qc.run_tick_backtest(
    strategy=MyStrategy(),
    tick_data={'AAPL': qc.load_tick_csv('data/aapl_ticks.csv', 'AAPL')},
    initial_capital=100_000.0,
    mm_refresh_interval_ns=1_000_000_000,   # refresh MM quotes once per second
    equity_snapshot_interval_ns=60_000_000_000,  # snapshot equity once per minute
)
print(results)

The same strategy class works for both. event.close is the tick price when running on tick data.

You can also aggregate ticks to bars before running:

ticks = qc.load_tick_csv('data/aapl_ticks.csv', 'AAPL')
bars  = qc.aggregate_ticks_to_bars(ticks, bar_duration_ns=60_000_000_000)  # 1-minute bars

results = qc.run_backtest(strategy=MyStrategy(), data={'AAPL': bars}, initial_capital=100_000.0)

The engine handles fills, position tracking, and PnL automatically. For a full tearsheet:

from quantcore.analytics import calculate_all_metrics, calculate_returns
from quantcore.plotting import plot_full_tearsheet
import numpy as np

equity = np.array(results['equity_curve'])
returns = calculate_returns(equity)
print(calculate_all_metrics(equity))
plot_full_tearsheet(equity, returns)

For the full API reference and usage guide, see docs/usage.md.


Architecture

Every action goes through the event queue. When a strategy calls generate_signal, that signal becomes an OrderEvent, which goes through the order book, produces a FillEvent, which updates the portfolio. All in timestamp order. This is what prevents look-ahead bias: the strategy never sees data from the future.

img.png


Strategy Development

Python Strategy

Subclass qc.Strategy and implement on_data. The same strategy works for bar and tick data - event.close is the close price for bars and the trade price for ticks.

class BollingerBreakout(qc.Strategy):
    def __init__(self, window=20, n_std=2.0):
        super().__init__("BollingerBreakout")
        self.window = window
        self.n_std  = n_std
        self.prices = []

    def on_data(self, event):
        self.prices.append(event.close)
        if len(self.prices) < self.window:
            return

        window_prices = self.prices[-self.window:]
        mean = sum(window_prices) / self.window
        std  = (sum((p - mean) ** 2 for p in window_prices) / self.window) ** 0.5

        upper = mean + self.n_std * std
        lower = mean - self.n_std * std
        pos   = self.get_position(event.symbol)

        if event.close > upper and pos <= 0:
            self.generate_signal(event.symbol, qc.SignalType.BUY,  1.0, event.timestamp_ns)
        elif event.close < lower and pos >= 0:
            self.generate_signal(event.symbol, qc.SignalType.SELL, 1.0, event.timestamp_ns)

    def on_fill(self, fill):
        pass  # optional: react to fills

Portfolio Context

Strategies can access full portfolio state:

def on_data(self, event):
    portfolio = self.get_portfolio()
    if portfolio:
        equity     = portfolio.get_portfolio_value()
        cash       = portfolio.get_cash()
        position   = portfolio.get_position(event.symbol)

Position Sizing

The engine ships with several sizing methods:

from quantcore import FixedPercentage, RiskBased, KellyCriterion

engine = qc.BacktestEngine(100_000.0)
engine.set_position_sizer(qc.FixedPercentage(0.10))  # 10% of capital per trade

Built-in sizers: FixedPercentage, RiskBased, KellyCriterion, EqualWeight, VolatilityTargeting, FixedShares.


Tick Data

Loading

# from CSV - columns: timestamp, price, quantity  (or with side: B/S/buy/sell)
ticks = qc.load_tick_csv('data/aapl_ticks.csv', 'AAPL')

# from Parquet
ticks = qc.load_tick_parquet('data/aapl_ticks.parquet', 'AAPL')

# numpy fast path - (N, 4) array: [timestamp_ns, price, quantity, side]
# side: 0.0 = Buy, 1.0 = Sell
arr = qc.load_tick_parquet('data/aapl_ticks.parquet', use_numpy=True)
engine.add_tick_data('AAPL', arr)

Data Normalization

QuantCore does not adjust raw prices for corporate actions internally. Feed it adjusted data, where close prices are continuous across splits and dividends, for correct results.

Most data vendors offer adjusted data:

  • Yahoo Finance: use Adj Close instead of Close
  • Polygon.io: use the adjusted=true parameter
  • CRSP: adjusted by default
  • Quandl/NASDAQ Data Link: WIKI/PRICES table uses adjusted prices

If you have raw unadjusted data, use CorporateActionsAdjuster:

from quantcore import CorporateActionsAdjuster

adjuster = CorporateActionsAdjuster.from_csv(
    splits_csv='data/aapl_splits.csv',
    dividends_csv='data/aapl_dividends.csv',
)
raw_bars    = qc.load_csv_data('data/aapl_raw.csv', 'AAPL')
adj_bars    = adjuster.adjust(raw_bars)
results     = qc.run_backtest(strategy=MyStrategy(), data={'AAPL': adj_bars}, ...)

Aggregation

# aggregate to any bar duration
bars_1min  = qc.aggregate_ticks_to_bars(ticks, bar_duration_ns=60_000_000_000)
bars_1hour = qc.aggregate_ticks_to_bars(ticks, bar_duration_ns=3_600_000_000_000)
bars_1day  = qc.aggregate_ticks_to_bars(ticks, bar_duration_ns=86_400_000_000_000)

Engine configuration for tick data

Two settings matter most for tick performance:

engine = qc.BacktestEngine(100_000.0)
engine.add_tick_data('AAPL', ticks)

# How often the market maker refreshes its quotes.
# 0 = every tick (default, slowest). 1s interval gives ~5x speedup on 1-second tick data.
engine.set_mm_refresh_interval(1_000_000_000)       # 1 second

# How often the equity curve is snapshotted.
# 0 = every tick (default). Has negligible performance impact but keeps the
# equity curve manageable for large tick datasets.
engine.set_equity_snapshot_interval(60_000_000_000) # 1 minute

run_tick_backtest() sets both to sensible defaults (1s and 60s respectively).

CSV format

timestamp,price,quantity
1700000000,150.25,100
1700000001,150.30,50

# with aggressor side
timestamp,price,quantity,side
1700000000,150.25,100,buy
1700000001,150.30,50,sell

Timestamps in seconds, milliseconds, microseconds, or nanoseconds - detected automatically.


Execution Simulation

Order Types

GOOD_TILL_CANCEL, IMMEDIATE_OR_CANCEL, FILL_OR_KILL, MARKET, GOOD_FOR_DAY

Fees & Slippage

from quantcore import ExecutionConfig

config = ExecutionConfig()
config.maker_fee     = 0.001   # 0.1% maker
config.taker_fee     = 0.002   # 0.2% taker
config.slippage_pct  = 0.0005  # 0.05% slippage
config.latency_ns    = 1_000_000  # 1ms order latency

engine = qc.BacktestEngine(100_000.0, config)

Risk Management

from quantcore import RiskLimits

limits = qc.RiskLimits()
limits.max_position_pct = 0.20   # max 20% per position
limits.max_leverage     = 2.0
limits.max_loss_pct     = 0.15   # halt at 15% drawdown

engine.set_risk_limits(limits)

Performance

Single-threaded. Measured on Windows (Release build, MSVC). Full results in benchmarks/RESULTS.md.

Order book

Pattern Ops/s
Add + cancel (market-maker quote refresh) 13.0 M ops/s
Add + match (taker sweep) 4.9 M ops/s

End-to-end backtest - bar mode

Scenario Bars/s Latency (p99)
1-year (252 bars) ~270 K bars/s 0.93 ms
5-year (1,260 bars) ~270 K bars/s -
1,000-year stress (252,000 bars) ~290 K bars/s -

End-to-end backtest - tick mode

Scenario Ticks/s Wall time
10K ticks, no MM throttle 315 K/s 31.7 ms
10K ticks, 1s MM throttle 1.66 M/s 6.0 ms
10K ticks, 10s MM throttle 2.78 M/s 3.6 ms
1M ticks → 1-min bars (aggregation) 247 M/s 4.1 ms

The market-maker refresh interval is the main performance lever for tick data. With a 1-second throttle the engine processes 1-second tick data at ~5x the unthrottled rate. See benchmarks/RESULTS.md for the full breakdown.

Run the benchmarks yourself:

cmake --build build --target bench_backtest_engine bench_tick_data
./build/bench_backtest_engine
./build/bench_tick_data

python benchmarks/bench_python.py
python benchmarks/bench_tick_python.py

Analytics

After running a backtest, the results dict contains an equity curve and trade log you can feed straight into the analytics module.

from quantcore.analytics import calculate_all_metrics, calculate_returns

equity  = np.array(results['equity_curve'])
returns = calculate_returns(equity)
metrics = calculate_all_metrics(equity)

print(metrics)
# Total Return:     24.31%
# Annualized:       11.82%
# Sharpe Ratio:     1.43
# Sortino Ratio:    2.01
# Max Drawdown:     -8.74%
# Win Rate:         58.3%

Available metrics: total return, CAGR, Sharpe, Sortino, Calmar, max drawdown, drawdown duration, win rate, profit factor, avg win/loss, largest win/loss.

Visualizations

from quantcore.plotting import (
    plot_full_tearsheet,
    plot_equity_curve,
    plot_underwater,
    plot_returns_distribution,
    plot_rolling_metrics,
    plot_monthly_returns_heatmap,
)

plot_full_tearsheet(equity, returns, timestamps=ts)

Example Notebooks

Notebook Strategy Concepts
mean_reversion.ipynb Z-score mean reversion Parameter sensitivity, OU process
sma_crossover.ipynb SMA crossover Trend following, signal generation
pairs_trading.ipynb Statistical arbitrage Cointegration, spread trading
build_your_own_strategy.ipynb Bollinger Band Breakout Full walkthrough from scratch

Installation

Prerequisites

  • CMake 3.15+
  • C++20 compiler (GCC 10+, Clang 12+, MSVC 2022)
  • Python 3.8+
  • pybind11 (pip install pybind11)

Build

git clone https://github.com/SLMolenaar/quantcore.git
cd quantcore

# build the C++ core
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build

# build the Python bindings
cd python
pip install pybind11
python build_module.py

# verify
python -c "import quantcore; print(quantcore.version())"

Run Tests

cmake --build build --target quantcore_tests
./build/quantcore_tests

For the full Python API reference, see docs/usage.md.


Project Structure

quantcore/
├── cpp/
│   ├── backtesting/          # Engine, events, portfolio
│   │   ├── tick_data.h       # TickData struct and TickSeries
│   │   └── tick_data_loader.h# CSV loader and aggregate_to_bars
│   ├── strategies/           # C++ strategy implementations
│   ├── orderbook/            # Order book (from orderbook-simulator-cpp)
│   └── tests/                # GoogleTest suite
├── python/
│   ├── quantcore/            # Python package
│   │   ├── __init__.py       # Public API
│   │   ├── analytics.py      # Performance metrics
│   │   ├── plotting.py       # Visualizations
│   │   └── tick_parquet_loader.py  # Parquet loader for tick data
│   ├── bindings.cpp          # pybind11 bindings
│   └── build_module.py       # Build helper
├── examples/                 # Jupyter notebooks
├── benchmarks/               # Benchmark suite
│   ├── bench_backtest_engine.cpp
│   ├── bench_tick_data.cpp
│   ├── bench_python.py
│   ├── bench_tick_python.py
│   └── RESULTS.md
├── CMakeLists.txt
└── README.md

vs. Alternatives

QuantCore Backtrader Zipline
Core language C++20 Python Python
Order book simulation ✅ Real LOB
Tick data support ✅ Native
Event-driven
Look-ahead prevention ✅ Priority queue
Python strategy API ✅ pybind11 ✅ native ✅ native
Throughput (bars/s) ~300K ~7.7K unverified
Maintenance Active Stale Inactive

Throughput measured on SMA(50/200) crossover, 50K daily bars, Release build, Windows. Reproduce: python benchmarks/qcVsBacktrader.py --bars 50000 --runs 20

The main differentiator is the order book. Backtrader and Zipline assume you fill at the bar's close price. QuantCore routes orders through a real price-time priority matching engine, which gives you realistic partial fills, spread simulation, and tick-level execution when you have tick data.


Contributing

See CONTRIBUTING.md. Open areas if you want to dig in:

  • Stop / Stop-Limit orders: order type enum and matching engine
  • VWAP / TWAP algos: ExecutionEngine, child order slicing
  • Trading calendar: holiday/early-close filtering before bars hit the engine
  • Multi-strategy portfolio: shared capital across strategies with a meta-allocator
  • Parallel sweeps on Linux: n_jobs exists but Windows spawn overhead kills it; a Linux worker pool would make it actually useful

The engine doesn't handle corporate actions, survivorship bias, or timezone normalization. Feed it clean adjusted data and none of those are problems.


License

MIT: see LICENSE.

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

quantcore-0.2.3-cp312-cp312-win_amd64.whl (373.6 kB view details)

Uploaded CPython 3.12Windows x86-64

quantcore-0.2.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl (1.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.26+ x86-64manylinux: glibc 2.28+ x86-64

quantcore-0.2.3-cp312-cp312-macosx_15_0_arm64.whl (326.9 kB view details)

Uploaded CPython 3.12macOS 15.0+ ARM64

quantcore-0.2.3-cp311-cp311-win_amd64.whl (371.2 kB view details)

Uploaded CPython 3.11Windows x86-64

quantcore-0.2.3-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl (1.0 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.26+ x86-64manylinux: glibc 2.28+ x86-64

quantcore-0.2.3-cp311-cp311-macosx_15_0_arm64.whl (325.4 kB view details)

Uploaded CPython 3.11macOS 15.0+ ARM64

quantcore-0.2.3-cp310-cp310-win_amd64.whl (370.3 kB view details)

Uploaded CPython 3.10Windows x86-64

quantcore-0.2.3-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl (1.0 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.26+ x86-64manylinux: glibc 2.28+ x86-64

quantcore-0.2.3-cp310-cp310-macosx_15_0_arm64.whl (324.1 kB view details)

Uploaded CPython 3.10macOS 15.0+ ARM64

quantcore-0.2.3-cp39-cp39-win_amd64.whl (387.6 kB view details)

Uploaded CPython 3.9Windows x86-64

quantcore-0.2.3-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl (1.0 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.26+ x86-64manylinux: glibc 2.28+ x86-64

quantcore-0.2.3-cp39-cp39-macosx_15_0_arm64.whl (324.2 kB view details)

Uploaded CPython 3.9macOS 15.0+ ARM64

File details

Details for the file quantcore-0.2.3-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: quantcore-0.2.3-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 373.6 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for quantcore-0.2.3-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 e12ce54cd83047a6a44abcdab0a13178a8be2f2997b773d898013294803a15a8
MD5 58102dbca1a4e037704ed03f22d5008d
BLAKE2b-256 eeb5d4308f92dc1b40cddfafe4abb2922639206a70764a0f4897435bb6bcbdbd

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for quantcore-0.2.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 cbdffd79f6637c1bbc92f5258cd19ac16f0562f611f3a37a20dadd4f6797ce7b
MD5 7b870ca667fd627b41c6e17187fba4ba
BLAKE2b-256 7ef0c74ec50d6821d44969f7390f41fbff61e7ec03b937bfb8fbb7a531467869

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp312-cp312-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for quantcore-0.2.3-cp312-cp312-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 c0f78868e71f9af2a66fbe9be896de4d0c8da9975fece0429657df7ab58a70d0
MD5 5207f9feaa6c6a5b7e24773de03d6779
BLAKE2b-256 20ffa611ef1eb96f9b96f744b6eab683bde521f93eae3f77e5415edf208e450f

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: quantcore-0.2.3-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 371.2 kB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for quantcore-0.2.3-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 ece1df9d94087bd22acc6da4ef5b3a5c35c0166bfe80965648682b426cbb8a5b
MD5 b8271487ac5e0162d5e778f964ac7145
BLAKE2b-256 89bbe098174c06384744b0acab8e6a1c1e1800acaee80f133b82204ef3de9c32

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for quantcore-0.2.3-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e874f0fda55deb7befc1820343391b3ecb67844dd16cfc9916fbe907a022ffea
MD5 e1f0e3325a835011a315fd1cd7f6225a
BLAKE2b-256 418131179facbccc89199204a69a315b43ae72472308da3afe6c34a759cf4b8f

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp311-cp311-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for quantcore-0.2.3-cp311-cp311-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 1db512a442eec5628e9cb88ca681f0371b7dd74081fc1c61f85abb5ef5b792a2
MD5 3be95cdc454fd9cef829ee8673d2e4fa
BLAKE2b-256 d319abc75bc62bc387d299bf4641ac73c5eae19a031b8dd832d842da0da5bac5

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: quantcore-0.2.3-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 370.3 kB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for quantcore-0.2.3-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 88db8fcff7934b593c0c2535f18c2c9ac9bc381da3a468a055edd1f6f2911627
MD5 8e527f57e55d60e75734fa89bc2f1211
BLAKE2b-256 88f6cd26a252147ad126588aafd1e1a8e9d37c862f0750af64f1505bec242b70

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for quantcore-0.2.3-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 2288aa5f406f0291fb2f358c1462f267a4ac02f4ff2bd405f1ac959df38f4b04
MD5 80a4627db85cff582a5d548f5fb93c38
BLAKE2b-256 227173dedb26eac512b374498b59817060a0e459b49ba8af5b70599fa6d28895

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp310-cp310-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for quantcore-0.2.3-cp310-cp310-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 8935aba1a2189e1a80827d8808b2395825000806a531419607eefa9e29ff2557
MD5 211db868c4c9f857be82b5d86712bb59
BLAKE2b-256 635560b126fdcb4db25a39ea36c545973d7378b5e0d2b6f6a99ba0a777cf7b1a

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp39-cp39-win_amd64.whl.

File metadata

  • Download URL: quantcore-0.2.3-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 387.6 kB
  • Tags: CPython 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for quantcore-0.2.3-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 babdf78fcffb122501600bf61dec39ce2e33a5f5b210081a611105d638eba1f4
MD5 89ffae3889b34bf2ff1af417a9986c0f
BLAKE2b-256 d4094b972f26951757f69d4de5c307359182a542bdf093f118fddf616c13d588

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for quantcore-0.2.3-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8085bf5207616e7b7706c1a4fdc7fd7845c14528974d08f0773f910f511e058a
MD5 e633b2066d99df1df14f8bea11c2b6be
BLAKE2b-256 e166a9ae15779c175cf892a4ff56d9db5393517da72ce4ac43cd4219b7bb77c8

See more details on using hashes here.

File details

Details for the file quantcore-0.2.3-cp39-cp39-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for quantcore-0.2.3-cp39-cp39-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 1d4f94dbbc7070b0bc9cc9e63e205d135c5f6e28bcb174f57a8a552b89112dbe
MD5 a77e91c320936ba42816fec1d95953f4
BLAKE2b-256 05952c0533c64f260cf816e3acbed43d938dec3b34227989cd6c2df36852c263

See more details on using hashes here.

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