File size: 25,209 Bytes
0ff5c02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f437c9c
 
 
 
0ff5c02
f437c9c
 
 
c0277eb
f437c9c
 
0ff5c02
f437c9c
0ff5c02
f437c9c
0ff5c02
 
 
 
 
 
 
 
 
f437c9c
0ff5c02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c0277eb
364c636
0ff5c02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa0609d
0ff5c02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1c92ace
0ff5c02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89aea35
 
fa0609d
 
0ff5c02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa0609d
0ff5c02
 
 
 
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
# @title Texto de t铆tulo predeterminado
# Instala la librer铆a gdown para descargar archivos de Google Drive
# Esto solo necesita ejecutarse una vez al inicio.
# Se incluye aqu铆 para que el c贸digo sea autocontenido.
import plotly.graph_objects as go
import gradio as gr
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import os
from IPython.display import HTML
import numpy as np
import requests
from bs4 import BeautifulSoup
import re
from openpyxl.utils import get_column_letter
import shutil
import gdown
from processes import all_historical_data
from processes import data_option

# Diccionarios de datos y funciones auxiliares
tickers_to_ids = {
  "MSTR": 1, "MARA": 4, "CEP": 436, "BSTR": 674, "RIOT": 7, "DJT": 671, "3350.T": 176, "GLXY": 10, "CLSK": 28, "TSLA": 2,
  "HUT": 6, "COIN": 8, "XYZ": 5, "NXTT": 473, "CCCM": 595, "SMLR": 194, "GME": 517, "CANG": 337, "ADE.DE": 11, "0434.HK": 238,
  "HOLO": 508, "SQNS": 644, "HIVE": 9, "EXOD": 168, "ALTBG.PA": 295, "3659.T": 14, "FUFU": 325, "BTDR": 324, "SWC.AQ": 440, "FLD": 256,
  "CAN": 111, "BITF": 12, "CIFR": 82, "3825.T": 301, "KULR": 341, "NA": 331, "CORZ": 3, "MSW": 356, "SOS": 426, "BTCT.V": 608,
  "AKER.OL": 15, "GS9": 491, "CASH3.SA": 395, "MELI": 182, "SRAG.DU": 279, "JAS.BK": 307, "DCC.AX": 24, "ARLP": 425, "377030.KQ": 401, "BTBT": 18,
  "NDA.V": 32, "DDC": 500, "NTHOL.IS": 40, "DMGI.V": 23, "NCTY": 442, "VLCN": 679, "PNIX.AQ": 582, "ABT.DU": 21, "LQWD.V": 29, "CS.ST": 288,
  "VIRT": 472, "112040.KQ": 403, "RUM": 402, "DEFI.NE": 254, "GNS": 294, "PRE": 573, "3189.T": 445, "BTCB.SE": 636, "LMFA": 91, "BMNR": 550,
  "SIX.CN": 597, "BNXAF": 30, "PHX.AD": 449, "HKHC": 480, "042420.KQ": 406, "BTC.BK": 31, "K33.ST": 538, "COIN.AQ": 483, "BIGG.V": 17, "BTM": 372,
  "KWM": 643, "BTC.L": 292, "8005.HK": 342, "MATA.V": 357, "VANA.MC": 535, "V3TC.AQ": 586, "SBC": 380, "FRMO": 35, "ATAI": 479, "CBIT.V": 27,
  "MLGO": 511, "MOGO": 36, "8267.HK": 239, "WNDR.TO": 242, "SATO.V": 240, "9238.T": 434, "1723.HK": 384, "PXI.CN": 634, "SATS.L": 666, "ALBDM.PA": 580,
  "IREN": 12, "WULF": 15, "GREE": 21, "EBON": 14, "DGHI": 25, "SDIG": 17, "MIGI": 16, "SLNH": 24, "ANY": 23, "APLD": 20,
  "BTCS": 19, "RVYL": 30, "ICG": 29, "HOOD": 35, "PYPL": 2, "NVDA": 22, "CME": 26, "DEFI": 31, "IBIT": 119, "BTF": 33,
  "SQ": 34, "BKKT": 37, "BTCM": 38, "MA": 39, "IBM": 40, "GBTC": 50, "BITO": 41, "BITB": 117, "BTC-USD": 0, "MTPLF": 176
}

# Diccionario global para almacenar los datos hist贸ricos


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]


def limpiar_texto(texto):
    """Elimina caracteres especiales y espacios extra."""
    texto_limpio_parcial = re.sub(r'[^a-zA-Z0-9.,\s]', '', texto)
    texto_final = texto_limpio_parcial.strip()
    return texto_final

