diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-02 09:50:47 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-02 09:50:47 +0900 |
| commit | c32ea21d0e29a0894fe94ecc4236145541bce3ab (patch) | |
| tree | b753768aa134f9e58dad8c457dc8d6e47b7813e3 /cli/src/trading_cli/commands | |
| parent | b9d21e2e2f7ae096c2f8a01bb142a685683b5b90 (diff) | |
refactor: dead code cleanup
- Remove placeholder 'backtest report' command (no stored results)
- Connect walk-forward analysis to CLI: 'trading backtest walk-forward'
- Update test for renamed command
Diffstat (limited to 'cli/src/trading_cli/commands')
| -rw-r--r-- | cli/src/trading_cli/commands/backtest.py | 79 |
1 files changed, 73 insertions, 6 deletions
diff --git a/cli/src/trading_cli/commands/backtest.py b/cli/src/trading_cli/commands/backtest.py index b9e3c1b..01fe092 100644 --- a/cli/src/trading_cli/commands/backtest.py +++ b/cli/src/trading_cli/commands/backtest.py @@ -102,9 +102,76 @@ def run(strategy, symbol, timeframe, balance, output_format, file_path): asyncio.run(_run()) -@backtest.command() -@click.option("--id", "backtest_id", required=True, help="Backtest run ID") -def report(backtest_id): - """Show a backtest report by ID.""" - click.echo(f"Showing backtest report for ID: {backtest_id}...") - click.echo("(Not yet implemented - requires stored backtest results)") +@backtest.command("walk-forward") +@click.option("--strategy", required=True, help="Strategy name") +@click.option("--symbol", required=True, help="Trading symbol") +@click.option("--timeframe", default="1h", show_default=True, help="Candle timeframe") +@click.option("--balance", default=10000.0, show_default=True, help="Initial balance") +@click.option("--windows", default=5, show_default=True, help="Number of walk-forward windows") +def walk_forward(strategy, symbol, timeframe, balance, windows): + """Run walk-forward analysis to detect overfitting.""" + try: + from strategy_engine.plugin_loader import load_strategies + from backtester.walk_forward import WalkForwardEngine + from shared.db import Database + from shared.config import Settings + from shared.models import Candle + except ImportError as e: + click.echo(f"Error: Could not import required modules: {e}", err=True) + sys.exit(1) + + strategies_dir = _ROOT / "services" / "strategy-engine" / "strategies" + strategies = load_strategies(strategies_dir) + matched = [s for s in strategies if s.name == strategy] + if not matched: + click.echo(f"Error: Strategy '{strategy}' not found.", err=True) + sys.exit(1) + + strategy_cls = type(matched[0]) + + async def _run(): + settings = Settings() + db = Database(settings.database_url) + await db.connect() + try: + rows = await db.get_candles(symbol, timeframe, limit=2000) + if not rows: + click.echo(f"Error: No candles found for {symbol} {timeframe}", err=True) + sys.exit(1) + + candles = [ + Candle( + symbol=r["symbol"], + timeframe=r["timeframe"], + open_time=r["open_time"], + open=r["open"], + high=r["high"], + low=r["low"], + close=r["close"], + volume=r["volume"], + ) + for r in reversed(rows) + ] + + param_grid = [{}] # Default params — extend as needed + engine = WalkForwardEngine( + strategy_factory=strategy_cls, + param_grid=param_grid, + initial_balance=Decimal(str(balance)), + num_windows=windows, + ) + result = engine.run(candles) + + click.echo(f"Walk-Forward Analysis: {result.strategy_name}") + click.echo(f"Windows: {result.num_windows}") + click.echo(f"In-sample profit: {result.in_sample_profit_pct:.2f}%") + click.echo(f"Out-of-sample profit: {result.out_of_sample_profit_pct:.2f}%") + click.echo(f"Efficiency ratio: {result.efficiency_ratio:.2f}") + if result.efficiency_ratio > 0.5: + click.echo("-> Strategy appears robust (ratio > 0.5)") + else: + click.echo("-> WARNING: Possible overfitting (ratio < 0.5)") + finally: + await db.close() + + asyncio.run(_run()) |
