File size: 12,076 Bytes
02b7d96
 
 
d6d3b9b
02b7d96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3ebb101
a6afe49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
abb67ac
a6afe49
 
 
 
 
 
 
 
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
import pandas as pd
import yfinance as yf
from datetime import datetime, timedelta
from data import all_historical_data, top_holding
from config import tickers_to_ids
import plotly.graph_objects as go

#     -----------------------------------------------
#      Función de Correlación
#     -----------------------------------------------

def evolucion_correlacion(ticker_company, step, ticker_btc="BTC-USD", window_months=12):
    """
    Calcula la evolución de la correlación entre el precio de las acciones de una empresa y Bitcoin.
    Args:
        ticker_company (str): El símbolo de cotización de la empresa.
        step (int): El número de días para el desplazamiento de la correlación.
        ticker_btc (str): El símbolo de cotización de Bitcoin (por defecto: "BTC-USD").
        window_months (int): El tamaño de la ventana móvil en meses.
    outputs:
        pandas.Series: Una serie con la evolución de la correlación, o None si faltan datos.
    """
    end_date = datetime.now()
    start_date = end_date - timedelta(days=10*365)

    if ticker_company not in all_historical_data:
        print(f"Descargando datos para {ticker_company}...")
        data = yf.download(ticker_company, start=start_date, end=end_date)
        if not data.empty:
            all_historical_data[ticker_company] = data
            print(f"Datos de {ticker_company} descargados con éxito.")
        else:
            print(f"Error al descargar datos para {ticker_company}.")
            return None

    if ticker_btc not in all_historical_data:
        print(f"Descargando datos para {ticker_btc}...")
        data = yf.download(ticker_btc, start=start_date, end=end_date)
        if not data.empty:
            all_historical_data[ticker_btc] = data
            print(f"Datos de {ticker_btc} descargados con éxito.")
        else:
            print(f"Error al descargar datos para {ticker_btc}.")
            return None

    company_data = all_historical_data.get(ticker_company)
    btc_data = all_historical_data.get(ticker_btc)

    if company_data.empty or btc_data.empty:
        print(f"No podemos calcular la correlación dinámica: No hay datos disponibles para {ticker_company} o {ticker_btc}.")
        return None

    company_prices = company_data['Close']
    btc_prices = btc_data['Close']

    combined_prices = pd.concat([company_prices, btc_prices], axis=1).dropna()
    combined_prices.columns = [ticker_company, ticker_btc]

    if combined_prices.empty:
        print(f"No hay datos históricos en común entre {ticker_company} y {ticker_btc}.")
        return None

    company_returns = combined_prices[ticker_company].pct_change().dropna()
    btc_returns = combined_prices[ticker_btc].pct_change().dropna()

    window_days = window_months * 30
    if step == 0:
        dynamic_corr = company_returns.rolling(window=f"{window_days}D").corr(btc_returns).iloc[-1]
        return pd.Series([dynamic_corr], index=[end_date])
    else:
        dynamic_corr = company_returns.rolling(window=f"{window_days}D").corr(btc_returns).dropna()
        return dynamic_corr.iloc[9::step]




#     -----------------------------------------------
#                PLOT DE LA CORRELACIÓN
#     -----------------------------------------------



