Skip to main content

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:

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:

  1. Create a strategy class by inheriting from Strategy.
  2. In Strategy.init(), attach one or more data sources with Strategy.add_data().
  3. Still in Strategy.init(), build indicator arrays with the built-in indicator catalog on self.ta or the package-level indicators, then register them with Strategy.Indicator().
  4. In Strategy.next(), read the current bar through properties such as DataSource.COpen and DataSource.CClose, then place orders through the broker stored in Strategy.positions.
  5. Run the strategy with SimpleBacktester.run().
  6. Inspect the returned BacktestReport, including BacktestReport.total_return, BacktestReport.periods_per_year, BacktestReport.plot(), and the printable summary from BacktestReport.__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:

  • Open
  • High
  • Low
  • Close
  • Volume

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():

5. Understand order sizing

Order sizing in Quantex is simple but important:

  • Broker.buy() treats quantity as a fraction of available cash unless you pass amount
  • Broker.sell() uses the same sizing calculation and can open or increase a short position
  • Broker.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:

Data sources

DataSource wraps a pandas DataFrame and exposes both full history and current-bar values.

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:

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


Download files

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

Source Distribution

quantex-0.5.2.tar.gz (64.4 kB view details)

Uploaded Source

Built Distribution

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

quantex-0.5.2-py3-none-any.whl (68.8 kB view details)

Uploaded Python 3

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

Hashes for quantex-0.5.2.tar.gz
Algorithm Hash digest
SHA256 7bc39a687f7030987ad70b98c15529c4cdda29477390005dce5b365d76908d26
MD5 8678d4b3dba191465dbf565f77dc18d6
BLAKE2b-256 fb0248e5fe0d0950356f3a13d7aea69e519d9a2abbc70d3ea02f771525771c73

See more details on using hashes here.

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

Hashes for quantex-0.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b312818e2aee1a065dbdc84cb567302262a56a1f9ebe66722274ccceb78a3fea
MD5 c80c2e9075486a63b8bde777da04a8b1
BLAKE2b-256 e424cfc40c2552bf204105f44b62e7e5467a37f4ebeab9d88c69c3ff79821ad2

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