def expand_money(val):
    """Convierte strings de dinero (ej. '$1.2B') a valores flotantes."""
    if pd.isna(val):
        return np.nan
    val = str(val).replace('$', '').replace(',','').upper()
    try:
        if 'B' in val:
            return float(val.replace('B', '')) * 1_000_000_000
        elif 'M' in val:
            return float(val.replace('M', '')) * 1_000_000
        elif 'K' in val:
            return float(val.replace('K', '')) * 1_000
        else:
            return float(val)
    except:
        return np.nan

import requests
from bs4 import BeautifulSoup
import pandas as pd

def descargar_tabla_web(url, indice_tabla=0):
    """
    Descarga una tabla de una URL usando ScraperAPI para evitar bloqueos.
    """
    api_key =  '7b033896bff116ad0fcc94b29c52c923'  # Reemplaza esto con tu clave de ScraperAPI
    api_url = f"http://api.scraperapi.com?api_key={api_key}&url={url}"

    try:
        response = requests.get(api_url)
        response.raise_for_status()

        soup = BeautifulSoup(response.text, 'html.parser')
        tables = soup.find_all('table')

        if len(tables) > indice_tabla:
            table = tables[indice_tabla]
        else:
            print(f"No se encontr贸 la tabla con 铆ndice {indice_tabla} en la p谩gina: {url}.")
            return None

        # El resto del c贸digo para extraer los datos de la tabla sigue igual
        headers = [th.text.strip() for th in table.find_all('th')]
        data = []
        for row in table.find_all('tr'):
            cols = row.find_all('td')
            cols = [ele.text.strip() for ele in cols]
            if cols:
                data.append(cols)

        if headers and data and len(headers) == len(data[0]):
            df = pd.DataFrame(data, columns=headers)
        else:
            df = pd.DataFrame(data)
        return df

    except requests.exceptions.RequestException as e:
        print(f"Error al acceder a la URL: {e}")
        return None
    except Exception as e:
        print(f"Ocurri贸 un error inesperado: {e}")
        return None
def generar_tickers():
    """
    Genera una lista global de tickers a partir de las tablas de stocks y ETFs.
    """
    global tickers
    tickers = []

    # URLs de donde obtener los datos
    url_stocks =  "https://bitcointreasuries.net/"
    url_etfs =  "https://bitcointreasuries.net/etfs-and-exchanges"

    # Descargar tablas web (se asume que estas funciones existen)
    df_stocks = descargar_tabla_web(url_stocks, indice_tabla=3)
    df_etfs = descargar_tabla_web(url_etfs, indice_tabla=3)

    # Procesar Stocks si existen
    if df_stocks is not None and not df_stocks.empty:
        # Asumiendo que limpiar_texto ya est谩 definida
        df_stocks[['Name', 'Ticker']] = df_stocks[2].apply(limpiar_texto).str.rsplit(' ', n=1, expand=True)
        tickers.extend(df_stocks['Ticker'].dropna().tolist())

    # Procesar ETFs si existen
    if df_etfs is not None and not df_etfs.empty:
        df_etfs[['Name', 'Ticker']] = df_etfs[2].apply(limpiar_texto).str.rsplit(' ', n=1, expand=True)
        df_etfs['Ticker'].fillna(df_etfs['Name'], inplace=True)
        tickers.extend(df_etfs['Ticker'].dropna().tolist())

generar_tickers()

def extract_file_id(url):
    """Extrae el ID del archivo de una URL de Google Drive."""
    match = re.search(r'/d/([a-zA-Z0-9_-]+)', url)
    if match:
        return match.group(1)
    return None

