TradingTest / main.py
MrBKM555's picture
Update main.py
80bf9cf verified
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
}