Skip to content

Commit 806586a

Browse files
committed
fix: sanitize volume to prevent bigint overflow
1 parent 9fd3a77 commit 806586a

1 file changed

Lines changed: 51 additions & 3 deletions

File tree

src/yfinance_manager.py

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from datetime import datetime, timedelta
44
import pandas as pd
55
import yfinance as yf
6+
import numpy as np
67

78
from src.logger import get_logger
89

@@ -53,9 +54,56 @@ def _normalize_data(self, data: pd.DataFrame, tickers: List) -> List[Tuple]:
5354
self.logger.warning("Nessun dato ricevuto da yfinance (data è vuoto).")
5455
return all_data
5556

56-
df_flat = data.stack(level=0, future_stack=True).reset_index()
57-
df_flat['Date'] = df_flat['Date'].dt.date
58-
all_data = list(df_flat[['Ticker','Date','Open','High','Low','Close','Volume']].itertuples(index=False, name=None))
57+
# Gestione Multi-Index vs Single-Index
58+
# Se scarichiamo 1 solo ticker, yfinance non usa il MultiIndex sulle colonne.
59+
if isinstance(data.columns, pd.MultiIndex):
60+
df_flat = data.stack(level=0, future_stack=True).reset_index()
61+
else:
62+
# Caso singolo ticker: aggiungiamo la colonna Ticker manualmente
63+
df_flat = data.reset_index()
64+
df_flat['Ticker'] = tickers[0] if tickers else "UNKNOWN"
65+
66+
# Rinominiamo la colonna Date/Datetime se necessario
67+
if 'Date' in df_flat.columns:
68+
df_flat['date_col'] = df_flat['Date']
69+
elif 'Datetime' in df_flat.columns:
70+
df_flat['date_col'] = df_flat['Datetime']
71+
else:
72+
self.logger.error("Colonna data non trovata nel DataFrame.")
73+
return []
74+
75+
# Conversione e Pulizia Data
76+
df_flat['date_col'] = pd.to_datetime(df_flat['date_col']).dt.date
77+
78+
# --- SANITIZZAZIONE VOLUME (FIX BIGINT ERROR) ---
79+
# 1. Sostituisce NaN con 0
80+
df_flat['Volume'] = df_flat['Volume'].fillna(0)
81+
82+
# 2. Rimuove infiniti (+inf, -inf)
83+
import numpy as np # Assicurati di avere import numpy as np in alto
84+
df_flat = df_flat.replace([np.inf, -np.inf], 0)
85+
86+
# 3. Clamping (Taglio valori eccessivi)
87+
# Il max di Postgres BIGINT è ~9.22 * 10^18.
88+
# Impostiamo un limite sicuro (es. 10^15) che è comunque irraggiungibile per volumi reali.
89+
MAX_BIGINT_SAFE = 10**15
90+
df_flat['Volume'] = df_flat['Volume'].clip(upper=MAX_BIGINT_SAFE)
91+
92+
# 4. Conversione finale a Intero
93+
df_flat['Volume'] = df_flat['Volume'].astype(int)
94+
# ------------------------------------------------
95+
96+
# Selezione colonne finali
97+
# Assicuriamoci che l'ordine sia quello atteso dal DatabaseManager
98+
# (ticker, date, open, high, low, close, volume)
99+
try:
100+
target_df = df_flat[['Ticker', 'date_col', 'Open', 'High', 'Low', 'Close', 'Volume']]
101+
except KeyError as e:
102+
self.logger.error(f"Colonne mancanti nel DataFrame normalizzato: {e}")
103+
return []
104+
105+
# Conversione in lista di tuple (più veloce per psycopg)
106+
all_data = list(target_df.itertuples(index=False, name=None))
59107

60108
return all_data
61109

0 commit comments

Comments
 (0)