def descargar_resumen_holding_excel(template_url):
    """
    Descarga los datos de holding de BTC para stocks y ETFs,
    los procesa, calcula correlaciones y exporta a un archivo Excel
    usando una plantilla de Google Drive.
    """
    try:
        # Extraer el ID del archivo de la URL
        file_id = extract_file_id(template_url)
        if not file_id:
            return None, "Error: No se pudo extraer el ID del archivo de la URL de Google Drive."

        local_template_path = 'plantilla_temporal.xlsx'

        # Descargar la plantilla de Google Drive
        try:
            gdown.download(id=file_id, output=local_template_path, quiet=False)
        except Exception as e:
            return None, f"Error al descargar la plantilla desde Google Drive: {e}"

        url_stocks = "https://bitcointreasuries.net/"
        url_ETFs = "https://bitcointreasuries.net/etfs-and-exchanges"

        df_stocks = descargar_tabla_web(url_stocks, indice_tabla=3)
        df_etfs = descargar_tabla_web(url_ETFs, indice_tabla=3)

        if df_stocks is None and df_etfs is None:
            return None, "Error: No se pudieron descargar los datos de holding."

        # Procesar Stocks
        if df_stocks is not None:
            df_descargado_stocks = df_stocks[[2,3,4,5,12]].copy()
            df_descargado_stocks.columns = ['Name','Bitcoin', 'BTC in USD', 'Marketcap', '% Total BTC 21M']
            df_descargado_stocks[['Name','Ticker']] = df_descargado_stocks['Name'].apply(limpiar_texto).str.rsplit(' ', n=1, expand=True)

            df_descargado_stocks['Bitcoin'] = df_descargado_stocks['Bitcoin'].apply(limpiar_texto)
            df_descargado_stocks['Marketcap'] = df_descargado_stocks['Marketcap'].apply(limpiar_texto)
            df_descargado_stocks['BTC in USD'] = df_descargado_stocks['BTC in USD'].apply(expand_money)
            df_descargado_stocks['Marketcap'] = df_descargado_stocks['Marketcap'].apply(expand_money)
            df_descargado_stocks['Ratio Holding/Mcap'] = df_descargado_stocks['BTC in USD'] / df_descargado_stocks['Marketcap']

            corrls = []
            for stock in df_descargado_stocks['Ticker']:
                try:
                    corr_value = evolucion_correlacion(stock, step=0, window_months=12).iloc[0]
                    corrls.append(corr_value)
                except Exception as e:
                    print(f"Error al calcular correlaci贸n para {stock}: {e}")
                    corrls.append(np.nan)
            df_descargado_stocks['Correlaci贸n con BTC (12 meses)'] = pd.Series(corrls)
            df_descargado_stocks.set_index('Ticker', inplace=True)
        else:
            df_descargado_stocks = pd.DataFrame()


        # Procesar ETFs
        if df_etfs is not None:
            global df_descargado_ETFs
            df_descargado_ETFs = df_etfs[[2,3,4,5]].copy()
            df_descargado_ETFs.columns = ['Name','Bitcoin', 'BTC in USD','% Total BTC 21M']
            df_descargado_ETFs[['Name','Ticker']] = df_descargado_ETFs['Name'].apply(limpiar_texto).str.rsplit(' ', n=1, expand=True)
            df_descargado_ETFs['Ticker'].fillna(df_descargado_ETFs['Name'], inplace=True)
            df_descargado_ETFs['BTC in USD'] = df_descargado_ETFs['BTC in USD'].apply(expand_money)
            df_descargado_ETFs['Bitcoin'] = df_descargado_ETFs['Bitcoin'].apply(limpiar_texto).apply(expand_money)
            df_descargado_ETFs.set_index('Ticker', inplace=True)
        else:
            df_descargado_ETFs = pd.DataFrame()

        # A帽adir la columna de 'Options' como solicitaste
        global tickers
        tickers = []
        if not df_descargado_stocks.empty:
            tickers.extend(df_descargado_stocks.index.tolist())
        if not df_descargado_ETFs.empty:
            tickers.extend(df_descargado_ETFs.index.tolist())

        options_bool = {}
        for ticker in tickers:
            if ticker is not None:
                try:
                    options_bool[ticker] = False if not yf.Ticker(ticker).options else True
                except:
                    options_bool[ticker] = False

        options_series = pd.Series(options_bool)

        if not df_descargado_stocks.empty:
            df_descargado_stocks['Options'] = options_series
        if not df_descargado_ETFs.empty:
            df_descargado_ETFs['Options'] = options_series


        # Exportaci贸n a Excel
        if not df_descargado_stocks.empty or not df_descargado_ETFs.empty:
            nuevo_archivo_path = 'resumen_datos_holding.xlsx'

            # Copiar la plantilla descargada antes de escribir
            try:
                shutil.copyfile(local_template_path, nuevo_archivo_path)
            except FileNotFoundError:
                return None, f"Error: No se encontr贸 la plantilla en la ruta: {local_template_path}"

            # Escribir en el archivo copiado
            with pd.ExcelWriter(nuevo_archivo_path, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer:
                if not df_descargado_stocks.empty:
                    df_descargado_stocks.to_excel(writer, sheet_name='Stocks', index=True)
                if not df_descargado_ETFs.empty:
                    df_descargado_ETFs.to_excel(writer, sheet_name='ETFs', index=True)

            # Eliminar la plantilla temporal descargada para limpiar el entorno
            os.remove(local_template_path)

            return nuevo_archivo_path, "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}"


# Funciones para Gradio

def plot_evolucion_correlacion_gradio(ticker_company, step, window_months):

    """
    Genera el gr谩fico de la evoluci贸n de la correlaci贸n.
    """
    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."

    """
    if serie is None or serie.empty:
        return plt.figure(), f"No hay datos de correlaci贸n disponibles para {ticker_company}."
    max_value = serie.max()
    min_value = serie.min()
    max_index = serie.idxmax()
    plt.figure(figsize=(10, 6))
    plt.plot(serie)
    plt.xlabel('Fecha')
    plt.ylabel('Correlaci贸n')
    plt.title(f'Evoluci贸n de la correlaci贸n entre {ticker_company} y BTC-USD')
    plt.plot(max_index, max_value, 'ro')
    plt.annotate(f' Corr_max: {max_value:.4f} \n Fecha: {max_index.date()}', (max_index, max_value), xytext=(max_index, max_value - (max_value - min_value) * 0.15))
    lower_margin = min_value - 0.1 * abs(min_value)
    upper_margin = max_value + 0.05 * abs(max_value)
    plt.ylim(lower_margin, upper_margin)
    plt.tight_layout()
    return plt.gcf(), "Gr谩fico de correlaci贸n generado."
    """

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}"

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}."

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}."


