Showing
2 changed files
with
202 additions
and
9 deletions
| ... | @@ -5,19 +5,37 @@ import FinanceDataReader as fdr | ... | @@ -5,19 +5,37 @@ import FinanceDataReader as fdr |
| 5 | from scipy.optimize import minimize | 5 | from scipy.optimize import minimize |
| 6 | import json | 6 | import json |
| 7 | 7 | ||
| 8 | +#소숫점 표현 | ||
| 9 | +pd.options.display.float_format = '{:.3f}'.format | ||
| 10 | +np.set_printoptions(precision=3, suppress=True) | ||
| 11 | + | ||
| 8 | class c_Models: | 12 | class c_Models: |
| 9 | #Input 값으로, 자산 list, 사용자 포트폴리오 비중, 시작일, 마지막일 | 13 | #Input 값으로, 자산 list, 사용자 포트폴리오 비중, 시작일, 마지막일 |
| 10 | def __init__(self, assets, assets_w, start, end): | 14 | def __init__(self, assets, assets_w, start, end): |
| 11 | self.result = None | 15 | self.result = None |
| 12 | self.graph = None | 16 | self.graph = None |
| 17 | + | ||
| 18 | + stocks = pd.read_csv('stockcodename.csv', index_col=0) | ||
| 19 | + symbol = '' | ||
| 20 | + self.asset_name = assets[:] | ||
| 21 | + for k in range(len(assets)): | ||
| 22 | + for i in enumerate(stocks.Name): | ||
| 23 | + if i[1] == assets[k]: | ||
| 24 | + assets[k] = (stocks.iloc[i[0]].Symbol) | ||
| 25 | + break | ||
| 13 | 26 | ||
| 14 | data = pd.DataFrame() | 27 | data = pd.DataFrame() |
| 15 | # 전체 자산 data들을 가지고 온 후, 정리함 | 28 | # 전체 자산 data들을 가지고 온 후, 정리함 |
| 29 | + | ||
| 16 | for asset in assets: #total_list: | 30 | for asset in assets: #total_list: |
| 17 | tmp = fdr.DataReader(asset,start,end).Close | 31 | tmp = fdr.DataReader(asset,start,end).Close |
| 18 | - tmp.rename(columns={'Close': asset}, inplace=True) | 32 | + if len(data) == 0 : |
| 19 | - data = pd.concat([data, tmp], axis=1) | 33 | + data = tmp |
| 20 | - | 34 | + else: |
| 35 | + data = pd.concat([data,tmp], axis=1) | ||
| 36 | + | ||
| 37 | + data.columns = self.asset_name | ||
| 38 | + | ||
| 21 | if data.isnull().values.any() == True: #불러온 data에 오류가 있다면 | 39 | if data.isnull().values.any() == True: #불러온 data에 오류가 있다면 |
| 22 | return "No Data",'' | 40 | return "No Data",'' |
| 23 | 41 | ||
| ... | @@ -39,9 +57,9 @@ class c_Models: | ... | @@ -39,9 +57,9 @@ class c_Models: |
| 39 | constraints = ({'type':'eq', 'fun':lambda x: np.sum(x)-1}) | 57 | constraints = ({'type':'eq', 'fun':lambda x: np.sum(x)-1}) |
| 40 | bd = ((0,1),) * n_assets | 58 | bd = ((0,1),) * n_assets |
| 41 | #cov = data.cov() * 12 | 59 | #cov = data.cov() * 12 |
| 42 | - | ||
| 43 | gmv = minimize(fun, w0, method = 'SLSQP', constraints=constraints, bounds=bd) | 60 | gmv = minimize(fun, w0, method = 'SLSQP', constraints=constraints, bounds=bd) |
| 44 | - return gmv.x | 61 | + result = dict(zip(self.asset_name, np.round(gmv.x,3))) |
| 62 | + return result | ||
| 45 | 63 | ||
| 46 | #Max Sharp ratio : risk free rate은 0.8%로 지정했고, | 64 | #Max Sharp ratio : risk free rate은 0.8%로 지정했고, |
| 47 | def ms_opt(self): | 65 | def ms_opt(self): |
| ... | @@ -51,7 +69,8 @@ class c_Models: | ... | @@ -51,7 +69,8 @@ class c_Models: |
| 51 | bd = ((0,1),) * n_assets | 69 | bd = ((0,1),) * n_assets |
| 52 | constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) | 70 | constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) |
| 53 | maxsharp = minimize(fun, w0, method ='SLSQP', constraints=constraints, bounds=bd) | 71 | maxsharp = minimize(fun, w0, method ='SLSQP', constraints=constraints, bounds=bd) |
| 54 | - return maxsharp.x | 72 | + result = dict(zip(self.asset_name, np.round(maxsharp.x,3))) |
| 73 | + return result | ||
| 55 | 74 | ||
| 56 | def rp_opt(self): | 75 | def rp_opt(self): |
| 57 | def RC(cov, w): | 76 | def RC(cov, w): |
| ... | @@ -80,5 +99,5 @@ class c_Models: | ... | @@ -80,5 +99,5 @@ class c_Models: |
| 80 | bd = ((0,1),) * n_assets | 99 | bd = ((0,1),) * n_assets |
| 81 | 100 | ||
| 82 | rp = minimize(RP_objective, w0, constraints=constraints, bounds = bd, method='SLSQP') | 101 | rp = minimize(RP_objective, w0, constraints=constraints, bounds = bd, method='SLSQP') |
| 83 | - | 102 | + result = dict(zip(self.asset_name, np.round(rp.x,3))) |
| 84 | - return rp.x #, RC(self.cov, rp.x) | 103 | + return result #, RC(self.cov, rp.x) |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | "cells": [ | 2 | "cells": [ |
| 3 | { | 3 | { |
| 4 | "cell_type": "code", | 4 | "cell_type": "code", |
| 5 | - "execution_count": 1, | 5 | + "execution_count": 2, |
| 6 | "metadata": {}, | 6 | "metadata": {}, |
| 7 | "outputs": [], | 7 | "outputs": [], |
| 8 | "source": [ | 8 | "source": [ |
| ... | @@ -66,6 +66,180 @@ | ... | @@ -66,6 +66,180 @@ |
| 66 | "stocks = fdr.StockListing('KOSPI') # 코스피\n", | 66 | "stocks = fdr.StockListing('KOSPI') # 코스피\n", |
| 67 | "stocks.to_csv(\"stockcodename.csv\",mode='w', encoding='utf-8-sig')" | 67 | "stocks.to_csv(\"stockcodename.csv\",mode='w', encoding='utf-8-sig')" |
| 68 | ] | 68 | ] |
| 69 | + }, | ||
| 70 | + { | ||
| 71 | + "cell_type": "code", | ||
| 72 | + "execution_count": 123, | ||
| 73 | + "metadata": {}, | ||
| 74 | + "outputs": [], | ||
| 75 | + "source": [ | ||
| 76 | + "import datetime\n", | ||
| 77 | + "import pandas as pd\n", | ||
| 78 | + "import numpy as np\n", | ||
| 79 | + "import FinanceDataReader as fdr\n", | ||
| 80 | + "from scipy.optimize import minimize\n", | ||
| 81 | + "import json\n", | ||
| 82 | + "\n", | ||
| 83 | + "#소숫점 표현\n", | ||
| 84 | + "pd.options.display.float_format = '{:.3f}'.format\n", | ||
| 85 | + "np.set_printoptions(precision=3, suppress=True)\n", | ||
| 86 | + "\n", | ||
| 87 | + "class c_Models:\n", | ||
| 88 | + " #Input 값으로, 자산 list, 사용자 포트폴리오 비중, 시작일, 마지막일\n", | ||
| 89 | + " def __init__(self, assets, assets_w, start, end):\n", | ||
| 90 | + " self.result = None\n", | ||
| 91 | + " self.graph = None\n", | ||
| 92 | + " \n", | ||
| 93 | + " stocks = pd.read_csv('stockcodename.csv', index_col=0)\n", | ||
| 94 | + " symbol = ''\n", | ||
| 95 | + " self.asset_name = assets[:]\n", | ||
| 96 | + " for k in range(len(assets)):\n", | ||
| 97 | + " for i in enumerate(stocks.Name):\n", | ||
| 98 | + " if i[1] == assets[k]:\n", | ||
| 99 | + " assets[k] = (stocks.iloc[i[0]].Symbol)\n", | ||
| 100 | + " break\n", | ||
| 101 | + "\n", | ||
| 102 | + " data = pd.DataFrame()\n", | ||
| 103 | + " # 전체 자산 data들을 가지고 온 후, 정리함\n", | ||
| 104 | + " \n", | ||
| 105 | + " for asset in assets: #total_list:\n", | ||
| 106 | + " tmp = fdr.DataReader(asset,start,end).Close\n", | ||
| 107 | + " if len(data) == 0 :\n", | ||
| 108 | + " data = tmp\n", | ||
| 109 | + " else:\n", | ||
| 110 | + " data = pd.concat([data,tmp], axis=1)\n", | ||
| 111 | + " \n", | ||
| 112 | + " data.columns = self.asset_name\n", | ||
| 113 | + " \n", | ||
| 114 | + " if data.isnull().values.any() == True: #불러온 data에 오류가 있다면\n", | ||
| 115 | + " return \"No Data\",''\n", | ||
| 116 | + "\n", | ||
| 117 | + " else:\n", | ||
| 118 | + " data = data.resample('M').mean() #일별 데이터를 월별 데이터로 만들어줌\n", | ||
| 119 | + " data = data.pct_change() #월별 주가 데이터를 이용해 수익률 데이터로 변환\n", | ||
| 120 | + " data.dropna(inplace=True) #결측치 제외(첫 row)\n", | ||
| 121 | + "\n", | ||
| 122 | + " self.data = data\n", | ||
| 123 | + " self.assets_w = assets_w\n", | ||
| 124 | + " self.mu = data.mean() * 12\n", | ||
| 125 | + " self.cov = data.cov() * 12\n", | ||
| 126 | + "\n", | ||
| 127 | + " #GMV 최적화 : 제약 조건은 비중합=1, 공매도 불가능\n", | ||
| 128 | + " def gmv_opt(self):\n", | ||
| 129 | + " n_assets = len(self.data.columns)\n", | ||
| 130 | + " w0 = np.ones(n_assets) / n_assets\n", | ||
| 131 | + " fun = lambda w: np.dot(w.T, np.dot(self.cov, w))\n", | ||
| 132 | + " constraints = ({'type':'eq', 'fun':lambda x: np.sum(x)-1})\n", | ||
| 133 | + " bd = ((0,1),) * n_assets\n", | ||
| 134 | + " #cov = data.cov() * 12\n", | ||
| 135 | + " gmv = minimize(fun, w0, method = 'SLSQP', constraints=constraints, bounds=bd)\n", | ||
| 136 | + " result = dict(zip(self.asset_name, np.round(gmv.x,3)))\n", | ||
| 137 | + " return result\n", | ||
| 138 | + " \n", | ||
| 139 | + " #Max Sharp ratio : risk free rate은 0.8%로 지정했고, \n", | ||
| 140 | + " def ms_opt(self):\n", | ||
| 141 | + " n_assets = len(self.data.columns)\n", | ||
| 142 | + " w0 = np.ones(n_assets) / n_assets\n", | ||
| 143 | + " fun = lambda w: -(np.dot(w.T, self.mu) - 0.008) / np.sqrt(np.dot(w.T, np.dot(self.cov, w)))\n", | ||
| 144 | + " bd = ((0,1),) * n_assets \n", | ||
| 145 | + " constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})\n", | ||
| 146 | + " maxsharp = minimize(fun, w0, method ='SLSQP', constraints=constraints, bounds=bd)\n", | ||
| 147 | + " result = dict(zip(self.asset_name, np.round(maxsharp.x,3)))\n", | ||
| 148 | + " return result\n", | ||
| 149 | + " \n", | ||
| 150 | + " def rp_opt(self):\n", | ||
| 151 | + " def RC(cov, w):\n", | ||
| 152 | + " pfo_std = np.sqrt(np.dot(w.T, np.dot(self.cov, w)))\n", | ||
| 153 | + " mrc = 1/pfo_std * (np.dot(self.cov, w))\n", | ||
| 154 | + " rc = mrc * w\n", | ||
| 155 | + " rc = rc / rc.sum()\n", | ||
| 156 | + " return rc\n", | ||
| 157 | + " \n", | ||
| 158 | + " \n", | ||
| 159 | + " def RP_objective(x):\n", | ||
| 160 | + " pfo_std = np.sqrt(np.dot(x.T, np.dot(self.cov, x)))\n", | ||
| 161 | + " mrc = 1/pfo_std * (np.dot(self.cov, x))\n", | ||
| 162 | + " rc = mrc * x\n", | ||
| 163 | + " rc = rc / rc.sum()\n", | ||
| 164 | + "\n", | ||
| 165 | + " a = np.reshape(rc, (len(rc),1))\n", | ||
| 166 | + " differs = a - a.T\n", | ||
| 167 | + " objective = np.sum(np.square(differs))\n", | ||
| 168 | + "\n", | ||
| 169 | + " return objective \n", | ||
| 170 | + " \n", | ||
| 171 | + " n_assets = len(self.data.columns)\n", | ||
| 172 | + " w0 = np.ones(n_assets) / n_assets\n", | ||
| 173 | + " constraints = [{'type':'eq', 'fun': lambda x: np.sum(x) -1}]\n", | ||
| 174 | + " bd = ((0,1),) * n_assets\n", | ||
| 175 | + "\n", | ||
| 176 | + " rp = minimize(RP_objective, w0, constraints=constraints, bounds = bd, method='SLSQP')\n", | ||
| 177 | + " result = dict(zip(self.asset_name, np.round(rp.x,3)))\n", | ||
| 178 | + " return result #, RC(self.cov, rp.x)" | ||
| 179 | + ] | ||
| 180 | + }, | ||
| 181 | + { | ||
| 182 | + "cell_type": "code", | ||
| 183 | + "execution_count": 122, | ||
| 184 | + "metadata": {}, | ||
| 185 | + "outputs": [ | ||
| 186 | + { | ||
| 187 | + "data": { | ||
| 188 | + "text/plain": [ | ||
| 189 | + "{'삼성전자': 0.727, 'LG전자': 0.0, '카카오': 0.273}" | ||
| 190 | + ] | ||
| 191 | + }, | ||
| 192 | + "execution_count": 122, | ||
| 193 | + "metadata": {}, | ||
| 194 | + "output_type": "execute_result" | ||
| 195 | + } | ||
| 196 | + ], | ||
| 197 | + "source": [ | ||
| 198 | + "#gmv 포트폴리오 -> 해당 종목을 각각 몇 퍼센트로 투자해야 위험이 제일 적은가\n", | ||
| 199 | + "c_Models(['삼성전자','LG전자','카카오'],[0,0,0],'2015-01-01','2021-04-01').gmv_opt()" | ||
| 200 | + ] | ||
| 201 | + }, | ||
| 202 | + { | ||
| 203 | + "cell_type": "code", | ||
| 204 | + "execution_count": 124, | ||
| 205 | + "metadata": {}, | ||
| 206 | + "outputs": [ | ||
| 207 | + { | ||
| 208 | + "data": { | ||
| 209 | + "text/plain": [ | ||
| 210 | + "{'삼성전자': 0.674, 'LG전자': 0.0, '카카오': 0.326}" | ||
| 211 | + ] | ||
| 212 | + }, | ||
| 213 | + "execution_count": 124, | ||
| 214 | + "metadata": {}, | ||
| 215 | + "output_type": "execute_result" | ||
| 216 | + } | ||
| 217 | + ], | ||
| 218 | + "source": [ | ||
| 219 | + "#maxsharp ratio -> 위험대비 수익률이 제일 좋은 포트폴리오 비중 , 즉 가성비가 좋다\n", | ||
| 220 | + "c_Models(['삼성전자','LG전자','카카오'],[0,0,0],'2015-01-01','2021-04-01').ms_opt()" | ||
| 221 | + ] | ||
| 222 | + }, | ||
| 223 | + { | ||
| 224 | + "cell_type": "code", | ||
| 225 | + "execution_count": 125, | ||
| 226 | + "metadata": {}, | ||
| 227 | + "outputs": [ | ||
| 228 | + { | ||
| 229 | + "data": { | ||
| 230 | + "text/plain": [ | ||
| 231 | + "{'삼성전자': 0.443, 'LG전자': 0.238, '카카오': 0.319}" | ||
| 232 | + ] | ||
| 233 | + }, | ||
| 234 | + "execution_count": 125, | ||
| 235 | + "metadata": {}, | ||
| 236 | + "output_type": "execute_result" | ||
| 237 | + } | ||
| 238 | + ], | ||
| 239 | + "source": [ | ||
| 240 | + "#risk parity -> 포트폴리오에 대한 자산 위험 비중을 동일하게 조정, 즉 삼전,lg,카카오의 포트폴리오 위험 기여도를 0.33으로 하게 만드는 비중\n", | ||
| 241 | + "c_Models(['삼성전자','LG전자','카카오'],[0,0,0],'2015-01-01','2021-04-01').rp_opt()" | ||
| 242 | + ] | ||
| 69 | } | 243 | } |
| 70 | ], | 244 | ], |
| 71 | "metadata": { | 245 | "metadata": { | ... | ... |
-
Please register or login to post a comment