diff --git a/src/backend/app/api/backtest.py b/src/backend/app/api/backtest.py index 9e07072..747a005 100644 --- a/src/backend/app/api/backtest.py +++ b/src/backend/app/api/backtest.py @@ -22,6 +22,7 @@ def run_backtest_sync( backtest_id: str, db_url: str, bot_id: str, config: Dict[str, Any] ): import asyncio + import json from ..services.backtest.engine import BacktestEngine from ..core.database import SessionLocal @@ -31,6 +32,19 @@ def run_backtest_sync( running_backtests[backtest_id] = engine try: results = await engine.run() + + # Convert datetime objects to ISO strings for JSON serialization + def convert_datetime(obj): + if isinstance(obj, datetime): + return obj.isoformat() + elif isinstance(obj, dict): + return {k: convert_datetime(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [convert_datetime(i) for i in obj] + return obj + + results = convert_datetime(results) + db = SessionLocal() try: backtest = db.query(Backtest).filter(Backtest.id == backtest_id).first() @@ -41,17 +55,18 @@ def run_backtest_sync( db.commit() for signal in engine.signals: + signal_data = convert_datetime(signal) db_signal = Signal( - id=signal["id"], - bot_id=signal["bot_id"], - run_id=signal["run_id"], - signal_type=signal["signal_type"], - token=signal["token"], - price=signal["price"], - confidence=signal.get("confidence"), - reasoning=signal.get("reasoning"), - executed=signal.get("executed", False), - created_at=signal["created_at"], + id=signal_data["id"], + bot_id=signal_data["bot_id"], + run_id=signal_data["run_id"], + signal_type=signal_data["signal_type"], + token=signal_data["token"], + price=signal_data["price"], + confidence=signal_data.get("confidence"), + reasoning=signal_data.get("reasoning"), + executed=signal_data.get("executed", False), + created_at=signal_data["created_at"], ) db.add(db_signal) db.commit() diff --git a/src/frontend/src/routes/bot/[id]/backtest/+page.svelte b/src/frontend/src/routes/bot/[id]/backtest/+page.svelte index 29b1d43..e9032b7 100644 --- a/src/frontend/src/routes/bot/[id]/backtest/+page.svelte +++ b/src/frontend/src/routes/bot/[id]/backtest/+page.svelte @@ -32,6 +32,15 @@ if ($isAuthenticated && botId) { await loadBot(); await loadBacktests(); + + // Poll for backtest updates every 5 seconds if any are running + const pollInterval = setInterval(async () => { + if ($backtestStore.backtestHistory.some(b => b.status === 'running')) { + await loadBacktests(); + } + }, 5000); + + return () => clearInterval(pollInterval); } }); @@ -90,7 +99,7 @@ } function selectBacktest(backtest: Backtest) { - if (backtest.status === 'completed' && backtest.result) { + if (backtest.status === 'completed' && backtest.result && !backtest.result.error) { selectedBacktest = backtest; } } @@ -165,7 +174,11 @@ {backtest.status} {new Date(backtest.started_at).toLocaleDateString()} - {#if backtest.result} + {#if backtest.result && backtest.result.error} +