marrtinagg commited on
Commit
d327abb
·
1 Parent(s): 9aac6c4

Inicializar backend FastAPI con Dockerfile

Browse files
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # ============================================================
2
- # 📈 app.py — Backend FastAPI para predicciones BBVA
3
  # ============================================================
4
 
5
  from fastapi import FastAPI
@@ -14,9 +14,8 @@ import pandas as pd
14
  # ============================================================
15
  # 1️⃣ Configuración base de FastAPI
16
  # ============================================================
17
- app = FastAPI(title="Predicción BBVA API")
18
 
19
- # Permitir acceso desde cualquier dominio (para GitHub Pages luego)
20
  app.add_middleware(
21
  CORSMiddleware,
22
  allow_origins=["*"],
@@ -26,7 +25,7 @@ app.add_middleware(
26
  )
27
 
28
  # ============================================================
29
- # 2️⃣ Definición del modelo LSTM (idéntico al entrenamiento)
30
  # ============================================================
31
  class LSTMModel(nn.Module):
32
  def __init__(self, n_features, hidden_size=64):
@@ -39,91 +38,132 @@ class LSTMModel(nn.Module):
39
  out = self.fc(out)
40
  return out.squeeze()
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  # ============================================================
43
- # 3️⃣ Cargar el modelo y los scalers
44
  # ============================================================
45
- MODEL_PATH = "models_bbva/bbva_lstm_model.pth"
46
- SCALER_FULL_PATH = "models_bbva/scaler_features_full.pkl"
47
- SCALER_TARGET_PATH = "models_bbva/scaler_target.pkl"
48
-
49
- n_features = 5
50
- hidden_size = 64
51
  device = torch.device("cpu")
52
 
53
- # Cargar modelo y scalers
54
- model = LSTMModel(n_features, hidden_size)
55
- model.load_state_dict(torch.load(MODEL_PATH, map_location=device))
56
- model.to(device)
57
- model.eval()
58
-
59
- scaler_features = joblib.load(SCALER_FULL_PATH)
60
- scaler_target = joblib.load(SCALER_TARGET_PATH)
 
 
 
 
 
 
 
61
 
62
  # ============================================================
63
- # 4️⃣ Modelo de entrada (JSON)
64
  # ============================================================
65
  class PredictRequest(BaseModel):
66
  n_future: int = 3
67
 
68
  # ============================================================
69
- # 5️⃣ Endpoint raíz de prueba
70
  # ============================================================
71
- @app.get("/")
72
- def home():
73
- return {"message": "✅ Backend BBVA activo y listo para predecir"}
74
-
75
- # ============================================================
76
- # 6️⃣ Endpoint de predicción
77
- # ============================================================
78
- @app.post("/predict/bbva")
79
- def predict_bbva(req: PredictRequest):
80
- # === Cargar dataset base ===
81
- PATH_BBVA = "data/bbva_pred.csv" # opcional, puedes cambiarlo
82
- try:
83
- bbva = pd.read_csv(PATH_BBVA)
84
- except Exception:
85
- return {"error": "No se encontró el archivo de datos base (data/bbva_pred.csv)"}
86
-
87
- bbva["Date"] = pd.to_datetime(bbva["Date"])
88
- bbva = bbva.set_index("Date")
89
- start_date = "2021-01-01"
90
- end_date = "2025-10-31"
91
- bbva = bbva.loc[(bbva.index >= start_date) & (bbva.index <= end_date)].copy()
92
-
93
- # === Columnas del modelo ===
94
- cols_to_scale = [
95
- 'Open', 'High', 'Low', 'Close', 'Adj Close',
96
- 'ma_5', 'close_lag1', 'close_lag2', 'close_lag3',
97
- 'Volume', 'ibex_momentum_5d'
98
- ]
99
- features = ['Open', 'High', 'Low', 'return_1d', 'return_3d']
100
- window_size = 3
101
- n_future = req.n_future
102
-
103
- # === Escalar ===
104
- bbva_scaled = bbva.copy()
105
- bbva_scaled[cols_to_scale] = scaler_features.transform(bbva[cols_to_scale])
106
 
107
- # === Última ventana ===
108
- last_window = bbva_scaled[features].iloc[-window_size:].values
109
 
110
- # === Predicción iterativa ===
111
  preds_scaled = []
112
  window = last_window.copy()
113
  for _ in range(n_future):
114
- X_input = torch.tensor(window, dtype=torch.float32).unsqueeze(0).to(device)
115
  with torch.no_grad():
116
  pred_scaled = model(X_input).cpu().numpy().flatten()[0]
117
  preds_scaled.append(pred_scaled)
118
  next_day = window[-1].copy()
119
  window = np.vstack([window[1:], next_day])
120
 
121
- # === Desescalar ===
122
  preds_real = scaler_target.inverse_transform(np.array(preds_scaled).reshape(-1, 1)).flatten().tolist()
123
 
124
- # === Fechas futuras ===
125
- last_date = bbva.index[-1]
126
  future_dates = pd.date_range(last_date + pd.Timedelta(days=1), periods=n_future, freq="B")
127
  fechas = [str(d.date()) for d in future_dates]
128
 
129
- return {"fechas": fechas, "predicciones": preds_real}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # ============================================================
2
+ # 📈 app.py — Backend FastAPI para predicciones BBVA y SANTANDER
3
  # ============================================================
4
 
5
  from fastapi import FastAPI
 
14
  # ============================================================
15
  # 1️⃣ Configuración base de FastAPI
16
  # ============================================================
17
+ app = FastAPI(title="Predicciones BBVA y SANTANDER API")
18
 
 
19
  app.add_middleware(
20
  CORSMiddleware,
21
  allow_origins=["*"],
 
25
  )
26
 
27
  # ============================================================
28
+ # 2️⃣ Definir ambos modelos (LSTM y GRU)
29
  # ============================================================
30
  class LSTMModel(nn.Module):
31
  def __init__(self, n_features, hidden_size=64):
 
38
  out = self.fc(out)
39
  return out.squeeze()
40
 
41
+
42
+ class GRUModel(nn.Module):
43
+ def __init__(self, n_features, hidden_size=64, num_layers=1):
44
+ super().__init__()
45
+ self.gru = nn.GRU(
46
+ input_size=n_features,
47
+ hidden_size=hidden_size,
48
+ num_layers=num_layers,
49
+ batch_first=True
50
+ )
51
+ self.fc = nn.Linear(hidden_size, 1)
52
+ def forward(self, x):
53
+ out, _ = self.gru(x)
54
+ out = out[:, -1, :]
55
+ out = self.fc(out)
56
+ return out.squeeze()
57
+
58
  # ============================================================
59
+ # 3️⃣ Cargar ambos modelos y scalers
60
  # ============================================================
 
 
 
 
 
 
61
  device = torch.device("cpu")
62
 
63
+ # --- BBVA ---
64
+ bbva_model = LSTMModel(n_features=5, hidden_size=64)
65
+ bbva_model.load_state_dict(torch.load("models_bbva/bbva_lstm_model.pth", map_location=device))
66
+ bbva_model.to(device)
67
+ bbva_model.eval()
68
+ bbva_scaler_features = joblib.load("models_bbva/scaler_features_full.pkl")
69
+ bbva_scaler_target = joblib.load("models_bbva/scaler_target.pkl")
70
+
71
+ # --- SANTANDER ---
72
+ santander_model = GRUModel(n_features=5, hidden_size=64)
73
+ santander_model.load_state_dict(torch.load("models_santander/santander_gru_model.pth", map_location=device))
74
+ santander_model.to(device)
75
+ santander_model.eval()
76
+ santander_scaler_features = joblib.load("models_santander/santander_scaler_features_full.pkl")
77
+ santander_scaler_target = joblib.load("models_santander/santander_scaler_target.pkl")
78
 
79
  # ============================================================
80
+ # 4️⃣ Modelo de entrada
81
  # ============================================================
82
  class PredictRequest(BaseModel):
83
  n_future: int = 3
84
 
85
  # ============================================================
86
+ # 5️⃣ Función genérica para predicción
87
  # ============================================================
88
+ def generar_prediccion(df, model, scaler_features, scaler_target, features, cols_to_scale, window_size, n_future):
89
+ # Escalar
90
+ df_scaled = df.copy()
91
+ df_scaled[cols_to_scale] = scaler_features.transform(df[cols_to_scale])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
+ # Última ventana
94
+ last_window = df_scaled[features].iloc[-window_size:].values
95
 
 
96
  preds_scaled = []
97
  window = last_window.copy()
98
  for _ in range(n_future):
99
+ X_input = torch.tensor(window, dtype=torch.float32).unsqueeze(0)
100
  with torch.no_grad():
101
  pred_scaled = model(X_input).cpu().numpy().flatten()[0]
102
  preds_scaled.append(pred_scaled)
103
  next_day = window[-1].copy()
104
  window = np.vstack([window[1:], next_day])
105
 
106
+ # Desescalar
107
  preds_real = scaler_target.inverse_transform(np.array(preds_scaled).reshape(-1, 1)).flatten().tolist()
108
 
109
+ # Fechas futuras
110
+ last_date = df.index[-1]
111
  future_dates = pd.date_range(last_date + pd.Timedelta(days=1), periods=n_future, freq="B")
112
  fechas = [str(d.date()) for d in future_dates]
113
 
114
+ return fechas, preds_real
115
+
116
+ # ============================================================
117
+ # 6️⃣ Endpoint raíz
118
+ # ============================================================
119
+ @app.get("/")
120
+ def home():
121
+ return {"message": "✅ API de predicciones BBVA y Santander operativa"}
122
+
123
+ # ============================================================
124
+ # 7️⃣ Endpoint principal
125
+ # ============================================================
126
+ @app.post("/predict/{banco}")
127
+ def predict(banco: str, req: PredictRequest):
128
+ banco = banco.lower()
129
+ n_future = req.n_future
130
+
131
+ # === BBVA ===
132
+ if banco == "bbva":
133
+ path = "data/bbva_pred.csv"
134
+ df = pd.read_csv(path)
135
+ df["Date"] = pd.to_datetime(df["Date"])
136
+ df = df.set_index("Date")
137
+ df = df.loc["2021-01-01":"2025-10-31"]
138
+
139
+ cols_to_scale = [
140
+ 'Open','High','Low','Close','Adj Close',
141
+ 'ma_5','close_lag1','close_lag2','close_lag3',
142
+ 'Volume','ibex_momentum_5d'
143
+ ]
144
+ features = ['Open','High','Low','return_1d','return_3d']
145
+
146
+ fechas, preds = generar_prediccion(df, bbva_model, bbva_scaler_features, bbva_scaler_target, features, cols_to_scale, 3, n_future)
147
+ return {"banco": "BBVA", "fechas": fechas, "predicciones": preds}
148
+
149
+ # === SANTANDER ===
150
+ elif banco == "santander":
151
+ path = "data/santander_pred.csv"
152
+ df = pd.read_csv(path)
153
+ df["Date"] = pd.to_datetime(df["Date"])
154
+ df = df.set_index("Date")
155
+ df = df.loc["2021-01-01":"2025-10-31"]
156
+
157
+ cols_to_scale = [
158
+ 'Open','High','Low','Close','Adj Close',
159
+ 'ma_5','close_lag1','close_lag2','close_lag3',
160
+ 'Volume','ibex_momentum_5d'
161
+ ]
162
+ features = ['Open','High','Low','close_lag1','close_lag2']
163
+
164
+ fechas, preds = generar_prediccion(df, santander_model, santander_scaler_features, santander_scaler_target, features, cols_to_scale, 3, n_future)
165
+ return {"banco": "Santander", "fechas": fechas, "predicciones": preds}
166
+
167
+ # === Otro banco no válido ===
168
+ else:
169
+ return {"error": "Banco no reconocido. Usa 'bbva' o 'santander'."}
data/santander_pred.csv ADDED
The diff for this file is too large to render. See raw diff
 
models_santader/santander_gru_model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9f2bb2bfe7b4da753119b7683ddd0388741bdfa196c180154b7ed32cbf1750fb
3
+ size 57357
models_santader/santander_scaler_features_full.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d11143dd74d2817061746a0c07dec85b46ffaf90da5d205b5850b2b11fa31773
3
+ size 1487
models_santader/santander_scaler_features_train.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6788dbc73e8c87885bb50c84627d7c5223bd48b311067abb6f5c516b76eee780
3
+ size 1487
models_santader/santander_scaler_target.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3029f5f5a1fd50ca84d134fc652263d0c9aa9cfa9f63e54e96c8c84716c7398f
3
+ size 975