fix: handle datetime serialization in backtest and show errors in frontend

This commit is contained in:
shokollm
2026-04-10 10:34:29 +00:00
parent bb40193fc3
commit a601ebb08b
2 changed files with 54 additions and 12 deletions

View File

@@ -22,6 +22,7 @@ def run_backtest_sync(
backtest_id: str, db_url: str, bot_id: str, config: Dict[str, Any] backtest_id: str, db_url: str, bot_id: str, config: Dict[str, Any]
): ):
import asyncio import asyncio
import json
from ..services.backtest.engine import BacktestEngine from ..services.backtest.engine import BacktestEngine
from ..core.database import SessionLocal from ..core.database import SessionLocal
@@ -31,6 +32,19 @@ def run_backtest_sync(
running_backtests[backtest_id] = engine running_backtests[backtest_id] = engine
try: try:
results = await engine.run() 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() db = SessionLocal()
try: try:
backtest = db.query(Backtest).filter(Backtest.id == backtest_id).first() backtest = db.query(Backtest).filter(Backtest.id == backtest_id).first()
@@ -41,17 +55,18 @@ def run_backtest_sync(
db.commit() db.commit()
for signal in engine.signals: for signal in engine.signals:
signal_data = convert_datetime(signal)
db_signal = Signal( db_signal = Signal(
id=signal["id"], id=signal_data["id"],
bot_id=signal["bot_id"], bot_id=signal_data["bot_id"],
run_id=signal["run_id"], run_id=signal_data["run_id"],
signal_type=signal["signal_type"], signal_type=signal_data["signal_type"],
token=signal["token"], token=signal_data["token"],
price=signal["price"], price=signal_data["price"],
confidence=signal.get("confidence"), confidence=signal_data.get("confidence"),
reasoning=signal.get("reasoning"), reasoning=signal_data.get("reasoning"),
executed=signal.get("executed", False), executed=signal_data.get("executed", False),
created_at=signal["created_at"], created_at=signal_data["created_at"],
) )
db.add(db_signal) db.add(db_signal)
db.commit() db.commit()

View File

@@ -32,6 +32,15 @@
if ($isAuthenticated && botId) { if ($isAuthenticated && botId) {
await loadBot(); await loadBot();
await loadBacktests(); 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) { function selectBacktest(backtest: Backtest) {
if (backtest.status === 'completed' && backtest.result) { if (backtest.status === 'completed' && backtest.result && !backtest.result.error) {
selectedBacktest = backtest; selectedBacktest = backtest;
} }
} }
@@ -165,7 +174,11 @@
<span class="backtest-status status-{backtest.status}">{backtest.status}</span> <span class="backtest-status status-{backtest.status}">{backtest.status}</span>
<span class="backtest-date">{new Date(backtest.started_at).toLocaleDateString()}</span> <span class="backtest-date">{new Date(backtest.started_at).toLocaleDateString()}</span>
</div> </div>
{#if backtest.result} {#if backtest.result && backtest.result.error}
<div class="backtest-error">
<span class="error-label">Error:</span> {typeof backtest.result.error === 'string' ? backtest.result.error : JSON.stringify(backtest.result.error)}
</div>
{:else if backtest.result}
<div class="backtest-results"> <div class="backtest-results">
<div class="result-item"> <div class="result-item">
<span class="result-label">Total Return</span> <span class="result-label">Total Return</span>
@@ -271,6 +284,20 @@
font-size: 0.9rem; font-size: 0.9rem;
} }
.backtest-error {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.3);
color: #fca5a5;
padding: 0.75rem;
border-radius: 8px;
font-size: 0.85rem;
margin-bottom: 0.75rem;
}
.error-label {
font-weight: 600;
}
.form-row { .form-row {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;