[Project] 주식을 몇 시에 매수하는 것이 가장 좋을까??
Quant2022. 9. 23. 22:11
반응형
Project : 주식 매수를 몇시에 하는 것이 가장 좋을까?¶
인베스팅에서 제공하는 차트에서 nasdaq 100의 데이터를 크롤링한다.
이때 데이터가 있는 사이트는 정적 페이지이므로 bs4를 사용한다.
In [1]:
import requests
from bs4 import BeautifulSoup
In [2]:
# simbol - nasdaq 100
# resolution - hour (60min)
# from & to - time line
url = "https://tvc4.investing.com/9bc5a8c1e5c8929dcfe46983235e7b41/1644651978/18/18/88/history?symbol=20&resolution=60&from=1583096400&to=1644652046"
from urllib.request import Request, urlopen
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
webpage = urlopen(req).read()
In [3]:
# 358일치의 시간단위 데이터
(1644537600 - 1613606400)/ 3600 / 24
Out[3]:
358.0
In [ ]:
# HTML -> Str
str_data = webpage.decode('utf-8')
print(str_data)
In [5]:
# string 형식의 data set을 dictionary로 바꿔줌
data = eval(str_data)
primary_time, recent_time = data['t'][0] , data['t'][-1]
In [6]:
# 가장 과거와 최근 시간 파악
from datetime import datetime
import time
# timestamp to datetime
datetimeobj = datetime.fromtimestamp(recent_time)
datetimeobj.strftime("%Y-%m-%d-%H")
Out[6]:
'2022-02-11-21'
In [7]:
# 개장 시간이 14시이고 장 마감이 21시로 되어있기에 수정
for i in range(0,len(data['t'])):
data['t'][i] -= 18000
In [8]:
# 시간 데이터 "%Y-%m-%d-%H" 꼴로 변환
date_list = []
for tmp in data['t']:
d = datetime.fromtimestamp(tmp)
date_list.append(d.strftime("%Y-%m-%d-%H"))
# 마지막 20개의 시간 데이터 확인
date_list[-20:]
Out[8]:
['2022-02-09-13',
'2022-02-09-14',
'2022-02-09-15',
'2022-02-09-16',
'2022-02-10-09',
'2022-02-10-10',
'2022-02-10-11',
'2022-02-10-12',
'2022-02-10-13',
'2022-02-10-14',
'2022-02-10-15',
'2022-02-10-16',
'2022-02-11-09',
'2022-02-11-10',
'2022-02-11-11',
'2022-02-11-12',
'2022-02-11-13',
'2022-02-11-14',
'2022-02-11-15',
'2022-02-11-16']
bs4로 크롤링한 데이터의 형식은 str이므로, str을 사용가능한 dict 형태로 바꾸어 준 후,
DataFrame으로 다시 변환 시킨다.
In [9]:
# dict형 OHLC 데이터 -> dataframe
import pandas as pd
z = zip(data['o'],data['h'],data['l'],data['c'])
z = list(z)
df_date = pd.DataFrame(date_list, columns=['date'])
df_price = pd.DataFrame(z, columns=['Open','High','Low','Close'])
df_price
Out[9]:
Open | High | Low | Close | |
---|---|---|---|---|
0 | 8565.199219 | 8590.081055 | 8491.083008 | 8517.889648 |
1 | 8520.058594 | 8596.576172 | 8446.653320 | 8560.044922 |
2 | 8557.462891 | 8727.137695 | 8557.357422 | 8722.620117 |
3 | 8725.032227 | 8733.438477 | 8659.299805 | 8727.702148 |
4 | 8729.794922 | 8734.330078 | 8635.952148 | 8679.777344 |
... | ... | ... | ... | ... |
3939 | 14573.364258 | 14604.063477 | 14518.272461 | 14521.224609 |
3940 | 14521.410156 | 14553.126953 | 14327.309570 | 14339.782227 |
3941 | 14337.040039 | 14394.203125 | 14225.609375 | 14272.733398 |
3942 | 14271.288086 | 14342.342773 | 14194.586914 | 14247.276367 |
3943 | 14253.838867 | 14253.838867 | 14253.838867 | 14253.838867 |
3944 rows × 4 columns
In [10]:
# 시간데이터 가격데이터 결합
df = pd.concat([df_date,df_price], axis=1, ignore_index=False)
해당 시간대의 변동성을 나타내는 'Volatility' 특성을 새로 추가한다.
In [11]:
# Hour로 몇시인지 새로운 열로 구분
df['Hour'] = df['date'].str[-2:]
# 캔들의 크기 = 해당 시간대의 변동성을 파악
df['Volatility'] = (df['High'] - df['Low'])/ df['Low']
df.head()
Out[11]:
date | Open | High | Low | Close | Hour | Volatility | |
---|---|---|---|---|---|---|---|
0 | 2020-03-02-09 | 8565.199219 | 8590.081055 | 8491.083008 | 8517.889648 | 09 | 0.011659 |
1 | 2020-03-02-10 | 8520.058594 | 8596.576172 | 8446.653320 | 8560.044922 | 10 | 0.017749 |
2 | 2020-03-02-11 | 8557.462891 | 8727.137695 | 8557.357422 | 8722.620117 | 11 | 0.019840 |
3 | 2020-03-02-12 | 8725.032227 | 8733.438477 | 8659.299805 | 8727.702148 | 12 | 0.008562 |
4 | 2020-03-02-13 | 8729.794922 | 8734.330078 | 8635.952148 | 8679.777344 | 13 | 0.011392 |
In [12]:
# 종가 데이터는 open과 end가 같으므로 제거
# 써머 타임인 경우 장 마감이 15이므로
## 써머 타임이 아닌 경우의 데이터를 보존하기위해 캔들크기가 0인 경우만 제거
condition1 = df[(df['Hour'] == '16')].index
condition2 = df[(df['Hour'] == '15') & (df['Volatility'] == 0)].index
df = df.drop(condition1)
df = df.drop(condition2)
각 시간대 별 Volatility 분포를 시각화한다.
In [13]:
df.plot.scatter(x='Hour', y='Volatility',figsize=(16,8))
Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fe1654e0e90>
각 시간대별 캔들의 크기분포 결과 큰 차이가 없어보인다.
대부분이 0.2%의 변동성을 가지고 있음을 확인 할 수 있다.
간단한 회귀전략을 사용한다면 0.2%의 변동을 넘어서는 순간 반대 포지션을 잡는 전략을 세울 수 있을 것이다.
scatter plot 상으로는 의미를 추출하기 어렵기에 boxplot으로도 살펴본다.
In [14]:
import matplotlib.pyplot as plt
boxplot = df.boxplot(column='Volatility', by = 'Hour',figsize=(16,8))
plt.show()
/usr/local/lib/python3.7/dist-packages/matplotlib/cbook/__init__.py:1376: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
X = np.atleast_1d(X.T if isinstance(X, np.ndarray) else np.asarray(X))
boxplot 상 (Upper Fence)1.5*IQR의 결과를 보면
8 ~ 9 시에 커진 변동성이 10 ~ 12시에 다시 줄어들다 13시부터 다시 커짐을 확인 할 수 있다.
프로젝트의 메인 목표인 "몇시에 사는 것이 가장 좋을지" 확인해보자¶
사는 것이 가장 좋을 때란 가장 가격이 낮은 시간을 말한다.
In [18]:
# 각 날별로 시초가 대비 가격 변화율인 Day_rate 특성을 새로 만든다.
day_rate = []
flag = 0
for idx in range(len(df)):
if (df['Hour'].iloc[idx] == '08'):
open = df['Open'].iloc[idx]
elif ((df['Hour'].iloc[idx] == '09') & (df['Hour'].iloc[idx-1] != '08')):
open = df['Open'].iloc[idx]
day_rate.append( ((df['Close'].iloc[idx] - open)/ open)*100 )
In [19]:
df['Day_rate'] = day_rate
df.head(10)
Out[19]:
date | Open | High | Low | Close | Hour | Volatility | Day_rate | |
---|---|---|---|---|---|---|---|---|
0 | 2020-03-02-09 | 8565.199219 | 8590.081055 | 8491.083008 | 8517.889648 | 09 | 0.011659 | -0.552346 |
1 | 2020-03-02-10 | 8520.058594 | 8596.576172 | 8446.653320 | 8560.044922 | 10 | 0.017749 | -0.060177 |
2 | 2020-03-02-11 | 8557.462891 | 8727.137695 | 8557.357422 | 8722.620117 | 11 | 0.019840 | 1.837913 |
3 | 2020-03-02-12 | 8725.032227 | 8733.438477 | 8659.299805 | 8727.702148 | 12 | 0.008562 | 1.897246 |
4 | 2020-03-02-13 | 8729.794922 | 8734.330078 | 8635.952148 | 8679.777344 | 13 | 0.011392 | 1.337717 |
5 | 2020-03-02-14 | 8677.587891 | 8703.663086 | 8577.958008 | 8610.159180 | 14 | 0.014654 | 0.524914 |
6 | 2020-03-02-15 | 8611.547852 | 8876.590820 | 8610.048828 | 8876.590820 | 15 | 0.030957 | 3.635544 |
8 | 2020-03-03-09 | 8892.039062 | 8892.039062 | 8748.486328 | 8782.742188 | 09 | 0.016409 | -1.229154 |
9 | 2020-03-03-10 | 8781.586914 | 8999.271484 | 8781.586914 | 8931.540039 | 10 | 0.024789 | 0.444229 |
10 | 2020-03-03-11 | 8928.881836 | 8935.212891 | 8708.589844 | 8833.012695 | 11 | 0.026023 | -0.663811 |
In [20]:
import matplotlib.pyplot as plt
#plt.figure()
boxplot = df.boxplot(column='Day_rate', by = 'Hour',figsize=(16,8))
plt.show()
/usr/local/lib/python3.7/dist-packages/matplotlib/cbook/__init__.py:1376: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
X = np.atleast_1d(X.T if isinstance(X, np.ndarray) else np.asarray(X))
Day_rate의 Box plot 결과 14, 15시 즈음이 변동성이 가장 큰 부분임을 확인 할 수 있다
따라서 13~15시에 주식을 매수하는 것이 통계적으로 가장 효율적인 타이밍이다.
FeedBack¶
- 358일치 데이터를 사용했지만 그 사이에 시장의 성질이 바뀌었음을 고려하지 않았다.
- Volatility, Day_rate은 모든 날들의 시계열 데이터를 Hour에 투영하므로
시계열의 본질적인 특성이 없어졌다. - 이 부분을 고려한다면, 하루 단위로 시계열 흐름을 표기하는 것이 맞다.
In [22]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
반응형
'Quant' 카테고리의 다른 글
[금융상품] 원자재 투자의 대표 산업 금속과 선물거래 특징 (0) | 2022.09.28 |
---|---|
[Skewness Volatility] 왜 떨어짐은 크고, 상승은 작은가? (0) | 2022.09.20 |
[금융 상품] 달러의 역사 - 미국이 기축통화 지위를 버릴 수 밖에 없는 이유 (월가아재) (38) | 2022.09.07 |
Finance 참고 블로그 (27) | 2022.03.20 |
[Quant Strategy] NCAV 전략 - 2022.03.16 기준 (12) | 2022.03.17 |
댓글()