summaryrefslogtreecommitdiff
path: root/cli/src/trading_cli/commands/backtest.py
diff options
context:
space:
mode:
Diffstat (limited to 'cli/src/trading_cli/commands/backtest.py')
-rw-r--r--cli/src/trading_cli/commands/backtest.py79
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())