가설 검증을 위한 데이터 처리
Contents
import FinanceDataReader as fdr
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import datetime
pd.options.display.float_format = '{:,.2f}'.format
pd.set_option('display.expand_frame_repr', False)
가설 검증을 위한 데이터 처리#
앞서 만든 return_all (주가 데이터에 지수데이터가 추가된 파일) 을 아래와 같이 로드하고, Missing Data 는 제거합니다.
return_all = pd.read_pickle('return_all.pkl').dropna()
return_all.index = [datetime.datetime.strftime(dt, '%Y-%m-%d') for dt in return_all.index]
return_all.head().style.set_table_attributes('style="font-size: 12px"')
open | high | low | close | volume | change | code | name | kosdaq_return | return | win_market | |
---|---|---|---|---|---|---|---|---|---|---|---|
2021-01-05 | 2270 | 2285 | 2200 | 2250 | 410263 | -0.004425 | 060310 | 3S | 1.008326 | 0.995575 | 0 |
2021-01-06 | 2225 | 2310 | 2215 | 2290 | 570349 | 0.017778 | 060310 | 3S | 0.995567 | 1.017778 | 1 |
2021-01-07 | 2290 | 2340 | 2240 | 2290 | 519777 | 0.000000 | 060310 | 3S | 1.007612 | 1.000000 | 0 |
2021-01-08 | 2300 | 2315 | 2225 | 2245 | 462568 | -0.019651 | 060310 | 3S | 0.998918 | 0.980349 | 0 |
2021-01-11 | 2230 | 2275 | 2130 | 2175 | 409057 | -0.031180 | 060310 | 3S | 0.988702 | 0.968820 | 0 |
일주일(5영업일)을 수익율의 관찰 기간으로 하고, 관찰 기간 동안 주가 상승이 있으면 저희가 세운 가설들을 유의미한 가설로 판단하겠습니다. 여기서 주가 상승의 기준은 “종가 매수 일부터 다음 5 영업일 동안 최고 종가 수익율” 하겠습니다.
첫 번째 종목 060310 에 대하여 처리를 먼저 해 보겠습니다. df[‘close’] * shift(-1) 은 다음 영업일의 종가 수익율을 참조하고, df[‘close’]*shift(-2) 은 그 다음의 영업일의 종가 수익율을 참조합니다. 따라서 매수 후 2 영업일 후, 종가 수익율은 { df[‘close’] * shift(-1) } * { df[‘close’] * shift(-2) } 로 계산됩니다. 이렇게 1 영업일, 2 영업일, 3 영업일, 4 영업일, 5 영업일 후 종가 수익율을 새로운 컬럼에 생성하고, 그 중에서 가장 큰 수익율을 고르면 됩니다. 생성된 컬럼 중 가장 큰 값은 max(axis=1) 로 찾습니다. 참고로 max() 에서는 axis=0 이 Default 라서 axis=1 로 정해주지 않으면 열에서 가장 큰 값을 찾게 됩니다. 이 부분을 유의해 주세요.
s = '060310'
df = return_all[return_all['code']==s].sort_index().copy()
df['close_r1'] = df['close'].shift(-1)/df['close'] # 1 일후 종가 수익률
df['close_r2'] = df['close'].shift(-2)/df['close'] # 2 일후 종가 수익률
df['close_r3'] = df['close'].shift(-3)/df['close'] # 3 일후 종가 수익률
df['close_r4'] = df['close'].shift(-4)/df['close'] # 4 일후 종가 수익률
df['close_r5'] = df['close'].shift(-5)/df['close'] # 5 일후 종가 수익률
''' 위 코드와 같은 결과
df['return_1'] = df['return'].shift(-1)
df['return_2'] = df['return'].shift(-2)*df['return'].shift(-1)
df['return_3'] = df['return'].shift(-3)*df['return'].shift(-2)*df['return'].shift(-1)
df['return_4'] = df['return'].shift(-4)*df['return'].shift(-3)*df['return'].shift(-2)*df['return'].shift(-1)
df['return_5'] = df['return'].shift(-5)*df['return'].shift(-4)*df['return'].shift(-3)*df['return'].shift(-2)*df['return'].shift(-1)
'''
df['target'] = df[['close_r1','close_r2','close_r3','close_r4','close_r5']].max(axis=1) # 주어지 컬럼에서 최대 값을 찾고 'target' 에 저장
df.dropna(subset=['close_r1','close_r2','close_r3','close_r4','close_r5'], inplace=True) # 주어진 컬럼 중에 missing 값이 있으면 행을 제거(dropna)하고, 자신을 덮어 씀(inplace=True).
df.head(10).style.set_table_attributes('style="font-size: 12px"')
open | high | low | close | volume | change | code | name | kosdaq_return | return | win_market | close_r1 | close_r2 | close_r3 | close_r4 | close_r5 | target | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2021-01-05 | 2270 | 2285 | 2200 | 2250 | 410263 | -0.004425 | 060310 | 3S | 1.008326 | 0.995575 | 0 | 1.017778 | 1.017778 | 0.997778 | 0.966667 | 0.971111 | 1.017778 |
2021-01-06 | 2225 | 2310 | 2215 | 2290 | 570349 | 0.017778 | 060310 | 3S | 0.995567 | 1.017778 | 1 | 1.000000 | 0.980349 | 0.949782 | 0.954148 | 0.949782 | 1.000000 |
2021-01-07 | 2290 | 2340 | 2240 | 2290 | 519777 | 0.000000 | 060310 | 3S | 1.007612 | 1.000000 | 0 | 0.980349 | 0.949782 | 0.954148 | 0.949782 | 0.958515 | 0.980349 |
2021-01-08 | 2300 | 2315 | 2225 | 2245 | 462568 | -0.019651 | 060310 | 3S | 0.998918 | 0.980349 | 0 | 0.968820 | 0.973274 | 0.968820 | 0.977728 | 0.973274 | 0.977728 |
2021-01-11 | 2230 | 2275 | 2130 | 2175 | 409057 | -0.031180 | 060310 | 3S | 0.988702 | 0.968820 | 0 | 1.004598 | 1.000000 | 1.009195 | 1.004598 | 1.002299 | 1.009195 |
2021-01-12 | 2165 | 2225 | 2125 | 2185 | 244835 | 0.004598 | 060310 | 3S | 0.997020 | 1.004598 | 1 | 0.995423 | 1.004577 | 1.000000 | 0.997712 | 1.013730 | 1.013730 |
2021-01-13 | 2185 | 2210 | 2170 | 2175 | 127817 | -0.004577 | 060310 | 3S | 1.005556 | 0.995423 | 0 | 1.009195 | 1.004598 | 1.002299 | 1.018391 | 1.022989 | 1.022989 |
2021-01-14 | 2180 | 2205 | 2150 | 2195 | 174996 | 0.009195 | 060310 | 3S | 1.001185 | 1.009195 | 0 | 0.995444 | 0.993166 | 1.009112 | 1.013667 | 1.029613 | 1.029613 |
2021-01-15 | 2190 | 2265 | 2185 | 2185 | 345872 | -0.004556 | 060310 | 3S | 0.983831 | 0.995444 | 0 | 0.997712 | 1.013730 | 1.018307 | 1.034325 | 1.032037 | 1.034325 |
2021-01-18 | 2185 | 2220 | 2150 | 2180 | 251311 | -0.002288 | 060310 | 3S | 0.979501 | 0.997712 | 0 | 1.016055 | 1.020642 | 1.036697 | 1.034404 | 1.052752 | 1.052752 |
이제 모든 종목에 대하여 For loop 로 매수 종가로 매도 시 수익율을 최대값을 생성합니다. ‘max_close’ 의 분포를 보니 평균은 1.033, 최소값 0.326, 최대값 3.703 입니다. 단, max_close 는 가설 검정으로 활용할 지표입니다. 매수 후, 몇 번 째 영업일이 최고 수익율인지 알 수 없기 때문에 기간 중 최고 수익율을 이용합니다.
kosdaq_list = pd.read_pickle('kosdaq_list.pkl')
mdl_data = pd.DataFrame()
for code in kosdaq_list['code']:
df = return_all[return_all['code']==code].sort_index().copy()
df['close_r1'] = df['close'].shift(-1)/df['close']
df['close_r2'] = df['close'].shift(-2)/df['close']
df['close_r3'] = df['close'].shift(-3)/df['close']
df['close_r4'] = df['close'].shift(-4)/df['close']
df['close_r5'] = df['close'].shift(-5)/df['close']
df['max_close'] = df[['close_r1','close_r2','close_r3','close_r4','close_r5']].max(axis=1) # 주어지 컬럼에서 최대 값을 찾음
df.dropna(subset=['close_r1','close_r2','close_r3','close_r4','close_r5'], inplace=True) # 주어진 컬럼 중에 missing 값이 있으면 행을 제거(dropna)하고, 자신을 덮어 씀(inplace=True).
mdl_data = pd.concat([mdl_data, df], axis=0)
mdl_data.to_pickle('mdl_data.pkl')
‘max_close’ 의 분포를 확인합니다.
mdl_data = pd.read_pickle('mdl_data.pkl')
print(mdl_data['max_close'].describe(percentiles=[0.1, 0.2, 0.5, 0.8, 0.9]))
count 426,517.00
mean 1.03
std 0.07
min 0.33
10% 0.98
20% 0.99
50% 1.02
80% 1.06
90% 1.10
max 3.70
Name: max_close, dtype: float64
매도 전략 데이터 프로세싱#
모델 개발을 위해서는 매도 전략에 따는 수익을 계산을 할 수 있어야 합니다. 이번 장에서는 기본적인 몇 가지 전략의 수익율을 계산해보겠습니다. 저장해 둔 mdl_data pickle 파일을 읽습니다. mdl_data 는 수익률 결과값이 있는 데이터입니다.
mdl_data = pd.read_pickle('mdl_data.pkl')
매도 전략 1 - 모든 종목 종가 매수 후, 5 영업일 기간 6% 익절 매도
한가지 전략을 테스트 해 보겠습니다. 모든 종목을 같은 금액으로 매일 종가 매수합니다. 매수 후 5 영업일 동안 수익율이 6% 이상되면 곧바로 익절합니다. 나머지 종목은 5 영업일에 전부 종가 매도하면 수익율은 어떻게 될까요?
kosdaq_list = pd.read_pickle('kosdaq_list.pkl')
data_all_5 = pd.DataFrame()
ub = 1.06
for code in kosdaq_list['code']:
# 종목별 처리
data = mdl_data[mdl_data['code']==code].sort_index().copy()
# 고가, 저가, 종가 수익율
for i in [1, 2, 3, 4, 5]:
data['high_r' + str(i)] = data['high'].shift(-1*i)/data['close']
data['low_r' + str(i)] = data['low'].shift(-1*i)/data['close']
data['close_r' + str(i)] = data['close'].shift(-1*i)/data['close']
data['max_high'] = (data[['high_r1','high_r2','high_r3','high_r4','high_r5']].max(axis=1) > ub).astype(int) # 5 영업일 최고가 중 최고가
data['ub_return'] = np.where(data['max_high']==1, ub, data['close_r5']) # 종가 수익률이 6% 이면 매도, 아니면 마지막 5 영업일 수익률
data.dropna(subset=['close_r1','close_r2','close_r3','close_r4','close_r5'], inplace=True)
data_all_5 = pd.concat([data, data_all_5], axis=0)
data_all_5.to_pickle('data_all_5.pkl')
data_all_5.head().style.set_table_attributes('style="font-size: 12px"')
open | high | low | close | volume | change | code | name | kosdaq_return | return | win_market | close_r1 | close_r2 | close_r3 | close_r4 | close_r5 | max_close | high_r1 | low_r1 | high_r2 | low_r2 | high_r3 | low_r3 | high_r4 | low_r4 | high_r5 | low_r5 | max_high | ub_return | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2021-01-05 | 13000 | 13050 | 12750 | 12900 | 190192 | -0.011494 | 238490 | 힘스 | 1.008326 | 0.988506 | 0 | 1.015504 | 1.015504 | 1.003876 | 1.011628 | 1.038760 | 1.038760 | 1.031008 | 0.984496 | 1.034884 | 1.007752 | 1.027132 | 0.992248 | 1.011628 | 0.965116 | 1.081395 | 1.007752 | 1 | 1.060000 |
2021-01-06 | 13050 | 13300 | 12700 | 13100 | 287008 | 0.015504 | 238490 | 힘스 | 0.995567 | 1.015504 | 1 | 1.000000 | 0.988550 | 0.996183 | 1.022901 | 1.015267 | 1.022901 | 1.019084 | 0.992366 | 1.011450 | 0.977099 | 0.996183 | 0.950382 | 1.064885 | 0.992366 | 1.045802 | 1.003817 | 1 | 1.060000 |
2021-01-07 | 13200 | 13350 | 13000 | 13100 | 203149 | 0.000000 | 238490 | 힘스 | 1.007612 | 1.000000 | 0 | 0.988550 | 0.996183 | 1.022901 | 1.015267 | 1.007634 | 1.022901 | 1.011450 | 0.977099 | 0.996183 | 0.950382 | 1.064885 | 0.992366 | 1.045802 | 1.003817 | 1.019084 | 1.000000 | 1 | 1.060000 |
2021-01-08 | 13200 | 13250 | 12800 | 12950 | 209722 | -0.011450 | 238490 | 힘스 | 0.998918 | 0.988550 | 0 | 1.007722 | 1.034749 | 1.027027 | 1.019305 | 1.027027 | 1.034749 | 1.007722 | 0.961390 | 1.077220 | 1.003861 | 1.057915 | 1.015444 | 1.030888 | 1.011583 | 1.073359 | 1.019305 | 1 | 1.060000 |
2021-01-11 | 12850 | 13050 | 12450 | 13050 | 365602 | 0.007722 | 238490 | 힘스 | 0.988702 | 1.007722 | 1 | 1.026820 | 1.019157 | 1.011494 | 1.019157 | 1.038314 | 1.038314 | 1.068966 | 0.996169 | 1.049808 | 1.007663 | 1.022989 | 1.003831 | 1.065134 | 1.011494 | 1.068966 | 0.996169 | 1 | 1.060000 |
수익률의 분포를 확인합니다. 수익이 되는 전략은 아닙니다.
data_all_5 = pd.read_pickle('data_all_5.pkl')
print(data_all_5['ub_return'].describe(percentiles=[0.01, 0.1, 0.5, 0.9, 0.99]))
print(data_all_5.groupby('max_high')['ub_return'].describe())
count 419,432.00
mean 1.00
std 0.06
min 0.29
1% 0.84
10% 0.93
50% 1.00
90% 1.06
99% 1.06
max 1.06
Name: ub_return, dtype: float64
count mean std min 25% 50% 75% max
max_high
0 280,145.00 0.97 0.05 0.29 0.95 0.98 1.00 1.06
1 139,287.00 1.06 0.00 1.06 1.06 1.06 1.06 1.06
매도 전략 2 - 모든 종목을 종가 매수 후, 아래와 같은 순서로 매도
익일 고가가 당일 고가 보다 크면 2 영업일 시가 매도
1 조건 만족하지 않으면 2 영업일 종가 매도
위와 같은 매도 전략은 수익율이 어떻게 될까요?
kosdaq_list = pd.read_pickle('kosdaq_list.pkl')
data_all_5 = pd.DataFrame()
def final_r(x):
if x['high_r0'] < x['high_r1']: # (당일 고가/매수 종가) 비율이 (익일 고가/매수 종가) 비율 값이 작으면 2 영업일 시가 매도
return x['open_r2']
else:
return x['close_r2'] # 매도 안된 종목은 전부 2 영업일 종가 매도
for code in kosdaq_list['code']:
# 종목별 처리
data = mdl_data[mdl_data['code']==code].sort_index().copy()
# 최고/최저 수익율
for i in [0, 1, 2]:
data['high_r' + str(i)] = data['high'].shift(-1*i)/data['close']
data['close_r' + str(i)] = data['close'].shift(-1*i)/data['close']
data['open_r' + str(i)] = data['open'].shift(-1*i)/data['close']
data['final_return'] = data.apply(final_r, axis=1) # 각 row 에 대하여 final_r 함수를 적용
data.dropna(subset=['close_r0','close_r1', 'close_r2','high_r0', 'high_r1', 'open_r2'], inplace=True) # 데아터 처리 중 missing 값이 사용된 경우는 제거
data_all_5 = pd.concat([data, data_all_5], axis=0)
data_all_5.to_pickle('data_all_5.pkl')
수익률을 확인합니다.
data_all_5 = pd.read_pickle('data_all_5.pkl')
data_all_5['final_return'].describe(percentiles=[0.01, 0.1, 0.5, 0.9, 0.99])
count 423,683.00
mean 1.00
std 0.05
min 0.00
1% 0.89
10% 0.96
50% 1.00
90% 1.04
99% 1.15
max 1.69
Name: final_return, dtype: float64
매수 전략 데이터 프로세싱#
모델 개발을 위해서는 매수 전략에 따라 매수 종목을 결정할 수 있어야 합니다. 이번 장에서는 기본적인 매수 종목을 찾는 데이터처리를 진행해 보겠습니다. 결과 수익률 데이터가 있는 mdl_data pickle 파일을 읽습니다.
mdl_data = pd.read_pickle('mdl_data.pkl')
매수 전략 1 - 시장 수익율보다 더 좋은 수익율을 보인 종목을 매수
시장 수익율보다 더 좋은 수익율을 보인 종목을 알기 위해 4.4.5 절에 ‘win_market’ 이라는 변수를 생성했습니다. 이것을 이용할 것인데요. 더 의미있는 지표를 생성하기 위해서 과거 60일 누적 합을 보겠습니다. 수익율은 max_close(5 영업일 중 최고 종가 수익율) 이용하겠습니다.
kosdaq_list = pd.read_pickle('kosdaq_list.pkl')
data_all_6 = pd.DataFrame()
for code in kosdaq_list['code']:
# 종목별 처리
data = mdl_data[mdl_data['code']==code].sort_index().copy()
# 과거 60일 win_market 누적 합
data['win_market_sum'] = data['win_market'].rolling(60).sum() # 과거 60일 누적합
# 고가, 저가, 종가 수익율
for i in [1,2,3,4,5]:
data['high_r' + str(i)] = data['high'].shift(-1*i)/data['close']
data['low_r' + str(i)] = data['low'].shift(-1*i)/data['close']
data['close_r' + str(i)] = data['close'].shift(-1*i)/data['close']
data['max_close'] = data[['close_r1','close_r2','close_r3','close_r4','close_r5']].max(axis=1) # 5 영업일 종가 수익율 중 최고 값
data.dropna(subset=['win_market_sum','close_r1','close_r2','close_r3','close_r4','close_r5'], inplace=True) # missing 이 있는 행은 제거
data_all_6 = pd.concat([data, data_all_6], axis=0)
data_all_6.to_pickle('data_all_6.pkl')
data_all_6.head().style.set_table_attributes('style="font-size: 12px"')
open | high | low | close | volume | change | code | name | kosdaq_return | return | win_market | close_r1 | close_r2 | close_r3 | close_r4 | close_r5 | max_close | win_market_sum | high_r1 | low_r1 | high_r2 | low_r2 | high_r3 | low_r3 | high_r4 | low_r4 | high_r5 | low_r5 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2021-04-01 | 13100 | 13650 | 13100 | 13400 | 194185 | 0.022901 | 238490 | 힘스 | 1.010051 | 1.022901 | 0 | 1.007463 | 1.022388 | 1.018657 | 1.041045 | 1.026119 | 1.041045 | 9.000000 | 1.018657 | 0.992537 | 1.029851 | 1.000000 | 1.037313 | 1.007463 | 1.041045 | 1.007463 | 1.048507 | 1.026119 |
2021-04-02 | 13500 | 13650 | 13300 | 13500 | 136673 | 0.007463 | 238490 | 힘스 | 1.004463 | 1.007463 | 0 | 1.014815 | 1.011111 | 1.033333 | 1.018519 | 1.022222 | 1.033333 | 9.000000 | 1.022222 | 0.992593 | 1.029630 | 1.000000 | 1.033333 | 1.000000 | 1.040741 | 1.018519 | 1.029630 | 1.014815 |
2021-04-05 | 13600 | 13800 | 13400 | 13700 | 219062 | 0.014815 | 238490 | 힘스 | 0.999670 | 1.014815 | 1 | 0.996350 | 1.018248 | 1.003650 | 1.007299 | 0.992701 | 1.018248 | 9.000000 | 1.014599 | 0.985401 | 1.018248 | 0.985401 | 1.025547 | 1.003650 | 1.014599 | 1.000000 | 1.010949 | 0.989051 |
2021-04-06 | 13800 | 13900 | 13500 | 13650 | 135914 | -0.003650 | 238490 | 힘스 | 0.998824 | 0.996350 | 0 | 1.021978 | 1.007326 | 1.010989 | 0.996337 | 1.003663 | 1.021978 | 9.000000 | 1.021978 | 0.989011 | 1.029304 | 1.007326 | 1.018315 | 1.003663 | 1.014652 | 0.992674 | 1.007326 | 0.996337 |
2021-04-07 | 13700 | 13950 | 13500 | 13950 | 195408 | 0.021978 | 238490 | 힘스 | 1.004739 | 1.021978 | 0 | 0.985663 | 0.989247 | 0.974910 | 0.982079 | 1.017921 | 1.017921 | 9.000000 | 1.007168 | 0.985663 | 0.996416 | 0.982079 | 0.992832 | 0.971326 | 0.985663 | 0.974910 | 1.021505 | 0.971326 |
win_market_sum 에 따른 수익률의 변화를 확인합니다. win_market_sum 이 클수록 수익률이 높아지는 경향이 있다는 것을 확인했습니다.
data_all_6 = pd.read_pickle('data_all_6.pkl')
ranks = pd.qcut(data_all_6['win_market_sum'], q=8)
print(data_all_6.groupby(ranks)['max_close'].mean())
data_all_6.groupby(ranks)['max_close'].mean().plot(figsize=(12,5))
win_market_sum
(-0.001, 4.0] 1.02
(4.0, 5.0] 1.03
(5.0, 6.0] 1.03
(6.0, 7.0] 1.03
(7.0, 8.0] 1.03
(8.0, 9.0] 1.03
(9.0, 11.0] 1.04
(11.0, 22.0] 1.04
Name: max_close, dtype: float64
<AxesSubplot:xlabel='win_market_sum'>