def plot_evolucion_correlacion_gradio(ticker_company, step, window_months):

    """
    Genera el gráfico de la evolución de la correlación.
    """
    ticker_company = ticker_company.upper()
    serie = evolucion_correlacion(ticker_company, step, window_months=12)

    if serie is None or serie.empty:
        return None, f"No hay datos de correlación disponibles para {ticker_company}."

    # --- Create the Plotly graph ---
    fig = go.Figure()

    # Add the correlation series
    fig.add_trace(go.Scatter(
        x=serie.index,
        y=serie.values,
        mode='lines',
        name='Correlación',
        line=dict(color='#2263eb', width=2), # electric blue color for the line
        fill='tozeroy', # Fill the area down to Y=0 axis
        fillcolor='rgba(34,99,235,0.1)', # Very faint elctric blue shadow (10% opacity)
        hovertemplate='Fecha: %{x|%Y-%m-%d}<br>Correlación: %{y:.4f}<extra></extra>'
    ))

    # --- Configure axes and range selector ---
    fig.update_layout(
        title_text=f'<b>Evolución de la correlación entre {ticker_company} y BTC-USD<b>',
        title_x=0, # Align title to the left
        height=500, # Graph height
        template="plotly_white", # Graph theme (clean and modern)
        margin=dict(l=20, r=20, t=60, b=20), # Graph margins
        showlegend=False, # No legend needed if only one main series

        xaxis=go.layout.XAxis(
            rangeslider=dict(
                visible=True,
                thickness=0.08, # Rangeslider thickness
                bgcolor="rgba(50, 0, 128, 0.1)", # Very transparent purple for rangeslider
                bordercolor="rgba(50, 0, 128, 0.2)", # Slightly more visible border
                borderwidth=1,
                range=[serie.index.min(), serie.index.max()] # Initial slider range
            ),
            rangeselector=dict(
                buttons=list([
                    dict(count=1, label="1m", step="month", stepmode="backward"),
                    dict(count=3, label="3m", step="month", stepmode="backward"),
                    dict(count=6, label="6m", step="month", stepmode="backward"),
                    dict(step="year", count=1, label="YTD", stepmode="todate"), # YTD
                    dict(count=1, label="1y", step="year", stepmode="backward"),
                    dict(step="all", label="All")
                ]),
                bgcolor="rgba(247,247,247,255)", # Transparent background for buttons
                bordercolor="rgba(0,0,0,0)", # Transparent border
                borderwidth=0,
                activecolor="rgba(230,233,255,255)", # Active button color (slightly darker purple)
                font=dict(color="#000000", size=10), # Button font style
                x=0.9, y=1.1, xanchor='right', yanchor='top' # Button position
            ),
            type="date",
            domain=[0, 1], # Occupy full graph width
            rangeslider_yaxis_rangemode="auto", # Rangeslider Y-axis range adjusts automatically
            showgrid=False,
            gridcolor='#e0e0e0',
            zeroline=False
        ),
        yaxis=go.layout.YAxis(
            title_text='Correlación',
            side='left',
            showgrid=True,
            gridcolor='#e0e0e0',
            zeroline=False,
            tickformat=".2f", # 2 decimal places format
            range=[-1.0, 1.0] # Fixed range for correlation
        ),
        hovermode="x unified", # Unify hover to show data for the series
        paper_bgcolor='rgba(255,255,255,1)', # Transparent background for notebook integration
        plot_bgcolor='rgba(255,255,255,1)'
    )

    # --- Add annotation for max value (similar to Matplotlib) ---
    max_value = serie.max()
    max_index = serie.idxmax()
    fig.add_annotation(
        x=max_index,
        y=max_value,
        text=f'Corr_max: {max_value:.4f}<br>Fecha: {max_index.date()}',
        showarrow=True,
        arrowhead=2,
        ax=0,
        ay=-40, # Adjust vertical position of the annotation
        bgcolor="rgba(255, 255, 255, 0.8)", # Semi-transparent white background
        bordercolor="#c7c7c7",
        borderwidth=1,
        borderpad=4,
        font=dict(size=10, color="#333"),
        # You can adjust text position with xanchor/yanchor if needed
    )

    return fig, "Gráfico de correlación generado."



#     -----------------------------------------------
#             EXPORTACIÓN DE LA CORRELACIÓN
#     -----------------------------------------------


def exportar_correlacion(ticker_company, step, window_months):

    """
    Exporta la serie de correlación a un archivo Excel.
    """
    try:
        serie = evolucion_correlacion(ticker_company, step, window_months=window_months)
        if serie is not None and not serie.empty:
            nombre_archivo = f'{ticker_company}_evolucion_correlacion.xlsx'
            serie.to_excel(nombre_archivo)
            return nombre_archivo, "Archivo Excel generado con éxito."
        else:
            return None, "No se pudieron generar los datos para exportar."
    except Exception as e:
        return None, f"Error al exportar: {e}"


