File size: 7,566 Bytes
310c773 f04a13f 310c773 d99afc8 f6be544 310c773 d99afc8 f6be544 310c773 d99afc8 2090b17 310c773 d99afc8 310c773 2090b17 d99afc8 310c773 837880e 54e7bf2 310c773 54e7bf2 12b0b0e 640467d 821c8ab 640467d 310c773 12b0b0e 54e7bf2 12b0b0e e1e8259 12b0b0e 2090b17 e9d15d9 2090b17 12b0b0e 821c8ab 2090b17 e1e8259 12b0b0e e1e8259 2090b17 12b0b0e 232a8d8 12b0b0e 54e7bf2 640467d 54e7bf2 cd729b3 640467d cd729b3 f3294f6 cd729b3 f3294f6 cd729b3 821c8ab f04a13f 640467d 821c8ab f04a13f 640467d 821c8ab 640467d f04a13f 821c8ab f04a13f 821c8ab 640467d f04a13f | 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | import streamlit as st
import yfinance as yf
import numpy as np
import pandas as pd
from scipy.stats import norm
import datetime
import requests
import os
import plotly.graph_objects as go
def obtener_tickers_desde_nombres(empresas):
api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
st.error("La variable de entorno 'GEMINI_API_KEY' no está definida.")
return []
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={api_key}"
prompt = (
"Devuélveme únicamente una lista separada por comas con los tickers bursátiles reales de las siguientes empresas: "
f"{empresas}. No expliques nada más, solo dame los tickers exactos, sin nombres ni texto adicional."
)
headers = {"Content-Type": "application/json"}
data = {"contents": [{"role": "user", "parts": [{"text": prompt}]}]}
response = requests.post(url, headers=headers, json=data)
if response.status_code != 200:
st.error(f"Error {response.status_code} al consultar Gemini: {response.text}")
return []
try:
result = response.json()
raw_text = result["candidates"][0]["content"]["parts"][0]["text"]
tickers = [t.strip().upper() for t in raw_text.split(",") if t.strip()]
return tickers
except Exception:
st.error("Error al procesar la respuesta de Gemini.")
return []
st.title("Calculadora de VaR y CVaR con Gemini y Yahoo Finance")
empresa_input = st.text_input("Escribe los nombres de las empresas separadas por coma (ej. Apple, Google, Meta):")
fecha_inicio = st.date_input(
"Selecciona la fecha de inicio para los históricos:",
value=datetime.date(datetime.datetime.today().year, 1, 2),
min_value=datetime.date(2000, 1, 1),
max_value=datetime.date.today()
)
confidence_percent = st.slider(
"Nivel de confianza (%) [valores recomendados: 95% o 99%]",
min_value=90,
max_value=99,
value=95,
step=1
)
confidence_level = confidence_percent / 100
if st.button("Identificar Tickers") and empresa_input:
tickers_detectados = obtener_tickers_desde_nombres(empresa_input)
if len(tickers_detectados) >= 2:
st.session_state["tickers"] = tickers_detectados
base = int(100 / len(tickers_detectados))
pesos = [base] * (len(tickers_detectados) - 1)
pesos.append(100 - sum(pesos)) # Ajuste final
for i, ticker in enumerate(tickers_detectados):
st.session_state[f"weight_{ticker}"] = pesos[i]
else:
st.warning("Se requieren al menos dos tickers válidos.")
if "tickers" in st.session_state:
tickers = st.session_state["tickers"]
st.success(f"Tickers detectados: {', '.join(tickers)}")
st.subheader("Asignar pesos a cada activo (múltiplos de 5%)")
cols = st.columns(len(tickers))
total_weight = 0
weight_inputs = []
for i, ticker in enumerate(tickers):
with cols[i]:
key = f"weight_{ticker}"
if key not in st.session_state:
st.session_state[key] = float(round(100 / len(tickers), 0))
weight = st.number_input(
f"{ticker} (%)",
min_value=0.0,
max_value=100.0,
value=float(st.session_state[key]),
key=key,
step=5.0,
format="%.0f"
)
weight_inputs.append(weight)
total_weight += weight
st.markdown(f"**Suma actual:** {total_weight:.0f}%")
if abs(total_weight - 100.0) > 0.01:
st.warning("⚠️ La suma de los pesos debe ser exactamente 100% para continuar.")
else:
if st.button("Calcular VaR y CVaR"):
weights = np.array(weight_inputs) / 100
start_date = fecha_inicio.strftime("%Y-%m-%d")
end_date = datetime.datetime.today().strftime("%Y-%m-%d")
data = yf.download(tickers, start=start_date, end=end_date)["Close"]
if data.empty or data.isnull().all().all():
st.error("No se encontraron datos históricos para la fecha seleccionada.")
else:
data = data.dropna()
returns = data.pct_change().dropna()
portfolio_returns = returns.dot(weights)
if portfolio_returns.empty:
st.error("No se pudieron calcular retornos del portafolio.")
else:
tail_prob = 1 - confidence_level
historical_VaR = np.percentile(portfolio_returns, tail_prob * 100)
mean_ret = portfolio_returns.mean()
std_ret = portfolio_returns.std()
z_score = norm.ppf(tail_prob)
parametric_VaR = mean_ret + z_score * std_ret
simulated_returns = np.random.normal(mean_ret, std_ret, 10000)
mc_VaR = np.percentile(simulated_returns, tail_prob * 100)
historical_CVaR = portfolio_returns[portfolio_returns <= historical_VaR].mean()
st.subheader("Resultados del Portafolio:")
st.markdown(f"**Historical VaR:** {historical_VaR:.4%}")
st.markdown(f"**Parametric VaR:** {parametric_VaR:.4%}")
st.markdown(f"**Monte Carlo VaR:** {mc_VaR:.4%}")
st.markdown(f"**Historical CVaR:** {historical_CVaR:.4%}")
# 📈 Gráfico Plotly con leyenda clara y líneas completas
hist_values = np.histogram(portfolio_returns.values, bins=50)
max_y = max(hist_values[0]) + 1
fig = go.Figure()
fig.add_trace(go.Histogram(
x=portfolio_returns,
nbinsx=50,
marker_color='rgba(200,200,200,0.6)',
name="Retornos del portafolio",
hovertemplate="%{x:.2%}<extra></extra>"
))
for val, color, label in zip(
[historical_VaR, parametric_VaR, mc_VaR],
["red", "blue", "green"],
["Historical VaR", "Parametric VaR", "Monte Carlo VaR"]
):
fig.add_trace(go.Scatter(
x=[val, val],
y=[0, max_y],
mode="lines",
line=dict(color=color, dash="dash", width=2),
name=label,
hoverinfo="skip",
showlegend=True
))
fig.update_layout(
title="Distribución de Retornos del Portafolio con líneas VaR",
xaxis_title="Retorno diario",
yaxis_title="Frecuencia",
legend=dict(
orientation="h",
yanchor="top",
y=-0.25,
xanchor="center",
x=0.5
),
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
font=dict(color='white', size=14),
margin=dict(t=80, l=40, r=40, b=100),
height=500
)
st.plotly_chart(fig, use_container_width=True)
|