매수 전략 2 - 섹터 평균 수익율보다 더 높은 수익율을 보인 종목을 매수
kosdaq_list 에 있는 종목별 섹터 정보를 이용하겠습니다. 우선, 종목별 최근 60일 평균 수익율을 rolling 함수를 이용하여 으로 계산합니다. for Loop 을 이용하여 종목에 섹터 정보를 추가합니다.
kosdaq_list = pd.read_pickle('kosdaq_list.pkl')
data_all_6 = pd.DataFrame()
for code, sector in zip(kosdaq_list['code'], kosdaq_list['sector']):
# 종목별 처리
data = mdl_data[mdl_data['code']==code].sort_index().copy()
data.dropna(inplace=True)
# 최근 60일 평균 수익율
data['return_mean'] = data['return'].rolling(60).mean() # 종목별 최근 60 일 수익율의 평균
data['sector'] = sector
data.dropna(subset=['return_mean'], inplace=True)
data_all_6 = pd.concat([data, data_all_6], axis=0)
data_all_6.to_pickle('data_all_6.pkl')
data_all_6 = pd.read_pickle('data_all_6.pkl')
data_all_6.head().style.set_table_attributes('style="font-size: 12px"')
open | high | low | close | volume | change | code | name | kosdaq_return | return | win_market | close_r1 | close_r2 | close_r3 | close_r4 | close_r5 | max_close | return_mean | sector | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2021-04-01 | 13100 | 13650 | 13100 | 13400 | 194185 | 0.022901 | 238490 | 힘스 | 1.010051 | 1.022901 | 0 | 1.007463 | 1.022388 | 1.018657 | 1.041045 | 1.026119 | 1.041045 | 1.000755 | 특수 목적용 기계 제조업 |
2021-04-02 | 13500 | 13650 | 13300 | 13500 | 136673 | 0.007463 | 238490 | 힘스 | 1.004463 | 1.007463 | 0 | 1.014815 | 1.011111 | 1.033333 | 1.018519 | 1.022222 | 1.033333 | 1.001071 | 특수 목적용 기계 제조업 |
2021-04-05 | 13600 | 13800 | 13400 | 13700 | 219062 | 0.014815 | 238490 | 힘스 | 0.999670 | 1.014815 | 1 | 0.996350 | 1.018248 | 1.003650 | 1.007299 | 0.992701 | 1.018248 | 1.001059 | 특수 목적용 기계 제조업 |
2021-04-06 | 13800 | 13900 | 13500 | 13650 | 135914 | -0.003650 | 238490 | 힘스 | 0.998824 | 0.996350 | 0 | 1.021978 | 1.007326 | 1.010989 | 0.996337 | 1.003663 | 1.021978 | 1.000998 | 특수 목적용 기계 제조업 |
2021-04-07 | 13700 | 13950 | 13500 | 13950 | 195408 | 0.021978 | 238490 | 힘스 | 1.004739 | 1.021978 | 0 | 0.985663 | 0.989247 | 0.974910 | 0.982079 | 1.017921 | 1.017921 | 1.001556 | 특수 목적용 기계 제조업 |
최근 60 일 평균수익율 정보를 섹터 별, 일 별로 요약한 값을 추가합니다. 이때 apply 대신 Transform 함수가 이용되었습니다. apply 는 그룹의 숫자 만큼 행을 리턴하나, transform 은 그룹핑 하기 전의 행 수 를 리턴합니다. 그 값을 ‘return over sector’ 라는 변수에 저장합니다.
data_all_6['sector_return'] = data_all_6.groupby(['sector', data_all_6.index])['return'].transform(lambda x: x.mean())
data_all_6['return over sector'] = (data_all_6['return']/data_all_6['sector_return']) # 섹터의 평균 수익률 대비 종목 수익률의 비율
결과를 보니, 섹터를 이용하여 종목을 선정할 때는 섹터 평균 수익율보다 많이 높거나, 많이 낮는 종목을 선정하는 것이 수익율이 좋게 나왔습니다. 섹터 평균 수익율 대비 종목 수익율은 미래 수익율 예측에 도움이 되는 정보입니다.
pd.options.display.float_format = '{:,.3f}'.format
ranks = pd.qcut(data_all_6['return over sector'], q=10)
print(data_all_6.groupby(ranks)['max_close'].describe(percentiles=[0.01, 0.99]))
data_all_6.groupby(ranks)['max_close'].mean().plot(figsize=(12,5))
count mean std min 1% 50% 99% max
return over sector
(0.378, 0.974] 34,292.000 1.042 0.086 0.700 0.920 1.022 1.384 2.968
(0.974, 0.984] 34,291.000 1.034 0.067 0.702 0.945 1.018 1.302 2.171
(0.984, 0.99] 34,292.000 1.030 0.061 0.700 0.949 1.016 1.278 2.330
(0.99, 0.994] 34,291.000 1.028 0.059 0.701 0.952 1.014 1.265 2.269
(0.994, 0.999] 34,291.000 1.027 0.058 0.708 0.954 1.013 1.256 2.729
(0.999, 1.002] 34,291.000 1.029 0.066 0.700 0.949 1.013 1.295 3.701
(1.002, 1.007] 34,292.000 1.027 0.059 0.700 0.951 1.012 1.255 3.027
(1.007, 1.013] 34,291.000 1.028 0.062 0.700 0.949 1.013 1.276 3.380
(1.013, 1.026] 34,291.000 1.031 0.067 0.326 0.944 1.014 1.307 2.412
(1.026, 1.399] 34,292.000 1.042 0.103 0.700 0.910 1.019 1.419 3.703
<AxesSubplot:xlabel='return over sector'>