### CONFIGURACION DE GRADIO:

with gr.Blocks(title="An谩lisis de Correlaci贸n") as demo:
    gr.Image("https://cdn.prod.website-files.com/67174dcb1d43b06a0eae317e/672bbbc7a2b88045e5661994_logo-afi.svg", show_label=False)
    gr.Markdown("# Analizador de Correlaci贸n de Bitcoin")
    gr.Markdown("Explora la correlaci贸n de diferentes activos con Bitcoin y sus holdings.")

    with gr.Row():
        input_ticker = gr.Dropdown(
            label="Ticker de la Empresa",
            choices= tickers, ### hacer que la lista sea dinamica, luego miraremos el tema de los ids
            value="MSTR",
            allow_custom_value=True
        )

    with gr.Tab("Evoluci贸n de Correlaci贸n"):
        gr.Markdown("### Evoluci贸n de la correlaci贸n")
        with gr.Row():
          input_step = gr.Slider(minimum=1, maximum=30, value=1, step=1, label="Indica cada cu谩ntos d铆as se calcula la correlaci贸n")
          input_window = gr.Slider(minimum=1, maximum=60, value=12, step=1, label="Ventana rolling (meses)")
        btn_plot = gr.Button("Mostrar Gr谩fico")
        output_plot = gr.Plot(label="Gr谩fico de Evoluci贸n")
        btn_excel = gr.Button("Descargar en Excel")





        output_file = gr.File(label="Archivo Excel")
        output_msg = gr.Label()

        btn_plot.click(
            fn=plot_evolucion_correlacion_gradio,
            inputs=[input_ticker, input_step, input_window],
            outputs=[output_plot, output_msg]
        )
        btn_excel.click(
            fn=exportar_correlacion,
            inputs=[input_ticker, input_step, input_window],
            outputs=[output_file, output_msg]
        )

    with gr.Tab("Stock vs BTC"):
        gr.Markdown("### Gr谩fico de Stock vs Bitcoin")
        btn_stock_btc = gr.Button("Cargar Gr谩fico")
        gr.Markdown("**NAV Multiplier:** \n\n Muestra la relaci贸n entre el precio de la acci贸n de la empresa y el valor de sus activos.")
        output_iframe_stock = gr.HTML(label="Stock vs Bitcoin Chart")

        btn_stock_btc.click(
            fn=get_iframe_stock_vs_btc,
            inputs=[input_ticker],
            outputs=[output_iframe_stock]
        )

    with gr.Tab("Holding de Bitcoin"):
        gr.Markdown("### Gr谩fico de Holding de Bitcoin")
        btn_holdings = gr.Button("Cargar Gr谩fico")
        output_iframe_holdings = gr.HTML(label="Bitcoin Holdings Chart")

        btn_holdings.click(
            fn=get_iframe_holdings,
            inputs=[input_ticker],
            outputs=[output_iframe_holdings]
        )

       


    with gr.Tab("Exportar Resumen Holding"):
        gr.Markdown("### Descargar resumen de holdings a Excel")
        gr.Markdown("Esto descargar谩 los datos de holding de BTC de la web, los procesar谩 y crear谩 un archivo Excel a partir de una plantilla.")

        # Agregado el input para la URL de la plantilla de Google Drive
        input_template_url = gr.Textbox(
            label="URL de la plantilla de Excel (Google Drive)",
            value="https://docs.google.com/spreadsheets/d/1Ad3bbYQAAZO6ljkaCGTGSw5aG_FQF7Lg/edit?usp=sharing&ouid=109222615757657219494&rtpof=true&sd=true",
            placeholder="Ej: https://docs.google.com/spreadsheets/d/ID_DEL_ARCHIVO/..."
        )


        btn_descargar_resumen = gr.Button("Descargar Resumen a Excel")
        output_resumen_file = gr.File(label="Archivo de Resumen de Holdings")
        output_resumen_msg = gr.Label()

        btn_descargar_resumen.click(
            fn=descargar_resumen_holding_excel,
            inputs=[input_template_url], # Ahora pasamos la URL como input
            outputs=[output_resumen_file, output_resumen_msg]
        )




#Inicia la aplicaci贸n
demo.launch(share=True)