Upload 38 files
Browse files- runtime.py +64 -22
runtime.py
CHANGED
|
@@ -1244,25 +1244,7 @@ def load_tplus1_test_predictions() -> pd.DataFrame:
|
|
| 1244 |
return df.sort_values("date").reset_index(drop=True)
|
| 1245 |
|
| 1246 |
|
| 1247 |
-
def sync_mfe_outputs() -> bool:
|
| 1248 |
-
"""Refresh bundled MFE artifacts from the forecasting project when available."""
|
| 1249 |
-
if not MFE_SOURCE_OUTPUT_DIR.exists():
|
| 1250 |
-
return MFE_SUMMARY_PATH.exists()
|
| 1251 |
-
MFE_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
| 1252 |
-
synced = False
|
| 1253 |
-
for name in ("summary.json", "latest_prediction.csv", "test_predictions.csv"):
|
| 1254 |
-
source = MFE_SOURCE_OUTPUT_DIR / name
|
| 1255 |
-
target = MFE_OUTPUT_DIR / name
|
| 1256 |
-
if not source.exists():
|
| 1257 |
-
continue
|
| 1258 |
-
if not target.exists() or source.stat().st_mtime > target.stat().st_mtime:
|
| 1259 |
-
target.write_bytes(source.read_bytes())
|
| 1260 |
-
synced = True
|
| 1261 |
-
return synced or MFE_SUMMARY_PATH.exists()
|
| 1262 |
-
|
| 1263 |
-
|
| 1264 |
def load_mfe_summary() -> dict[str, Any]:
|
| 1265 |
-
sync_mfe_outputs()
|
| 1266 |
if not MFE_SUMMARY_PATH.exists():
|
| 1267 |
return {}
|
| 1268 |
try:
|
|
@@ -1272,7 +1254,6 @@ def load_mfe_summary() -> dict[str, Any]:
|
|
| 1272 |
|
| 1273 |
|
| 1274 |
def load_mfe_latest() -> dict[str, Any]:
|
| 1275 |
-
sync_mfe_outputs()
|
| 1276 |
summary = load_mfe_summary()
|
| 1277 |
if MFE_LATEST_PATH.exists():
|
| 1278 |
try:
|
|
@@ -1280,7 +1261,7 @@ def load_mfe_latest() -> dict[str, Any]:
|
|
| 1280 |
up_pts = float(row.get("predicted_up_points", summary.get("latest_predicted_up_points", 0)))
|
| 1281 |
down_pts = float(row.get("predicted_down_points", summary.get("latest_predicted_down_points", 0)))
|
| 1282 |
return {
|
| 1283 |
-
"input_date": str(row.get("input_date") or summary.get("latest_input_date", ""))[:10],
|
| 1284 |
"first5_start": row.get("first5_start") or summary.get("latest_first5_start"),
|
| 1285 |
"first5_end": row.get("first5_end") or summary.get("latest_first5_end"),
|
| 1286 |
"first5_close": float(row.get("first5_close") or summary.get("latest_first5_close") or 0),
|
|
@@ -1306,7 +1287,6 @@ def load_mfe_latest() -> dict[str, Any]:
|
|
| 1306 |
|
| 1307 |
|
| 1308 |
def load_mfe_backtest() -> pd.DataFrame:
|
| 1309 |
-
sync_mfe_outputs()
|
| 1310 |
if not MFE_TEST_PREDICTIONS_PATH.exists():
|
| 1311 |
return pd.DataFrame()
|
| 1312 |
frame = pd.read_csv(MFE_TEST_PREDICTIONS_PATH)
|
|
@@ -1697,6 +1677,58 @@ def refresh_first5_prediction(session_date: date | None = None, minutes: pd.Data
|
|
| 1697 |
return prediction
|
| 1698 |
|
| 1699 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1700 |
def refresh_daily_data() -> dict[str, Any]:
|
| 1701 |
daily = fetch_yahoo_daily(period="1mo")
|
| 1702 |
combined = append_parquet_rows(NIFTY_1D_PATH, daily, ["date"])
|
|
@@ -2151,6 +2183,7 @@ def stale_data_status(now: datetime | None = None) -> dict[str, Any]:
|
|
| 2151 |
latest_daily = latest_parquet_date(NIFTY_1D_PATH)
|
| 2152 |
latest_minutes = latest_parquet_date(NIFTY_1M_PATH)
|
| 2153 |
latest_t5 = latest_prediction_input_date(LATEST_PATH)
|
|
|
|
| 2154 |
latest_tomorrow = latest_tomorrow_input_date()
|
| 2155 |
latest_tplus1 = latest_prediction_input_date(TPLUS1_LATEST_PATH)
|
| 2156 |
return {
|
|
@@ -2161,11 +2194,13 @@ def stale_data_status(now: datetime | None = None) -> dict[str, Any]:
|
|
| 2161 |
"latest_daily_date": latest_daily.isoformat() if latest_daily else None,
|
| 2162 |
"latest_minute_date": latest_minutes.isoformat() if latest_minutes else None,
|
| 2163 |
"latest_t5_date": latest_t5.isoformat() if latest_t5 else None,
|
|
|
|
| 2164 |
"latest_tomorrow_date": latest_tomorrow.isoformat() if latest_tomorrow else None,
|
| 2165 |
"latest_tplus1_date": latest_tplus1.isoformat() if latest_tplus1 else None,
|
| 2166 |
"daily_stale": is_stale(latest_daily, expected_daily),
|
| 2167 |
"minutes_stale": is_stale(latest_minutes, expected_minutes),
|
| 2168 |
"t5_stale": is_stale(latest_t5, expected_minutes),
|
|
|
|
| 2169 |
"tomorrow_stale": is_stale(latest_tomorrow, expected_daily),
|
| 2170 |
"tplus1_stale": is_stale(latest_tplus1, expected_tplus1),
|
| 2171 |
}
|
|
@@ -2174,7 +2209,7 @@ def stale_data_status(now: datetime | None = None) -> dict[str, Any]:
|
|
| 2174 |
def refresh_stale_data_once(now: datetime | None = None) -> dict[str, Any]:
|
| 2175 |
now = now or datetime.now(IST)
|
| 2176 |
status = stale_data_status(now)
|
| 2177 |
-
if not any(status[key] for key in ("daily_stale", "minutes_stale", "t5_stale", "tomorrow_stale", "tplus1_stale")):
|
| 2178 |
return {"status": "fresh", **status, "actions": []}
|
| 2179 |
if not _stale_refresh_lock.acquire(blocking=False):
|
| 2180 |
return {"status": "skipped", "reason": "stale refresh already running", **status, "actions": []}
|
|
@@ -2221,6 +2256,13 @@ def refresh_stale_data_once(now: datetime | None = None) -> dict[str, Any]:
|
|
| 2221 |
prediction = refresh_first5_prediction(session_date=now.date())
|
| 2222 |
actions.append({"name": "t5_prediction", "input_date": prediction.input_date})
|
| 2223 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2224 |
if status["tplus1_stale"] and is_trading_day(now.date()) and now.time() >= TPLUS1_READY:
|
| 2225 |
prediction = refresh_tplus1_prediction(session_date=now.date())
|
| 2226 |
actions.append({"name": "tplus1_prediction", "input_date": prediction.get("input_date")})
|
|
|
|
| 1244 |
return df.sort_values("date").reset_index(drop=True)
|
| 1245 |
|
| 1246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1247 |
def load_mfe_summary() -> dict[str, Any]:
|
|
|
|
| 1248 |
if not MFE_SUMMARY_PATH.exists():
|
| 1249 |
return {}
|
| 1250 |
try:
|
|
|
|
| 1254 |
|
| 1255 |
|
| 1256 |
def load_mfe_latest() -> dict[str, Any]:
|
|
|
|
| 1257 |
summary = load_mfe_summary()
|
| 1258 |
if MFE_LATEST_PATH.exists():
|
| 1259 |
try:
|
|
|
|
| 1261 |
up_pts = float(row.get("predicted_up_points", summary.get("latest_predicted_up_points", 0)))
|
| 1262 |
down_pts = float(row.get("predicted_down_points", summary.get("latest_predicted_down_points", 0)))
|
| 1263 |
return {
|
| 1264 |
+
"input_date": str(row.get("input_date") or row.get("date") or summary.get("latest_input_date", ""))[:10],
|
| 1265 |
"first5_start": row.get("first5_start") or summary.get("latest_first5_start"),
|
| 1266 |
"first5_end": row.get("first5_end") or summary.get("latest_first5_end"),
|
| 1267 |
"first5_close": float(row.get("first5_close") or summary.get("latest_first5_close") or 0),
|
|
|
|
| 1287 |
|
| 1288 |
|
| 1289 |
def load_mfe_backtest() -> pd.DataFrame:
|
|
|
|
| 1290 |
if not MFE_TEST_PREDICTIONS_PATH.exists():
|
| 1291 |
return pd.DataFrame()
|
| 1292 |
frame = pd.read_csv(MFE_TEST_PREDICTIONS_PATH)
|
|
|
|
| 1677 |
return prediction
|
| 1678 |
|
| 1679 |
|
| 1680 |
+
def refresh_mfe_prediction(session_date: date | None = None, minutes: pd.DataFrame | None = None) -> dict[str, Any]:
|
| 1681 |
+
if session_date is None:
|
| 1682 |
+
today = datetime.now(IST).date()
|
| 1683 |
+
if not is_trading_day(today):
|
| 1684 |
+
raise RuntimeError(f"{today.isoformat()} is not an NSE trading session.")
|
| 1685 |
+
else:
|
| 1686 |
+
today = session_date
|
| 1687 |
+
|
| 1688 |
+
minutes = fetch_yahoo_minutes(period="7d") if minutes is None else minutes
|
| 1689 |
+
first5 = first5_features_from_minutes(minutes, session_date=session_date)
|
| 1690 |
+
row = build_model_row(first5)
|
| 1691 |
+
|
| 1692 |
+
payload = joblib.load(MFE_OUTPUT_DIR / "nifty_opening_mfe_regressor.joblib")
|
| 1693 |
+
up_model = payload["up_model"]
|
| 1694 |
+
down_model = payload["down_model"]
|
| 1695 |
+
up_features = payload["up_features"]
|
| 1696 |
+
down_features = payload["down_features"]
|
| 1697 |
+
|
| 1698 |
+
up_pred = float(up_model.predict(row[up_features])[0])
|
| 1699 |
+
down_pred = float(down_model.predict(row[down_features])[0])
|
| 1700 |
+
|
| 1701 |
+
input_date_str = str(pd.to_datetime(row["date"].iloc[0]).date())
|
| 1702 |
+
out = {
|
| 1703 |
+
"input_date": input_date_str,
|
| 1704 |
+
"first5_start": str(pd.to_datetime(row["first5_start"].iloc[0])),
|
| 1705 |
+
"first5_end": str(pd.to_datetime(row["first5_end"].iloc[0])),
|
| 1706 |
+
"first5_close": float(row["first5_close"].iloc[0]) if "first5_close" in row.columns else float(row["close"].iloc[0]),
|
| 1707 |
+
"predicted_up_points": up_pred,
|
| 1708 |
+
"predicted_down_points": down_pred,
|
| 1709 |
+
}
|
| 1710 |
+
|
| 1711 |
+
pd.DataFrame([out]).to_csv(MFE_LATEST_PATH, index=False)
|
| 1712 |
+
|
| 1713 |
+
if MFE_TEST_PREDICTIONS_PATH.exists():
|
| 1714 |
+
history = pd.read_csv(MFE_TEST_PREDICTIONS_PATH)
|
| 1715 |
+
if "date" in history.columns:
|
| 1716 |
+
history["date_obj"] = pd.to_datetime(history["date"], errors="coerce").dt.date
|
| 1717 |
+
history = history[history["date_obj"] != pd.to_datetime(input_date_str).date()].copy()
|
| 1718 |
+
history.drop(columns=["date_obj"], inplace=True)
|
| 1719 |
+
|
| 1720 |
+
new_row = pd.DataFrame([{
|
| 1721 |
+
"date": input_date_str,
|
| 1722 |
+
"first5_close": out["first5_close"],
|
| 1723 |
+
"predicted_up_points": out["predicted_up_points"],
|
| 1724 |
+
"predicted_down_points": out["predicted_down_points"]
|
| 1725 |
+
}])
|
| 1726 |
+
history = pd.concat([history, new_row], ignore_index=True)
|
| 1727 |
+
history.to_csv(MFE_TEST_PREDICTIONS_PATH, index=False)
|
| 1728 |
+
|
| 1729 |
+
return out
|
| 1730 |
+
|
| 1731 |
+
|
| 1732 |
def refresh_daily_data() -> dict[str, Any]:
|
| 1733 |
daily = fetch_yahoo_daily(period="1mo")
|
| 1734 |
combined = append_parquet_rows(NIFTY_1D_PATH, daily, ["date"])
|
|
|
|
| 2183 |
latest_daily = latest_parquet_date(NIFTY_1D_PATH)
|
| 2184 |
latest_minutes = latest_parquet_date(NIFTY_1M_PATH)
|
| 2185 |
latest_t5 = latest_prediction_input_date(LATEST_PATH)
|
| 2186 |
+
latest_mfe = latest_prediction_input_date(MFE_LATEST_PATH)
|
| 2187 |
latest_tomorrow = latest_tomorrow_input_date()
|
| 2188 |
latest_tplus1 = latest_prediction_input_date(TPLUS1_LATEST_PATH)
|
| 2189 |
return {
|
|
|
|
| 2194 |
"latest_daily_date": latest_daily.isoformat() if latest_daily else None,
|
| 2195 |
"latest_minute_date": latest_minutes.isoformat() if latest_minutes else None,
|
| 2196 |
"latest_t5_date": latest_t5.isoformat() if latest_t5 else None,
|
| 2197 |
+
"latest_mfe_date": latest_mfe.isoformat() if latest_mfe else None,
|
| 2198 |
"latest_tomorrow_date": latest_tomorrow.isoformat() if latest_tomorrow else None,
|
| 2199 |
"latest_tplus1_date": latest_tplus1.isoformat() if latest_tplus1 else None,
|
| 2200 |
"daily_stale": is_stale(latest_daily, expected_daily),
|
| 2201 |
"minutes_stale": is_stale(latest_minutes, expected_minutes),
|
| 2202 |
"t5_stale": is_stale(latest_t5, expected_minutes),
|
| 2203 |
+
"mfe_stale": is_stale(latest_mfe, expected_minutes),
|
| 2204 |
"tomorrow_stale": is_stale(latest_tomorrow, expected_daily),
|
| 2205 |
"tplus1_stale": is_stale(latest_tplus1, expected_tplus1),
|
| 2206 |
}
|
|
|
|
| 2209 |
def refresh_stale_data_once(now: datetime | None = None) -> dict[str, Any]:
|
| 2210 |
now = now or datetime.now(IST)
|
| 2211 |
status = stale_data_status(now)
|
| 2212 |
+
if not any(status[key] for key in ("daily_stale", "minutes_stale", "t5_stale", "mfe_stale", "tomorrow_stale", "tplus1_stale")):
|
| 2213 |
return {"status": "fresh", **status, "actions": []}
|
| 2214 |
if not _stale_refresh_lock.acquire(blocking=False):
|
| 2215 |
return {"status": "skipped", "reason": "stale refresh already running", **status, "actions": []}
|
|
|
|
| 2256 |
prediction = refresh_first5_prediction(session_date=now.date())
|
| 2257 |
actions.append({"name": "t5_prediction", "input_date": prediction.input_date})
|
| 2258 |
|
| 2259 |
+
if status["mfe_stale"] and is_trading_day(now.date()) and now.time() >= FIRST5_READY:
|
| 2260 |
+
try:
|
| 2261 |
+
mfe_pred = refresh_mfe_prediction(session_date=now.date())
|
| 2262 |
+
actions.append({"name": "mfe_prediction", "input_date": mfe_pred["input_date"]})
|
| 2263 |
+
except Exception as e:
|
| 2264 |
+
actions.append({"name": "mfe_prediction", "error": str(e)})
|
| 2265 |
+
|
| 2266 |
if status["tplus1_stale"] and is_trading_day(now.date()) and now.time() >= TPLUS1_READY:
|
| 2267 |
prediction = refresh_tplus1_prediction(session_date=now.date())
|
| 2268 |
actions.append({"name": "tplus1_prediction", "input_date": prediction.get("input_date")})
|