LSTM_API / app.py
Miruzen's picture
Update app.py
fa6a7c9 verified
from fastapi import FastAPI
from pydantic import BaseModel
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow.keras.models import load_model
import json
import os
app = FastAPI(
title="Forex LSTM Prediction API",
description="Prediksi harga EUR/USD H+1 dengan LSTM menggunakan scaler harian",
version="2.0"
)
# ==========================================================
# LOAD MODEL DAN KONFIGURASI
# ==========================================================
MODEL_PATH = "lstm_model.h5"
PARAMS_PATH = "best_params.json"
SCALER_FILE = "scaler_config.json"
print("πŸ“₯ Loading LSTM model...")
model = load_model(MODEL_PATH, compile=False)
print("πŸ“₯ Loading best parameters...")
with open(PARAMS_PATH, "r") as f:
best_params = json.load(f)
LOOKBACK = best_params.get("lookback", 7)
FEATURE_ORDER = best_params.get("features", [
"mood_score", "t_pos", "t_neg", "c_pos", "c_neg",
"norm_ema20", "norm_ema50", "norm_close"
])
# ==========================================================
# LOAD SCALER CONFIG
# ==========================================================
def load_scaler_config():
if not os.path.exists(SCALER_FILE):
print("⚠️ Scaler config not found, using default values.")
return {"CLOSE_MIN": 1.05, "CLOSE_MAX": 1.15}
with open(SCALER_FILE, "r") as f:
return json.load(f)
scaler_cfg = load_scaler_config()
CLOSE_MIN = scaler_cfg["CLOSE_MIN"]
CLOSE_MAX = scaler_cfg["CLOSE_MAX"]
print(f"βœ… Scaler range loaded: {CLOSE_MIN:.5f} - {CLOSE_MAX:.5f}")
# ==========================================================
# INPUT SCHEMA
# ==========================================================
class LSTMInput(BaseModel):
data: list
# ==========================================================
# HELPER FUNCTIONS
# ==========================================================
def prepare_input(data):
df = pd.DataFrame(data)
missing_cols = [f for f in FEATURE_ORDER if f not in df.columns]
if missing_cols:
raise ValueError(f"Missing columns: {missing_cols}")
X = df[FEATURE_ORDER].values[-LOOKBACK:]
if X.shape[0] < LOOKBACK:
raise ValueError(f"Need at least {LOOKBACK} timesteps, got {X.shape[0]}")
X = np.expand_dims(X, axis=0)
return X, df
def inverse_scale(norm_value):
"""Denormalisasi nilai close dari [0,1] ke skala asli"""
return (norm_value * (CLOSE_MAX - CLOSE_MIN)) + CLOSE_MIN
# ==========================================================
# ENDPOINT
# ==========================================================
@app.post("/predict")
def predict_price(input_data: LSTMInput):
try:
X, df = prepare_input(input_data.data)
pred_norm = model.predict(X)[0][0]
pred_close = inverse_scale(pred_norm)
last_date = pd.to_datetime(df["date"].iloc[-1])
next_date = (last_date + pd.Timedelta(days=1)).strftime("%Y-%m-%d")
result = {
"next_date": next_date,
"predicted_norm_close": float(pred_norm),
"predicted_close": float(pred_close),
"scaler_used": {"min": CLOSE_MIN, "max": CLOSE_MAX},
"features_used": FEATURE_ORDER
}
return {"status": "ok", "result": result}
except Exception as e:
return {"status": "error", "message": str(e)}
@app.get("/")
def root():
return {"message": "Forex LSTM Prediction API is active!"}