Skip to content

contango.runners.grid.grid_search_runner

contango.runners.grid.grid_search_runner

ExperimentResult

Bases: NamedTuple

The resulting experiment of backtesting a single strategy variant from a grid search.

Attributes:

Name Type Description
label str

The name for the experiment result.

kwargs dict[str, Any]

A dictionary mapping strings to the parameters tested for the experiment.

metrics Metrics

The metrics resulting from the experiment.

GridSearchRunner

Bases: Generic[TConfig]

Runner for fetching data and executing every strategy variant produced by a Cartesian parameter grid search.

fetch_data staticmethod

fetch_data(provider: HistoricalDataProvider[TConfig], config: TConfig, database_path: str | Path | None = None) -> list[MarketDataEvent]

Fetches and stores data into the database.

Parameters:

Name Type Description Default
provider HistoricalDataProvider[TConfig]

The historical data provider (i.e. Yfinance) implementation.

required
config TConfig

The configuration that matches the data provider (i.e. YfinanceConfig).

required
database_path str | Path | None

The optional database path; if not provided, defaults to the OS-standard user data directory.

None

Returns:

Type Description
list[MarketDataEvent]

list[MarketDataEvent]: The OHLCV data from the database and/or historical data provider.

Source code in src\contango\runners\grid\grid_search_runner.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@staticmethod
def fetch_data(
    provider: HistoricalDataProvider[TConfig],
    config: TConfig,
    database_path: str | Path | None = None,
) -> list[MarketDataEvent]:
    """
    Fetches and stores data into the database.

    Args:
        provider: The historical data provider (i.e. Yfinance) implementation.
        config: The configuration that matches the data provider (i.e. YfinanceConfig).
        database_path: The optional database path; if not provided, defaults to the OS-standard user data directory.

    Returns:
        list[MarketDataEvent]: The OHLCV data from the database and/or historical data provider.
    """
    return DataRepository[config].get_data_and_store(
        provider=provider,
        config=config,
        expected_timestamps=provider.get_expected_timestamps(config),
        database_path=database_path,
    )

fetch_data_multi staticmethod

fetch_data_multi(provider: HistoricalDataProvider[TConfig], configs: Mapping[str, TConfig], database_path: str | Path | None = None) -> dict[str, list[MarketDataEvent]]

Fetches and stores data for multiple symbols, keyed by the same symbol strings intend to use in param_grid["symbol"].

Parameters:

Name Type Description Default
provider HistoricalDataProvider[TConfig]

The historical data provider (i.e. Yfinance) implementation.

required
configs Mapping[str, TConfig]

A mapping of symbol -> config for the symbol (e.g. {"AAPL": YfinanceConfig(ticker="AAPL", ...)}).

required
database_path str | Path | None

The optional database path. if not provided, defaults to the OS-standard user data directory.

None

Returns:

Type Description
dict[str, list[MarketDataEvent]]

dict[str, list[MarketDataEvent]]: OHLCV data per symbol.

Source code in src\contango\runners\grid\grid_search_runner.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@staticmethod
def fetch_data_multi(
    provider: HistoricalDataProvider[TConfig],
    configs: Mapping[str, TConfig],
    database_path: str | Path | None = None,
) -> dict[str, list[MarketDataEvent]]:
    """
    Fetches and stores data for multiple symbols, keyed by the same
    symbol strings intend to use in param_grid["symbol"].

    Args:
        provider: The historical data provider (i.e. Yfinance) implementation.
        configs: A mapping of symbol -> config for the symbol (e.g. {"AAPL": YfinanceConfig(ticker="AAPL", ...)}).
        database_path: The optional database path. if not provided, defaults to the OS-standard user data directory.

    Returns:
        dict[str, list[MarketDataEvent]]: OHLCV data per symbol.
    """
    return {
        symbol: GridSearchRunner[TConfig].fetch_data(provider=provider, config=config, database_path=database_path)
        for symbol, config in configs.items()
    }

run staticmethod

run(ohlcv_data: Mapping[str, list[MarketDataEvent]], strategy_cls: type[StrategyT], param_grid: Mapping[str, Sequence[Any]], backtester_config: BacktesterConfig, base_label: str | None = None, verbose: bool = True) -> list[ExperimentResult]

Runs a backtest for multiple instances of a strategy, against per-symbol data.

Parameters:

Name Type Description Default
ohlcv_data Mapping[str, list[MarketDataEvent]]

A mapping of symbol -> historical market data events for the symbol. Every value that appears in param_grid["symbol"] must be a key here.

required
strategy_cls type[StrategyT]

The strategy class that will be parameterized.

required
param_grid Mapping[str, Sequence[Any]]

A mapping of strings (parameter names) to sequences of the kwargs to test. Must include a "symbol" key whose values are all present in ohlcv_data.

required
base_label str | None

The base label for each result.

None
backtester_config BacktesterConfig

The configuration for the backtester.

required
verbose bool

Whether to print the current strategy being tested to terminal.

True

Returns:

Type Description
list[ExperimentResult]

list[ExperimentResult]: The metrics along with parameters & labels that were tested.

Source code in src\contango\runners\grid\grid_search_runner.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
@staticmethod
def run(
    ohlcv_data: Mapping[str, list[MarketDataEvent]],
    strategy_cls: type[StrategyT],
    param_grid: Mapping[str, Sequence[Any]],
    backtester_config: BacktesterConfig,
    base_label: str | None = None,
    verbose: bool = True
) -> list[ExperimentResult]:
    """
    Runs a backtest for multiple instances of a strategy, against
    per-symbol data.

    Args:
        ohlcv_data: A mapping of symbol -> historical market data events for the symbol.
                    Every value that appears in param_grid["symbol"] must be a key here.
        strategy_cls: The strategy class that will be parameterized.
        param_grid: A mapping of strings (parameter names) to sequences of the kwargs to test.
                    Must include a "symbol" key whose values are all present in ohlcv_data.
        base_label: The base label for each result.
        backtester_config: The configuration for the backtester.
        verbose: Whether to print the current strategy being tested to terminal.

    Returns:
        list[ExperimentResult]: The metrics along with parameters & labels that were tested.
    """
    if "symbol" not in param_grid:
        raise ValueError('param_grid must include a "symbol" key to select data from ohlcv_data')

    results: list[ExperimentResult] = []
    base_label = base_label if base_label is not None else strategy_cls.__name__
    varying_keys = GridSearchRunner._varying_keys(param_grid)
    for factory, kwargs in cartesian_search(strategy_cls, param_grid):
        symbol = kwargs["symbol"]
        if symbol not in ohlcv_data:
            raise KeyError(f"No fetched data for symbol {symbol!r}; available symbols: {sorted(ohlcv_data)}")

        name = f"{base_label}({GridSearchRunner._format_kwargs(kwargs, varying_keys)})"

        if verbose:
            logger.info(f"Testing {name}")

        raw_results = StrategyBacktester.backtest(
            ohlcv_data=ohlcv_data[symbol], strategy=factory(), config=backtester_config
        )
        results.append(
            ExperimentResult(
                label=name, 
                kwargs=dict(kwargs), 
                metrics=calculate_metrics(raw_results)
            )
        )
    return results