박하늘

All functions update

...@@ -12,6 +12,9 @@ const bodyParser = require('body-parser'); ...@@ -12,6 +12,9 @@ const bodyParser = require('body-parser');
12 var app = express(); 12 var app = express();
13 13
14 14
15 +var holder1 = '';
16 +var holder2 = '';
17 +
15 18
16 19
17 app.use(bodyParser.json()); 20 app.use(bodyParser.json());
...@@ -25,20 +28,39 @@ app.post('/hook', function (req, res) { ...@@ -25,20 +28,39 @@ app.post('/hook', function (req, res) {
25 console.log('[request]', req.body); 28 console.log('[request]', req.body);
26 console.log('[request source] ', eventObj.source); 29 console.log('[request source] ', eventObj.source);
27 console.log('[request message]', eventObj.message); 30 console.log('[request message]', eventObj.message);
31 + if(eventObj.type == 'postback')
32 + {
33 + if(eventObj.postback.data == 'action=datetemp&selectId=1')
34 + {
35 + console.log("optimizer 실행")
36 + app.use('/simages', express.static(__dirname + '/src'));
37 + optimizer(eventObj.replyToken, holder1, holder2, eventObj.postback.params.date)
38 + app.use('/simages', express.static(__dirname + '/src'));
39 + }
40 + }
41 + else
42 + {
43 + if(eventObj.message.text.indexOf(' ') != -1)
44 + {
45 + date(eventObj.replyToken, eventObj.message.text)
46 + }
47 + else
48 + {
49 + basicinform(eventObj.replyToken, eventObj.message.text)
50 + }
28 51
29 - basicinform(eventObj.replyToken, eventObj.message.text) 52 + }
53 +
30 54
31 res.sendStatus(200); 55 res.sendStatus(200);
32 56
33 }); 57 });
34 58
35 function basicinform(replyToken, message) { 59 function basicinform(replyToken, message) {
36 -
37 var pystring; 60 var pystring;
38 const spawn = require("child_process").spawn; 61 const spawn = require("child_process").spawn;
39 const process = spawn("python", ["basic.py", message]); 62 const process = spawn("python", ["basic.py", message]);
40 const Callback = (data) => { 63 const Callback = (data) => {
41 - console.log("Data :", data.toString());
42 pystring = data.toString(); 64 pystring = data.toString();
43 if(pystring[0] == '1') 65 if(pystring[0] == '1')
44 { 66 {
...@@ -113,11 +135,133 @@ function basicinform(replyToken, message) { ...@@ -113,11 +135,133 @@ function basicinform(replyToken, message) {
113 console.log(body) 135 console.log(body)
114 }); 136 });
115 } 137 }
116 - 138 + };
139 + process.stdout.on("data", Callback);
140 +}
117 141
142 +function optimizer(replyToken, stock1, stock2, sdate) {
143 + sdate = sdate.toString();
144 + console.log(typeof(stock1), typeof(stock2), typeof(sdate))
145 + console.log(stock1, stock2, sdate)
146 + const spawn = require("child_process").spawn;
147 + const process = spawn("python", ["optimizer.py", stock1, stock2, sdate]);
148 + const Callback = (data) => {
149 + console.log(stock1, stock2, sdate)
150 + request.post(
151 + {
152 + url: TARGET_URL,
153 + headers: {
154 + 'Authorization': `Bearer ${TOKEN}`
155 + },
156 + json: {
157 + "replyToken":replyToken,
158 + "messages":[
159 + {
160 + "type":"text",
161 + "text":'조회하신 ' + holder1 +', ' + holder2 + '의 백테스트 결과입니다.'
162 + },
163 + {
164 + "type":"image",
165 + "originalContentUrl": "https://2017103989.oss2021.tk:23023/simages/test.png",
166 + "previewImageUrl": "https://2017103989.oss2021.tk:23023/simages/test.png"
167 + }
168 + ]
169 + }
170 + },(error, response, body) => {
171 + console.log(body)
172 + });
173 + }
174 + process.stdout.on("data", Callback);
175 +}
176 +
177 +function date(replyToken, message) {
178 + var holder = message.split(' ')
179 + holder1 = holder[0]
180 + holder2 = holder[1]
181 + var today = new Date();
182 + var year = today.getFullYear();
183 + var month = today.getMonth() + 1;
184 + var date = today.getDate();
185 + if(month < 10)
186 + {
187 + month = '0'+ month
188 + }
189 + if(date < 10)
190 + {
191 + date = '0'+ date
192 + }
193 + var stoday = year + '-' + month + '-' + date;
118 194
195 +
196 + const messageObject = {
197 + "type": "template",
198 + "altText": "this is a buttons template",
199 + "template": {
200 + "type": "buttons",
201 + "title": "조회하실 날짜를 선택하세요.",
202 + "text": "선택하신 날짜에서 현재(오늘)까지 조회됩니다.",
203 + "actions": [
204 + {
205 + "type": "datetimepicker",
206 + "label": "날짜 선택",
207 + "mode": "date",
208 + "initial":"2020-01-01",
209 + "max":stoday,
210 + "min":"2010-01-01",
211 + "data": "action=datetemp&selectId=1"
212 + },
213 + {
214 + "type": "postback",
215 + "label": "처음부터 다시할래요",
216 + "data": "action=cancel&selectId=2"
217 + },
218 + ]
219 + }
119 }; 220 };
120 - process.stdout.on("data", Callback); 221 + request.post(
222 + {
223 + url: TARGET_URL,
224 + headers: {
225 + 'Authorization': `Bearer ${TOKEN}`
226 + },
227 + json: {
228 + "replyToken":replyToken,
229 + "messages":[
230 + // {
231 + // "type":"text",
232 + // "text":'조회하실 날짜를 선택하세요. 선택하신 날짜에서 현재까지 조회됩니다.',
233 + // "quickReply": {
234 + // "items": [
235 + // {
236 + // "type": "action",
237 + // "action": {
238 + // "type": "datetimepicker",
239 + // "label":"날짜 선택하기",
240 + // "data":"storeId=12345",
241 + // "mode":"date",
242 + // "initial":"2015-01-01",
243 + // "max":stoday,
244 + // "min":"2010-01-01"
245 + // }
246 + // }
247 + // ]
248 + // }
249 + // },
250 + // {
251 + // "type":"text",
252 + // "text":req.body.postback.params
253 + // }
254 + messageObject
255 + ]
256 +
257 +
258 + }
259 + },(error, response, body) => {
260 + console.log(body)
261 + });
262 +
263 +
264 +
121 } 265 }
122 266
123 try { 267 try {
...@@ -134,4 +278,4 @@ try { ...@@ -134,4 +278,4 @@ try {
134 console.log('[HTTPS] HTTPS 오류가 발생하였습니다. HTTPS 서버는 실행되지 않습니다.'); 278 console.log('[HTTPS] HTTPS 오류가 발생하였습니다. HTTPS 서버는 실행되지 않습니다.');
135 console.log(error); 279 console.log(error);
136 } 280 }
137 - 281 +
......
...@@ -10,7 +10,8 @@ def get_matches(query, choices, limit=3): ...@@ -10,7 +10,8 @@ def get_matches(query, choices, limit=3):
10 10
11 11
12 def basicinform(input): 12 def basicinform(input):
13 - stocks = pd.read_csv('stockcodename.csv', names=['Symbol', 'Market', 'Name', 'Sector', 'Industry', 'ListingDate', 'SettleMonth', 'Represetitive', 'HomePage', 'Region'], index_col=0) 13 + stocks = pd.read_csv('stockcodename.csv', names=['Symbol', 'Market', 'Name'
14 + , 'Sector', 'Industry', 'ListingDate', 'SettleMonth', 'Represetitive', 'HomePage', 'Region'], index_col=0)
14 symbol = '' 15 symbol = ''
15 16
16 for i in enumerate(stocks.Name): 17 for i in enumerate(stocks.Name):
...@@ -23,8 +24,8 @@ def basicinform(input): ...@@ -23,8 +24,8 @@ def basicinform(input):
23 cand = '' 24 cand = ''
24 for i in fuzzy: 25 for i in fuzzy:
25 cand += i[0] 26 cand += i[0]
26 - cand += " " 27 + cand += "\n"
27 - cand += "중 찾는게 있으신가요? \n다시 입력해주세요." 28 + cand += "중 찾는게 있으신가요? 다시 입력해주세요."
28 return cand 29 return cand
29 30
30 df = fdr.DataReader(symbol) 31 df = fdr.DataReader(symbol)
...@@ -33,15 +34,19 @@ def basicinform(input): ...@@ -33,15 +34,19 @@ def basicinform(input):
33 price = df.Close.iloc[-1] 34 price = df.Close.iloc[-1]
34 ror = ror_df[-1] 35 ror = ror_df[-1]
35 36
36 - value = { 37 + ror = round(ror, 4)
37 - "현재가": price, 38 + ror = ror * 100
38 - "거래랑": volume, 39 + value = ''
39 - "전일 대비 수익률:": ror 40 + value = "1현재가: " + str(price) + "원\n거래랑: " + str(volume) + "건\n전일대비: " + str(ror) + "%"
40 - } 41 + # value = {
42 + # "현재가": price,
43 + # "거래랑": volume,
44 + # "전일 대비 수익률:": ror
45 + # }
41 return value 46 return value
42 47
43 48
44 -# print(basicinform('신라호텔')) 49 +# print(basicinform('호텔신라'))
45 50
46 args = sys.argv 51 args = sys.argv
47 print(basicinform(args[1])) 52 print(basicinform(args[1]))
......
1 +import datetime
2 +import pandas as pd
3 +import numpy as np
4 +import FinanceDataReader as fdr
5 +from scipy.optimize import minimize
6 +import json
7 +from datetime import date
8 +import math
9 +import itertools as it
10 +import operator
11 +from datetime import datetime
12 +from scipy import stats
13 +from scipy.stats import norm
14 +from dateutil import rrule
15 +from calendar import monthrange
16 +from dateutil.relativedelta import relativedelta
17 +from ast import literal_eval
18 +from matplotlib import pyplot as plt
19 +import numpy as np
20 +import matplotlib.ticker as ticker
21 +import sys
22 +
23 +
24 +#소숫점 표현
25 +pd.options.display.float_format = '{:.3f}'.format
26 +np.set_printoptions(precision=3, suppress=True)
27 +
28 +class c_Models:
29 + #Input 값으로, 자산 list, 사용자 포트폴리오 비중, 시작일, 마지막일
30 + def __init__(self, assets, assets_w, start, end):
31 + self.result = None
32 + self.graph = None
33 +
34 + stocks = pd.read_csv('stockcodename.csv', index_col=0)
35 + symbol = ''
36 + self.asset_name = assets[:]
37 + for k in range(len(assets)):
38 + for i in enumerate(stocks.Name):
39 + if i[1] == assets[k]:
40 + assets[k] = (stocks.iloc[i[0]].Symbol)
41 + break
42 +
43 + data = pd.DataFrame()
44 + # 전체 자산 data들을 가지고 온 후, 정리함
45 +
46 + for asset in assets: #total_list:
47 + tmp = fdr.DataReader(asset,start,end).Close
48 + if len(data) == 0 :
49 + data = tmp
50 + else:
51 + data = pd.concat([data,tmp], axis=1)
52 +
53 + data.columns = self.asset_name
54 +
55 + if data.isnull().values.any() == True: #불러온 data에 오류가 있다면
56 + return "No Data",''
57 +
58 + else:
59 + data = data.resample('M').mean() #일별 데이터를 월별 데이터로 만들어줌
60 + data = data.pct_change() #월별 주가 데이터를 이용해 수익률 데이터로 변환
61 + data.dropna(inplace=True) #결측치 제외(첫 row)
62 +
63 + self.data = data
64 + self.assets_w = assets_w
65 + self.mu = data.mean() * 12
66 + self.cov = data.cov() * 12
67 +
68 + #GMV 최적화 : 제약 조건은 비중합=1, 공매도 불가능
69 + def gmv_opt(self):
70 + n_assets = len(self.data.columns)
71 + w0 = np.ones(n_assets) / n_assets
72 + fun = lambda w: np.dot(w.T, np.dot(self.cov, w))
73 + constraints = ({'type':'eq', 'fun':lambda x: np.sum(x)-1})
74 + bd = ((0,1),) * n_assets
75 + #cov = data.cov() * 12
76 + gmv = minimize(fun, w0, method = 'SLSQP', constraints=constraints, bounds=bd)
77 + result = dict(zip(self.asset_name, np.round(gmv.x,3)))
78 + return result
79 +
80 + #Max Sharp ratio : risk free rate은 0.8%로 지정했고,
81 + def ms_opt(self):
82 + n_assets = len(self.data.columns)
83 + w0 = np.ones(n_assets) / n_assets
84 + fun = lambda w: -(np.dot(w.T, self.mu) - 0.008) / np.sqrt(np.dot(w.T, np.dot(self.cov, w)))
85 + bd = ((0,1),) * n_assets
86 + constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
87 + maxsharp = minimize(fun, w0, method ='SLSQP', constraints=constraints, bounds=bd)
88 + result = dict(zip(self.asset_name, np.round(maxsharp.x,3)))
89 + return result
90 +
91 + def rp_opt(self):
92 + def RC(cov, w):
93 + pfo_std = np.sqrt(np.dot(w.T, np.dot(self.cov, w)))
94 + mrc = 1/pfo_std * (np.dot(self.cov, w))
95 + rc = mrc * w
96 + rc = rc / rc.sum()
97 + return rc
98 +
99 +
100 + def RP_objective(x):
101 + pfo_std = np.sqrt(np.dot(x.T, np.dot(self.cov, x)))
102 + mrc = 1/pfo_std * (np.dot(self.cov, x))
103 + rc = mrc * x
104 + rc = rc / rc.sum()
105 +
106 + a = np.reshape(rc, (len(rc),1))
107 + differs = a - a.T
108 + objective = np.sum(np.square(differs))
109 +
110 + return objective
111 +
112 + n_assets = len(self.data.columns)
113 + w0 = np.ones(n_assets) / n_assets
114 + constraints = [{'type':'eq', 'fun': lambda x: np.sum(x) -1}]
115 + bd = ((0,1),) * n_assets
116 +
117 + rp = minimize(RP_objective, w0, constraints=constraints, bounds = bd, method='SLSQP')
118 + result = dict(zip(self.asset_name, np.round(rp.x,3)))
119 + return result #, RC(self.cov, rp.x)
120 +
121 + def plotting(self):
122 + wt_gmv = np.asarray(list(self.gmv_opt().values()))
123 + wt_ms = np.asarray(list(self.ms_opt().values()))
124 + wt_rp = np.asarray(list(self.rp_opt().values()))
125 +
126 + ret_gmv = np.dot(wt_gmv, self.mu)
127 + ret_ms = np.dot(wt_ms, self.mu)
128 + ret_rp = np.dot(wt_rp, self.mu)
129 + vol_gmv = np.sqrt(np.dot(wt_gmv.T, np.dot(self.cov, wt_gmv)))
130 + vol_ms = np.sqrt(np.dot(wt_ms.T, np.dot(self.cov, wt_ms)))
131 + vol_rp = np.sqrt(np.dot(wt_rp.T, np.dot(self.cov, wt_rp)))
132 +
133 + wt_gmv = wt_gmv.tolist()
134 + wt_ms = wt_ms.tolist()
135 + wt_rp = wt_rp.tolist()
136 +
137 + user_ret = np.dot(self.assets_w, self.mu)
138 + user_risk = np.sqrt(np.dot(self.assets_w, np.dot(self.cov, self.assets_w)))
139 +
140 + weights = {'gmv': wt_gmv, "ms" : wt_ms, "rp": wt_rp}
141 +
142 + #rec_rs = recommended_asset()
143 +
144 + trets = np.linspace(ret_gmv, max(self.mu), 30) # 30개 짜르기
145 + tvols = []
146 +
147 + efpoints = dict()
148 + for i, tret in enumerate(trets): #이 개별 return마다 최소 risk 찾기
149 + n_assets = len(self.data.columns)
150 + w0 = np.ones(n_assets) / n_assets
151 + fun = lambda w: np.dot(w.T ,np.dot(self.cov, w))
152 + constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
153 + {'type': 'ineq', 'fun': lambda x: np.dot(x, self.mu) - tret}]
154 + #{'type': 'ineq', 'fun': lambda x: x}]
155 + bd = ((0,1),) * n_assets
156 +
157 + minvol = minimize(fun, w0, method='SLSQP',bounds = bd, constraints=constraints)
158 + tvols.append(np.sqrt(np.dot(minvol.x, np.dot(self.cov, minvol.x))))
159 +
160 + pnumber = '{}point'.format(i+1)
161 + efpoints[pnumber] = minvol.x.tolist()
162 +
163 + if self.data.shape[0] <= 1:
164 + error = '기간에러'
165 + return error,1,1
166 + else:
167 + ret_vol = {"GMV": [vol_gmv, ret_gmv],"MaxSharp": [vol_ms, ret_ms],"RiskParity": [vol_rp, ret_rp], "Trets" : trets.tolist(), "Tvols": tvols, "User" : [user_risk,user_ret]} #, "Recommended" : rec_rs}
168 + return ret_vol, json.dumps(efpoints), json.dumps(weights)
169 +
170 +
171 +class back_test:
172 + # 단순 일별수익률의 평균을 *365하여 연간수익률을 산출
173 + def __init__(self):
174 + self.test = 0
175 +
176 + def Arithmetic_Mean_Annual(self,ret):
177 + month_return = np.mean(ret)
178 + return (month_return*252)
179 +
180 + # 기간중 투자했을때 하락할 수 있는 비율
181 + def dd(self,ret):
182 + cum_ret = (1 + ret).cumprod()
183 + max_drawdown = 0
184 + max_ret = 1
185 + dd_list = []
186 + c = 0
187 + for ix_ret in cum_ret.values:
188 + if max_ret < ix_ret:
189 + max_ret = ix_ret
190 + dd_list.append((ix_ret - max_ret) / max_ret)
191 + c= c+1
192 + return dd_list
193 +
194 + # 기간중 투자했을때 최고로 많이 하락할 수 있는 비율
195 + def mdd(self,ret):
196 +
197 + cum_ret = (1 + ret).cumprod()
198 + max_drawdown = 0
199 + max_ret = 1
200 + for ix_ret in cum_ret.values:
201 + if max_drawdown > (ix_ret - max_ret) / max_ret:
202 + max_drawdown = (ix_ret - max_ret) / max_ret
203 + if max_ret < ix_ret:
204 + max_ret = ix_ret
205 +
206 + return abs(max_drawdown)
207 +
208 + # 포트폴리오 수익률에서 무위험 수익률을 제한 후 이를 포트폴리오의 표준편차로 나눠 산출한 값, 즉 위험대비 얼마나 수익이 좋은지의 척도
209 + def sharpe_ratio(self,ret, rf=0.008, num_of_date=252):
210 +
211 + return ((np.mean(ret - (rf / num_of_date))) / (np.std(ret))) * np.sqrt(num_of_date)
212 +
213 + # 설정한 confidence level에 따른(95%) 확률로 발생할 수 있는 손실액의 최대 액수
214 + def value_at_risk(self,ret, para_or_hist="para", confidence_level=0.95):
215 +
216 + vol = np.std(ret)
217 + if para_or_hist == "para":
218 + VaR = np.mean(ret) - vol * norm.ppf(confidence_level)
219 + else:
220 + print('error')
221 +
222 + return VaR
223 +
224 + # 전체 투자기간에서 상승한 ( ret > 0 ) 기간의 비율
225 + def winning_rate(self,ret):
226 + var_winning_rate = np.sum(ret > 0) / len(ret)
227 + return var_winning_rate
228 +
229 + # 상승한날의 평균상승값을 하락한날의 평균하락값으로 나눈 비율
230 + def profit_loss_ratio(self,ret):
231 +
232 + if np.sum(ret > 0) == 0:
233 + var_profit_loss_ratio = 0
234 + elif np.sum(ret < 0) == 0:
235 + var_profit_loss_ratio = np.inf
236 + else:
237 + win_mean = np.mean(ret[ret > 0])
238 + loss_mean = np.mean(ret[ret < 0])
239 + var_profit_loss_ratio = win_mean / loss_mean
240 + return abs(var_profit_loss_ratio)
241 +
242 + # 데이터 취합하는 코드
243 + #임시로 5가지 데이터 예시를 활용해 코드작성
244 + # 선택한 종목의 이름과 비중, 투자기간을 input 값으로 받음
245 +
246 + def backtest_data(self, assets,weight,start_data_1, end_data_1,start_amount,rebalancing_month, interval, opt_option):
247 + # input으로 받는 assetnames 입력
248 + a = assets
249 + stock_num = len(a)
250 + # input으로 받는 assetweights 입력
251 + rebal_month = int(rebalancing_month)
252 + # input으로 받는 rebalancing_month를 입력
253 + # 나타내는 데이터 간격을 표시
254 +
255 + # weight 간격
256 + b = list(map(float, weight))
257 +
258 +
259 + # input으로 받는 from_period와 to_period 입력
260 + stock_return = pd.date_range(start=start_data_1, end=end_data_1)
261 + stock_return = pd.DataFrame(stock_return)
262 + stock_return.columns = ['Date']
263 +
264 + stocks = pd.read_csv('stockcodename.csv', index_col=0)
265 + symbol = ''
266 + asset_name = assets[:]
267 + for k in range(len(assets)):
268 + for i in enumerate(stocks.Name):
269 + if i[1] == assets[k]:
270 + assets[k] = (stocks.iloc[i[0]].Symbol)
271 + break
272 +
273 + # input으로 받는 from_period와 to_period 입력
274 + stock_return = pd.date_range(start=start_data_1, end=end_data_1)
275 + stock_return = pd.DataFrame(stock_return)
276 + stock_return.columns = ['Date']
277 +
278 +
279 + for asset in assets: #total_list:
280 + tmp = fdr.DataReader(asset,start_data_1,end_data_1)
281 + tmp.insert(1,"Date",tmp.index.copy(),True)
282 + tmp = tmp[['Date','Change']]
283 + tmp.columns = ['Date',asset]
284 + tmp = tmp.reset_index(drop=True)
285 + stock_return = pd.merge(stock_return,tmp,how='inner', on='Date')
286 +
287 + stock_return = stock_return.dropna(axis=0)
288 +
289 + #print(stock_return)
290 + if opt_option == 'basic' :
291 +
292 + # 투자비중으로 이루어진 dataframe 만들기
293 +
294 + start_datetime = stock_return.iloc[0,0]
295 + end_datetime = stock_return.iloc[-1,0]
296 + diff_months_list = list(rrule.rrule(rrule.MONTHLY, dtstart=start_datetime, until=end_datetime))
297 + month_gap = len(diff_months_list)
298 + rebal_roof = month_gap//rebal_month
299 + rebal_weight = pd.DataFrame()
300 +
301 + for i in range(rebal_roof+1):
302 + # 데이터로부터 리밸런싱기간만큼 가져오기
303 + filtered_df =stock_return.loc[stock_return["Date"].between(start_datetime,
304 + start_datetime + relativedelta(months=rebal_month)+relativedelta(days = -1))]
305 + # 리밸런싱 기간의 누적수익률 산출
306 + for j in range(stock_num):
307 + filtered_df.iloc[:,j+1] = (1 + filtered_df.iloc[:,j+1]).cumprod()
308 + # 해당 누적수익률에 initial 투자비중을 곱해준다
309 + for j in range(stock_num):
310 + filtered_df.iloc[:,j+1] = filtered_df.iloc[:,j+1]*float(b[j])
311 + # 이후 각각의 종목의 비중을 계산해서 산출한다
312 + filtered_df['total_value'] = filtered_df.sum(axis=1)
313 + for j in range(stock_num):
314 + filtered_df.iloc[:,j+1] = filtered_df.iloc[:,j+1]/filtered_df['total_value']
315 +
316 + rebal_weight = pd.concat([rebal_weight,filtered_df])
317 + start_datetime = start_datetime + relativedelta(months=rebal_month)
318 +
319 + #final_day = monthrange(start_datetime.year, start_datetime.month)
320 +
321 + stock_weight = rebal_weight.iloc[:,:-1]
322 + #print(stock_weight)
323 + '''
324 + stock_weight = stock_return.Date
325 + stock_weight = pd.DataFrame(stock_weight)
326 + c = 0
327 + for stockweight in b:
328 + stock_weight[a[c]] = float(stockweight)
329 + c = c + 1
330 + #print(stock_weight)
331 + '''
332 + else :
333 + # 포트폴리오 최적화 코드를 통한 리벨런싱 이중 리스트 weight 산출
334 + # 1. 입력 받은 start ~ end 날짜를 리밸런싱 기간으로 쪼개기
335 + opt_start_datetime = stock_return.iloc[0,0]
336 + opt_end_datetime = stock_return.iloc[-1,0]
337 + opt_diff_months_list = list(rrule.rrule(rrule.MONTHLY, dtstart=opt_start_datetime, until=opt_end_datetime))
338 + opt_month_gap = len(opt_diff_months_list)
339 + opt_rebal_roof = opt_month_gap//rebal_month
340 + opt_rebal_weight = pd.DataFrame()
341 + #opt_array = [[0]*stock_num]*(opt_rebal_roof+1)
342 +
343 + for i in range(opt_rebal_roof+1):
344 + opt_df = stock_return.loc[stock_return["Date"].between(opt_start_datetime,opt_start_datetime + relativedelta(months=rebal_month)+relativedelta(days = -1))]
345 + # 최적화 코드에서 기간마다의 가중치를 가져온다
346 + c_m = c_Models(a,b,opt_df.iat[0,0]- relativedelta(months=3),opt_df.iat[-1,0])
347 + ret_vol, efpoints, weights = c_m.plotting()
348 + weights = literal_eval(weights)
349 + weights = weights.get(opt_option)
350 + ##print(weights)
351 + # 리밸런싱 기간의 누적수익률 산출
352 + for j in range(stock_num):
353 + opt_df.iloc[:,j+1] = (1 + opt_df.iloc[:,j+1]).cumprod()
354 + # 해당 누적수익률에 initial 투자비중을 곱해준다
355 + for j in range(stock_num):
356 + opt_df.iloc[:,j+1] = opt_df.iloc[:,j+1]*float(weights[j])
357 + # 이후 각각의 종목의 비중을 계산해서 산출한다
358 + opt_df['total_value'] = opt_df.sum(axis=1)
359 + for j in range(stock_num):
360 + opt_df.iloc[:,j+1] = opt_df.iloc[:,j+1]/opt_df['total_value']
361 +
362 + # 이후 각각의 종목의 비중을 계산해서 산출한다
363 + #print(opt_df)
364 + opt_rebal_weight = pd.concat([opt_rebal_weight,opt_df])
365 + opt_start_datetime = opt_start_datetime + relativedelta(months=rebal_month)
366 + #리밸런싱으로 start 기간이 고객이 원하는 end 기간보다 커지게 되면 종료
367 + if opt_start_datetime > stock_return.iloc[-1,0]: # i가 100일 때
368 + break
369 + stock_weight = opt_rebal_weight.iloc[:,:-1]
370 + ##print(stock_weight)
371 + # 수익률 데이터와 투자비중을 곱한 하나의 데이터 생성
372 + pfo_return = stock_weight.Date
373 + pfo_return = pd.DataFrame(pfo_return)
374 + # weight 와 return의 날짜 맞춰주기
375 + #pfo_return = pfo_return[0:len(stock_weight)]
376 + pfo_return = pd.merge(pfo_return, stock_return, left_on='Date', right_on='Date', how='left')
377 + pfo_return['mean_return'] = 0
378 + ##print(pfo_return)
379 + for i in range(0,len(pfo_return)):
380 + return_result = list(pfo_return.iloc[i,1:1+stock_num])
381 + return_weight = list(stock_weight.iloc[i,1:1+stock_num])
382 + pfo_return.iloc[i,1+stock_num] = np.dot(return_result,return_weight)
383 + #rint(pfo_return)
384 + pfo_return['acc_return'] = [x+1 for x in pfo_return['mean_return']]
385 + pfo_return['acc_return'] = list(it.accumulate(pfo_return['acc_return'], operator.mul))
386 + pfo_return['acc_return'] = [x-1 for x in pfo_return['acc_return']]
387 + pfo_return['final_balance'] = float(start_amount) + float(start_amount)*pfo_return['acc_return']
388 + pfo_return['Drawdown_list'] = back_test.dd(input,pfo_return['mean_return'])
389 + pfo_return = pfo_return.set_index('Date')
390 + #print(pfo_return)
391 +
392 +
393 + ### 벤치마크 데이터 로드 및 전처리
394 +
395 + tiker_list = ['KS11','US500']
396 + bench_list = [fdr.DataReader(ticker, start_data_1, end_data_1)['Change'] for ticker in tiker_list]
397 + bench = pd.concat(bench_list, axis=1)
398 + bench.columns = ['KOSPI', 'S&P500']
399 + bench['KOSPI'] = bench['KOSPI'].fillna(0)
400 + bench['S&P500'] = bench['S&P500'].fillna(0)
401 + #bench = bench.dropna()
402 +
403 + # 벤치마크 누적수익률, DD 값
404 +
405 + bench['KOSPI_acc'] = [x+1 for x in bench['KOSPI']]
406 + bench['KOSPI_acc'] = list(it.accumulate(bench['KOSPI_acc'], operator.mul))
407 + bench['KOSPI_acc'] = [x-1 for x in bench['KOSPI_acc']]
408 + bench['KOSPI_balance'] = float(start_amount) + float(start_amount)*bench['KOSPI_acc']
409 + bench['KOSPI_Drawdown'] = back_test.dd(input,bench['KOSPI'])
410 + bench['S&P500_acc'] = [x+1 for x in bench['S&P500']]
411 + bench['S&P500_acc'] = list(it.accumulate(bench['S&P500_acc'], operator.mul))
412 + bench['S&P500_acc'] = [x-1 for x in bench['S&P500_acc']]
413 + bench['S&P500_balance'] = float(start_amount) + float(start_amount)*bench['S&P500_acc']
414 + bench['S&P500_Drawdown'] = back_test.dd(input,bench['S&P500'])
415 +
416 + if interval == 'monthly' or interval == 'weekly' :
417 + if interval == 'monthly' :
418 + inter = 'M'
419 + if interval == 'weekly' :
420 + inter = 'W'
421 + pfo_return_interval = pfo_return.resample(inter).last()
422 + pfo_return_first = pd.DataFrame(pfo_return.iloc[0]).transpose()
423 + pfo_return_interval = pd.concat([pfo_return_first, pfo_return_interval])
424 + pfo_return_interval['mean_return'] = pfo_return_interval['final_balance'].pct_change()
425 + pfo_return_interval = pfo_return_interval.dropna()
426 +
427 + # 월별 간격으로 만들어주기, 여기서는 return과 value만 monthly로 산출함 나머지값은 daily
428 + bench_interval = bench.resample(inter).last()
429 + #bench_ex['KOSPI'] = bench_ex['final_balance'].pct_change()
430 + bench_first = pd.DataFrame(bench.iloc[0]).transpose()
431 + bench_interval = pd.concat([bench_first, bench_interval])
432 + bench_interval['KOSPI'] = bench_interval['KOSPI_balance'].pct_change()
433 + bench_interval['S&P500'] = bench_interval['S&P500_balance'].pct_change()
434 + bench_interval = bench_interval.dropna()
435 +
436 + # 날짜타입 열로 만들기 및 str 타입으로 전처리
437 + pfo_return = pfo_return.rename_axis('Date').reset_index()
438 + pfo_return['Date'] = pd.to_datetime(pfo_return['Date'], format='%d/%m/%Y').dt.date
439 + pfo_return['Date'] = list(map(str, pfo_return['Date']))
440 +
441 + pfo_return_interval = pfo_return_interval.rename_axis('Date').reset_index()
442 + pfo_return_interval['Date'] = pd.to_datetime(pfo_return_interval['Date'], format='%d/%m/%Y').dt.date
443 + pfo_return_interval['Date'] = list(map(str, pfo_return_interval['Date']))
444 +
445 + bench = bench.rename_axis('Date').reset_index()
446 + bench['Date'] = pd.to_datetime(bench['Date'], format='%d/%m/%Y').dt.date
447 + bench['Date'] = list(map(str, bench['Date']))
448 +
449 + bench_interval = bench_interval.rename_axis('Date').reset_index()
450 + bench_interval['Date'] = pd.to_datetime(bench_interval['Date'], format='%d/%m/%Y').dt.date
451 + bench_interval['Date'] = list(map(str, bench_interval['Date']))
452 +
453 + backtest_return = {
454 + 'pfo_return': [
455 + {
456 + 'Date': list(pfo_return_interval['Date']),
457 + 'mean_return': list(pfo_return_interval['mean_return']),
458 + 'acc_return ratio': list(pfo_return_interval['acc_return']),
459 + 'final_balance': list(pfo_return_interval['final_balance']),
460 + 'Drawdown_list' : list(pfo_return_interval['Drawdown_list'])
461 + }
462 + ],
463 + 'bench': [
464 + {
465 + 'Date': list(bench_interval['Date']),
466 + 'KOSPI_return': list(bench_interval['KOSPI']),
467 + 'S&P500_return': list(bench_interval['S&P500']),
468 + 'KOSPI_acc_return': list(bench_interval['KOSPI_acc']),
469 + 'KOSPI_balance' : list(bench_interval['KOSPI_balance']),
470 + 'KOSPI_Drawdown': list(bench_interval['KOSPI_Drawdown']),
471 + 'S&P500_acc_return': list(bench_interval['S&P500_acc']),
472 + 'S&P500_balance' : list(bench_interval['S&P500_balance']),
473 + 'S&P500_Drawdown': list(bench_interval['S&P500_Drawdown'])
474 + }
475 + ],
476 + 'indicator': [
477 + {
478 + 'Mean': back_test.Arithmetic_Mean_Annual(input,pfo_return['mean_return']),
479 + 'Std': pfo_return['mean_return'].std() * np.sqrt(365),
480 + 'Sharpe ratio': back_test.sharpe_ratio(input,pfo_return['mean_return']),
481 + 'VaR': back_test.value_at_risk(input,pfo_return['mean_return']),
482 + 'MDD': back_test.mdd(input,pfo_return['mean_return']),
483 + 'Winning ratio': back_test.winning_rate(input,pfo_return['mean_return']),
484 + 'Gain/Loss Ratio': back_test.profit_loss_ratio(input,pfo_return['mean_return'])
485 + }
486 + ],
487 + 'KOSPI_indicator': [
488 + {
489 + 'Mean': back_test.Arithmetic_Mean_Annual(input,bench['KOSPI']),
490 + 'Std': bench['KOSPI'].std() * np.sqrt(365),
491 + 'Sharpe ratio': back_test.sharpe_ratio(input,bench['KOSPI']),
492 + 'VaR': back_test.value_at_risk(input,bench['KOSPI']),
493 + 'MDD': back_test.mdd(input,bench['KOSPI']),
494 + 'Winning ratio': back_test.winning_rate(input,bench['KOSPI']),
495 + 'Gain/Loss Ratio': back_test.profit_loss_ratio(input,bench['KOSPI'])
496 + }
497 + ],
498 + 'S&P500_indicator': [
499 + {
500 + 'Mean': back_test.Arithmetic_Mean_Annual(input,bench['S&P500']),
501 + 'Std': bench['S&P500'].std() * np.sqrt(365),
502 + 'Sharpe ratio': back_test.sharpe_ratio(input,bench['S&P500']),
503 + 'VaR': back_test.value_at_risk(input,bench['S&P500']),
504 + 'MDD': back_test.mdd(input,bench['S&P500']),
505 + 'Winning ratio': back_test.winning_rate(input,bench['S&P500']),
506 + 'Gain/Loss Ratio': back_test.profit_loss_ratio(input,bench['S&P500'])
507 + }
508 + ]
509 + }
510 +
511 + else :
512 + # 날짜타입 열로 만들기 및 str 타입으로 전처리
513 + pfo_return = pfo_return.rename_axis('Date').reset_index()
514 + pfo_return['Date'] = pd.to_datetime(pfo_return['Date'], format='%d/%m/%Y').dt.date
515 + pfo_return['Date'] = list(map(str, pfo_return['Date']))
516 +
517 + bench = bench.rename_axis('Date').reset_index()
518 + bench['Date'] = pd.to_datetime(bench['Date'], format='%d/%m/%Y').dt.date
519 + bench['Date'] = list(map(str, bench['Date']))
520 + backtest_return = {
521 + 'pfo_return': [
522 + {
523 + 'Date': list(pfo_return['Date']),
524 + 'mean_return': list(pfo_return['mean_return']),
525 + 'acc_return ratio': list(pfo_return['acc_return']),
526 + 'final_balance': list(pfo_return['final_balance']),
527 + 'Drawdown_list' : list(pfo_return['Drawdown_list'])
528 + }
529 + ],
530 + 'bench': [
531 + {
532 + 'Date': list(bench['Date']),
533 + 'KOSPI_return': list(bench['KOSPI']),
534 + 'S&P500_return': list(bench['S&P500']),
535 + 'KOSPI_acc_return': list(bench['KOSPI_acc']),
536 + 'KOSPI_balance' : list(bench['KOSPI_balance']),
537 + 'KOSPI_Drawdown': list(bench['KOSPI_Drawdown']),
538 + 'S&P500_acc_return': list(bench['S&P500_acc']),
539 + 'S&P500_balance' : list(bench['S&P500_balance']),
540 + 'S&P500_Drawdown': list(bench['S&P500_Drawdown'])
541 + }
542 + ],
543 + 'indicator': [
544 + {
545 + 'Mean': back_test.Arithmetic_Mean_Annual(input,pfo_return['mean_return']),
546 + 'Std': pfo_return['mean_return'].std() * np.sqrt(365),
547 + 'Sharpe ratio': back_test.sharpe_ratio(input,pfo_return['mean_return']),
548 + 'VaR': back_test.value_at_risk(input,pfo_return['mean_return']),
549 + 'MDD': back_test.mdd(input,pfo_return['mean_return']),
550 + 'Winning ratio': back_test.winning_rate(input,pfo_return['mean_return']),
551 + 'Gain/Loss Ratio': back_test.profit_loss_ratio(input,pfo_return['mean_return'])
552 + }
553 + ],
554 + 'KOSPI_indicator': [
555 + {
556 + 'Mean': back_test.Arithmetic_Mean_Annual(input,bench['KOSPI']),
557 + 'Std': bench['KOSPI'].std() * np.sqrt(365),
558 + 'Sharpe ratio': back_test.sharpe_ratio(input,bench['KOSPI']),
559 + 'VaR': back_test.value_at_risk(input,bench['KOSPI']),
560 + 'MDD': back_test.mdd(input,bench['KOSPI']),
561 + 'Winning ratio': back_test.winning_rate(input,bench['KOSPI']),
562 + 'Gain/Loss Ratio': back_test.profit_loss_ratio(input,bench['KOSPI'])
563 + }
564 + ],
565 + 'S&P500_indicator': [
566 + {
567 + 'Mean': back_test.Arithmetic_Mean_Annual(input,bench['S&P500']),
568 + 'Std': bench['S&P500'].std() * np.sqrt(365),
569 + 'Sharpe ratio': back_test.sharpe_ratio(input,bench['S&P500']),
570 + 'VaR': back_test.value_at_risk(input,bench['S&P500']),
571 + 'MDD': back_test.mdd(input,bench['S&P500']),
572 + 'Winning ratio': back_test.winning_rate(input,bench['S&P500']),
573 + 'Gain/Loss Ratio': back_test.profit_loss_ratio(input,bench['S&P500'])
574 + }
575 + ]
576 + }
577 +
578 + return backtest_return
579 +
580 +
581 +
582 +# print(back_test().backtest_data(['삼성전자','LG전자'],[0.9,0.1],'2010-01-01', '2021-01-01',10000000,3, 'monthly', 'gmv')['pfo_return'].mean_return)
583 +# print(back_test().backtest_data(['삼성전자','LG전자'],[0.9,0.1],'2010-01-01', '2021-01-01',10000000,3, 'monthly', 'gmv')['pfo_return'][0]['acc_return_ratio'])
584 +# print(back_test().backtest_data(['삼성전자','LG전자'],[0.9,0.1],'2018-01-01', '2021-01-01',10000000,6, 'monthly', 'gmv'))
585 +
586 +
587 +data = back_test().backtest_data([sys.argv[1],sys.argv[2]],[0.5,0.5],sys.argv[3], '2021-01-02',10000000,6, 'monthly', 'gmv')
588 +# data = back_test().backtest_data(['삼성전자','LG전자'],[0.5,0.5],'2020-01-01', '2021-01-02',10000000,6, 'monthly', 'gmv')
589 +x = data['pfo_return'][0]['Date']
590 +y = data['pfo_return'][0]['acc_return ratio']
591 +y2 = data['bench'][0]['KOSPI_acc_return']
592 +y3 = data['bench'][0]['S&P500_acc_return']
593 +x_ticks = []
594 +for i,j in enumerate(x):
595 + if (i % 6) == 0:
596 + x_ticks.append(j)
597 + else:
598 + x_ticks.append('')
599 +x_ticks[-1]= x[-1]
600 +plt.figure(figsize=(10,5))
601 +ax=plt.gca()
602 +ax.xaxis.set_major_locator(ticker.MultipleLocator(12))
603 +plt.plot(x,y,label = 'gmv result')
604 +plt.plot(x,y2 ,label = 'kospi result')
605 +plt.plot(x,y3, label = 's&p500 result')
606 +plt.xticks(x_ticks,rotation=60)
607 +plt.xlabel('Date')
608 +plt.ylabel('Return')
609 +plt.title('result')
610 +plt.legend()
611 +plt.show()
612 +plt.savefig("./src/test.png", dpi = 400)
613 +print("end")
614 +