A simple quant strategy creation and backtesting package.
Project description
Quantex
Quantex is a Python library for writing trading strategies and replaying them on historical market data.
It gives you a small set of building blocks:
Strategyfor your trading rulesDataSourceplusCSVDataSourceandParquetDataSourcefor OHLCV market dataBrokerobjects, created automatically per symbol, for order placement and position stateSimpleBacktesterfor simulation and parameter searchBacktestReportfor results, equity history, and summary statistics
The project is intentionally small. It does not try to be a full research platform, portfolio database, or live-trading engine. Instead, it focuses on a straightforward workflow: load historical bars, define strategy logic, simulate orders, and inspect the results.
Installation
Quantex requires Python 3.11 or newer and is published as quantex.
pip install quantex
What the library actually does
At runtime, a typical Quantex workflow looks like this:
- Create a strategy class by inheriting from
Strategy. - In
Strategy.init(), attach one or more data sources withStrategy.add_data(). - Still in
Strategy.init(), build indicator arrays with the built-in indicator catalog onself.taor the package-levelindicators, then register them withStrategy.Indicator(). - In
Strategy.next(), read the current bar through properties such asDataSource.COpenandDataSource.CClose, then place orders through the broker stored inStrategy.positions. - Run the strategy with
SimpleBacktester.run(). - Inspect the returned
BacktestReport, includingBacktestReport.total_return,BacktestReport.periods_per_year,BacktestReport.plot(), and the printable summary fromBacktestReport.__str__().
Usage
The example below assumes no prior knowledge of the library.
1. Prepare a data file
Quantex expects historical bar data with these columns:
OpenHighLowCloseVolume
The first column in a CSV file is typically the timestamp index used by CSVDataSource.
Example:
Date,Open,High,Low,Close,Volume
2024-01-01,100,101,99,100.5,1500
2024-01-02,100.5,102,100,101.8,1700
2024-01-03,101.8,103,101,102.6,1650
2. Write a strategy
from quantex import Strategy, CSVDataSource, SimpleBacktester
import pandas as pd
class MovingAverageCross(Strategy):
def __init__(self, fast_period=5, slow_period=20):
super().__init__()
self.fast_period = fast_period
self.slow_period = slow_period
def init(self):
data = CSVDataSource("data.csv")
self.add_data(data, "TEST")
close = self.data["TEST"].Close
self.fast_ma = self.Indicator(self.ta.sma(close, self.fast_period))
self.slow_ma = self.Indicator(self.ta.sma(close, self.slow_period))
def next(self):
if len(self.fast_ma) < 2 or len(self.slow_ma) < 2:
return
broker = self.positions["TEST"]
crossed_up = self.fast_ma[-2] <= self.slow_ma[-2] and self.fast_ma[-1] > self.slow_ma[-1]
crossed_down = self.fast_ma[-2] >= self.slow_ma[-2] and self.fast_ma[-1] < self.slow_ma[-1]
if crossed_up and broker.is_closed():
broker.buy(quantity=1.0)
elif crossed_down and broker.is_long():
broker.close()
3. Run a backtest
strategy = MovingAverageCross(fast_period=5, slow_period=20)
backtester = SimpleBacktester(strategy, cash=10_000)
report = backtester.run()
print(report)
report.plot()
4. Understand what happens during the backtest
When you call SimpleBacktester.run():
- the backtester deep-copies your strategy in
SimpleBacktester.__init__() - starting cash is split evenly across all attached symbols in
SimpleBacktester.run() - each data source advances one bar at a time by updating
DataSource.current_index - each symbol's broker processes pending orders through
Broker._iterate() - market orders execute at the current bar's open price through
DataSource.COpen - equity is tracked into the final
BacktestReport.PnlRecord
5. Understand order sizing
Order sizing in Quantex is simple but important:
Broker.buy()treatsquantityas a fraction of available cash unless you passamountBroker.sell()uses the same sizing calculation and can open or increase a short positionBroker.close()places a market order that offsets the current position
Because of this design, buy(0.5) means “use roughly half of the broker cash for this symbol”, not “buy half a share”.
Core concepts
Strategy
Your strategy is a class that implements two methods:
Strategy.init(): called once before the bar-by-bar loop startsStrategy.next(): called on every step of the simulation
Data sources
DataSource wraps a pandas DataFrame and exposes both full history and current-bar values.
- historical arrays:
DataSource.Open,DataSource.High,DataSource.Low,DataSource.Close,DataSource.Volume - current bar values:
DataSource.COpen,DataSource.CHigh,DataSource.CLow,DataSource.CClose,DataSource.CVolume
Indicators
Strategy now exposes a built-in technical analysis catalog at self.ta, and the package exports the same catalog as quantex.indicators.
Included indicators cover trend, momentum, volatility, volume, and more advanced studies, including:
- moving averages:
sma,ema,wma,dema,tema,kama - momentum and oscillators:
rsi,stochastic_oscillator,cci,williams_r,roc,momentum,trix,fisher_transform,ultimate_oscillator - volatility and channels:
atr,bollinger_bands,keltner_channels,donchian_channels,volatility - volume and trend strength:
obv,mfi,adx,aroon,vortex - advanced and research-oriented tools:
ichimoku_cloud,zscore,sharpe_ratio,sortino_ratio,hurst_exponent,linear_regression_slope
Strategy.Indicator() still wraps each NumPy array in TimeNDArray, which only exposes data up to the current bar.
Example:
from quantex import Strategy, CSVDataSource, SimpleBacktester
class MacdTrendStrategy(Strategy):
def init(self):
self.add_data(CSVDataSource("data.csv"), "TEST")
close = self.data["TEST"].Close
self.ema_20 = self.Indicator(self.ta.ema(close, 20))
self.rsi_14 = self.Indicator(self.ta.rsi(close, 14))
macd_line, macd_signal, _ = self.ta.macd(close)
self.macd_line = self.Indicator(macd_line)
self.macd_signal = self.Indicator(macd_signal)
Broker and orders
Each call to Strategy.add_data() also creates a Broker for that symbol.
Supported order behavior in the current codebase:
- market orders and limit orders via
OrderType - pending, active, and complete order states via
OrderStatus - optional stop-loss and take-profit triggers stored on
Order - percentage or cash commissions via
CommissionType
Optimization
SimpleBacktester.optimize() runs a grid search over every parameter combination you provide.
SimpleBacktester.optimize_parallel() does the same work in multiple processes, then re-runs the best parameter set locally to produce a full BacktestReport.
Minimal example:
params = {
"fast_period": range(5, 11),
"slow_period": range(15, 31, 5),
}
best_params, best_report, results = backtester.optimize(
params,
constraint=lambda p: p["fast_period"] < p["slow_period"],
)
print(best_params)
print(best_report)
print(results.head())
Train/Validate/Test Optimization
SimpleBacktester.optimize_with_split() performs grid search with ML-style train/validate/test data splits to help detect overfitting.
result = backtester.optimize_with_split(
{"fast_period": [5, 10, 15], "slow_period": [20, 30, 50]},
train_ratio=0.6,
validate_ratio=0.2,
test_ratio=0.2,
selection_criterion="validate", # Select best params based on validate performance
)
print(f"Best params: {result.best_params}")
print(f"Train Sharpe: {result.train_metrics['sharpe']}")
print(f"Validate Sharpe: {result.validate_metrics['sharpe']}")
print(f"Test Sharpe: {result.test_metrics['sharpe']}")
Gradient Descent Optimization
SimpleBacktester.optimize_gradient_descent() uses gradient descent for continuous parameter optimization, supporting momentum, learning rate schedules, and integer parameter handling.
result = backtester.optimize_gradient_descent(
param_init={"fast_period": 10.0, "slow_period": 30.0},
param_bounds={
"fast_period": (2.0, 50.0),
"slow_period": (10.0, 100.0)
},
learning_rate=0.01,
iterations=100,
momentum=0.9,
integer_params={"fast_period", "slow_period"},
)
print(f"Optimized params: {result.best_params}")
print(result.train_report)
Monte Carlo Simulation
SimpleBacktester.monte_carlo() runs Monte Carlo simulations to test strategy robustness through two modes:
- Trade Order Randomization (
MonteCarloMode.TRADE_ORDER): Shuffles the sequence of trade execution while keeping the same trades - Price Path Resampling (
MonteCarloMode.PRICE_PATH): Creates synthetic market scenarios from historical returns
result = backtester.monte_carlo(
simulations=500,
mode="both", # Run both trade order and price path simulations
seed=42,
)
print(result) # Print summary statistics (percentiles, confidence intervals)
result.plot() # Show spaghetti plot of equity curves
Documentation
Project documentation is built with MkDocs. The main docs entry point is docs/index.md, and usage guides live under docs/usage/.
Published documentation: https://dangreen07.github.io/quantex/
Development
The package metadata and dependencies are defined in pyproject.toml.
Typical local workflow:
poetry install
poetry run pytest
poetry run pre-commit install
Tests in tests/test_backtester.py, tests/test_broker.py, tests/test_datasource.py, and tests/test_strategy.py are useful references for the library's current behavior.
License
See LICENSE.md.
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 quantex-0.5.2.tar.gz.
File metadata
- Download URL: quantex-0.5.2.tar.gz
- Upload date:
- Size: 64.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.13.1 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7bc39a687f7030987ad70b98c15529c4cdda29477390005dce5b365d76908d26
|
|
| MD5 |
8678d4b3dba191465dbf565f77dc18d6
|
|
| BLAKE2b-256 |
fb0248e5fe0d0950356f3a13d7aea69e519d9a2abbc70d3ea02f771525771c73
|
File details
Details for the file quantex-0.5.2-py3-none-any.whl.
File metadata
- Download URL: quantex-0.5.2-py3-none-any.whl
- Upload date:
- Size: 68.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.13.1 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b312818e2aee1a065dbdc84cb567302262a56a1f9ebe66722274ccceb78a3fea
|
|
| MD5 |
c80c2e9075486a63b8bde777da04a8b1
|
|
| BLAKE2b-256 |
e424cfc40c2552bf204105f44b62e7e5467a37f4ebeab9d88c69c3ff79821ad2
|