Skip to content

turtle_quant_1.backtesting.hyperparameters

turtle_quant_1.backtesting.hyperparameters

Hyperparameter tuning script for trading strategies using Optuna.

get_config

get_config(trial: Trial) -> Dict[str, Any]

Create a strategy engine configuration for the given trial.

Parameters:

Name Type Description Default
trial Trial

Optuna trial object for hyperparameter suggestions

required

Returns:

Type Description
Dict[str, Any]

Dictionary containing strategy engine configuration

Source code in src/turtle_quant_1/backtesting/hyperparameters.py
def get_config(trial: optuna.Trial) -> Dict[str, Any]:
    """Create a strategy engine configuration for the given trial.

    Args:
        trial: Optuna trial object for hyperparameter suggestions

    Returns:
        Dictionary containing strategy engine configuration
    """

    # NOTE: All strategies are included by default.
    # TODO: Add a way to exclude strategies.
    # TODO: Clean up.
    strategies = {
        strategy_type.__name__: {}
        # pyrefly: ignore
        for strategy_type in BaseStrategy.__subclasses__()
    }

    weights = {
        strategy_type.__name__: trial.suggest_float(
            f"{strategy_type.__name__}_weight", 0.0, 1.0
        )
        # pyrefly: ignore
        for strategy_type in BaseStrategy.__subclasses__()
    }

    # Strategy engine configuration
    # TODO: Add weights.
    config = {
        "strategies": strategies,
        "weights": weights,
        "buy_unit_threshold": trial.suggest_float("buy_unit_threshold", 0.1, 0.9),
        "sell_threshold": trial.suggest_float("sell_threshold", -0.9, -0.1),
    }

    return config

get_objective_metric

get_objective_metric(
    results: TestCaseResults,
    metric_name: str = "sharpe_ratio",
) -> float

Extract the objective metric from backtest results.

NOTE: This is currently a wrapper around the quantstats metrics in the results. NOTE: Ensure that metric_name is a valid quantstats metric. TODO: Implement our own metrics.

Parameters:

Name Type Description Default
results TestCaseResults

BacktestingResults object

required
metric_name str

Name of the metric to optimize

'sharpe_ratio'

Returns:

Type Description
float

Metric value (returns large negative value if error occurred)

Source code in src/turtle_quant_1/backtesting/hyperparameters.py
def get_objective_metric(
    results: TestCaseResults, metric_name: str = "sharpe_ratio"
) -> float:
    """Extract the objective metric from backtest results.

    NOTE: This is currently a wrapper around the quantstats metrics in the results.
    NOTE: Ensure that metric_name is a valid quantstats metric.
    TODO: Implement our own metrics.

    Args:
        results: BacktestingResults object
        metric_name: Name of the metric to optimize

    Returns:
        Metric value (returns large negative value if error occurred)
    """
    metrics = results.metrics
    if not metrics:
        return -1000.0  # Large negative value for failed runs

    # Try to get the requested metric from quantstats
    if metric_name in metrics:
        value = metrics[metric_name]
        # Handle potential NaN or infinite values
        if isinstance(value, (int, float)) and not (
            value != value or abs(value) == float("inf")
        ):
            return float(value)

    # Fallback metrics from basic backtest results
    fallback_metrics = {
        "total_return_percent": results.total_return_percent,
        "total_return_dollars": results.total_return_dollars,
    }

    if metric_name in fallback_metrics:
        return fallback_metrics[metric_name]

    # Default to total return percentage if metric not found
    return results.total_return_percent

objective

objective(trial: Trial) -> float

Optuna objective function for hyperparameter optimization.

Parameters:

Name Type Description Default
trial Trial

Optuna trial object

required

Returns:

Type Description
float

Objective metric value to maximize

