Jitendra12421 commited on
Commit
7c662b5
·
verified ·
1 Parent(s): c8eb97d

Upload runtime.py

Browse files
Files changed (1) hide show
  1. runtime.py +127 -62
runtime.py CHANGED
@@ -40,10 +40,11 @@ NIFTY_1D_PATH = DATA_DIR / "nifty50_1d.parquet"
40
  MODEL_PATH = MODEL_DIR / "nifty_opening_direction_model.joblib"
41
  LATEST_PATH = MODEL_DIR / "latest_prediction.csv"
42
  TEST_PREDICTIONS_PATH = DATA_DIR / "test_predictions.parquet"
43
- TOMORROW_MODEL_PATH = MODEL_DIR / "nifty_tomorrow_direction_model.joblib"
44
- TOMORROW_LATEST_PATH = MODEL_DIR / "tomorrow_latest_prediction.csv"
45
- TOMORROW_SUMMARY_PATH = MODEL_DIR / "tomorrow_summary.json"
46
  TOMORROW_TEST_PREDICTIONS_PATH = DATA_DIR / "tomorrow_test_predictions.parquet"
 
47
  FORECASTING_PROJECT_ROOT = Path(
48
  os.environ.get(
49
  "FORECASTING_PROJECT_ROOT",
@@ -55,14 +56,16 @@ DAILY_FORECASTER_SUMMARY_PATH = DAILY_FORECASTER_OUTPUT_DIR / "forecaster_summar
55
  DAILY_FORECASTER_LATEST_PATH = DAILY_FORECASTER_OUTPUT_DIR / "forecaster_latest.csv"
56
  DAILY_FORECASTER_PREDICTIONS_PATH = DAILY_FORECASTER_OUTPUT_DIR / "forecaster_test_predictions.csv"
57
  TPLUS1_MODEL_PATH = MODEL_DIR / "nifty_1420_tplus1_logistic_model.joblib"
58
- TPLUS1_LATEST_PATH = MODEL_DIR / "tplus1_latest_prediction.csv"
59
- TPLUS1_SUMMARY_PATH = MODEL_DIR / "tplus1_summary.json"
60
- TPLUS1_TEST_PREDICTIONS_PATH = DATA_DIR / "tplus1_test_predictions.parquet"
61
- REFRESH_STATE_PATH = MODEL_DIR / "refresh_state.json"
62
- REFRESH_WAITING = "waiting_second_payload"
63
- REFRESH_REFRESHING = "refreshing"
64
- REFRESH_READY = "ready"
65
- REFRESH_FAILED = "failed"
 
 
66
  REFRESH_NORMAL = "normal"
67
  LIVE_ACCURACY_PATH = MODEL_DIR / "live_accuracy.json"
68
 
@@ -381,19 +384,24 @@ def fetch_yahoo_daily(period: str = "1mo") -> pd.DataFrame:
381
  return yahoo_history_to_ohlcv(raw, daily=True)
382
 
383
 
384
- def append_parquet_rows(path: Path, new_rows: pd.DataFrame, subset: list[str]) -> pd.DataFrame:
385
- if new_rows.empty:
386
- if path.exists():
387
- return pd.read_parquet(path)
388
- raise RuntimeError(f"No rows returned for {path.name}; leaving parquet unchanged.")
389
  if path.exists():
390
  existing = pd.read_parquet(path)
391
  combined = pd.concat([existing, new_rows], ignore_index=True)
392
  else:
393
  combined = new_rows.copy()
394
- combined = combined.drop_duplicates(subset=subset, keep="last").sort_values(subset).reset_index(drop=True)
395
- combined.to_parquet(path, index=False, compression="zstd")
396
- return combined
 
 
 
 
 
397
 
398
 
399
  def latest_parquet_date(path: Path) -> date | None:
@@ -502,7 +510,7 @@ def build_model_row(first5_row: pd.DataFrame) -> pd.DataFrame:
502
  return output
503
 
504
 
505
- def predict_row(row: pd.DataFrame) -> Prediction:
506
  payload = load_model()
507
  model = payload["model"]
508
  features = payload["features"]
@@ -515,19 +523,28 @@ def predict_row(row: pd.DataFrame) -> Prediction:
515
  pred = apply_decision_overlays(raw_pred, row, payload.get("decision_overlays", DECISION_OVERLAYS))
516
  is_overridden = bool(raw_pred[0] != pred[0])
517
  confidence = directional_confidence(prob_up, pred, threshold)
518
- prediction = Prediction(
519
- input_date=pd.to_datetime(row["date"].iloc[0]).date().isoformat(),
520
- first5_start=str(pd.to_datetime(row["first5_start"].iloc[0])),
521
- first5_end=str(pd.to_datetime(row["first5_end"].iloc[0])),
522
- prediction="UP" if int(pred[0]) == 1 else "DOWN",
523
  prob_up=float(prob_up[0]),
524
  confidence=float(confidence[0]),
525
  threshold=threshold,
526
- model_name=str(payload.get("model_name", "nifty_opening_direction_model")),
527
- is_overridden=is_overridden,
528
- )
529
- pd.DataFrame([prediction.to_dict()]).to_csv(LATEST_PATH, index=False)
530
- return prediction
 
 
 
 
 
 
 
 
 
531
 
532
 
533
  def _file_cache_key(path: Path) -> tuple[str, int | None, int | None]:
@@ -925,23 +942,25 @@ def refresh_tplus1_prediction(session_date: date | None = None) -> dict[str, Any
925
  input_day = pd.to_datetime(row["date"].iloc[0]).date()
926
  target_day = next_trading_day(input_day + timedelta(days=1))
927
  summary = load_tplus1_summary()
928
- out = {
929
- "input_date": input_day.isoformat(),
930
- "target_date": target_day.isoformat(),
931
- "forecast_for": f"next trading session after {input_day.isoformat()}",
932
  "prediction": prediction,
933
  "prob_up": float(prob_up[0]),
934
  "confidence": float(max(prob_up[0], 1.0 - prob_up[0])),
935
  "threshold": threshold,
936
  "model_name": str(payload.get("model_name", summary.get("model_name", "nifty_1420_tplus1_logistic_model"))),
937
  "decision_overlay": summary.get("decision_overlay"),
938
- "validation_accuracy": summary.get("validation_accuracy"),
939
- "test_accuracy": summary.get("test_accuracy"),
940
- "accuracy_goal": summary.get("accuracy_goal"),
941
- }
942
- pd.DataFrame([out]).to_csv(TPLUS1_LATEST_PATH, index=False)
943
- clear_dashboard_payload_cache()
944
- return out
 
 
945
 
946
 
947
  def _tomorrow_probability_from_daily(daily: pd.DataFrame, fallback_prob: float) -> float:
@@ -971,6 +990,8 @@ def refresh_tomorrow_prediction(session_date: date | None = None) -> dict[str, A
971
  if synced is not None and TOMORROW_LATEST_PATH.exists():
972
  latest = pd.read_csv(TOMORROW_LATEST_PATH).iloc[-1].to_dict()
973
  cleaned = {k: (None if pd.isna(v) else v) for k, v in latest.items()}
 
 
974
  if session_date is None:
975
  clear_dashboard_payload_cache()
976
  return cleaned
@@ -995,20 +1016,22 @@ def refresh_tomorrow_prediction(session_date: date | None = None) -> dict[str, A
995
  prob_up = _tomorrow_probability_from_daily(daily[daily["date"].dt.date <= input_day], fallback_prob)
996
  prediction = "UP" if prob_up >= threshold else "DOWN"
997
  confidence = float(max(prob_up, 1.0 - prob_up))
998
- row = {
999
- "input_date": input_day.isoformat(),
1000
- "target_date": target_day.isoformat(),
1001
- "prediction": prediction,
1002
- "prob_up": prob_up,
1003
- "confidence": confidence,
1004
- "threshold": threshold,
1005
- "model_name": str(summary.get("model_name", "nifty_tomorrow_direction_model")),
1006
- "source_model": str(summary.get("source_model", "tuned_daily_forest_single")),
1007
- "validation_accuracy": float(summary.get("validation_accuracy", 0.5780141843971631)),
1008
- "test_accuracy": float(summary.get("test_accuracy", 0.6182795698924731)),
1009
- }
1010
- pd.DataFrame([row]).to_csv(TOMORROW_LATEST_PATH, index=False)
1011
- summary = dict(summary)
 
 
1012
  summary.update(
1013
  {
1014
  "latest_forecast_date": row["input_date"],
@@ -1393,16 +1416,41 @@ def update_opening_outcomes_from_daily() -> dict[str, Any]:
1393
 
1394
  def load_live_accuracy() -> dict[str, Any]:
1395
  """Load the live accuracy ledger from disk."""
 
 
 
 
 
 
1396
  if LIVE_ACCURACY_PATH.exists():
1397
  try:
1398
- return json.loads(LIVE_ACCURACY_PATH.read_text(encoding="utf-8"))
1399
  except Exception:
1400
- pass
1401
- return {
1402
- "tomorrow": {"entries": [], "accuracy": None, "total": 0, "correct_count": 0},
1403
- "t5": {"entries": [], "accuracy": None, "total": 0, "correct_count": 0},
1404
- "tplus1": {"entries": [], "accuracy": None, "total": 0, "correct_count": 0},
1405
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1406
 
1407
 
1408
  def save_live_accuracy(data: dict[str, Any]) -> None:
@@ -1410,6 +1458,23 @@ def save_live_accuracy(data: dict[str, Any]) -> None:
1410
  LIVE_ACCURACY_PATH.write_text(json.dumps(data, indent=2), encoding="utf-8")
1411
 
1412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1413
  def update_live_accuracy(session_date: date) -> dict[str, Any]:
1414
  """Score today's predictions against actual outcomes and update the ledger.
1415
 
 
40
  MODEL_PATH = MODEL_DIR / "nifty_opening_direction_model.joblib"
41
  LATEST_PATH = MODEL_DIR / "latest_prediction.csv"
42
  TEST_PREDICTIONS_PATH = DATA_DIR / "test_predictions.parquet"
43
+ TOMORROW_MODEL_PATH = MODEL_DIR / "nifty_tomorrow_direction_model.joblib"
44
+ TOMORROW_LATEST_PATH = MODEL_DIR / "tomorrow_latest_prediction.csv"
45
+ TOMORROW_SUMMARY_PATH = MODEL_DIR / "tomorrow_summary.json"
46
  TOMORROW_TEST_PREDICTIONS_PATH = DATA_DIR / "tomorrow_test_predictions.parquet"
47
+ TOMORROW_PREDICTION_HISTORY_PATH = MODEL_DIR / "tomorrow_prediction_history.parquet"
48
  FORECASTING_PROJECT_ROOT = Path(
49
  os.environ.get(
50
  "FORECASTING_PROJECT_ROOT",
 
56
  DAILY_FORECASTER_LATEST_PATH = DAILY_FORECASTER_OUTPUT_DIR / "forecaster_latest.csv"
57
  DAILY_FORECASTER_PREDICTIONS_PATH = DAILY_FORECASTER_OUTPUT_DIR / "forecaster_test_predictions.csv"
58
  TPLUS1_MODEL_PATH = MODEL_DIR / "nifty_1420_tplus1_logistic_model.joblib"
59
+ TPLUS1_LATEST_PATH = MODEL_DIR / "tplus1_latest_prediction.csv"
60
+ TPLUS1_SUMMARY_PATH = MODEL_DIR / "tplus1_summary.json"
61
+ TPLUS1_TEST_PREDICTIONS_PATH = DATA_DIR / "tplus1_test_predictions.parquet"
62
+ TPLUS1_PREDICTION_HISTORY_PATH = MODEL_DIR / "tplus1_prediction_history.parquet"
63
+ T5_PREDICTION_HISTORY_PATH = MODEL_DIR / "t5_prediction_history.parquet"
64
+ REFRESH_STATE_PATH = MODEL_DIR / "refresh_state.json"
65
+ REFRESH_WAITING = "waiting_second_payload"
66
+ REFRESH_REFRESHING = "refreshing"
67
+ REFRESH_READY = "ready"
68
+ REFRESH_FAILED = "failed"
69
  REFRESH_NORMAL = "normal"
70
  LIVE_ACCURACY_PATH = MODEL_DIR / "live_accuracy.json"
71
 
 
384
  return yahoo_history_to_ohlcv(raw, daily=True)
385
 
386
 
387
+ def append_parquet_rows(path: Path, new_rows: pd.DataFrame, subset: list[str]) -> pd.DataFrame:
388
+ if new_rows.empty:
389
+ if path.exists():
390
+ return pd.read_parquet(path)
391
+ raise RuntimeError(f"No rows returned for {path.name}; leaving parquet unchanged.")
392
  if path.exists():
393
  existing = pd.read_parquet(path)
394
  combined = pd.concat([existing, new_rows], ignore_index=True)
395
  else:
396
  combined = new_rows.copy()
397
+ combined = combined.drop_duplicates(subset=subset, keep="last").sort_values(subset).reset_index(drop=True)
398
+ combined.to_parquet(path, index=False, compression="zstd")
399
+ return combined
400
+
401
+
402
+ def append_prediction_history(path: Path, row: dict[str, Any], subset: list[str]) -> pd.DataFrame:
403
+ frame = pd.DataFrame([row])
404
+ return append_parquet_rows(path, frame, subset)
405
 
406
 
407
  def latest_parquet_date(path: Path) -> date | None:
 
510
  return output
511
 
512
 
513
+ def predict_row(row: pd.DataFrame) -> Prediction:
514
  payload = load_model()
515
  model = payload["model"]
516
  features = payload["features"]
 
523
  pred = apply_decision_overlays(raw_pred, row, payload.get("decision_overlays", DECISION_OVERLAYS))
524
  is_overridden = bool(raw_pred[0] != pred[0])
525
  confidence = directional_confidence(prob_up, pred, threshold)
526
+ prediction = Prediction(
527
+ input_date=pd.to_datetime(row["date"].iloc[0]).date().isoformat(),
528
+ first5_start=str(pd.to_datetime(row["first5_start"].iloc[0])),
529
+ first5_end=str(pd.to_datetime(row["first5_end"].iloc[0])),
530
+ prediction="UP" if int(pred[0]) == 1 else "DOWN",
531
  prob_up=float(prob_up[0]),
532
  confidence=float(confidence[0]),
533
  threshold=threshold,
534
+ model_name=str(payload.get("model_name", "nifty_opening_direction_model")),
535
+ is_overridden=is_overridden,
536
+ )
537
+ pd.DataFrame([prediction.to_dict()]).to_csv(LATEST_PATH, index=False)
538
+ _record_prediction_history(
539
+ T5_PREDICTION_HISTORY_PATH,
540
+ {
541
+ **prediction.to_dict(),
542
+ "target_date": prediction.input_date,
543
+ "source": "live",
544
+ },
545
+ ["target_date"],
546
+ )
547
+ return prediction
548
 
549
 
550
  def _file_cache_key(path: Path) -> tuple[str, int | None, int | None]:
 
942
  input_day = pd.to_datetime(row["date"].iloc[0]).date()
943
  target_day = next_trading_day(input_day + timedelta(days=1))
944
  summary = load_tplus1_summary()
945
+ out = {
946
+ "input_date": input_day.isoformat(),
947
+ "target_date": target_day.isoformat(),
948
+ "forecast_for": f"next trading session after {input_day.isoformat()}",
949
  "prediction": prediction,
950
  "prob_up": float(prob_up[0]),
951
  "confidence": float(max(prob_up[0], 1.0 - prob_up[0])),
952
  "threshold": threshold,
953
  "model_name": str(payload.get("model_name", summary.get("model_name", "nifty_1420_tplus1_logistic_model"))),
954
  "decision_overlay": summary.get("decision_overlay"),
955
+ "validation_accuracy": summary.get("validation_accuracy"),
956
+ "test_accuracy": summary.get("test_accuracy"),
957
+ "accuracy_goal": summary.get("accuracy_goal"),
958
+ "source": "live",
959
+ }
960
+ pd.DataFrame([out]).to_csv(TPLUS1_LATEST_PATH, index=False)
961
+ _record_prediction_history(TPLUS1_PREDICTION_HISTORY_PATH, out, ["target_date"])
962
+ clear_dashboard_payload_cache()
963
+ return out
964
 
965
 
966
  def _tomorrow_probability_from_daily(daily: pd.DataFrame, fallback_prob: float) -> float:
 
990
  if synced is not None and TOMORROW_LATEST_PATH.exists():
991
  latest = pd.read_csv(TOMORROW_LATEST_PATH).iloc[-1].to_dict()
992
  cleaned = {k: (None if pd.isna(v) else v) for k, v in latest.items()}
993
+ cleaned["source"] = "live"
994
+ _record_prediction_history(TOMORROW_PREDICTION_HISTORY_PATH, cleaned, ["target_date"])
995
  if session_date is None:
996
  clear_dashboard_payload_cache()
997
  return cleaned
 
1016
  prob_up = _tomorrow_probability_from_daily(daily[daily["date"].dt.date <= input_day], fallback_prob)
1017
  prediction = "UP" if prob_up >= threshold else "DOWN"
1018
  confidence = float(max(prob_up, 1.0 - prob_up))
1019
+ row = {
1020
+ "input_date": input_day.isoformat(),
1021
+ "target_date": target_day.isoformat(),
1022
+ "prediction": prediction,
1023
+ "prob_up": prob_up,
1024
+ "confidence": confidence,
1025
+ "threshold": threshold,
1026
+ "model_name": str(summary.get("model_name", "nifty_tomorrow_direction_model")),
1027
+ "source_model": str(summary.get("source_model", "tuned_daily_forest_single")),
1028
+ "validation_accuracy": float(summary.get("validation_accuracy", 0.5780141843971631)),
1029
+ "test_accuracy": float(summary.get("test_accuracy", 0.6182795698924731)),
1030
+ "source": "live",
1031
+ }
1032
+ pd.DataFrame([row]).to_csv(TOMORROW_LATEST_PATH, index=False)
1033
+ _record_prediction_history(TOMORROW_PREDICTION_HISTORY_PATH, row, ["target_date"])
1034
+ summary = dict(summary)
1035
  summary.update(
1036
  {
1037
  "latest_forecast_date": row["input_date"],
 
1416
 
1417
  def load_live_accuracy() -> dict[str, Any]:
1418
  """Load the live accuracy ledger from disk."""
1419
+ default = {
1420
+ "tomorrow": {"entries": [], "accuracy": None, "total": 0, "correct_count": 0, "backtest_count": 0, "live_count": 0},
1421
+ "t5": {"entries": [], "accuracy": None, "total": 0, "correct_count": 0, "backtest_count": 0, "live_count": 0},
1422
+ "tplus1": {"entries": [], "accuracy": None, "total": 0, "correct_count": 0, "backtest_count": 0, "live_count": 0},
1423
+ }
1424
+
1425
  if LIVE_ACCURACY_PATH.exists():
1426
  try:
1427
+ raw = json.loads(LIVE_ACCURACY_PATH.read_text(encoding="utf-8"))
1428
  except Exception:
1429
+ raw = None
1430
+ if isinstance(raw, dict):
1431
+ try:
1432
+ for model_id in default:
1433
+ current = raw.get(model_id, {})
1434
+ if not isinstance(current, dict):
1435
+ current = {}
1436
+ entries = current.get("entries", [])
1437
+ if not isinstance(entries, list):
1438
+ entries = []
1439
+ backtest_entries = [entry for entry in entries if str(entry.get("source", "backtest")).lower() == "backtest"]
1440
+ live_entries = [entry for entry in entries if str(entry.get("source", "backtest")).lower() != "backtest"]
1441
+ total = len(entries)
1442
+ correct = sum(1 for e in entries if e.get("correct"))
1443
+ current["entries"] = entries
1444
+ current["backtest_count"] = int(current.get("backtest_count") or len(backtest_entries))
1445
+ current["live_count"] = int(current.get("live_count") or len(live_entries))
1446
+ current["total"] = int(current.get("total") or total)
1447
+ current["correct_count"] = int(current.get("correct_count") or correct)
1448
+ current["accuracy"] = (current["correct_count"] / current["total"]) if current["total"] > 0 else None
1449
+ default[model_id].update(current)
1450
+ return default
1451
+ except Exception:
1452
+ pass
1453
+ return default
1454
 
1455
 
1456
  def save_live_accuracy(data: dict[str, Any]) -> None:
 
1458
  LIVE_ACCURACY_PATH.write_text(json.dumps(data, indent=2), encoding="utf-8")
1459
 
1460
 
1461
+ def _load_prediction_history(path: Path) -> pd.DataFrame:
1462
+ if not path.exists():
1463
+ return pd.DataFrame()
1464
+ frame = pd.read_parquet(path)
1465
+ for col in ("date", "input_date", "target_date", "forecast_date"):
1466
+ if col in frame.columns:
1467
+ frame[col] = pd.to_datetime(frame[col], errors="coerce")
1468
+ sort_cols = [col for col in ("target_date", "input_date", "date", "forecast_date") if col in frame.columns]
1469
+ if sort_cols:
1470
+ return frame.sort_values(sort_cols).reset_index(drop=True)
1471
+ return frame.reset_index(drop=True)
1472
+
1473
+
1474
+ def _record_prediction_history(path: Path, row: dict[str, Any], subset: list[str]) -> None:
1475
+ append_prediction_history(path, row, subset)
1476
+
1477
+
1478
  def update_live_accuracy(session_date: date) -> dict[str, Any]:
1479
  """Score today's predictions against actual outcomes and update the ledger.
1480