한 섹터에 최소 10 개 이상의 종목이 있어야 섹터의 평균 수익율이 의미가 있을 것 같습니다. 10개 이상의 종목이 있는 섹터만을 매수 대상으로 해서 다시 수익율을 계산해봅니다. 같은 결과를 얻었습니다. 섹터의 평균 수익율보다 아주 낮거나 높은 종목의 수익율의 상승이 높았습니다. 그래프 곡선이 더 부드러워졌습니다.
sector_count = data_all_6.groupby('sector')['code'].nunique().sort_values()
data_all_6x = data_all_6[data_all_6['sector'].isin(sector_count[sector_count>=10].index)]
ranks = pd.qcut(data_all_6x['return over sector'], q=10)
print(data_all_6x.groupby(ranks)['max_close'].describe(percentiles=[0.01, 0.99]))
data_all_6x.groupby(ranks)['max_close'].mean().plot(figsize=(12,5))
count mean std min 1% 50% 99% max
return over sector
(0.688, 0.973] 26,887.000 1.042 0.087 0.700 0.918 1.022 1.388 2.968
(0.973, 0.983] 26,886.000 1.034 0.067 0.702 0.944 1.019 1.299 2.171
(0.983, 0.989] 26,886.000 1.030 0.060 0.704 0.948 1.016 1.267 2.286
(0.989, 0.994] 26,886.000 1.028 0.060 0.700 0.952 1.014 1.269 2.194
(0.994, 0.998] 26,889.000 1.027 0.058 0.819 0.954 1.013 1.255 2.729
(0.998, 1.002] 26,883.000 1.026 0.059 0.700 0.954 1.012 1.257 2.652
(1.002, 1.007] 26,886.000 1.027 0.062 0.700 0.950 1.012 1.257 3.027
(1.007, 1.014] 26,886.000 1.028 0.062 0.700 0.949 1.013 1.285 3.380
(1.014, 1.027] 26,886.000 1.031 0.068 0.700 0.943 1.014 1.315 2.412
(1.027, 1.399] 26,887.000 1.043 0.105 0.700 0.909 1.019 1.423 3.703
<AxesSubplot:xlabel='return over sector'>
