import pandas as pd import yfinance as yf def fetch_pair_data(ticker_a: str, ticker_b: str, period: str = "5y") -> pd.DataFrame: """ Récupère les prix adjusted close des deux tickers via yfinance. Retourne un DataFrame avec colonnes [ticker_a, ticker_b], index = date. Gère les cas d'erreur : ticker invalide, données manquantes. Supprime les lignes avec NaN (jours où un seul ticker a coté). Parameters ---------- ticker_a : str Premier ticker (ex: "KO"). ticker_b : str Second ticker (ex: "PEP"). period : str Période historique à récupérer (ex: "3y", "5y", "10y"). Returns ------- pd.DataFrame DataFrame avec colonnes [ticker_a, ticker_b] et index DatetimeIndex. Raises ------ ValueError Si un ticker est invalide ou si les données sont insuffisantes. """ ticker_a = ticker_a.strip().upper() ticker_b = ticker_b.strip().upper() # Téléchargement des données via yfinance donnees_brutes = yf.download( tickers=[ticker_a, ticker_b], period=period, auto_adjust=True, progress=False, ) # Vérification que le téléchargement a fonctionné if donnees_brutes.empty: raise ValueError( f"Aucune donnée récupérée pour {ticker_a} et/ou {ticker_b}. " "Vérifiez que les tickers sont valides." ) # Extraction des prix de clôture prix_cloture = donnees_brutes["Close"] # Vérification que les deux tickers sont présents dans les colonnes colonnes_presentes = prix_cloture.columns.tolist() for ticker in [ticker_a, ticker_b]: if ticker not in colonnes_presentes: raise ValueError( f"Le ticker '{ticker}' n'a retourné aucune donnée. " "Vérifiez qu'il est valide sur Yahoo Finance." ) # Sélection et renommage des colonnes prix_paire = prix_cloture[[ticker_a, ticker_b]].copy() # Suppression des lignes avec des NaN (jours où un seul ticker a coté) nb_lignes_avant = len(prix_paire) prix_paire = prix_paire.dropna() nb_lignes_supprimees = nb_lignes_avant - len(prix_paire) if nb_lignes_supprimees > 0: print( f"Info : {nb_lignes_supprimees} lignes supprimées (données manquantes)." ) # Vérification qu'il reste assez de données pour une analyse significative nb_jours_minimum = 252 # ~1 an de trading if len(prix_paire) < nb_jours_minimum: raise ValueError( f"Seulement {len(prix_paire)} jours de données disponibles " f"(minimum requis : {nb_jours_minimum}). " "Essayez une période plus longue ou vérifiez les tickers." ) return prix_paire