5일 이동 평균선이 오늘 종가보다 위에 위치해 있다.
import FinanceDataReader as fdr
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')
pd.options.display.float_format = '{:,.3f}'.format
5일 이동 평균선이 오늘 종가보다 위에 위치해 있다.#
rolling(5) 을 이용하여 이동평균선을 만듭니다. 그리고 당일의 종가보다 크면, 1 아니면 0 인 변수 ‘flag’ 을 생성합니다. 이 가설은 검증이 쉬운 것 같습니다.
mdl_data = pd.read_pickle('mdl_data.pkl') # 수익률 결과가 있는 데이터
mdl_data.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 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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 |
kosdaq_list = pd.read_pickle('kosdaq_list.pkl')
data_h2 = pd.DataFrame()
for code in kosdaq_list['code']:
data = mdl_data[mdl_data['code']==code].sort_index().copy()
data['5day_ma'] = data['close'].rolling(5).mean() # 5일 이동평균선
data['flag'] = (data['close'] < data['5day_ma']).astype(int) # 5일 이동평균선이 종가보다 크면 1, 아니면 0
data['max_close'] = data[['close_r1','close_r2','close_r3','close_r4','close_r5']].max(axis=1) # 5 영업일 종가 수익율 중 최고 값
data.dropna(subset=['5day_ma','close_r1','close_r2','close_r3','close_r4','close_r5'], inplace=True) # missing 이 있는 행은 제거
data_h2 = pd.concat([data, data_h2], axis=0)
data_h2.to_pickle('data_h2.pkl')
‘flag’ 가 0 인 경우와 1 인 경우를 비교해보니 이 가설은 데이터가 강하게 뒷받침하지 못하고 있습니다.
data_h2 = pd.read_pickle('data_h2.pkl')
data_h2.groupby('flag')['max_close'].describe().style.set_table_attributes('style="font-size: 12px"').format(precision=3)
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
flag | ||||||||
0 | 209501.000 | 1.032 | 0.077 | 0.326 | 1.000 | 1.012 | 1.044 | 3.703 |
1 | 211348.000 | 1.034 | 0.068 | 0.700 | 0.999 | 1.019 | 1.052 | 3.139 |
T-Test 를 해보겠습니다. T-Test 는 두 집단의 평균이 서로 유의미하게 다른 지 확인하는 검정입니다. 귀무가설이 “두 집단의 평균이 같다” 이기 때문에, p -value 가 유의수준(0.01) 보다 작으면 귀무가설을 기각합니다. 결과를 보니 P-Value 가 유의수준(0.01) 보다 큽니다. 따라서 귀무가설을 기각할 수 없습니다. 즉, flag 가 0 인 집단과 1 인 집단간의 차가 유의미하지 않은 것으로 판단됩니다. 왜 각 집단에서 샘플을 200 개만 뽑아서 테스트를 하는 지 궁금한 독자도 있으실 것 같습니다. 통계 검정은 샘플의 수가 많아지면 p value 가 작게 나오는 경향이 있습니다. 그렇게 되면 유의미하게 차이가 없는데도, 서로 다르다고 통계 결과가 나오게됩니다.
from scipy import stats
a = data_h2[data_h2['flag']==0]['max_close'].sample(200)
b = data_h2[data_h2['flag']==1]['max_close'].sample(200)
stats.ttest_ind(a, b, equal_var=False)
Ttest_indResult(statistic=-1.8358785283648644, pvalue=0.06714153725869931)
위 가설은 비교적 증명하기가 쉬웠습니다. 이번에는 5일선과 20일 이동평균선이 만나는 골든크로스에서 매수를 하면 어떤지 보겠습니다. 골든 크로스에서 매수한다고 더 좋은 수익율을 보장하지 않는 것 같습니다.
kosdaq_list = pd.read_pickle('kosdaq_list.pkl')
data_h2 = pd.DataFrame()
for code in kosdaq_list['code']:
data = mdl_data[mdl_data['code']==code].sort_index().copy()
data['5day_ma'] = data['close'].rolling(5).mean() # 5일 이동평균선
data['20day_ma'] = data['close'].rolling(20).mean() # 20일 이동평균선
data['golden_cross'] = (data['5day_ma'].shift(1) < data['20day_ma'].shift(1))*(data['5day_ma'] > data['20day_ma']).astype(int) # 5일선이 20일 이동평균선보다 작았다가 커지는 시점
data['max_close'] = data[['close_r1','close_r2','close_r3','close_r4','close_r5']].max(axis=1) # 5 영업일 종가 수익율 중 최고 값
data.dropna(subset=['5day_ma','20day_ma','golden_cross','close_r1','close_r2','close_r3','close_r4','close_r5'], inplace=True) # missing 이 있는 행은 제거
data_h2 = pd.concat([data, data_h2], axis=0)
data_h2.to_pickle('data_h2.pkl')
data_h2 = pd.read_pickle('data_h2.pkl')
data_h2.groupby('golden_cross')['max_close'].describe().style.set_table_attributes('style="font-size: 12px"').format(precision=3)
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
golden_cross | ||||||||
0 | 387430.000 | 1.032 | 0.071 | 0.326 | 1.000 | 1.016 | 1.048 | 3.703 |
1 | 12164.000 | 1.033 | 0.079 | 0.700 | 0.997 | 1.015 | 1.047 | 2.852 |