kaan1233/bistelligence-api
0
1import pandas as pd2import numpy as np3 4def calculate_correlation_matrix(stock_data, pf_df):5 """6 Portföydeki hisselerin son 1 yıllık günlük getirilerini kullanarak korelasyon matrisi oluşturur.7 stock_data: dict of DataFrames (from yfinance)8 pf_df: Portfolio dataframe containing 'ticker' column.9 """10 if pf_df is None or pf_df.empty:11 return None, "Korelasyon hesabı için portföyde hisse bulunamadı."12 13 tickers = pf_df['ticker'].tolist()14 if not tickers or len(tickers) < 2:15 return None, "Korelasyon hesabı için portföyde en az 2 hisse olmalıdır."16 17 # Fiyatları birleştir18 price_dict = {}19 for t in tickers:20 if t in stock_data:21 # Son 1 yıllık (yaklaşık 252 iş günü) veri22 df = stock_data[t].tail(252)23 price_dict[t] = df['Close']24 25 if len(price_dict) < 2:26 return None, "Yeterli fiyat verisi bulunamadı."27 28 prices_df = pd.DataFrame(price_dict)29 30 # Günlük getiriler31 returns = prices_df.pct_change().dropna()32 33 # Korelasyon Matrisi34 corr_matrix = returns.corr()35 36 return corr_matrix, None37 38def analyze_portfolio_risk(corr_matrix):39 """40 Korelasyon matrisine göre yüksek risk uyarısı üretir.41 Ortalama korelasyon 0.6'dan büyükse risklidir.42 """43 if corr_matrix is None:44 return []45 46 # Köşegen (1.0) değerlerini dahil etmemek için47 corr_values = corr_matrix.values48 n = corr_values.shape[0]49 if n < 2:50 return []51 52 # Üst üçgen değerleri53 upper_triangle = corr_values[np.triu_indices(n, k=1)]54 55 warnings = []56 avg_corr = np.mean(upper_triangle)57 if avg_corr > 0.6:58 warnings.append({59 "message": f"Portföyünüz yeterince çeşitli değil (Ortalama Korelasyon: {avg_corr:.2f}). Hisseler sürekli aynı yönde hareket ediyor, riskiniz yüksek.",60 "level": "high"61 })62 elif avg_corr > 0.4:63 warnings.append({64 "message": f"Portföyünüz kısmen çeşitli (Ortalama Korelasyon: {avg_corr:.2f}).",65 "level": "medium"66 })67 else:68 warnings.append({69 "message": f"Portföyünüz iyi çeşitlendirilmiş (Ortalama Korelasyon: {avg_corr:.2f}). Riskiniz dağıtılmış durumda.",70 "level": "low"71 })72 73 # En yüksek korelasyona sahip ikilileri bul74 max_corr = 075 pair = None76 for i in range(n):77 for j in range(i+1, n):78 if corr_matrix.iloc[i, j] > max_corr:79 max_corr = corr_matrix.iloc[i, j]80 pair = (corr_matrix.index[i], corr_matrix.columns[j])81 82 if max_corr > 0.8:83 warnings.append({84 "message": f"Dikkat: {pair[0]} ve {pair[1]} birbirine çok benzer hareket ediyor (Korelasyon: {max_corr:.2f}). İkisinden birinin ağırlığını azaltabilirsiniz.",85 "level": "high"86 })87 88 return warnings89 90def get_balancing_suggestions(stock_data, pf_df, all_tickers):91 """92 Tüm BİST hisselerini tarayarak portföyün geneliyle düşük veya negatif korelasyonlu hisse önerir.93 """94 if pf_df is None or pf_df.empty:95 return []96 97 pf_tickers = pf_df['ticker'].tolist()98 if not pf_tickers:99 return []100 101 # Portföy getirisi (eşit ağırlıklı varsayalım)102 pf_price_dict = {}103 for t in pf_tickers:104 if t in stock_data:105 pf_price_dict[t] = stock_data[t]['Close'].tail(252)106 107 if not pf_price_dict:108 return []109 110 pf_prices_df = pd.DataFrame(pf_price_dict)111 pf_returns = pf_prices_df.pct_change().dropna()112 pf_avg_return = pf_returns.mean(axis=1) # Portföyün ortalama günlük getirisi113 114 suggestions = []115 for t in all_tickers:116 if t in pf_tickers:117 continue118 if t in stock_data:119 t_prices = stock_data[t]['Close'].tail(252)120 t_returns = t_prices.pct_change().dropna()121 122 # Tarihleri eşleştir123 common_idx = pf_avg_return.index.intersection(t_returns.index)124 if len(common_idx) > 100:125 corr = pf_avg_return.loc[common_idx].corr(t_returns.loc[common_idx])126 # NaN check127 if not np.isnan(corr):128 suggestions.append({"ticker": t, "correlation": corr})129 130 # En düşük korelasyonlu (negatif veya sıfıra yakın) 3 hisseyi öner131 suggestions.sort(key=lambda x: x["correlation"])132 133 # Sadece ilk 3134 result = []135 for s in suggestions[:3]:136 result.append({137 "ticker": s["ticker"],138 "correlation": round(s["correlation"], 2),139 "reason": f"Portföyünüzle ters/düşük korelasyonlu (Korelasyon: {s['correlation']:.2f}). Düşüşlerde dengeleyici olabilir."140 })141 return result142 