Spaces:
Sleeping
Sleeping
File size: 5,258 Bytes
d61656f 80bf9cf d61656f d1d3783 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | from fastapi import FastAPI
from pydantic import BaseModel
import yfinance as yf
import ta
import pandas as pd
import numpy as np
from tensorflow.keras.models import load_model
import joblib
import warnings
import os
warnings.filterwarnings('ignore')
app = FastAPI()
print("🚀 Servidor FastAPI encendido. Esperando peticiones de n8n...")
# =========================================================
# 🚨 TRUCO ANTI-CRASH: Arrancamos con la RAM vacía
# =========================================================
MODELOS = {}
# IMPORTANTE: Revisa que estos nombres coincidan EXACTAMENTE con los que subiste
CONFIG = {
"ORO": {"ticker": "GC=F", "modelo": "cerebro_oro.keras", "scaler": "scaler_oro.pkl"},
"NVDA": {"ticker": "NVDA", "modelo": "cerebro_nvidia.keras", "scaler": "scaler_nvda.pkl"},
"WTI": {"ticker": "CL=F", "modelo": "cerebro_wti.keras", "scaler": "scaler_wti.pkl"},
"NASDAQ": {"ticker": "^NDX", "modelo": "cerebro_nasdaq.keras", "scaler": "scaler_nasdaq.pkl"}
}
# 🚨 NUEVO: El servidor ahora espera recibir el símbolo y el sentimiento
class PredictionRequest(BaseModel):
symbol: str
sentimiento: float = 0.0 # Si n8n no manda nada, asume Neutral (0.0)
@app.post("/predecir")
def predecir_mercado(req: PredictionRequest):
activo_solicitado = req.symbol.upper()
traductor = {
"GC=F": "ORO", "XAUUSD=X": "ORO", "ORO": "ORO",
"NVDA": "NVDA",
"CL=F": "WTI", "WTI": "WTI", "USOIL": "WTI",
"^IXIC": "NASDAQ", "^NDX": "NASDAQ", "NQ=F": "NASDAQ", "NASDAQ": "NASDAQ"
}
if activo_solicitado not in traductor:
return {"error": f"Activo no soportado. Usa uno de estos: {list(CONFIG.keys())}"}
id_activo = traductor[activo_solicitado]
# =========================================================
# 🚨 LAZY LOADING: Solo carga la IA a la RAM si n8n la pide
# =========================================================
if id_activo not in MODELOS:
print(f"🧠 Despertando IA de {id_activo} por primera vez...")
archivo_mod = CONFIG[id_activo]["modelo"]
archivo_scl = CONFIG[id_activo]["scaler"]
if not os.path.exists(archivo_mod) or not os.path.exists(archivo_scl):
return {"error": f"¡Falta el archivo {archivo_mod} o {archivo_scl} en Hugging Face!"}
try:
MODELOS[id_activo] = {
"ticker": CONFIG[id_activo]["ticker"],
"modelo": load_model(archivo_mod),
"escalador": joblib.load(archivo_scl)
}
except Exception as e:
return {"error": f"Error al cargar la IA de {id_activo}: {str(e)}"}
ticker = MODELOS[id_activo]["ticker"]
modelo = MODELOS[id_activo]["modelo"]
escalador = MODELOS[id_activo]["escalador"]
# 🎯 Obtener Precio Vivo
try:
df_minuto = yf.download(ticker, period="1d", interval="1m", progress=False)
if isinstance(df_minuto.columns, pd.MultiIndex):
df_minuto.columns = df_minuto.columns.droplevel(1)
precio_actual = float(df_minuto['Close'].dropna().iloc[-1])
except:
precio_actual = 0.0
# 🧠 Obtener Datos para IA
df = yf.download(ticker, period="150d", interval="1d", progress=False, auto_adjust=True)
if isinstance(df.columns, pd.MultiIndex):
df.columns = df.columns.droplevel(1)
for col in ['Open', 'High', 'Low', 'Close', 'Volume']:
df[col] = df[col].astype(float)
if precio_actual == 0.0:
precio_actual = float(df['Close'].dropna().iloc[-1])
# Calcular Indicadores Técnicos
df['SMA_20'] = ta.trend.sma_indicator(df['Close'], window=20)
df['SMA_50'] = ta.trend.sma_indicator(df['Close'], window=50)
df['RSI_14'] = ta.momentum.rsi(df['Close'], window=14)
df['MACD'] = ta.trend.macd(df['Close'])
df['MACD_Signal'] = ta.trend.macd_signal(df['Close'])
df['ATR_14'] = ta.volatility.average_true_range(df['High'], df['Low'], df['Close'], window=14)
# =========================================================
# 🚨 INYECTAMOS EL SENTIMIENTO RECIBIDO DESDE N8N
# =========================================================
df['Sentimiento'] = req.sentimiento
df.dropna(inplace=True)
# 🚨 Matriz final con las 12 VARIABLES 🚨
features = ['Open', 'High', 'Low', 'Close', 'Volume', 'SMA_20', 'SMA_50', 'RSI_14', 'MACD', 'MACD_Signal', 'ATR_14', 'Sentimiento']
X_live = df.tail(60)[features].values.astype(np.float32)
X_scaled = escalador.transform(X_live)
X_reshaped = np.reshape(X_scaled, (1, 60, len(features)))
# Predicción
prediccion = float(modelo.predict(X_reshaped, verbose=0)[0][0])
decision = "LONG (COMPRAR)" if prediccion > 0.5 else "SHORT (VENDER/ESPERAR)"
confianza = round(prediccion * 100, 2) if prediccion > 0.5 else round((1 - prediccion) * 100, 2)
return {
"symbol": req.symbol,
"ticker_ia": ticker,
"precio_actual": precio_actual,
"decision_ia": decision,
"confianza": f"{confianza}%",
"probabilidad_matematica": prediccion,
"sentimiento_aplicado": req.sentimiento # Te lo devuelvo para que confirmes que llegó bien
} |