퀀트투자 아이디어를 생각한 것은 결국 투자를 하기 위함이다.
투자를 하더라도 몇 월에 할지, 어떤 조건으로 할지, 1년에 한 번만 투자를 할지 아니면 여러번에 나눠서 투자를 할지에 대한 많은 의문이 생겼다.
우선 월별, 조건별 투자수익률을 모두 계산했다.
월(12개월) x 시가총액(5백억~3천억, 6가지) x 거래대금(0~1억이상, 11가지) X 기간별(2003년부터, 2010년부터) CASE로 수익률을 확인해봤다.1,584가지의 경우의 수이다.
2003년부터 계산한 결과에서는 수익계산식을 수정하지 않았다. 기존에는 주식수가 늘고, 줄어듬에 따라 수익률을 보정했다. 하지만, 가져온 데이터가 이미 수정주가를 반영한다고 해서 수식을 변경했다.
2010년부터는 변경된 수식으로 계산이 되었기에 좀 더 정확하다. 중요한 것은 전체적인 투자 결정에 지장을 줄 만큼의 영향이 있지는 않기에 큰 흐름 위주로 참고하면 될 것 같다.
아래는 CASE별 수익률을 구하는 CODE이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
# 경우의 수를 이용해서 최적조건 찾기
# (저PBR, 저DPS) x 12(개월) x 2(월초, 월말) x 30(시가총액 0이상~3천억이상 조건) x 100(거래금액 0이상부터 ~ 10억이상)
# col은 투자월, 투자일, CAGR, MDD, 손실횟수, 누적이익률, 손실평균
import pandas as pd
import sqlite3
con = sqlite3.connect('krx_data_new.db')
# 주식거래일을 구하는 함수
def near_business_day(b_start, b_month, b_day):
df_date = pd.read_sql("SELECT 일자 FROM fund WHERE 일자 LIKE " + "'" + b_start + "%'", con)
df_date.drop_duplicates(inplace=True)
li_df = df_date['일자'].tolist()
date = b_start + b_month + b_day
if date in li_df:
b_date = date
else:
if int(b_day) < 15: # 날짜가 15미만이면 1씩 더해서 영업일 구하기
while 10: # 10번 정도 더하기 반복해서 DB에 있는 날짜 구하기
b_day = int(b_day) + 1
b_day = str(b_day).zfill(2)
b_date = date[:6] + b_day
if b_date in li_df:
break
else: # 날짜가 15이상이면 1씩 빼면서 영업일 구하기
while 10:
b_day = int(b_day) - 1
b_day = str(b_day).zfill(2)
b_date = date[:6] + b_day
if b_date in li_df:
break
return b_date
li = [] #CAGR, MDD, 손실횟수, 누계이익률, 손실평균 등을 담을 list
i = 0
d = 1
for m in range(1, 13):
for trans in range(0, 110000000, 10000000):
for cap in range(50000000000, 350000000000, 50000000000): #거래대금
li_result = []
for y in range(2010, 2022): #년
m = str(m).zfill(2)
d = str(d).zfill(2)
invest_day = near_business_day(str(y), m, d)
df = pd.read_sql("SELECT 일자, code, 종목명, 종가, 거래량, 거래대금, 상장주식수, 시가총액, "
"PBR, DPS FROM Fund WHERE 일자= " + invest_day, con)
# 거래량, PBR, DPS, 0이상인 종목만 가져오고, 거래대금은과 시가총액은 조건이상
df = df[(df['거래량'] > 0) & (df['PBR'] > 0) & (df['DPS'] > 0) & (df['거래대금'] >= trans) & (df['시가총액'] <= cap)]
df['PBR_rank'] = df['PBR'].rank()
df['DPS_rank'] = df['DPS'].rank()
df['PBR_DPS'] = df['PBR_rank'] + df['DPS_rank']
df['PBR_DPS_rank'] = df['PBR_DPS'].rank()
df.sort_values(by='PBR_DPS_rank', inplace=True)
df = df.iloc[:20]
#1년후의 날짜 구하기
sell_day = near_business_day(str(y + 1), m, d)
# 1년후의 날짜로 데이터 가져오기
df_later = pd.read_sql("SELECT 일자, code, 종가, 상장주식수 FROM fund WHERE 일자= " + sell_day, con)
df_later.columns = ['1년후의_일자', 'code', '1년후_종가', '1년후_상장주식수']
df = pd.merge(df, df_later, on='code')
df['수익률'] = (df['1년후_종가'] - df['종가']) / df['종가']
profit = df['수익률'].sum()/20
li_result.append([invest_day, profit])
if i == 0: # 첫번째 날짜는 만들어진 dataframe이 df_t로 저장되고, 나머지는 계속 concat로 붙여넣기
df_t = df
else:
df_t = pd.concat([df_t, df])
print(f'{m}월 {trans}거래대금 {cap}시가총액 {y}년도 작업중')
i += 1
df_result = pd.DataFrame(data=li_result, columns=['투자년도', '수익률']) # 투자년도와 수익률로 데이터프레임 만들기
li_acc = [] #누적수익률 구하기
for n, p in enumerate(df_result['수익률']):
if n == 0:
li_acc.append(1 + p)
else:
li_acc.append(li_acc[n - 1] * (1 + p))
print(li_acc)
df_result['누적수익률'] = pd.DataFrame(data=li_acc, columns=['누적수익률'])
cagr = (df_result['누적수익률'].iloc[-1]) ** (1 / len(df_result)) - 1
MDD = df_result['수익률'].min()
return_acc = df_result['누적수익률'].iloc[-1]
df_lost = df_result[df_result['수익률'] < 0]
count_lost = df_lost['수익률'].count() #손실횟수
mean_lost = df_lost['수익률'].mean() #손실평균
li.append([m, invest_day, cap, trans, cagr, MDD, return_acc, count_lost, mean_lost])
print(li)
df_find = pd.DataFrame(data=li, columns=['투자월', '투자일', '시가총액조건(이하)', '거래대금조건(이상)',
'CAGR', 'MDD', '누적수익률', '손실횟수', '손실평균'])
df_find.to_excel('최적조건찾기(2010-2021)1월-12월 거래대금0-1억이상 시가총액5백억-3천억이하.xlsx')
|
cs |
이렇게 결과값이 나오면 또 다른 고민이 떠오른다.
언제 투자하는 것이 합리적일까? 그래서 월별 통계값을 만들었다.
2003년부터와 2010년부터를 나눈 이유는 2008년 금융위기시 폭락장이 너무 커서 혹시나 왜곡의 가능성이 있지 않을까 생각했다. 그래서 2010년부터의 데이터를 별도로 만들었다.
평균으로 보자면 5, 6월이 가장 합리적으로 보인다.
여기서 MDD란 년도별 MDD다. 일별이나 월별로 만들지 않았다. 어차피 1년에 한 번만 거래를 할 계획이기에 매도 시점에 내가 매도를 할 수 있을지를 보기 위해서 계산하였다.
중간값을 보면 대체로 20%~30% CAGR이 나온다. 위에서 언급한 것처럼 2003년부터 데이터는 수익률 계산이 잘못 된 부분이 있기에 아마 조금 더 낮게 나올 수도 있다. 하지만 전체적으로 20%이상의 수익률을 기대할 수 있다.
좀 더 Worst 한 경우를 보기 위해서 최소값을 보더라도 CAGR 자체는 상당히 높은 것을 확인할 수 있다.
좀 더 긍정적으로 보자면 최대 35%까지 CAGR을 기대할 수 있다.
아래는 통계값을 구하는 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
import pandas as pd
df = pd.read_excel('최적조건찾기(2010-2021)1월-12월 거래대금0-1억이상 시가총액5백억-3천억이하.xlsx')
df = df.iloc[:, 1:]
df_mean = df.groupby('투자월').mean()
df_mean['구분'] = '평균'
df_max = df.groupby('투자월').max()
df_max['구분'] = '최대값'
df_min = df.groupby('투자월').min()
df_min['구분'] = '최소값'
df_med = df.groupby('투자월').median()
df_med['구분'] = '중간값'
df_50 = df['CAGR'].quantile(0.5)
print(f'50분위 값 : {df_50}')
df_30 = df['CAGR'].quantile(0.3)
print(f'30분위 값 : {df_30}')
df_10 = df['CAGR'].quantile(0.1)
print(f'10분위 값 : {df_10}')
df_5 = df['CAGR'].quantile(0.05)
print(f'5분위 값 : {df_5}')
df_1 = df['CAGR'].quantile(0.01)
print(f'1분위 값 : {df_1}')
df_t = pd.concat([df_mean, df_max, df_min, df_med])
df_t = df_t[['투자일', 'CAGR', 'MDD', '누적수익률', 'MDD', '구분']]
df_cor = df.corr()
df_cor = round(df_cor, 4)
print(df_cor)
print(df_t)
# df_t.to_excel('퀀트투자 통계(2010-2021).xlsx')
# df_cor.to_excel('퀀트투자 인자별 상관관계(2021-2021).xlsx')
|
cs |
통계를 구하면서 각 인자별 상관관계도 구했다.
2003년부터와 2010년부터를 각각 나눠서 계산했다.
전체적으로 보면 시가총액이과 거래대금 조건은 수익률과 반비례함을 알 수 있다. 하지만, 거래대금 조건이 일정 이상이어야 손실횟수가 줄어든다.
CAGR과 누적수익률은 상관관계가 1이 나와야 하지만, 위에서 언급한대로 계산에 약간의 오류가 있어서 1이 나오지 않았다.
이제 이런 여정의 최종목표인 어떻게 투자 할것인지에 대한 본론으로 가보자
여러가지 시행착오 및 검증과 수정이 이루어 졌지만, 지금까지의 결론은 위의 표와 같다.
우선 투자횟수는 1년에 3번으로 하는 것이 지금 상황에서는 가장 좋을 것 같다. 백테스트는 실전과 어느정도의 괴리가 있을 것이라 본다. 그래서 Risk를 분산하기 위해 2, 6, 12월에 나눠서 투자하는 방향으로 결정했다.
각 월별로 최적의 투자 Filtering 조건은 "최적조건찾기" 엑셀파일에서 CAGR이 높고, MDD가 낮고, 손실횟수가 적은 것 위주로 입력했다.
CAGR은 31%가 나왔고, 25% 정도는 기대할 수 있으리라 생각한다. 보수적으로 생각해서 나의 목표 CAGR은 20%이상이다.
'돈을 벌기 위한 자료 > 투자정보' 카테고리의 다른 글
퀀트투자 최적값 찾기 파이썬 코드 (2) | 2023.10.18 |
---|---|
파이썬으로 만든 퀀트투자 백테스트 툴 업데이트 (0) | 2023.10.15 |
투자종목 관리를 위한 스크래핑 코드(엑셀, 파이썬) (1) | 2023.09.04 |
효성ITX 094280 - 2 (3) | 2023.06.13 |
효성ITX 094280 (0) | 2023.06.09 |
댓글