eth-usd-predictor / src /model.py
Jony Ling
Clean initial commit for HF Space
b717bee
Raw
History Blame Contribute Delete
3.09 kB
"""Load calibrated V5 model + exact predict_return_signal from notebook"""
import joblib
from huggingface_hub import hf_hub_download # type: ignore
import numpy as np
import pandas as pd
from src.config import MODEL_PATH, HF_REPO_ID
from src.inference_features import build_live_v5_features
def load_model():
if not HF_REPO_ID or HF_REPO_ID.startswith("yourusername/"):
model = joblib.load(MODEL_PATH)
print("Model loaded from local file")
return model
try:
path = hf_hub_download(repo_id=HF_REPO_ID, filename="v5_lgbm_24h_dualfilter.joblib")
model = joblib.load(path)
print("✅ Model loaded from Hugging Face Hub")
except Exception:
model = joblib.load(MODEL_PATH)
print("✅ Model loaded from local file")
return model
def predict_return_signal(model, X):
"""Exact function from latest notebook (handles calibrated classifier)"""
if isinstance(model, dict) and "model" in model: # calibrated classifier
proba = model["model"].predict_proba(X)[:, 1]
calibrator = model.get("calibrator")
if calibrator is None:
return (proba - 0.5) * 2.0 * 0.006
# Apply calibrator
idx = np.searchsorted(calibrator["edges"][1:-1], proba, side="right")
return calibrator["values"][idx]
else:
return model.predict(X) # type: ignore
def core_model(model):
return model.get("model") if isinstance(model, dict) and "model" in model else model
def model_feature_cols(model) -> list[str] | None:
if isinstance(model, dict):
cols = model.get("feature_cols") or model.get("features")
if cols is not None:
return list(cols)
return None
def build_live_matrix(df_raw: pd.DataFrame, model):
df_feat, feature_cols = build_live_v5_features(df_raw)
latest = df_feat.iloc[-1:].copy()
trained_feature_cols = model_feature_cols(model)
if trained_feature_cols is not None:
missing = [col for col in trained_feature_cols if col not in latest.columns]
if missing:
raise ValueError(f"Live data is missing {len(missing)} trained features, e.g. {missing[:10]}")
X = latest[trained_feature_cols]
else:
X = latest[feature_cols]
expected_features = getattr(core_model(model), "n_features_in_", None)
if expected_features is not None and X.shape[1] != expected_features:
raise ValueError(
f"Live feature count is {X.shape[1]}, but the model expects {expected_features}. "
"Re-export the notebook model as a dict containing feature_cols=feat_v5."
)
return df_feat, latest, X
def classifier_positive_proba(model, X) -> float | None:
base_model = core_model(model)
if hasattr(base_model, "predict_proba"):
return float(base_model.predict_proba(X)[:, 1][0])
return None
def predict_next_8h(df_raw: pd.DataFrame, model): # type: ignore
"""Main inference entry point"""
_, _, X = build_live_matrix(df_raw, model)
pred = predict_return_signal(model, X)
return float(pred[0])