CoolFace
Apppublic

kaan1233/bistelligence-api

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
markowitz.py142 linesDownload Raw Back to root
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