Lk / persistence.py
Kashaf1's picture
Upload moirai_forecast_app contents
ef20ebe
Raw History Blame Contribute Delete
3.22 kB
"""
Local, self-contained persistence: an immutable forecast registry plus
model-artifact metadata (spec sections 45-46, 66, 76, 90). SQLite only —
no external DB service, consistent with the single-application /
HF-Spaces-friendly deployment target (section 76).
"""
from __future__ import annotations
import sqlite3
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
import numpy as np
SCHEMA = """
CREATE TABLE IF NOT EXISTS forecasts (
id TEXT PRIMARY KEY,
created_at TEXT,
symbol TEXT, timeframe TEXT, horizon INTEGER,
model_checkpoint TEXT, config_hash TEXT,
origin_ts TEXT, price_now REAL,
p10 REAL, p50 REAL, p90 REAL,
action TEXT, rationale TEXT,
realized_price REAL, realized_return REAL, forecast_error REAL,
direction_correct INTEGER
);
CREATE TABLE IF NOT EXISTS model_artifacts (
id TEXT PRIMARY KEY,
created_at TEXT,
checkpoint TEXT, library_versions TEXT,
feature_schema TEXT, seed INTEGER,
training_range TEXT, validation_range TEXT, test_range TEXT
);
"""
class Store:
def __init__(self, path: str = "registry.sqlite3"):
self.path = Path(path)
self.conn = sqlite3.connect(self.path)
self.conn.executescript(SCHEMA)
self.conn.commit()
def record_forecast(self, **fields) -> str:
"""A forecast row is written once and never mutated except by
match_realized_outcome() filling in the realized_* columns
later (section 90: 'A forecast should remain immutable after
creation')."""
fid = str(uuid.uuid4())
fields = {"id": fid, "created_at": datetime.now(timezone.utc).isoformat(), **fields}
cols = ",".join(fields.keys())
qs = ",".join(["?"] * len(fields))
self.conn.execute(f"INSERT INTO forecasts ({cols}) VALUES ({qs})", list(fields.values()))
self.conn.commit()
return fid
def match_realized_outcome(self, forecast_id: str, realized_price: float) -> None:
row = self.conn.execute(
"SELECT price_now, p50 FROM forecasts WHERE id=?", (forecast_id,)
).fetchone()
if row is None:
raise KeyError(f"No forecast with id {forecast_id}")
price_now, p50 = row
realized_return = realized_price / price_now - 1.0
error = abs(p50 - realized_return)
direction_correct: Optional[int] = (
int(np.sign(p50) == np.sign(realized_return)) if realized_return != 0 else None
)
self.conn.execute(
"UPDATE forecasts SET realized_price=?, realized_return=?, forecast_error=?, "
"direction_correct=? WHERE id=?",
(realized_price, realized_return, error, direction_correct, forecast_id),
)
self.conn.commit()
def save_model_artifact(self, **fields) -> str:
aid = str(uuid.uuid4())
fields = {"id": aid, "created_at": datetime.now(timezone.utc).isoformat(), **fields}
cols = ",".join(fields.keys())
qs = ",".join(["?"] * len(fields))
self.conn.execute(f"INSERT INTO model_artifacts ({cols}) VALUES ({qs})", list(fields.values()))
self.conn.commit()
return aid