Skip to content

Feat/pipeline refinements#27

Merged
LeFi8 merged 13 commits into
mainfrom
feat/pipeline-refinements
Jun 13, 2026
Merged

Feat/pipeline refinements#27
LeFi8 merged 13 commits into
mainfrom
feat/pipeline-refinements

Conversation

@LeFi8

@LeFi8 LeFi8 commented Jun 12, 2026

Copy link
Copy Markdown
Owner

No description provided.

LeFi8 added 10 commits June 9, 2026 23:15
- Removed hard rejection of identical configurations at the diversity_config stage, allowing candidates to proceed to backtest.
- Introduced a new diversity_config_pool method to manage historical and batch configurations for diversity checks.
- Updated the _check_diversity_gate method to utilize the new pool and adjusted logic for applying jitter.
- Enhanced the candidate evaluation process to include post-quality checks for returns diversity, rejecting only exact duplicates based on a hard threshold.
- Added new configuration options for select gate comparisons, allowing for flexibility in metric evaluation (deflated vs. raw).
- Updated tests to reflect changes in diversity gate behavior and ensure proper handling of identical candidates.
- Improved error messages and logging for lookahead bias checks, providing clearer feedback on failures.
…n, and improve environment variable handling

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the strategy layout to a subdirectory structure (strategies/<name>/strategy.py and config.yaml) while maintaining fallback support for the legacy flat layout. It also introduces several new configuration parameters for the optimization loop and safety gates, including a default DSR-based comparison mode (select_compare_metric), near-tie tolerance, and improved diagnostic messages for lookahead and regime stress test failures. The review feedback highlights three key areas for improvement: correctly resolving the strategy name from its parent directory when using the new layout, falling back to raw metric comparison when the target metric is not Sharpe in deflated mode, and adding numerical stability guards (capping and thresholding) to the tradeoff scale factor calculation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/autobacktest/commands/evaluate.py Outdated
@@ -47,7 +47,11 @@ def evaluate(
raise typer.Exit(code=1)

strategy_name = strategy_path.stem

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When using the new subdirectory layout, the strategy file is named strategy.py. Using strategy_path.stem directly results in the strategy name being resolved as "strategy" for all such strategies, which clutters reports and leaderboards. It is better to fall back to the parent directory's name when the stem is "strategy".

Suggested change
strategy_name = strategy_path.stem
strategy_name = strategy_path.parent.name if strategy_path.stem == "strategy" else strategy_path.stem

Comment thread src/autobacktest/gate.py Outdated
Comment on lines +134 to +137
if compare_metric == "deflated":
val = report.deflated_sharpe
else:
return _get_in_sample_metric_val(report, target_metric)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When compare_metric is set to "deflated" but the target_metric is not TargetMetric.SHARPE (e.g., SORTINO or IR), comparing the candidate's deflated_sharpe against the baseline's deflated_sharpe is mathematically incorrect and violates the user's optimization target. We should automatically fall back to raw in-sample metric comparison when the target metric is not Sharpe.

    if compare_metric == "deflated" and target_metric == TargetMetric.SHARPE:\n        val = report.deflated_sharpe\n    else:\n        return _get_in_sample_metric_val(report, target_metric)

Comment thread src/autobacktest/gate.py
Comment on lines +523 to +527
if compare_metric == "deflated":
base_raw = baseline.observed_sharpe
base_dsr = baseline.deflated_sharpe
scale = (base_dsr / base_raw) if base_raw > 0.0 else 1.0
tradeoff_term *= scale

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If base_raw is extremely close to zero, the division base_dsr / base_raw can blow up, leading to an excessively large scale factor and an unstable tradeoff threshold. Additionally, if base_dsr is negative, the scale factor becomes negative, which reverses the tradeoff logic. Capping the scale factor between 0.0 and 1.0 and adding a safe threshold for base_raw prevents these numerical stability issues.

        if compare_metric == "deflated":\n            base_raw = baseline.observed_sharpe\n            base_dsr = baseline.deflated_sharpe\n            scale = max(0.0, min(1.0, base_dsr / base_raw)) if base_raw > 1e-6 else 0.0\n            tradeoff_term *= scale

@LeFi8

LeFi8 commented Jun 13, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the strategy layout from a flat structure to a subdirectory-based layout (strategies/<name>/strategy.py and config.yaml), updating the CLI commands, documentation, and tests to support this new structure while maintaining backward compatibility. It also introduces new configuration parameters for the safety gates and optimization loop, including near-tie tolerances and options to compare candidates using raw or deflated metrics. The review feedback identifies a high-severity issue in the evaluate command where relative strategy paths can resolve to empty or . parent names, breaking fallback configuration lookups, and provides a code suggestion to resolve the path first.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/autobacktest/commands/evaluate.py Outdated
Comment on lines 49 to 56
strategy_name = strategy_path.parent.name if strategy_path.stem == "strategy" else strategy_path.stem
config_path = strategy_path.parent / "config.yaml"
if not config_path.exists():
config_path = strategy_path.parent / f"{strategy_name}.yaml"
if not config_path.exists():
config_path = settings.configs_dir / f"{strategy_name}.yaml"
if not config_path.exists():
config_path = strategy_path.resolve().parent.parent / "configs" / f"{strategy_name}.yaml"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If strategy_path is passed as a relative path (for example, just strategy.py when running the command from within the strategy's subdirectory), strategy_path.parent.name will resolve to an empty string "" or ".". This causes strategy_name to be empty, which breaks all subsequent fallback config file lookups.

Resolving the path first via .resolve() ensures that the parent directory name is correctly extracted even when a relative path is provided.

Suggested change
strategy_name = strategy_path.parent.name if strategy_path.stem == "strategy" else strategy_path.stem
config_path = strategy_path.parent / "config.yaml"
if not config_path.exists():
config_path = strategy_path.parent / f"{strategy_name}.yaml"
if not config_path.exists():
config_path = settings.configs_dir / f"{strategy_name}.yaml"
if not config_path.exists():
config_path = strategy_path.resolve().parent.parent / "configs" / f"{strategy_name}.yaml"
resolved_path = strategy_path.resolve()
strategy_name = resolved_path.parent.name if resolved_path.stem == "strategy" else resolved_path.stem
config_path = resolved_path.parent / "config.yaml"
if not config_path.exists():
config_path = resolved_path.parent / f"{strategy_name}.yaml"
if not config_path.exists():
config_path = settings.configs_dir / f"{strategy_name}.yaml"
if not config_path.exists():
config_path = resolved_path.parent.parent / "configs" / f"{strategy_name}.yaml"

@LeFi8 LeFi8 merged commit fb51144 into main Jun 13, 2026
3 checks passed
@LeFi8 LeFi8 deleted the feat/pipeline-refinements branch June 13, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant