molba2see commited on
Commit
579d747
·
verified ·
1 Parent(s): c82dbab

Update src/analyze_portfolio_risk.py

Browse files
Files changed (1) hide show
  1. src/analyze_portfolio_risk.py +82 -82
src/analyze_portfolio_risk.py CHANGED
@@ -1,83 +1,83 @@
1
- # compute_my_srisk.py
2
- # ==================================================
3
- # 💼 개인 포트폴리오 Srisk 계산기 (최종 안정화 버전)
4
- # ==================================================
5
- import pandas as pd
6
- import numpy as np
7
- import re
8
-
9
- # === 문자열에서 숫자만 추출 ===
10
- def extract_number(x):
11
- if isinstance(x, str):
12
- match = re.search(r"[-+]?\d*\.\d+|\d+", x)
13
- return float(match.group()) if match else np.nan
14
- return x
15
-
16
-
17
- # === Robust Z-score (IQR 기준 + 클리핑) ===
18
- def robust_zscore(val, series):
19
- series = series.dropna()
20
- q1, q3 = series.quantile([0.25, 0.75])
21
- iqr = q3 - q1
22
- med = series.median()
23
- if iqr == 0:
24
- return 0
25
- z = (val - med) / iqr
26
- return np.clip(z, -3, 3)
27
-
28
-
29
- # === Srisk 계산 함수 ===
30
- def compute_srisk_from_portfolio(portfolio, full_df):
31
- df = full_df[full_df["Ticker"].isin(portfolio.keys())].copy()
32
-
33
- # === 가중 평균 ===
34
- df["Weight"] = df["Ticker"].map(portfolio)
35
- sigma_p = np.average(df["Sigma"], weights=df["Weight"])
36
- mdd_p = np.average(df["MDD"], weights=df["Weight"])
37
- beta_p = np.average(df["Beta"], weights=df["Weight"])
38
-
39
- # === 섹터 기반 HHI ===
40
- sector_map = df.set_index("Ticker")["Sector"].to_dict()
41
- sector_weights = {}
42
- for ticker, w in portfolio.items():
43
- sector = sector_map.get(ticker, "Unknown")
44
- sector_weights[sector] = sector_weights.get(sector, 0) + w
45
- hhi_p = sum([w**2 for w in sector_weights.values()])
46
-
47
- # === 시장 전체 기준 robust Z-score 계산 ===
48
- S_sigma = robust_zscore(sigma_p, full_df["Sigma"]) # 변동성 ↑ → 위험 ↑
49
- S_mdd = -robust_zscore(abs(mdd_p), abs(full_df["MDD"])) # 낙폭 ↑ → 위험 ↑
50
- S_beta = robust_zscore(beta_p, full_df["Beta"]) # 민감도 ↑ → 위험 ↑
51
- S_hhi = robust_zscore(hhi_p, pd.Series([(1/len(portfolio))**2]*len(full_df)))
52
-
53
- # === Srisk 계산 (조정된 가중치)
54
- Srisk = 0.35*S_sigma + 0.25*S_mdd + 0.20*S_beta + 0.10*S_hhi
55
-
56
- # === 구간 분류
57
- if Srisk < 0.33:
58
- category = "SAFE"
59
- elif Srisk < 0.66:
60
- category = "NEUTRAL"
61
- else:
62
- category = "AGGRESSIVE"
63
-
64
- return {
65
- "Srisk": Srisk,
66
- "Category": category,
67
- "Sigma_p": sigma_p,
68
- "MDD_p": mdd_p,
69
- "Beta_p": beta_p,
70
- "HHI": hhi_p,
71
- "Sectors": sector_weights
72
- }
73
-
74
- def classify_investment_style(full_df, portfolio_list):
75
- for col in ["Sigma", "MDD", "Beta"]:
76
- full_df[col] = full_df[col].apply(extract_number)
77
-
78
- df_port = pd.DataFrame(portfolio_list)
79
- total_value = df_port['total_value'].sum()
80
- portfolio_weights = (df_port.groupby('ticker')['total_value'].sum() / total_value).to_dict()
81
-
82
- r = compute_srisk_from_portfolio(portfolio_weights, full_df)
83
  return r['Srisk'], r['Category']
 
1
+ # compute_my_srisk.py
2
+ # ==================================================
3
+ # 💼 개인 포트폴리오 Srisk 계산기 (최종 안정화 버전)
4
+ # ==================================================
5
+ import pandas as pd
6
+ import numpy as np
7
+ import re
8
+
9
+ # === 문자열에서 숫자만 추출 ===
10
+ def extract_number(x):
11
+ if isinstance(x, str):
12
+ match = re.search(r"[-+]?\d*\.\d+|\d+", x)
13
+ return float(match.group()) if match else np.nan
14
+ return x
15
+
16
+
17
+ # === Robust Z-score (IQR 기준 + 클리핑) ===
18
+ def robust_zscore(val, series):
19
+ series = series.dropna()
20
+ q1, q3 = series.quantile([0.25, 0.75])
21
+ iqr = q3 - q1
22
+ med = series.median()
23
+ if iqr == 0:
24
+ return 0
25
+ z = (val - med) / iqr
26
+ return np.clip(z, -3, 3)
27
+
28
+
29
+ # === Srisk 계산 함수 ===
30
+ def compute_srisk_from_portfolio(portfolio, full_df):
31
+ df = full_df[full_df["Ticker"].isin(portfolio.keys())].copy()
32
+
33
+ # === 가중 평균 ===
34
+ df["Weight"] = df["Ticker"].map(portfolio)
35
+ sigma_p = np.average(df["Sigma"], weights=df["Weight"])
36
+ mdd_p = np.average(df["MDD"], weights=df["Weight"])
37
+ beta_p = np.average(df["Beta"], weights=df["Weight"])
38
+
39
+ # === 섹터 기반 HHI ===
40
+ sector_map = df.set_index("Ticker")["Sector"].to_dict()
41
+ sector_weights = {}
42
+ for ticker, w in portfolio.items():
43
+ sector = sector_map.get(ticker, "Unknown")
44
+ sector_weights[sector] = sector_weights.get(sector, 0) + w
45
+ hhi_p = sum([w**2 for w in sector_weights.values()])
46
+
47
+ # === 시장 전체 기준 robust Z-score 계산 ===
48
+ S_sigma = robust_zscore(sigma_p, full_df["Sigma"]) # 변동성 ↑ → 위험 ↑
49
+ S_mdd = -robust_zscore(abs(mdd_p), abs(full_df["MDD"])) # 낙폭 ↑ → 위험 ↑
50
+ S_beta = robust_zscore(beta_p, full_df["Beta"]) # 민감도 ↑ → 위험 ↑
51
+ S_hhi = robust_zscore(hhi_p, pd.Series([(1/len(portfolio))**2]*len(full_df)))
52
+
53
+ # === Srisk 계산 (조정된 가중치)
54
+ Srisk = 0.35*S_sigma + 0.25*S_mdd + 0.20*S_beta + 0.10*S_hhi
55
+
56
+ # === 구간 분류
57
+ if Srisk < 0.33:
58
+ category = "SAFE"
59
+ elif Srisk < 0.66:
60
+ category = "NEUTRAL"
61
+ else:
62
+ category = "RISKY"
63
+
64
+ return {
65
+ "Srisk": Srisk,
66
+ "Category": category,
67
+ "Sigma_p": sigma_p,
68
+ "MDD_p": mdd_p,
69
+ "Beta_p": beta_p,
70
+ "HHI": hhi_p,
71
+ "Sectors": sector_weights
72
+ }
73
+
74
+ def classify_investment_style(full_df, portfolio_list):
75
+ for col in ["Sigma", "MDD", "Beta"]:
76
+ full_df[col] = full_df[col].apply(extract_number)
77
+
78
+ df_port = pd.DataFrame(portfolio_list)
79
+ total_value = df_port['total_value'].sum()
80
+ portfolio_weights = (df_port.groupby('ticker')['total_value'].sum() / total_value).to_dict()
81
+
82
+ r = compute_srisk_from_portfolio(portfolio_weights, full_df)
83
  return r['Srisk'], r['Category']