Upload 42 files
Browse files
.gitattributes
CHANGED
|
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
models/yahoo_history_cache.sqlite3 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
models/yahoo_history_cache.sqlite3 filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
nifty_backend/__pycache__/runtime.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text
|
data/tomorrow_test_predictions.parquet
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a0f2de230f3ae32dd009829efe7b90333dfa7f07bdb9a627d96f6173c30c77bb
|
| 3 |
+
size 10739
|
models/nifty_tomorrow_direction_model.joblib
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fffacba0382c720974d006020e083b14fe13510c5347b8e2778bc514bb965e34
|
| 3 |
+
size 443
|
models/tomorrow_latest_prediction.csv
CHANGED
|
@@ -1,2 +1,2 @@
|
|
| 1 |
-
input_date,target_date,prediction,prob_up,confidence,threshold,model_name,source_model,validation_accuracy,test_accuracy
|
| 2 |
-
2026-05-27,2026-05-29,DOWN,0.
|
|
|
|
| 1 |
+
input_date,target_date,prediction,prob_up,confidence,threshold,model_name,source_model,validation_accuracy,test_accuracy,artifact_source
|
| 2 |
+
2026-05-27,2026-05-29,DOWN,0.4807130191016562,0.5192869808983438,0.54,nifty_tomorrow_direction_model,locked_multiwindow_nifty50_ensemble,0.5673758865248227,0.6368421052631579,C:\Users\jhaji\Downloads\forecasting project\Code\models\nifty_forecaster\outputs
|
models/tomorrow_summary.json
CHANGED
|
@@ -3,35 +3,40 @@
|
|
| 3 |
"horizon": "daily",
|
| 4 |
"horizon_bars": 1,
|
| 5 |
"config": {
|
| 6 |
-
"name": "
|
| 7 |
-
"use_intraday":
|
| 8 |
"use_external": true,
|
| 9 |
"use_institutional": false,
|
| 10 |
"use_options": true,
|
| 11 |
"use_engineered_macro_flow": false,
|
| 12 |
-
"blend_mode": "
|
| 13 |
"decision_overlay": "bank_body_near_threshold"
|
| 14 |
},
|
| 15 |
-
"threshold": 0.
|
| 16 |
-
"validation_accuracy": 0.
|
| 17 |
-
"test_accuracy": 0.
|
| 18 |
-
"baseline_accuracy": 0.
|
| 19 |
"n_train": 2221,
|
| 20 |
"n_valid": 282,
|
| 21 |
-
"n_test":
|
| 22 |
"train_start": "2015-01-09",
|
| 23 |
"train_end": "2023-12-31",
|
| 24 |
"valid_start": "2024-07-01",
|
| 25 |
"valid_end": "2025-08-17",
|
| 26 |
"test_start": "2025-08-18",
|
| 27 |
-
"test_end": "2026-05-
|
| 28 |
"latest_forecast_date": "2026-05-27",
|
| 29 |
-
"latest_forecast_for": "next trading
|
| 30 |
-
"latest_forecast_prob_up": 0.
|
| 31 |
"latest_forecast_signal": "DOWN",
|
| 32 |
-
"feature_count":
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
"model_name": "nifty_tomorrow_direction_model",
|
| 34 |
-
"source_model": "
|
| 35 |
"target": "next trading session NIFTY 50 direction",
|
| 36 |
-
"
|
|
|
|
| 37 |
}
|
|
|
|
| 3 |
"horizon": "daily",
|
| 4 |
"horizon_bars": 1,
|
| 5 |
"config": {
|
| 6 |
+
"name": "locked_multiwindow_nifty50_ensemble",
|
| 7 |
+
"use_intraday": true,
|
| 8 |
"use_external": true,
|
| 9 |
"use_institutional": false,
|
| 10 |
"use_options": true,
|
| 11 |
"use_engineered_macro_flow": false,
|
| 12 |
+
"blend_mode": "locked_nifty50_multiwindow",
|
| 13 |
"decision_overlay": "bank_body_near_threshold"
|
| 14 |
},
|
| 15 |
+
"threshold": 0.54,
|
| 16 |
+
"validation_accuracy": 0.5673758865248227,
|
| 17 |
+
"test_accuracy": 0.6368421052631579,
|
| 18 |
+
"baseline_accuracy": 0.5052631578947369,
|
| 19 |
"n_train": 2221,
|
| 20 |
"n_valid": 282,
|
| 21 |
+
"n_test": 190,
|
| 22 |
"train_start": "2015-01-09",
|
| 23 |
"train_end": "2023-12-31",
|
| 24 |
"valid_start": "2024-07-01",
|
| 25 |
"valid_end": "2025-08-17",
|
| 26 |
"test_start": "2025-08-18",
|
| 27 |
+
"test_end": "2026-05-26",
|
| 28 |
"latest_forecast_date": "2026-05-27",
|
| 29 |
+
"latest_forecast_for": "next trading bar after 2026-05-27",
|
| 30 |
+
"latest_forecast_prob_up": 0.48071301910165626,
|
| 31 |
"latest_forecast_signal": "DOWN",
|
| 32 |
+
"feature_count": 204,
|
| 33 |
+
"validation_prob_std": 0.07748322465953016,
|
| 34 |
+
"test_prob_std": 0.07363146825392504,
|
| 35 |
+
"test_prob_min": 0.37108281367720986,
|
| 36 |
+
"test_prob_max": 0.6611571963678617,
|
| 37 |
"model_name": "nifty_tomorrow_direction_model",
|
| 38 |
+
"source_model": "locked_multiwindow_nifty50_ensemble",
|
| 39 |
"target": "next trading session NIFTY 50 direction",
|
| 40 |
+
"artifact_type": "daily_forecaster_outputs",
|
| 41 |
+
"artifact_source": "C:\\Users\\jhaji\\Downloads\\forecasting project\\Code\\models\\nifty_forecaster\\outputs"
|
| 42 |
}
|
nifty_backend/__pycache__/runtime.cpython-311.pyc
CHANGED
|
Binary files a/nifty_backend/__pycache__/runtime.cpython-311.pyc and b/nifty_backend/__pycache__/runtime.cpython-311.pyc differ
|
|
|
nifty_backend/runtime.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
-
import json
|
| 4 |
-
import copy
|
| 5 |
-
import
|
| 6 |
-
import
|
|
|
|
| 7 |
from dataclasses import dataclass
|
| 8 |
from datetime import date, datetime, time, timedelta
|
| 9 |
from functools import lru_cache
|
|
@@ -42,8 +43,18 @@ TEST_PREDICTIONS_PATH = DATA_DIR / "test_predictions.parquet"
|
|
| 42 |
TOMORROW_MODEL_PATH = MODEL_DIR / "nifty_tomorrow_direction_model.joblib"
|
| 43 |
TOMORROW_LATEST_PATH = MODEL_DIR / "tomorrow_latest_prediction.csv"
|
| 44 |
TOMORROW_SUMMARY_PATH = MODEL_DIR / "tomorrow_summary.json"
|
| 45 |
-
TOMORROW_TEST_PREDICTIONS_PATH = DATA_DIR / "tomorrow_test_predictions.parquet"
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
TPLUS1_LATEST_PATH = MODEL_DIR / "tplus1_latest_prediction.csv"
|
| 48 |
TPLUS1_SUMMARY_PATH = MODEL_DIR / "tplus1_summary.json"
|
| 49 |
TPLUS1_TEST_PREDICTIONS_PATH = DATA_DIR / "tplus1_test_predictions.parquet"
|
|
@@ -541,19 +552,125 @@ def latest_saved_prediction() -> dict[str, Any]:
|
|
| 541 |
return dict(_latest_saved_prediction_cached(_file_cache_key(LATEST_PATH), _file_cache_key(MODEL_DIR / "summary.json")))
|
| 542 |
|
| 543 |
|
| 544 |
-
def _latest_saved_prediction_uncached() -> dict[str, Any]:
|
| 545 |
if LATEST_PATH.exists():
|
| 546 |
return pd.read_csv(LATEST_PATH).iloc[-1].to_dict()
|
| 547 |
summary_path = MODEL_DIR / "summary.json"
|
| 548 |
if summary_path.exists():
|
| 549 |
return json.loads(summary_path.read_text(encoding="utf-8"))
|
| 550 |
-
raise FileNotFoundError("No latest prediction is available yet.")
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
def
|
| 554 |
-
if
|
| 555 |
-
return
|
| 556 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
return {
|
| 558 |
"artifact_type": "daily_forecaster_snapshot",
|
| 559 |
"model_name": summary.get("model_name", "nifty_tomorrow_direction_model"),
|
|
@@ -562,23 +679,27 @@ def load_tomorrow_model_artifact() -> dict[str, Any]:
|
|
| 562 |
}
|
| 563 |
|
| 564 |
|
| 565 |
-
def load_tomorrow_summary() -> dict[str, Any]:
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
"
|
| 573 |
-
"
|
| 574 |
-
"
|
| 575 |
-
"
|
| 576 |
-
"
|
| 577 |
-
"
|
| 578 |
-
|
|
|
|
|
|
|
|
|
|
| 579 |
|
| 580 |
|
| 581 |
def latest_tomorrow_prediction() -> dict[str, Any]:
|
|
|
|
| 582 |
latest_daily = latest_parquet_date(NIFTY_1D_PATH)
|
| 583 |
if TOMORROW_LATEST_PATH.exists():
|
| 584 |
row = pd.read_csv(TOMORROW_LATEST_PATH).iloc[-1].to_dict()
|
|
@@ -589,9 +710,15 @@ def latest_tomorrow_prediction() -> dict[str, Any]:
|
|
| 589 |
input_day = None
|
| 590 |
if latest_daily is not None and (input_day is None or input_day < latest_daily):
|
| 591 |
try:
|
| 592 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 593 |
except Exception:
|
| 594 |
-
|
| 595 |
return cleaned
|
| 596 |
summary = load_tomorrow_summary()
|
| 597 |
try:
|
|
@@ -835,9 +962,23 @@ def _tomorrow_probability_from_daily(daily: pd.DataFrame, fallback_prob: float)
|
|
| 835 |
return float(np.clip(score, 0.35, 0.65))
|
| 836 |
|
| 837 |
|
| 838 |
-
def refresh_tomorrow_prediction(session_date: date | None = None) -> dict[str, Any]:
|
| 839 |
-
|
| 840 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 841 |
daily = pd.read_parquet(NIFTY_1D_PATH)
|
| 842 |
daily["date"] = pd.to_datetime(daily["date"], errors="coerce").dt.normalize()
|
| 843 |
daily = daily.dropna(subset=["date"]).sort_values("date")
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
+
import json
|
| 4 |
+
import copy
|
| 5 |
+
import os
|
| 6 |
+
import sys
|
| 7 |
+
import threading
|
| 8 |
from dataclasses import dataclass
|
| 9 |
from datetime import date, datetime, time, timedelta
|
| 10 |
from functools import lru_cache
|
|
|
|
| 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",
|
| 50 |
+
str(BACKEND_ROOT.parent.parent / "forecasting project"),
|
| 51 |
+
)
|
| 52 |
+
)
|
| 53 |
+
DAILY_FORECASTER_OUTPUT_DIR = FORECASTING_PROJECT_ROOT / "Code" / "models" / "nifty_forecaster" / "outputs"
|
| 54 |
+
DAILY_FORECASTER_SUMMARY_PATH = DAILY_FORECASTER_OUTPUT_DIR / "forecaster_summary.json"
|
| 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"
|
|
|
|
| 552 |
return dict(_latest_saved_prediction_cached(_file_cache_key(LATEST_PATH), _file_cache_key(MODEL_DIR / "summary.json")))
|
| 553 |
|
| 554 |
|
| 555 |
+
def _latest_saved_prediction_uncached() -> dict[str, Any]:
|
| 556 |
if LATEST_PATH.exists():
|
| 557 |
return pd.read_csv(LATEST_PATH).iloc[-1].to_dict()
|
| 558 |
summary_path = MODEL_DIR / "summary.json"
|
| 559 |
if summary_path.exists():
|
| 560 |
return json.loads(summary_path.read_text(encoding="utf-8"))
|
| 561 |
+
raise FileNotFoundError("No latest prediction is available yet.")
|
| 562 |
+
|
| 563 |
+
|
| 564 |
+
def _read_daily_forecaster_summary() -> dict[str, Any] | None:
|
| 565 |
+
if not DAILY_FORECASTER_SUMMARY_PATH.exists():
|
| 566 |
+
return None
|
| 567 |
+
raw = json.loads(DAILY_FORECASTER_SUMMARY_PATH.read_text(encoding="utf-8"))
|
| 568 |
+
if isinstance(raw, list):
|
| 569 |
+
matches = [row for row in raw if row.get("symbol") == "NIFTY 50"]
|
| 570 |
+
summary = dict(matches[0] if matches else raw[0])
|
| 571 |
+
elif isinstance(raw, dict):
|
| 572 |
+
summary = dict(raw)
|
| 573 |
+
else:
|
| 574 |
+
return None
|
| 575 |
+
config = summary.get("config") if isinstance(summary.get("config"), dict) else {}
|
| 576 |
+
summary.setdefault("symbol", "NIFTY 50")
|
| 577 |
+
summary.setdefault("horizon", "daily")
|
| 578 |
+
summary.setdefault("horizon_bars", 1)
|
| 579 |
+
summary["model_name"] = "nifty_tomorrow_direction_model"
|
| 580 |
+
summary["source_model"] = str(config.get("name") or summary.get("source_model") or "locked_multiwindow_nifty50_ensemble")
|
| 581 |
+
summary["target"] = "next trading session NIFTY 50 direction"
|
| 582 |
+
summary["artifact_type"] = "daily_forecaster_outputs"
|
| 583 |
+
summary["artifact_source"] = str(DAILY_FORECASTER_OUTPUT_DIR)
|
| 584 |
+
return summary
|
| 585 |
+
|
| 586 |
+
|
| 587 |
+
def _read_daily_forecaster_latest(summary: dict[str, Any]) -> dict[str, Any] | None:
|
| 588 |
+
if not DAILY_FORECASTER_LATEST_PATH.exists():
|
| 589 |
+
return None
|
| 590 |
+
latest = pd.read_csv(DAILY_FORECASTER_LATEST_PATH)
|
| 591 |
+
if latest.empty:
|
| 592 |
+
return None
|
| 593 |
+
if "symbol" in latest.columns:
|
| 594 |
+
filtered = latest[latest["symbol"].astype(str) == "NIFTY 50"]
|
| 595 |
+
if not filtered.empty:
|
| 596 |
+
latest = filtered
|
| 597 |
+
row = {k: (None if pd.isna(v) else v) for k, v in latest.iloc[-1].to_dict().items()}
|
| 598 |
+
input_date = row.get("latest_forecast_date") or row.get("input_date")
|
| 599 |
+
target_date = row.get("target_date")
|
| 600 |
+
if not target_date and input_date:
|
| 601 |
+
try:
|
| 602 |
+
target_date = next_trading_day(date.fromisoformat(str(input_date)[:10]) + timedelta(days=1)).isoformat()
|
| 603 |
+
except Exception:
|
| 604 |
+
target_date = None
|
| 605 |
+
prob_up = row.get("latest_forecast_prob_up", row.get("prob_up"))
|
| 606 |
+
prediction = row.get("latest_forecast_signal", row.get("prediction"))
|
| 607 |
+
threshold = row.get("threshold", summary.get("threshold"))
|
| 608 |
+
confidence = row.get("confidence")
|
| 609 |
+
if confidence is None and prob_up is not None:
|
| 610 |
+
try:
|
| 611 |
+
confidence = float(max(float(prob_up), 1.0 - float(prob_up)))
|
| 612 |
+
except Exception:
|
| 613 |
+
confidence = None
|
| 614 |
+
return {
|
| 615 |
+
"input_date": input_date,
|
| 616 |
+
"target_date": target_date,
|
| 617 |
+
"prediction": prediction,
|
| 618 |
+
"prob_up": prob_up,
|
| 619 |
+
"confidence": confidence,
|
| 620 |
+
"threshold": threshold,
|
| 621 |
+
"model_name": "nifty_tomorrow_direction_model",
|
| 622 |
+
"source_model": summary.get("source_model", "locked_multiwindow_nifty50_ensemble"),
|
| 623 |
+
"validation_accuracy": summary.get("validation_accuracy"),
|
| 624 |
+
"test_accuracy": summary.get("test_accuracy"),
|
| 625 |
+
"artifact_source": str(DAILY_FORECASTER_OUTPUT_DIR),
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
def sync_daily_forecaster_outputs() -> dict[str, Any] | None:
|
| 630 |
+
summary = _read_daily_forecaster_summary()
|
| 631 |
+
if summary is None:
|
| 632 |
+
return None
|
| 633 |
+
latest = _read_daily_forecaster_latest(summary)
|
| 634 |
+
TOMORROW_SUMMARY_PATH.write_text(json.dumps(summary, indent=2), encoding="utf-8")
|
| 635 |
+
if latest is not None:
|
| 636 |
+
pd.DataFrame([latest]).to_csv(TOMORROW_LATEST_PATH, index=False)
|
| 637 |
+
if DAILY_FORECASTER_PREDICTIONS_PATH.exists():
|
| 638 |
+
predictions = pd.read_csv(DAILY_FORECASTER_PREDICTIONS_PATH)
|
| 639 |
+
if "symbol" in predictions.columns:
|
| 640 |
+
predictions = predictions[predictions["symbol"].astype(str) == "NIFTY 50"].copy()
|
| 641 |
+
if not predictions.empty:
|
| 642 |
+
if "pred" in predictions.columns and "prediction" not in predictions.columns:
|
| 643 |
+
predictions["prediction"] = np.where(pd.to_numeric(predictions["pred"], errors="coerce") == 1, "UP", "DOWN")
|
| 644 |
+
if "correct" not in predictions.columns and {"target", "pred"}.issubset(predictions.columns):
|
| 645 |
+
predictions["correct"] = (
|
| 646 |
+
pd.to_numeric(predictions["target"], errors="coerce")
|
| 647 |
+
== pd.to_numeric(predictions["pred"], errors="coerce")
|
| 648 |
+
)
|
| 649 |
+
predictions.to_parquet(TOMORROW_TEST_PREDICTIONS_PATH, index=False)
|
| 650 |
+
artifact = {
|
| 651 |
+
"artifact_type": "daily_forecaster_outputs",
|
| 652 |
+
"model_name": "nifty_tomorrow_direction_model",
|
| 653 |
+
"source_model": summary.get("source_model", "locked_multiwindow_nifty50_ensemble"),
|
| 654 |
+
"threshold": float(summary.get("threshold", 0.54)),
|
| 655 |
+
"validation_accuracy": summary.get("validation_accuracy"),
|
| 656 |
+
"test_accuracy": summary.get("test_accuracy"),
|
| 657 |
+
"validation_prob_std": summary.get("validation_prob_std"),
|
| 658 |
+
"test_prob_std": summary.get("test_prob_std"),
|
| 659 |
+
"test_prob_min": summary.get("test_prob_min"),
|
| 660 |
+
"test_prob_max": summary.get("test_prob_max"),
|
| 661 |
+
"artifact_source": str(DAILY_FORECASTER_OUTPUT_DIR),
|
| 662 |
+
}
|
| 663 |
+
joblib.dump(artifact, TOMORROW_MODEL_PATH)
|
| 664 |
+
return latest or summary
|
| 665 |
+
|
| 666 |
+
|
| 667 |
+
def load_tomorrow_model_artifact() -> dict[str, Any]:
|
| 668 |
+
synced = sync_daily_forecaster_outputs()
|
| 669 |
+
if synced is not None and TOMORROW_MODEL_PATH.exists():
|
| 670 |
+
return joblib.load(TOMORROW_MODEL_PATH)
|
| 671 |
+
if TOMORROW_MODEL_PATH.exists():
|
| 672 |
+
return joblib.load(TOMORROW_MODEL_PATH)
|
| 673 |
+
summary = load_tomorrow_summary()
|
| 674 |
return {
|
| 675 |
"artifact_type": "daily_forecaster_snapshot",
|
| 676 |
"model_name": summary.get("model_name", "nifty_tomorrow_direction_model"),
|
|
|
|
| 679 |
}
|
| 680 |
|
| 681 |
|
| 682 |
+
def load_tomorrow_summary() -> dict[str, Any]:
|
| 683 |
+
synced = sync_daily_forecaster_outputs()
|
| 684 |
+
if synced is not None and TOMORROW_SUMMARY_PATH.exists():
|
| 685 |
+
return json.loads(TOMORROW_SUMMARY_PATH.read_text(encoding="utf-8"))
|
| 686 |
+
if TOMORROW_SUMMARY_PATH.exists():
|
| 687 |
+
return json.loads(TOMORROW_SUMMARY_PATH.read_text(encoding="utf-8"))
|
| 688 |
+
return {
|
| 689 |
+
"model_name": "nifty_tomorrow_direction_model",
|
| 690 |
+
"source_model": "locked_multiwindow_nifty50_ensemble",
|
| 691 |
+
"target": "next trading session NIFTY 50 direction",
|
| 692 |
+
"threshold": 0.54,
|
| 693 |
+
"validation_accuracy": 0.5673758865248227,
|
| 694 |
+
"test_accuracy": 0.6451612903225806,
|
| 695 |
+
"baseline_accuracy": 0.5053763440860215,
|
| 696 |
+
"n_test": 186,
|
| 697 |
+
"feature_count": 204,
|
| 698 |
+
}
|
| 699 |
|
| 700 |
|
| 701 |
def latest_tomorrow_prediction() -> dict[str, Any]:
|
| 702 |
+
sync_daily_forecaster_outputs()
|
| 703 |
latest_daily = latest_parquet_date(NIFTY_1D_PATH)
|
| 704 |
if TOMORROW_LATEST_PATH.exists():
|
| 705 |
row = pd.read_csv(TOMORROW_LATEST_PATH).iloc[-1].to_dict()
|
|
|
|
| 710 |
input_day = None
|
| 711 |
if latest_daily is not None and (input_day is None or input_day < latest_daily):
|
| 712 |
try:
|
| 713 |
+
refreshed = refresh_tomorrow_prediction(session_date=latest_daily)
|
| 714 |
+
try:
|
| 715 |
+
refreshed_day = date.fromisoformat(str(refreshed.get("input_date"))[:10])
|
| 716 |
+
except Exception:
|
| 717 |
+
refreshed_day = None
|
| 718 |
+
if refreshed_day is not None and refreshed_day >= latest_daily:
|
| 719 |
+
return refreshed
|
| 720 |
except Exception:
|
| 721 |
+
pass
|
| 722 |
return cleaned
|
| 723 |
summary = load_tomorrow_summary()
|
| 724 |
try:
|
|
|
|
| 962 |
return float(np.clip(score, 0.35, 0.65))
|
| 963 |
|
| 964 |
|
| 965 |
+
def refresh_tomorrow_prediction(session_date: date | None = None) -> dict[str, Any]:
|
| 966 |
+
synced = sync_daily_forecaster_outputs()
|
| 967 |
+
if synced is not None and TOMORROW_LATEST_PATH.exists():
|
| 968 |
+
latest = pd.read_csv(TOMORROW_LATEST_PATH).iloc[-1].to_dict()
|
| 969 |
+
cleaned = {k: (None if pd.isna(v) else v) for k, v in latest.items()}
|
| 970 |
+
if session_date is None:
|
| 971 |
+
clear_dashboard_payload_cache()
|
| 972 |
+
return cleaned
|
| 973 |
+
try:
|
| 974 |
+
input_day = date.fromisoformat(str(cleaned.get("input_date"))[:10])
|
| 975 |
+
except Exception:
|
| 976 |
+
input_day = None
|
| 977 |
+
if input_day is not None:
|
| 978 |
+
clear_dashboard_payload_cache()
|
| 979 |
+
return cleaned
|
| 980 |
+
summary = load_tomorrow_summary()
|
| 981 |
+
artifact = load_tomorrow_model_artifact()
|
| 982 |
daily = pd.read_parquet(NIFTY_1D_PATH)
|
| 983 |
daily["date"] = pd.to_datetime(daily["date"], errors="coerce").dt.normalize()
|
| 984 |
daily = daily.dropna(subset=["date"]).sort_values("date")
|