Quant0 commited on
Commit
91e1b87
·
verified ·
1 Parent(s): 9bb3deb

Create data.py

Browse files
Files changed (1) hide show
  1. data.py +320 -0
data.py ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import gradio as gr
3
+ import ccxt
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+ import yfinance as yf
7
+ import requests
8
+ import pandas as pd
9
+ import time
10
+ import re
11
+ import gdown
12
+ import shutil
13
+ import os
14
+
15
+ from datetime import datetime, timedelta
16
+ from config import API_KEY, SYMBOL, CURRENCY, LIMIT_PER_CALL, BASE_URL, YEARS, api_key, api_url, tickers_to_ids
17
+ from utils import limpiar_texto, expand_money, descargar_tabla_web
18
+
19
+
20
+ # -----------------------------------------------
21
+ # DESCARGA DE DATOS DE BTC: all_historical_data
22
+ # -----------------------------------------------
23
+
24
+
25
+ end_time = datetime.now()
26
+ start_time = end_time - timedelta(days=YEARS*365) # 10 años atrás aproximadamente
27
+
28
+ # Convertir a timestamp de Unix (segundos)
29
+ to_timestamp = int(end_time.timestamp())
30
+
31
+ all_data = []
32
+
33
+ print(f"Fetching BTC/{CURRENCY} hourly data from {start_time.strftime('%Y-%m-%d')} to {end_time.strftime('%Y-%m-%d')}")
34
+
35
+ while True:
36
+ params = {
37
+ "fsym": SYMBOL,
38
+ "tsym": CURRENCY,
39
+ "limit": LIMIT_PER_CALL,
40
+ "toTs": to_timestamp,
41
+ "api_key": API_KEY # Si tienes una clave
42
+ }
43
+ try:
44
+ response = requests.get(BASE_URL, params=params)
45
+ data = response.json()
46
+
47
+ if data['Response'] == 'Error':
48
+ print(f"API Error: {data['Message']}")
49
+ break
50
+
51
+ ohlcv_data = data['Data']['Data']
52
+ if not ohlcv_data:
53
+ break # No more data to fetch
54
+
55
+ all_data.extend(ohlcv_data)
56
+
57
+ # El siguiente `toTs` es el timestamp del primer punto de datos obtenido menos 1 hora (o 1 segundo)
58
+ # para asegurarnos de no duplicar y retroceder en el tiempo.
59
+ # Asegúrate de que los datos estén ordenados por tiempo ascendente desde la API.
60
+ # CryptoCompare devuelve datos desde 'fromTs' hasta 'toTs' en orden ascendente,
61
+ # pero cuando usamos `toTs`, devuelve los datos *anteriores* a `toTs`.
62
+ # Por lo tanto, el nuevo `toTs` debe ser el 'time' del primer elemento de la llamada actual.
63
+ to_timestamp = ohlcv_data[0]['time'] - 1 # Retrocede 1 segundo para la siguiente llamada
64
+
65
+ # Convertir el timestamp del último dato para referencia
66
+ oldest_data_time = datetime.fromtimestamp(ohlcv_data[0]['time'])
67
+ print(f"Fetched data up to {oldest_data_time.strftime('%Y-%m-%d %H:%M:%S')}. Total records: {len(all_data)}")
68
+
69
+ if oldest_data_time <= start_time:
70
+ print(f"Reached desired start time ({start_time.strftime('%Y-%m-%d')}). Stopping.")
71
+ break
72
+
73
+ time.sleep(1) # Respetar límites de rate de la API
74
+
75
+ except requests.exceptions.RequestException as e:
76
+ print(f"Network error: {e}")
77
+ time.sleep(5) # Esperar y reintentar
78
+ except Exception as e:
79
+ print(f"An unexpected error occurred: {e}")
80
+ break
81
+
82
+ # Convertir la lista de diccionarios a un DataFrame de Pandas
83
+ btc_hour_10y = pd.DataFrame(all_data)
84
+ btc_hour_10y['time'] = pd.to_datetime(btc_hour_10y['time'], unit='s')
85
+ btc_hour_10y.set_index('time', inplace=True)
86
+ btc_hour_10y.sort_index(inplace=True) # Asegurarse de que esté ordenado cronológicamente
87
+
88
+ # Eliminar duplicados si los hubiera (puede pasar si las llamadas se solapan)
89
+ btc_hour_10y = btc_hour_10y[~btc_hour_10y.index.duplicated(keep='first')]
90
+
91
+ print("\nDataFrame Head:")
92
+ print(btc_hour_10y.head())
93
+ print("\nDataFrame Tail:")
94
+ print(btc_hour_10y.tail())
95
+ print(f"\nTotal records fetched: {len(btc_hour_10y)}")
96
+
97
+ # Guardar a CSV
98
+ btc_hour_10y.to_csv('btc_hourly_cryptocompare_10_years.csv')
99
+ print("Data saved to btc_hourly_cryptocompare_10_years.csv")
100
+
101
+
102
+ # Ajuste del formato:
103
+
104
+ btc_hour_10y.index = btc_hour_10y.index.tz_localize('UTC')
105
+ btc_hour_10y.index = btc_hour_10y.index.tz_convert('America/New_York') #Convertir el dataframe de UTC a 'America NY'
106
+ btc_hour_10y = btc_hour_10y[btc_hour_10y.index.hour == 16] # Filtrar el dataframe para quedarnos la hora de cierre 4:00 PM EST
107
+ btc_hour_10y.index = btc_hour_10y.index.date #Limpiamos el índice para que sólo muestre las fechas de los días
108
+ btc_hour_10y.columns = btc_hour_10y.columns.str.title() #Ponemos los headers con la primera letra en Mayuscula
109
+ btc_hour_10y.index = pd.to_datetime(btc_hour_10y.index) # Para coincidir con el formato de yahoofinance debemos convertir los indices a fechas en formato: dtype='datetime64[ns]'
110
+
111
+
112
+ all_historical_data = { 'BTC-USD': btc_hour_10y }
113
+
114
+
115
+ # -----------------------------------------------
116
+ # TOP TICKERS DE BTC TREASURY
117
+ # -----------------------------------------------
118
+
119
+
120
+ ### descargamos por orden las empresas mas importantes
121
+ def generar_tickers():
122
+ """
123
+ Genera una lista global de tickers a partir de las tablas de stocks y ETFs.
124
+ """
125
+ global tickers
126
+ tickers = []
127
+
128
+ # URLs de donde obtener los datos
129
+ url_stocks = "https://bitcointreasuries.net/"
130
+ url_etfs = "https://bitcointreasuries.net/etfs-and-exchanges"
131
+
132
+ # Descargar tablas web (se asume que estas funciones existen)
133
+ df_stocks = descargar_tabla_web(url_stocks, indice_tabla=3)
134
+ df_etfs = descargar_tabla_web(url_etfs, indice_tabla=3)
135
+
136
+ # Procesar Stocks si existen
137
+ if df_stocks is not None and not df_stocks.empty:
138
+ # Asumiendo que limpiar_texto ya está definida
139
+ df_stocks['Name'] = df_stocks[2].apply(limpiar_texto)
140
+
141
+ tickers.extend(df_stocks['Name'].dropna().tolist())
142
+
143
+ # Procesar ETFs si existen
144
+ if df_etfs is not None and not df_etfs.empty:
145
+ df_etfs['Name'] = df_etfs[2].apply(limpiar_texto)
146
+
147
+ tickers.extend(df_etfs['Name'].dropna().tolist())
148
+ generar_tickers()
149
+
150
+
151
+ # -----------------------------------------------
152
+ # DESCARGA TOP HOLDING BTC TREASURY
153
+ # -----------------------------------------------
154
+
155
+ def top_holding():
156
+
157
+ url_stocks = "https://bitcointreasuries.net/"
158
+ url_ETFs = "https://bitcointreasuries.net/etfs-and-exchanges"
159
+
160
+ df_stocks = descargar_tabla_web(url_stocks, indice_tabla=3)
161
+ df_etfs = descargar_tabla_web(url_ETFs, indice_tabla=3)
162
+
163
+ if df_stocks is None and df_etfs is None:
164
+ return None, "Error: No se pudieron descargar los datos de holding."
165
+
166
+ # Procesar Stocks
167
+ if df_stocks is not None:
168
+
169
+ df_descargado_stocks = df_stocks[[2,3,12]].copy()
170
+ df_descargado_stocks.columns = ['Name','Bitcoin','% Total BTC 21M']
171
+ df_descargado_stocks['Name'] = df_descargado_stocks['Name'].apply(limpiar_texto)
172
+ df_descargado_stocks['Bitcoin'] = df_descargado_stocks['Bitcoin'].apply(limpiar_texto)
173
+
174
+ else:
175
+ df_descargado_stocks = pd.DataFrame()
176
+
177
+ # Procesar ETFs
178
+ if df_etfs is not None:
179
+
180
+ df_descargado_ETFs = df_etfs[[2,3,5]].copy()
181
+ df_descargado_ETFs.columns = ['Name','Bitcoin','% Total BTC 21M']
182
+ df_descargado_ETFs[['Name','Ticker']] = df_descargado_ETFs['Name'].apply(limpiar_texto).str.rsplit(' ', n=1, expand=True)
183
+ df_descargado_ETFs['Ticker'].fillna(df_descargado_ETFs['Name'], inplace=True)
184
+ df_descargado_ETFs['Bitcoin'] = df_descargado_ETFs['Bitcoin'].apply(limpiar_texto).apply(expand_money)
185
+ df_descargado_ETFs.set_index('Ticker', inplace=True)
186
+ else:
187
+ df_descargado_ETFs = pd.DataFrame()
188
+
189
+
190
+ return df_descargado_ETFs, df_descargado_stocks
191
+
192
+ # -----------------------------------------------
193
+ # DESCARGA HOLDING BTC TREASURY
194
+ # -----------------------------------------------
195
+
196
+
197
+
198
+ def descargar_resumen_holding_excel(template_url):
199
+ """
200
+ Descarga los datos de holding de BTC para stocks y ETFs,
201
+ los procesa, calcula correlaciones y exporta a un archivo Excel
202
+ usando una plantilla de Google Drive.
203
+ """
204
+ global df_descargado_stocks, df_descargado_ETFs
205
+ try:
206
+ # Extraer el ID del archivo de la URL
207
+ file_id = extract_file_id(template_url)
208
+ if not file_id:
209
+ return None, "Error: No se pudo extraer el ID del archivo de la URL de Google Drive."
210
+
211
+ local_template_path = 'plantilla_temporal.xlsx'
212
+
213
+ # Descargar la plantilla de Google Drive
214
+ try:
215
+ gdown.download(id=file_id, output=local_template_path, quiet=False)
216
+ except Exception as e:
217
+ return None, f"Error al descargar la plantilla desde Google Drive: {e}"
218
+
219
+ url_stocks = "https://bitcointreasuries.net/"
220
+ url_ETFs = "https://bitcointreasuries.net/etfs-and-exchanges"
221
+
222
+ df_stocks = descargar_tabla_web(url_stocks, indice_tabla=3)
223
+ df_etfs = descargar_tabla_web(url_ETFs, indice_tabla=3)
224
+
225
+ if df_stocks is None and df_etfs is None:
226
+ return None, "Error: No se pudieron descargar los datos de holding."
227
+
228
+
229
+
230
+ # Procesar Stocks
231
+ if df_stocks is not None:
232
+
233
+ df_descargado_stocks = df_stocks[[2,3,4,5,12]].copy()
234
+ df_descargado_stocks.columns = ['Name','Bitcoin', 'BTC in USD', 'Marketcap', '% Total BTC 21M']
235
+ df_descargado_stocks[['Name','Ticker']] = df_descargado_stocks['Name'].apply(limpiar_texto).str.rsplit(' ', n=1, expand=True)
236
+
237
+ df_descargado_stocks['Bitcoin'] = df_descargado_stocks['Bitcoin'].apply(limpiar_texto)
238
+ df_descargado_stocks['Marketcap'] = df_descargado_stocks['Marketcap'].apply(limpiar_texto)
239
+ df_descargado_stocks['BTC in USD'] = df_descargado_stocks['BTC in USD'].apply(expand_money)
240
+ df_descargado_stocks['Marketcap'] = df_descargado_stocks['Marketcap'].apply(expand_money)
241
+ df_descargado_stocks['Ratio Holding/Mcap'] = df_descargado_stocks['BTC in USD'] / df_descargado_stocks['Marketcap']
242
+
243
+ corrls = []
244
+ for stock in df_descargado_stocks['Ticker']:
245
+ try:
246
+ corr_value = evolucion_correlacion(stock, step=0, window_months=12).iloc[0]
247
+ corrls.append(corr_value)
248
+ except Exception as e:
249
+ print(f"Error al calcular correlación para {stock}: {e}")
250
+ corrls.append(np.nan)
251
+ df_descargado_stocks['Correlación con BTC (12 meses)'] = pd.Series(corrls)
252
+ df_descargado_stocks.set_index('Ticker', inplace=True)
253
+ else:
254
+ df_descargado_stocks = pd.DataFrame()
255
+
256
+
257
+ # Procesar ETFs
258
+ if df_etfs is not None:
259
+
260
+ df_descargado_ETFs = df_etfs[[2,3,4,5]].copy()
261
+ df_descargado_ETFs.columns = ['Name','Bitcoin', 'BTC in USD','% Total BTC 21M']
262
+ df_descargado_ETFs[['Name','Ticker']] = df_descargado_ETFs['Name'].apply(limpiar_texto).str.rsplit(' ', n=1, expand=True)
263
+ df_descargado_ETFs['Ticker'].fillna(df_descargado_ETFs['Name'], inplace=True)
264
+ df_descargado_ETFs['BTC in USD'] = df_descargado_ETFs['BTC in USD'].apply(expand_money)
265
+ df_descargado_ETFs['Bitcoin'] = df_descargado_ETFs['Bitcoin'].apply(limpiar_texto).apply(expand_money)
266
+ df_descargado_ETFs.set_index('Ticker', inplace=True)
267
+ else:
268
+ df_descargado_ETFs = pd.DataFrame()
269
+
270
+ # Añadir la columna de 'Options' como solicitaste
271
+ tckrs = []
272
+ if not df_descargado_stocks.empty:
273
+ tckrs.extend(df_descargado_stocks.index.tolist())
274
+ if not df_descargado_ETFs.empty:
275
+ tckrs.extend(df_descargado_ETFs.index.tolist())
276
+
277
+ options_bool = {}
278
+ for ticker in tckrs:
279
+ if ticker is not None:
280
+ try:
281
+ options_bool[ticker] = False if not yf.Ticker(ticker).options else True
282
+ except:
283
+ options_bool[ticker] = False
284
+
285
+ options_series = pd.Series(options_bool)
286
+
287
+ if not df_descargado_stocks.empty:
288
+ df_descargado_stocks['Options'] = options_series
289
+ if not df_descargado_ETFs.empty:
290
+ df_descargado_ETFs['Options'] = options_series
291
+
292
+ # Exportación a Excel
293
+ if not df_descargado_stocks.empty or not df_descargado_ETFs.empty:
294
+ nuevo_archivo_path = 'resumen_datos_holding.xlsx'
295
+
296
+ # Copiar la plantilla descargada antes de escribir
297
+ try:
298
+ shutil.copyfile(local_template_path, nuevo_archivo_path)
299
+ except FileNotFoundError:
300
+ return None, f"Error: No se encontró la plantilla en la ruta: {local_template_path}"
301
+
302
+ # Escribir en el archivo copiado
303
+ with pd.ExcelWriter(nuevo_archivo_path, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer:
304
+ if not df_descargado_stocks.empty:
305
+ df_descargado_stocks.to_excel(writer, sheet_name='Stocks', index=True)
306
+ if not df_descargado_ETFs.empty:
307
+ df_descargado_ETFs.to_excel(writer, sheet_name='ETFs', index=True)
308
+
309
+ # Eliminar la plantilla temporal descargada para limpiar el entorno
310
+ os.remove(local_template_path)
311
+
312
+ return nuevo_archivo_path, "Archivo Excel generado con éxito."
313
+ else:
314
+ return None, "No se pudieron generar los datos para exportar."
315
+
316
+ except Exception as e:
317
+ return None, f"Error al exportar: {e}"
318
+
319
+
320
+