WalkForwardGridOptimizer

WalkForwardGridOptimizer#

class sigtech.framework.analytics.optimization.walk_forward_optimizer.WalkForwardGridOptimizer

Helper class enabling the repeated grid optimisation of a strategy and subsequent use of the optimised parameters until the next optimisation. Walk-forward optimisation can help to reduce overfitting since each optimisation uses the same lookback. Additionally, the method avoids in-sample fitting since each optimisation at time \(t\) only uses data available up to \(t\).

Parameters:

  • strategy_generator: Method called to return the strategy. Must return a Strategy object. Must take at least as many arguments as is contained in parameter_space.

  • parameter_space: Dictionary keyed by parameter names (must match argument names in strategy_generator method) containing lists of possible values that will be iterated over at each optimisation step, e.g.: 'trend_lookback': [20, 60, 120].

  • fitting_metric: Metric method (chosen from methods in metrics or designed with an identical signature) used to evaluate each parameter combination.

  • fitting_lookback: Lookback of ‘training’ data used at each optimisation step. Can be expressed as either a tenor string ('1M', '2W-MON', etc.; see SchedulePeriodic for a complete list of possible inputs), or a pd.DateOffset, e.g. pd.DateOffset(months=3).

  • fitting_frequency: Time elapsed between each optimisation step. Can be expressed as either a tenor string ('1M', '2W-MON', etc.; see SchedulePeriodic for a complete list of possible inputs), or a list of dates (in which case the dates themselves will be used).

  • extra_strategy_kwargs: Any additional kwargs to pass to the strategy being optimised.

Note

The arguments in parameter_space and extra_strategy_kwargs together must match the arguments of the strategy_generator method.

Example usage:

import datetime as dtm
import pandas as pd
import numpy as np
import sigtech.framework as sig
from sigtech.framework.analytics.performance.metrics import sharpe_ratio
from sigtech.framework.analytics.optimization.walk_forward_optimizer import WalkForwardGridOptimizer

def generate_trend_strategy(
    trend_short_lookback,
    trend_long_lookback,
    contract_offset,
    equity_index_code
):
    asset = sig.RollingFutureStrategy(
        currency='USD',
        ticker=f'{trend_short_lookback}-{trend_long_lookback}-{contract_offset}-{equity_index_code} Trend',
        contract_code=equity_index_code,
        contract_sector='INDEX',
        start_date=dtm.date(2020, 9, 1),
        rolling_rule='front',
        contract_offset=contract_offset,
    )

    signal = sig.signal_library.technical_indicators.macd(
        close=asset.history(),
        short_window=trend_short_lookback,
        long_window=trend_long_lookback
    ).dropna()
    signal = pd.DataFrame(signal['MACD'].apply(np.sign))
    signal.columns = [asset.name]

    return sig.SignalStrategy(
        currency='USD',
        start_date=signal.first_valid_index(),
        signal_name=sig.signal_library.from_ts(signal).name,
        rebalance_frequency='1W',
    )

o = WalkForwardGridOptimizer(
    strategy_generator=generate_trend_strategy,
    parameter_space={
        'trend_short_lookback': [10, 12, 14, ],
        'trend_long_lookback': [24, 26, 30, ],
        'contract_offset': [0, 3, ],
    },
    fitting_metric=sharpe_ratio,
    fitting_lookback=pd.DateOffset(months=12),
    fitting_frequency='2W',
    equity_index_code='ES',
)

o.optimize(dtm.date(2022, 1, 1))
o.report()
generate_strategy_piecewise(ticker: str = 'PIECEWISE', delay: int = 1, **extra_args) PiecewiseStrategy

Take the results from the optimisation and generate a PiecewiseStrategy from the best parameters.

Note

PiecewiseStrategy would return incorrect results in the case that the strategies used in the grid optimiser are path-dependent.

Parameters:
  • ticker – Ticker identifier for the strategy (optional).

  • delay – Delay applied to the start date of the strategies, in number of days (default is 1).

  • extra_args – Extra arguments to pass to the PiecewiseStrategy being created.

Returns:

PiecewiseStrategy strategy.

optimize(start: Optional[date] = None, end: Optional[date] = None)

Run the walk-forward optimisation between a given date range.

Parameters:
  • start – Date of first optimisation (optional, if not provided will be set as maximum between the strategy start dates).

  • end – Date of last optimisation (optional).

params_from_strategy(strategy_name) tuple

Return the parameters (as tuple) represented by the strategy name.

Parameters:

strategy_name – Strategy name.

Returns:

Tuple represented by the strategy name.

performance_df(include_strategy_piecewise=True, params_tuple_as_columns=True) DataFrame

Generate a DataFrame containing the performance of the strategy variations.

Parameters:
  • include_strategy_piecewise – If True, include the optimal strategy defined by generate_strategy_piecewise (default is True).

  • params_tuple_as_columns – If True, use the parameter space (as tuples) as columns instead of the strategy tickers (default is True).

Returns:

pandas DaraFrame.

report() DataFrame

Generate a report of the results in DataFrame format.

strategy_piecewise_ts(delay: int = 1) Series

Return a timeseries of the strategies with their start dates used to generate a PiecewiseStrategy from the best parameters. See generate_strategy_piecewise() for details.

Parameters:

delay – Delay applied to the start date of the strategies, in number of days (default is 1).

Returns:

pandas Series.

strategy_variations() dict

Return a dictionary containing the parameter space as keys and the individual strategies as values.