Running from Python#

All functionality available through the Excel workflow is also accessible through Python. This lets you integrate backtests into larger scripts, notebooks, or automation pipelines.

Single Backtest#

The main() function runs the full workflow: data loading, backtest execution, metrics, Excel report, and optional interactive dashboard.

import kaxanuk.backtest_engine.backtest_engine
from kaxanuk.backtest_engine.config_handlers import ExcelConfigurator
from kaxanuk.backtest_engine.input_handlers.csv_input import CsvInput
from kaxanuk.backtest_engine.input_handlers.csv_portfolio_input_handler import CsvPortfolioInputHandler
from kaxanuk.backtest_engine.services.env_loader import load_config_env

load_config_env()

configurator = ExcelConfigurator(file_path="Config/backtest_engine_parameters.xlsx")
configuration = configurator.get_configuration()

market_data_handler = CsvInput(input_dir=configuration.input_market_data_directory)
portfolio_handler = CsvPortfolioInputHandler(base_dir=configuration.input_portfolio_directory)

result = kaxanuk.backtest_engine.backtest_engine.main(
    configuration=configuration,
    input_handlers=[market_data_handler],
    portfolio_handlers=[portfolio_handler],
    logger_level=configurator.get_logger_level(),
    launch_dashboard=True,
)

if result.success:
    print("Final value:", result.data["final_total_portfolio_value"])
else:
    print("Error:", result.error)

Parameter Sensitivity Analysis#

Run multiple parameter combinations while loading market data only once.

See also

Full method signatures in API Reference.

import datetime
from kaxanuk.backtest_engine.backtest import BacktestVariationRunner
from kaxanuk.backtest_engine.config_handlers import ExcelConfigurator
from kaxanuk.backtest_engine.data_processors.data_pipeline_executor import execute_data_pipeline
from kaxanuk.backtest_engine.input_handlers.csv_input import CsvInput
from kaxanuk.backtest_engine.input_handlers.csv_portfolio_input_handler import CsvPortfolioInputHandler
from kaxanuk.backtest_engine.modules.comparative_report import generate_comparative_excel_report
from kaxanuk.backtest_engine.backtest.orchestrators.backtest_results_summary import get_summary_dataframe
from kaxanuk.backtest_engine.services.env_loader import load_config_env

load_config_env()

configurator = ExcelConfigurator(file_path="Config/backtest_engine_parameters.xlsx")
configuration = configurator.get_configuration()

pipeline_result = execute_data_pipeline(
    configuration=configuration,
    input_handlers=[CsvInput(input_dir=configuration.input_market_data_directory)],
    portfolio_handlers=[CsvPortfolioInputHandler(base_dir=configuration.input_portfolio_directory)],
)

suite = BacktestVariationRunner(base_config=configuration, pipeline_result=pipeline_result)

# Commission grid
suite.add_commission_grid(commission_values=[0.0, 0.005, 0.01, 0.02, 0.05], name_prefix="commission")

# Named variations
suite.add_variation("baseline",   commission_cents=0.01, cash_reserve_percentage=0.05)
suite.add_variation("low_cost",   commission_cents=0.005, cash_reserve_percentage=0.05)
suite.add_variation("aggressive", commission_cents=0.005, cash_reserve_percentage=0.02)

# Rolling windows
suite.add_rolling_window_variations(
    start_date=datetime.date(2020, 1, 2),
    end_date=datetime.date(2024, 12, 31),
    window_months=12,
    step_months=6,
    name_prefix="rolling",
)

results = suite.run_all()
summary = get_summary_dataframe(results)
print(summary[["name", "final_value", "sharpe_ratio", "annualized_return", "max_drawdown"]])

report_path = generate_comparative_excel_report(
    suite_results=results,
    output_directory=configuration.backtest_results_output_directory,
    report_name="sensitivity_analysis",
    ranking_metric="sharpe_ratio",
)

Batch Testing — Multiple Portfolios#

Backtest several portfolios at once with optional per-portfolio parameter variations.

See also

Full method signatures in API Reference.