#     -----------------------------------------------
#          EMBED PLOT STOCK VS BTC DE BTC TREASURY
#     -----------------------------------------------


def get_iframe_stock_vs_btc(ticker):
    """
    Genera el código HTML para el iframe de Stock vs Bitcoin.
    """
    try:
        entity_id = tickers_to_ids[ticker]
        iframe_code = f"""
            <iframe src="https://bitcointreasuries.net/embed?component=StockVsBitcoinChart&entityId={entity_id}"
            title="Bitcoin Treasuries" credentialless style="width: 100%; height: 660px; border: none;"></iframe>
        """
        return iframe_code
    except KeyError:
        return f"No hay datos de holding de Bitcoin para {ticker}."



#     -----------------------------------------------
#          EMBED PLOT HOLDING BTC DE BTC TREASURY
#     -----------------------------------------------


def get_iframe_holdings(ticker):
    """
    Genera el código HTML para el iframe de Bitcoin Holdings.
    """
    try:
        entity_id = tickers_to_ids[ticker]
        iframe_code = f"""
            <iframe src="https://bitcointreasuries.net/embed?component=BitcoinHoldingsChart&entityId={entity_id}"
            title="Bitcoin Holdings Chart" credentialless style="width: 100%; height: 660px; border: none;"></iframe>
        """
        return iframe_code
    except KeyError:
        return f"No hay datos de holding de Bitcoin para {ticker}."




#     -----------------------------------------------
#          TABLAS TOP HOLDING RESUMEN
#     -----------------------------------------------



df_descargado_ETFs, df_descargado_stocks = top_holding()
df_descargado_ETFs = df_descargado_ETFs.reset_index()

# Crear un DataFrame de ejemplo
top5_ETFs = df_descargado_ETFs.copy().iloc[:,[1,2,3]].head()
top5_ETFs = pd.DataFrame(top5_ETFs)

top5_stocks = df_descargado_stocks.copy().iloc[:,[0,1,2]].head()
top5_stocks = pd.DataFrame(top5_stocks)

# Aplicar estilo con color de marca y bordes redondeados

AZUL_BORDE = "#0085CA"
GRIS_HEADER = "#F5F6F8"
BLANCO = "#FFFFFF"

styles = [
    # Perímetro y comportamiento de bordes
    dict(selector="table", props=[
        ("border", f"1.5px solid {AZUL_BORDE} !important"),
        ("border-radius", "11px"),
        ("border-collapse", "separate !important"),
        ("border-spacing", "0"),
        ("background-color", BLANCO)
    ]),
    # Eliminar TODOS los bordes internos
    dict(selector="th, td", props=[
        ("border", "none !important"),
        ("background-color", BLANCO)
    ]),
    # Header gris muy claro
    dict(selector="thead th", props=[
        ("background-color", GRIS_HEADER)
    ]),
    # Redondear esquinas visibles
    dict(selector="thead tr:first-child th:first-child", props=[
        ("border-top-left-radius", "12px")
    ]),
    dict(selector="thead tr:first-child th:last-child", props=[
        ("border-top-right-radius", "12px")
    ]),
    dict(selector="tbody tr:last-child td:first-child", props=[
        ("border-bottom-left-radius", "12px")
    ]),
    dict(selector="tbody tr:last-child td:last-child", props=[
        ("border-bottom-right-radius", "12px")
    ]),
]


styled_stocks = top5_stocks.style.set_table_styles(styles).hide(axis="index")
styled_stocks = styled_stocks.set_table_attributes(
    f'style="border:1.5px solid {AZUL_BORDE}; border-radius:11px; border-collapse:separate; border-spacing:0; background:#fff"'
)

styled_ETFs = top5_ETFs.style.set_table_styles(styles).hide(axis="index")
styled_ETFs = styled_ETFs.set_table_attributes(
    f'style="border:1.5px solid {AZUL_BORDE}; border-radius:11px; border-collapse:separate; border-spacing:0; background:#fff"'
)