fix: handle datetime serialization in backtest and show errors in frontend
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user