MrBKM555 commited on
Commit
d61656f
·
verified ·
1 Parent(s): 6fb5f3a

Update main.py

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