Spaces:
Running
Running
Upload runtime.py
Browse files- runtime.py +86 -97
runtime.py
CHANGED
|
@@ -1243,85 +1243,80 @@ def warm_dashboard_payload_cache() -> None:
|
|
| 1243 |
dashboard_payload()
|
| 1244 |
|
| 1245 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1246 |
def build_prediction_track_record(
|
| 1247 |
daily: pd.DataFrame,
|
| 1248 |
-
|
| 1249 |
-
tomorrow_test: pd.DataFrame,
|
| 1250 |
-
tomorrow_history: pd.DataFrame,
|
| 1251 |
-
tplus1_test: pd.DataFrame,
|
| 1252 |
-
t5_latest: dict[str, Any],
|
| 1253 |
-
tomorrow_latest: dict[str, Any],
|
| 1254 |
-
tplus1_latest: dict[str, Any],
|
| 1255 |
-
live_accuracy: dict[str, Any] | None = None,
|
| 1256 |
) -> list[dict[str, Any]]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1257 |
daily_rows = daily.copy()
|
| 1258 |
daily_rows["date"] = pd.to_datetime(daily_rows["date"], errors="coerce").dt.normalize()
|
| 1259 |
daily_rows = daily_rows.dropna(subset=["date"]).sort_values("date")
|
| 1260 |
daily_rows = daily_rows[
|
| 1261 |
-
daily_rows["
|
| 1262 |
-
& daily_rows["close"].map(lambda value: np.isfinite(float(value)) if pd.notna(value) else False)
|
| 1263 |
].copy()
|
| 1264 |
-
daily_rows = daily_rows[daily_rows["open"].astype(float) != 0]
|
| 1265 |
completed_day = expected_completed_daily_date()
|
| 1266 |
daily_rows = daily_rows[daily_rows["date"].dt.date <= completed_day]
|
| 1267 |
if daily_rows.empty:
|
| 1268 |
return []
|
| 1269 |
|
| 1270 |
-
predictions_by_date: dict[str, dict[str, Any]] = {}
|
| 1271 |
-
|
| 1272 |
-
def add_prediction(target_date: Any, prediction: Any, source: str, priority: int, meta: dict[str, Any] | None = None) -> None:
|
| 1273 |
-
day = str(target_date or "")[:10]
|
| 1274 |
-
pred = str(prediction or "").upper()
|
| 1275 |
-
if not day or pred not in {"UP", "DOWN"}:
|
| 1276 |
-
return
|
| 1277 |
-
existing = predictions_by_date.get(day)
|
| 1278 |
-
if existing and existing.get("_priority", 0) >= priority:
|
| 1279 |
-
return
|
| 1280 |
-
predictions_by_date[day] = {
|
| 1281 |
-
"prediction": pred,
|
| 1282 |
-
"source": source,
|
| 1283 |
-
"_priority": priority,
|
| 1284 |
-
**(meta or {}),
|
| 1285 |
-
}
|
| 1286 |
-
|
| 1287 |
-
for _, row in tomorrow_test.iterrows():
|
| 1288 |
-
pred = row.get("prediction")
|
| 1289 |
-
if pd.isna(pred) and "pred" in row:
|
| 1290 |
-
pred = "UP" if int(row.get("pred")) == 1 else "DOWN"
|
| 1291 |
-
add_prediction(row.get("target_date") or row.get("date"), pred, "Tomorrow", 20, {"prob_up": row.get("prob_up")})
|
| 1292 |
-
|
| 1293 |
-
if not tomorrow_history.empty:
|
| 1294 |
-
for _, row in tomorrow_history.iterrows():
|
| 1295 |
-
pred = row.get("prediction")
|
| 1296 |
-
if pd.isna(pred) and "pred" in row:
|
| 1297 |
-
pred = "UP" if int(row.get("pred")) == 1 else "DOWN"
|
| 1298 |
-
target_date = row.get("target_date") or row.get("date") or row.get("input_date")
|
| 1299 |
-
add_prediction(
|
| 1300 |
-
target_date,
|
| 1301 |
-
pred,
|
| 1302 |
-
"Tomorrow",
|
| 1303 |
-
30,
|
| 1304 |
-
{
|
| 1305 |
-
"prob_up": row.get("prob_up"),
|
| 1306 |
-
"source": row.get("source"),
|
| 1307 |
-
},
|
| 1308 |
-
)
|
| 1309 |
-
|
| 1310 |
-
add_prediction(tomorrow_latest.get("target_date"), tomorrow_latest.get("prediction"), "Tomorrow", 40, {"prob_up": tomorrow_latest.get("prob_up")})
|
| 1311 |
-
|
| 1312 |
-
ledger = live_accuracy or load_live_accuracy()
|
| 1313 |
-
for entry in ledger.get("tomorrow", {}).get("entries", []):
|
| 1314 |
-
day = str(entry.get("date", ""))[:10]
|
| 1315 |
-
pred = str(entry.get("prediction", "")).upper()
|
| 1316 |
-
if day and pred in {"UP", "DOWN"}:
|
| 1317 |
-
add_prediction(
|
| 1318 |
-
day,
|
| 1319 |
-
pred,
|
| 1320 |
-
"Tomorrow",
|
| 1321 |
-
50,
|
| 1322 |
-
{"source": entry.get("source", "live")},
|
| 1323 |
-
)
|
| 1324 |
-
|
| 1325 |
closes_by_date = {
|
| 1326 |
row["date"].date(): float(row["close"])
|
| 1327 |
for _, row in daily_rows.iterrows()
|
|
@@ -1329,31 +1324,38 @@ def build_prediction_track_record(
|
|
| 1329 |
}
|
| 1330 |
|
| 1331 |
records: list[dict[str, Any]] = []
|
| 1332 |
-
for _, row in daily_rows.tail(
|
| 1333 |
-
|
| 1334 |
day_close = float(row["close"])
|
| 1335 |
-
actual_move, actual_direction = _tomorrow_actual_outcome(
|
| 1336 |
if actual_direction is None:
|
| 1337 |
continue
|
| 1338 |
-
|
| 1339 |
-
|
| 1340 |
-
|
| 1341 |
-
|
| 1342 |
-
|
| 1343 |
-
|
| 1344 |
-
|
| 1345 |
-
|
| 1346 |
-
|
| 1347 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1348 |
records.append(
|
| 1349 |
{
|
| 1350 |
-
"date":
|
|
|
|
| 1351 |
"prediction": prediction,
|
| 1352 |
-
"prediction_source":
|
| 1353 |
-
"prob_up":
|
| 1354 |
"actual_move": actual_move,
|
| 1355 |
"actual_direction": actual_direction,
|
| 1356 |
-
"correct":
|
| 1357 |
}
|
| 1358 |
)
|
| 1359 |
return records
|
|
@@ -1450,20 +1452,7 @@ def _dashboard_payload_cached(key: tuple[tuple[str, int | None, int | None], ...
|
|
| 1450 |
"total_test_days": int(tomorrow_summary.get("n_test") or len(tomorrow_test) or 0),
|
| 1451 |
"models": model_metrics,
|
| 1452 |
}
|
| 1453 |
-
|
| 1454 |
-
ensure_completed_sessions_scored(datetime.now(IST), live_accuracy)
|
| 1455 |
-
live_accuracy = load_live_accuracy()
|
| 1456 |
-
track_record = build_prediction_track_record(
|
| 1457 |
-
daily,
|
| 1458 |
-
t5_test,
|
| 1459 |
-
tomorrow_test,
|
| 1460 |
-
tomorrow_history,
|
| 1461 |
-
tplus1_test,
|
| 1462 |
-
t5_latest,
|
| 1463 |
-
tomorrow_latest,
|
| 1464 |
-
tplus1_latest,
|
| 1465 |
-
live_accuracy=live_accuracy,
|
| 1466 |
-
)
|
| 1467 |
return {
|
| 1468 |
"latest": t5_latest,
|
| 1469 |
"tomorrow_latest": tomorrow_latest,
|
|
|
|
| 1243 |
dashboard_payload()
|
| 1244 |
|
| 1245 |
|
| 1246 |
+
def _load_forecaster_predictions_by_target() -> dict[date, dict[str, Any]]:
|
| 1247 |
+
"""Index bundled forecaster backtest rows by target session date."""
|
| 1248 |
+
path = DAILY_FORECASTER_PREDICTIONS_PATH
|
| 1249 |
+
if not path.exists():
|
| 1250 |
+
return {}
|
| 1251 |
+
try:
|
| 1252 |
+
frame = pd.read_csv(path)
|
| 1253 |
+
except Exception:
|
| 1254 |
+
return {}
|
| 1255 |
+
if frame.empty:
|
| 1256 |
+
return {}
|
| 1257 |
+
if "symbol" in frame.columns:
|
| 1258 |
+
frame = frame[frame["symbol"].astype(str) == "NIFTY 50"].copy()
|
| 1259 |
+
indexed: dict[date, dict[str, Any]] = {}
|
| 1260 |
+
for _, row in frame.iterrows():
|
| 1261 |
+
target_day = _parse_iso_date(row.get("target_date"))
|
| 1262 |
+
if target_day is None:
|
| 1263 |
+
continue
|
| 1264 |
+
pred_value = row.get("pred")
|
| 1265 |
+
if pd.isna(pred_value) and "raw_pred" in row:
|
| 1266 |
+
pred_value = row.get("raw_pred")
|
| 1267 |
+
try:
|
| 1268 |
+
pred_int = int(pred_value)
|
| 1269 |
+
except Exception:
|
| 1270 |
+
continue
|
| 1271 |
+
prob_up = row.get("prob_up")
|
| 1272 |
+
try:
|
| 1273 |
+
prob_up = float(prob_up) if pd.notna(prob_up) else None
|
| 1274 |
+
except Exception:
|
| 1275 |
+
prob_up = None
|
| 1276 |
+
indexed[target_day] = {
|
| 1277 |
+
"prediction": "UP" if pred_int == 1 else "DOWN",
|
| 1278 |
+
"prob_up": prob_up,
|
| 1279 |
+
"forecast_date": row.get("forecast_date"),
|
| 1280 |
+
"source": "Tomorrow (forecaster)",
|
| 1281 |
+
}
|
| 1282 |
+
return indexed
|
| 1283 |
+
|
| 1284 |
+
|
| 1285 |
+
def _rolling_tomorrow_prediction(
|
| 1286 |
+
input_day: date,
|
| 1287 |
+
daily_rows: pd.DataFrame,
|
| 1288 |
+
threshold: float,
|
| 1289 |
+
fallback_prob: float,
|
| 1290 |
+
) -> tuple[str, float]:
|
| 1291 |
+
"""Simulate the Tomorrow model using only daily bars available through ``input_day``."""
|
| 1292 |
+
history = daily_rows[daily_rows["date"].dt.date <= input_day].copy()
|
| 1293 |
+
prob_up = _tomorrow_probability_from_daily(history, fallback_prob)
|
| 1294 |
+
prediction = "UP" if prob_up >= threshold else "DOWN"
|
| 1295 |
+
return prediction, float(prob_up)
|
| 1296 |
+
|
| 1297 |
+
|
| 1298 |
def build_prediction_track_record(
|
| 1299 |
daily: pd.DataFrame,
|
| 1300 |
+
sessions: int = 10,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1301 |
) -> list[dict[str, Any]]:
|
| 1302 |
+
"""Rolling last-N Tomorrow simulation: predict each session, score vs prior close."""
|
| 1303 |
+
summary = load_tomorrow_summary()
|
| 1304 |
+
artifact = load_tomorrow_model_artifact()
|
| 1305 |
+
threshold = float(artifact.get("threshold", summary.get("threshold", 0.534)))
|
| 1306 |
+
fallback_prob = float(summary.get("latest_forecast_prob_up", 0.49900560447008563))
|
| 1307 |
+
forecaster_by_target = _load_forecaster_predictions_by_target()
|
| 1308 |
+
|
| 1309 |
daily_rows = daily.copy()
|
| 1310 |
daily_rows["date"] = pd.to_datetime(daily_rows["date"], errors="coerce").dt.normalize()
|
| 1311 |
daily_rows = daily_rows.dropna(subset=["date"]).sort_values("date")
|
| 1312 |
daily_rows = daily_rows[
|
| 1313 |
+
daily_rows["close"].map(lambda value: np.isfinite(float(value)) if pd.notna(value) else False)
|
|
|
|
| 1314 |
].copy()
|
|
|
|
| 1315 |
completed_day = expected_completed_daily_date()
|
| 1316 |
daily_rows = daily_rows[daily_rows["date"].dt.date <= completed_day]
|
| 1317 |
if daily_rows.empty:
|
| 1318 |
return []
|
| 1319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1320 |
closes_by_date = {
|
| 1321 |
row["date"].date(): float(row["close"])
|
| 1322 |
for _, row in daily_rows.iterrows()
|
|
|
|
| 1324 |
}
|
| 1325 |
|
| 1326 |
records: list[dict[str, Any]] = []
|
| 1327 |
+
for _, row in daily_rows.tail(sessions).iterrows():
|
| 1328 |
+
target_day = row["date"].date()
|
| 1329 |
day_close = float(row["close"])
|
| 1330 |
+
actual_move, actual_direction = _tomorrow_actual_outcome(target_day, day_close, closes_by_date)
|
| 1331 |
if actual_direction is None:
|
| 1332 |
continue
|
| 1333 |
+
|
| 1334 |
+
input_day = previous_trading_day(target_day - timedelta(days=1))
|
| 1335 |
+
cached = forecaster_by_target.get(target_day)
|
| 1336 |
+
if cached and cached.get("prediction") in {"UP", "DOWN"}:
|
| 1337 |
+
prediction = cached["prediction"]
|
| 1338 |
+
prob_up = cached.get("prob_up")
|
| 1339 |
+
source = cached.get("source", "Tomorrow (forecaster)")
|
| 1340 |
+
else:
|
| 1341 |
+
prediction, prob_up = _rolling_tomorrow_prediction(
|
| 1342 |
+
input_day,
|
| 1343 |
+
daily_rows,
|
| 1344 |
+
threshold,
|
| 1345 |
+
fallback_prob,
|
| 1346 |
+
)
|
| 1347 |
+
source = "Tomorrow (rolling)"
|
| 1348 |
+
|
| 1349 |
records.append(
|
| 1350 |
{
|
| 1351 |
+
"date": target_day.isoformat(),
|
| 1352 |
+
"input_date": input_day.isoformat(),
|
| 1353 |
"prediction": prediction,
|
| 1354 |
+
"prediction_source": source,
|
| 1355 |
+
"prob_up": prob_up,
|
| 1356 |
"actual_move": actual_move,
|
| 1357 |
"actual_direction": actual_direction,
|
| 1358 |
+
"correct": prediction == actual_direction,
|
| 1359 |
}
|
| 1360 |
)
|
| 1361 |
return records
|
|
|
|
| 1452 |
"total_test_days": int(tomorrow_summary.get("n_test") or len(tomorrow_test) or 0),
|
| 1453 |
"models": model_metrics,
|
| 1454 |
}
|
| 1455 |
+
track_record = build_prediction_track_record(daily, sessions=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1456 |
return {
|
| 1457 |
"latest": t5_latest,
|
| 1458 |
"tomorrow_latest": tomorrow_latest,
|