from kaxanuk.backtest_engine.backtest import MultiPortfolioBacktester
from kaxanuk.backtest_engine.config_handlers import BatchConfigParser
from kaxanuk.backtest_engine.input_handlers.csv_input import CsvInput
from kaxanuk.backtest_engine.input_handlers.csv_portfolio_input_handler import CsvPortfolioInputHandler
from kaxanuk.backtest_engine.backtest.orchestrators.backtest_results_summary import get_summary_dataframe
from kaxanuk.backtest_engine.services.env_loader import load_config_env

load_config_env()

batch_config = BatchConfigParser.parse_dict({
    "global_defaults": {
        "start_date": "2020-01-02",
        "end_date": "2024-12-31",
        "benchmark_file_name": "SPY",
        "commission_cents": 0.01,
        "cash_reserve_percentage": 0.05,
        "initial_capital": 100000,
        "market_data_input_format": "csv",
        "portfolio_input_format": "csv",
        "input_market_data_directory": "./Input/Data",
        "input_portfolio_directory": "./Input/Portfolios",
        "backtest_results_output_directory": "./Output/batch",
        "user_column_commission_price": "vwap",
        "user_column_trade_execution_price": "vwap_adjusted",
        "user_column_mark_to_market_price": "close_adjusted",
        "user_column_date": "date",
    },
    "portfolios": [
        {
            "name": "aapl_msft_60_40",
            "inline_weights": {
                "2020-01-02": {"AAPL": 0.60, "MSFT": 0.40},
                "2022-01-03": {"AAPL": 0.55, "MSFT": 0.45},
            },
        },
        {
            "name": "nvda_heavy",
            "inline_weights": {
                "2020-01-02": {"NVDA": 0.50, "AAPL": 0.30, "MSFT": 0.20},
            },
            "variations": [
                {"name": "low_cost",  "commission_cents": 0.005},
                {"name": "high_cost", "commission_cents": 0.05},
            ],
        },
    ],
    "output": {
        "directory": "./Output/batch",
        "batch_name": "portfolio_comparison",
        "ranking_metric": "sharpe_ratio",
    },
})

backtester = MultiPortfolioBacktester(
    input_handlers=[CsvInput(input_dir="./Input/Data")],
    portfolio_handlers=[CsvPortfolioInputHandler(base_dir="./Input/Portfolios")],
)

results = backtester.run(batch_config=batch_config)
summary = get_summary_dataframe(results)
print(summary[["name", "sharpe_ratio", "final_value"]])

backtester.generate_report(results, batch_config)
backtester.generate_individual_reports(results, batch_config)

Batch Testing — Directory Scan#

Automatically discover and backtest all portfolio files in a directory.

from kaxanuk.backtest_engine.backtest import MultiPortfolioBacktester
from kaxanuk.backtest_engine.config_handlers import BatchConfigParser
from kaxanuk.backtest_engine.input_handlers.csv_input import CsvInput
from kaxanuk.backtest_engine.input_handlers.csv_portfolio_input_handler import CsvPortfolioInputHandler
from kaxanuk.backtest_engine.services.env_loader import load_config_env

load_config_env()

batch_config = BatchConfigParser.parse_dict({
    "global_defaults": {
        "start_date": "auto",
        "end_date": "auto",
        "benchmark_file_name": "SPY",
        "commission_cents": 0.01,
        "cash_reserve_percentage": 0.05,
        "initial_capital": 100000,
        "market_data_input_format": "csv",
        "portfolio_input_format": "csv",
        "input_market_data_directory": "./Input/Data",
        "input_portfolio_directory": "./Input/Portfolios",
        "backtest_results_output_directory": "./Output/batch_directory",
        "user_column_commission_price": "close_adjusted",
        "user_column_trade_execution_price": "close_adjusted",
        "user_column_mark_to_market_price": "close_adjusted",
        "user_column_date": "date",
    },
    "portfolio_directory": "./Input/Portfolios",
    "output": {
        "directory": "./Output/batch_directory",
        "batch_name": "directory_scan",
        "ranking_metric": "sharpe_ratio",
    },
})

backtester = MultiPortfolioBacktester(
    input_handlers=[CsvInput(input_dir="./Input/Data")],
    portfolio_handlers=[CsvPortfolioInputHandler(base_dir="./Input/Portfolios")],
)

results = backtester.run(batch_config)
backtester.generate_report(results, batch_config)