퀀트투자 - 매년 초에 투자해서 매년 말까지 수익률 Test
본문 바로가기
파이썬으로 만든 것들/퀀트투자

퀀트투자 - 매년 초에 투자해서 매년 말까지 수익률 Test

by Squat Lee 2022. 10. 24.

2022.09.21 - [취미로 하는 파이썬/투자 실험실 with 파이썬] - 퀀트투자 - 통계적인 관점에서 백테스트 수익률 분석

 

퀀트투자 - 통계적인 관점에서 백테스트 수익률 분석

예전에 저PBR 과 저DPS 조합으로 퀀트투자 백테스트를 한 적이 있다. 저PBR X 저DBS 조합 퀀트투자 백테스트 그 당시에는 모든 것이 완벽할 것이라고 생각하고 바로 실행해 보았다. 막상 실제로 투자

dotsnlines.tistory.com


위와 같이 지난번에 pykrx를 가지고 매년 초에 투자해서 월별로 수익률을 구해보고, 최저값, 최고값을 구해 보았다.

매월 1번만 수익률을 구했기에 매일 종가로 수익률을 구하면 값이 어떻게 되는지 궁금했다.

그래서 DB에 영업일의 모든 Data를 다운로드 해서 저장했고, 일별로 1년치의 수익률을 구해 보았다.

수익률을 구하려면 해당 날짜의 종가를 가져와야 하고, DB에서 원하는 기간의 영업일만 가져와야 하는데, 쉬울거라 생각했지만 나에게는 어려웠다.

 

어떻게든 아래 코드와 같이 만들었다. 

 

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
import sqlite3
import pandas as pd
import matplotlib.pyplot as plt
 
#전역변수 선언
global df_data
global date_list
 
con = sqlite3.connect('krx_data.db')
df_dates = pd.read_sql("SELECT * FROM dates", con)
date_list = df_dates['일자'].to_list()
 
 
# 리스트를 연도별로 저장하기
year =[] #연도만 추출할 List - Step 1 , 2003, 2003, 2003,...
for y in date_list:
    year.append(y[:4])
 
new_y = [] #중복값 제거한 List, 2003, 2004, ...
for ny in year:
    if ny not in new_y:
        new_y.append(ny)
 
# 연도별로 List 담기 [[20030103, 20030106,...],[20040103,..],...]
yearly_list=[]
for n, dy in enumerate(new_y):
    d_list = []
    for d in date_list:
        if dy == d[:4]:
            d_list.append(d)
    yearly_list.append(d_list)
 
# 월별로 List 담기 [[20030103,...],[20030203,...]..]
monthly_list = []
for dy in yearly_list:
    for m in range(113):  # 1월부터 12월까지
        m_list = []
        for y in dy: #[20030103, 20030104,...]
            if m == int(y[4:6]):
                m_list.append(y)
        monthly_list.append(m_list)
 
 
