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)