Source code in src/turtle_quant_1/backtesting/hyperparameters.py
def objective(trial: optuna.Trial) -> float:
    """Optuna objective function for hyperparameter optimization.

    Args:
        trial: Optuna trial object

    Returns:
        Objective metric value to maximize
    """
    # Create strategy configuration
    config = get_config(trial)

    # Run backtest
    results = run_backtest(config)

    # Extract objective metric (try Sharpe ratio, fallback to total return)
    sharpe = get_objective_metric(
        results, "sharpe_ratio"
    )  # TODO: Enable multiple metrics.
    if sharpe == -1000.0:  # Error occurred, try fallback
        return get_objective_metric(results, "total_return_percent")

    return sharpe

run_backtest

run_backtest(
    config: Dict[str, Any], initial_capital: float = 10000.0
) -> TestCaseResults

Run a backtest with the given strategy engine configuration.

Parameters:

Name Type Description Default
config Dict[str, Any]

Strategy engine configuration dictionary

required
initial_capital float

Starting capital for the backtest

10000.0

Returns:

Type Description
TestCaseResults

BacktestingResults object

Source code in src/turtle_quant_1/backtesting/hyperparameters.py
def run_backtest(
    config: Dict[str, Any], initial_capital: float = 10000.0
) -> TestCaseResults:
    """Run a backtest with the given strategy engine configuration.

    Args:
        config: Strategy engine configuration dictionary
        initial_capital: Starting capital for the backtest

    Returns:
        BacktestingResults object
    """
    strategy_engine = WeightedMeanStrategyEngine.from_config(config)

    # Create backtesting engine
    backtesting_engine = BacktestingEngine(
        strategy_engine=strategy_engine,
        initial_capital=initial_capital,
        max_lookback_days=60,  # NOTE: Shorter for faster optimization
        max_lookforward_days=60,  # NOTE: Shorter for faster optimization
    )

    # Run backtest
    results = backtesting_engine.run_backtest()

    # Get evaluation metrics
    metrics = backtesting_engine.get_metrics(benchmark="SPY")

    # Combine results
    results.metrics = metrics

    return results

run_hyperparameter_tuning

run_hyperparameter_tuning(
    n_trials: int,
    n_jobs: int = -1,
    study_name: str = "strategy_engine_optimization",
    direction: str = "maximize",
) -> Study

Run hyperparameter optimization using Optuna.

Parameters:

Name Type Description Default
n_trials int

Number of optimization trials

required
study_name str

Name for the study

'strategy_engine_optimization'
direction str

Optimization direction ("maximize" or "minimize")

'maximize'

Returns:

Type Description
Study

Completed Optuna study object

Source code in src/turtle_quant_1/backtesting/hyperparameters.py
def run_hyperparameter_tuning(
    n_trials: int,
    n_jobs: int = -1,
    study_name: str = "strategy_engine_optimization",
    direction: str = "maximize",
) -> optuna.Study:
    """Run hyperparameter optimization using Optuna.

    Args:
        n_trials: Number of optimization trials
        study_name: Name for the study
        direction: Optimization direction ("maximize" or "minimize")

    Returns:
        Completed Optuna study object
    """
    logger.info(f"Starting hyperparameter optimization with {n_trials} trials...")

    # Create study
    study = optuna.create_study(direction=direction, study_name=study_name)

    # Run optimization
    study.optimize(objective, n_trials=n_trials, n_jobs=n_jobs)

    # Print results
    logger.info("Optimization complete!")
    logger.info(f"Best parameters: {study.best_params}")
    logger.info(f"Best value: {study.best_value}")

    # Print top 5 trials
    logger.info("Top 5 trials:")
    top_trials = study.trials_dataframe().nlargest(5, "value")
    for i, (_, trial_row) in enumerate(top_trials.iterrows(), 1):
        # Get the trial object to access parameters
        trial_number = int(trial_row["number"])
        trial_obj = study.trials[trial_number]
        logger.info(
            f"  {i}. Value: {trial_row['value']:.4f}, Parameters: {trial_obj.params}"
        )

    return study