# 투자 종목 고르기
def row_pbr_dps(date):
 
    df = pd.read_sql("SELECT 일자, code, 종목명, 종가, 거래량, 상장주식수, PBR, DPS "
                     "FROM fundamental WHERE 일자==" + date, con)
    df = df.set_index('code')
 
    df = df[df['PBR'> 0#PBR이 0이상만 구하기
    df['pbr_rank'= df['PBR'].rank()
 
    df = df[df['DPS'> 0#DPS 0이상만 구하기
    df['dps_rank'= df['DPS'].rank()
 
    df['pbr_dps'= df['pbr_rank'+ df['dps_rank']#pbr 순위와 dps 순위를 더하기
    df['pbr_dps_rank'= df['pbr_dps'].rank()#더한 PBR, DPS 순위의 순위 매기기
    df = df.sort_values(by='pbr_dps_rank'#pbr_dps_rank의 숫자가 낮은 순으로 정렬하기
 
    df = df[df['거래량'> 0#거래량이 0 이상인 종목만 구하기
    df = df.iloc[:20#20개 종목만 구하기
    df = df[['종목명''종가''상장주식수']] #컬럼이 많으면 복잡하니깐, 3개만 표시
    df['매수수량'= 1000000 // df['종가']
    df['매수금액'= df['매수수량'* df['종가']
    print(df)
 
    return df
 
# 고른 20개 종목의 종가 구하기
def price(date):
    df_p = pd.read_sql("SELECT code, 상장주식수, 종가 "
                       "FROM fundamental WHERE 일자==" + date, con)
    df_p = df_p.set_index('code')
    df_p.rename(columns={'상장주식수':'주식수'+date, '종가':'종가'+date}, inplace=True)
 
    return df_p
 
#백테스트 하기
 
yi_li = [] #수익
for n, day in enumerate(yearly_list[0]):
    if n == 0:
        df = row_pbr_dps(day)
    else:
        df_p = price(day)
        df = pd.merge(df, df_p, left_index=True, right_index=True, how='left')
        df['주수변동'+day] = df['상장주식수'- df['주식수'+day]
        df['수익'+day] = df['종가'+day]*df['매수수량'- df['매수금액']
        #pykrx는 수정주가를 우선적으로 가져오고, 상폐 종목은 krx에서 가져온다. 그래서 주식수에 따른 보정은 필요없다.
        yi = df['수익'+day].sum() #20개 종목의 수익 합계
        re = int((yi / df['매수금액'].sum()) * 100#수익률
        yi_li.append([day, re])
 
# df.to_excel('pbrxdps221005.xlsx')
 
df_yi = pd.DataFrame(data=yi_li, columns=['일자''수익률']) #일자별 수익률을 데이터프레임으로 만들기
df_yi['일자'= pd.to_datetime(df_yi['일자'])
df_yi.set_index('일자', inplace=True)
print(df_yi)
plt.figure(figsize=(169))
plt.plot(df_yi['수익률'])
plt.show()
 
 
cs

 

결과를 그래프로 나타내면 아래와 같다.

 

2003년만 우선 그려 보았는데 손실을 보다가 시간이 지날수록 수익률이 증가는 모습이다.

 

중요한 것은 해마다 수익이 나는지와 손실 범위를 알아내서 대응책을 마련하는 것이다.

 

그러기 위해서는 매년 백테스트 결과의 최대값과 최소값을 알아야 한다.

 

코드를 업그레이드 해 보았다.

 

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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import sqlite3
import pandas as pd
import matplotlib.pyplot as plt
 
# 전역변수 선언
global df_data
global date_list
 
con = sqlite3.connect('krx_data.db')
df_dates = pd.read_sql("SELECT * FROM dates", con)
date_list = df_dates['일자'].to_list()
 
# 리스트를 연도별로 저장하기
year = []  # 연도만 추출할 List - Step 1 , 2003, 2003, 2003,...
for y in date_list:
    year.append(y[:4])
 
new_y = []  # 중복값 제거한 List, 2003, 2004, ...
for ny in year:
    if ny not in new_y:
        new_y.append(ny)
 
# 연도별로 List 담기 [[20030103, 20030106,...],[20040103,..],...]
yearly_list = []
for n, dy in enumerate(new_y):
    d_list = []
    for d in date_list:
        if dy == d[:4]:
            d_list.append(d)
    yearly_list.append(d_list)
 
# 월별로 List 담기 [[20030103,...],[20030203,...]..]
monthly_list = []
for dy in yearly_list:
    for m in range(113):  # 1월부터 12월까지
        m_list = []
        for y in dy:  # [20030103, 20030104,...]
            if m == int(y[4:6]):
                m_list.append(y)
        monthly_list.append(m_list)
 
 
# 투자 종목 고르기
def row_pbr_dps(date):
    df = pd.read_sql("SELECT 일자, code, 종목명, 종가, 거래량, 상장주식수, PBR, DPS "
                     "FROM fundamental WHERE 일자==" + date, con)
    df = df.set_index('code')
 
    df = df[df['PBR'> 0]  # PBR이 0이상만 구하기
    df['PBR'= round(df['PBR'], 2)
    df['pbr_rank'= df['PBR'].rank()
 
    df = df[df['DPS'> 0]  # DPS 0이상만 구하기
    df['dps_rank'= df['DPS'].rank()
 
    df['pbr_dps'= df['pbr_rank'+ df['dps_rank']  # pbr 순위와 dps 순위를 더하기
    df['pbr_dps_rank'= df['pbr_dps'].rank()  # 더한 PBR, DPS 순위의 순위 매기기
    df = df.sort_values(by='pbr_dps_rank')  # pbr_dps_rank의 숫자가 낮은 순으로 정렬하기
 
    df = df[df['거래량'> 0]  # 거래량이 0 이상인 종목만 구하기
    df = df.iloc[:20]  # 20개 종목만 구하기
    df = df[['종목명''종가''상장주식수']]  # 컬럼이 많으면 복잡하니깐, 3개만 표시
    df['매수수량'= 1000000 // df['종가']
    df['매수금액'= df['매수수량'* df['종가']
 
    return df
 
 
# 고른 20개 종목의 종가 구하기
def price(date):
    df_p = pd.read_sql("SELECT code, 상장주식수, 종가 "
                       "FROM fundamental WHERE 일자==" + date, con)
    df_p.drop_duplicates(inplace=True#중복제거
    df_p = df_p.set_index('code')
    df_p.rename(columns={'상장주식수''주식수' + date, '종가''종가' + date}, inplace=True)
 
    return df_p
 
 
# 백테스트 하기
y_yi_li = []  # 연도별 수익
y_st_li =[] #연도별 통계
 
for y in range(len(yearly_list)): #연도별로 반복
    yi_li = []  # 수익
    for n, day in enumerate(yearly_list[y]):
        try:
            if n == 0:
                df = row_pbr_dps(day)
            else:
                df_p = price(day)
                df = pd.merge(df, df_p, left_index=True, right_index=True, how='left')
                df['수익' + day] = df['종가' + day] * df['매수수량'- df['매수금액']
                # pykrx는 수정주가를 우선적으로 가져오고, 상폐 종목은 krx에서 가져온다. 그래서 주식수에 따른 보정은 필요없다.
                yi = df['수익' + day].sum()  # 20개 종목의 수익 합계
                re = int((yi / df['매수금액'].sum()) * 100)  # 수익률
                yi_li.append([day, re])
        except:
            pass
 
    # df.to_excel('pbrxdps'+ yearly_list[0][0][:4] +'.xlsx')
 
    df_yi = pd.DataFrame(data=yi_li, columns=['일자''수익률'])  # 일자별 수익률을 데이터프레임으로 만들기
    df_yi['일자'= pd.to_datetime(df_yi['일자'])
    df_yi.set_index('일자', inplace=True)
    print(df_yi)
    y_st_li.append([yearly_list[y][0][:4], df_yi['수익률'].max(), df_yi['수익률'].min()])
    print(y_st_li)
 
    if y == 0:
        total_df = df_yi
    else:
        total_df = pd.concat([total_df, df_yi])
 
# print(total_df)
 
df_st = pd.DataFrame(data=y_st_li, columns=['년도''최대값''최소값'])
df_st.to_excel('PBRxDPS 연도별 수익 통계.xlsx')
 
print(df_st)
 
 
 
 
 
cs

결과는 엑셀파일로 나온다. 아래와 같다.

 

이 결과값이 맞다면, 전략을 만들기가 좀 더 수월할 것 같다.

 

수익률이 15% 미만이었던 적은 3번 밖에 없다. 10% 미만일 때는 2번이다. 그리고 그 두번은 최소값이 둘 다 -10% 이하다.

 

그러면 -10% 이하일때는 일부 추가 매수를 하고 -40%를 넘어설때 나머지를 적극적으로 추가 매수를 하면 될 것 같다.

 

그리고 매년 수익을 보려면 10%가 넘었을때 일부 매도하고 수익률 구간별로 분할 매도를 하면 매년 수익을 볼 수 있을 것 같다.

 

작년에도 퀀트투자 방법을 했지만, 쉽지가 않다. 20종목을 사고 팔고, 관리를 한다는게 나에게는 부담이 되었다. 그리고 매수, 매도 과정에서 슬리피지가 나와서 백테스트의 수익률과 차이가 생긴다.

 

이 투자법은 시간을 두고 좀 더 고민해 봐야 겠다.

728x90
반응형

댓글