Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Backtest & Optimization Engine

This is a standalone, purely mathematical engine for running algorithmic trading backtests and optimizations. It operates exclusively on pre-loaded raw data and instantiated strategy objects.

Prerequisites

To use the optimization features, you must have pygmo installed. Pygmo is used for advanced heuristic optimization (using the DE1220 algorithm and an island-model archipelago) to find the most robust strategy parameters.

pip install pygmo numba numpy

(Note: pygmo may require conda or specific system libraries depending on your OS. Refer to the pygmo documentation for more details.)

File Structure & What Each File Does

Core Modules

  • types.py: Defines canonical data structures used throughout the engine. Most importantly, it defines DataTuple—a standard tuple of NumPy arrays (symbol, timestamps, opens, highs, lows, closes, volume).
  • metrics.py: Contains numba-optimized (JIT-compiled) risk metric calculations such as calculate_sharpe, calculate_sortino, calculate_max_drawdown, and calculate_value_at_risk. These are pure mathematical functions.
  • backtest.py: Contains the execute_backtest logic. It takes a strategy instance and a list of DataTuples, evaluates the strategy, and returns a BacktestResult (Net Profit, Win Rate, Sharpe, Sortino, Max Drawdown, etc.). Supports multiprocessing.
  • optimize.py: Contains the execute_optimization logic using pygmo. It performs Walk-Forward Analysis (WFA) or single-period optimization over the raw data to find robust parameter combinations for a given strategy class.

base_strategy/ Sub-package

This directory houses the foundational class and utilities that all trading strategies must inherit from.

  • BaseStrategy.py: The BaseStrategy class itself. Exposes user-friendly methods like buy(), sell(), and close().
  • PositionManager.py: Tracks position sizing, entry prices, and calculates trade returns (handles both long and short positions).
  • StrategyContext.py: Manages the execution state (current index) to strictly prevent look-ahead bias during historical simulation.
  • IndicatorWrapper.py: Wraps indicators so they respect the current execution context and only return data up to the current simulation step.
  • DataAccessor.py: Provides context-aware access to price data (self.data.close, self.data.high, etc.) during the next() loop.

Usage

Because the engine is "dumb" and decoupled, you are responsible for feeding it clean DataTuples.

Running a Backtest

import numpy as np
from engine.base_strategy import BaseStrategy
from engine.backtest import execute_backtest
from engine.types import DataTuple

# 1. Define a strategy
class MyStrategy(BaseStrategy):
    def next(self):
        # Example logic: buy if close > open
        if self.data.close[-1] > self.data.open[-1]:
            self.buy(1.0)
        else:
            self.close()

# 2. Prepare raw data (symbol, timestamps, opens, highs, lows, closes, volumes)
data: DataTuple = (
    "AAPL",
    np.array([1, 2, 3]),
    np.array([100, 101, 102]),
    np.array([101, 103, 104]),
    np.array([99, 100, 101]),
    np.array([101, 102, 103]),
    np.array([1000, 1500, 1200])
)

# 3. Execute
strategy_instance = MyStrategy()
results = execute_backtest(
    strategy_instance=strategy_instance,
    data_list=[data],
    multiprocess=False
)

print(results)

Running an Optimization

The optimizer requires your strategy to implement validate_params and get_optimization_params.

from engine.optimize import execute_optimization

class MyOptimizableStrategy(BaseStrategy):
    def validate_params(self, **kwargs):
        return True
        
    @staticmethod
    def get_optimization_params():
        # param_name: (min, max)
        return {
            "ma_period": (10, 50),
            "threshold": (1.0, 5.0)
        }

    # ... implement next() using kwargs ...

# Execute Walk-Forward Optimization
best_params_results = execute_optimization(
    strategy_class=MyOptimizableStrategy,
    data=data,
    pop=40,   # Population size per island
    gen=50,   # Generations
    single=False # Set True for single-period optimization instead of WFA
)

print("Best Parameters from Walk-Forward Analysis:", best_params_results)

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages