바로 이전 글에서 어떤 API를 사용해서 증권 가격 정보를 파이썬으로 받아올 까 고민을 했었다.
이런 고민의 배경에는 미래의 공부를 위해 수정 종가(adjusted close price) 정보 뿐 아니라, 흔히 OHLC라고 부르는 시고저종가의 수정가격을 전부 알고 싶었기 때문이다.
과거의 가격 정보를 가지고 데이터를 뽑아낼 때는 반드시 수정 가격을 사용해야한다. 그렇지 않으면 이런 사태가 벌어진다.
위는 애플의 10년 종가 차트다. 2014년 6월 9일 애플의 주가가 한순간에 떡락을 해버린걸까?...
당연히 아니다. 그 날 너무 비싼 주당 가격을 1대 7로 액면분할 했을 뿐이다. 그 사실은 애플의 투자자를 위한 공식 자료에서도 볼 수 있다. (링크)
수정 가격을 사용하면 이런 과거의 절대 가격도 현재 가격의 시점으로 볼 수 있게 된다.
애플의 위엄이 이제야 제대로 보인다.
현재 시점으로 과거 가격에다 과거 발생한 액면분할(stock split)과 현금배당(cash dividends)을 반영한 가격이 수정가격(adjusted price)이라고 보면 된다.
안타깝게도 모든 수정 OHLC 가격을 그냥 뿌려주는 API는 없었다. 그래서 희망을 가져본 것이 FinanceDataReader 패키지였다. 잘은 몰라도 investing.com 등에서 이미 수정된 가격을 가져오는 것 같았다. 그런데 아무리 봐도 아쉬운 점이 있었으니, 과거 데이터로 갈수록 실제 수정 가격이랑 약간 오차가 있었다.*
*오차가 있다고 스스로 결론지었지만, 잘못된 정보를 드렸을 가능성이 있으므로 여러분도 직접 계산을 해보길 바랍니다.
주말 간 약간의 구글링과 시행착오 끝에 수정 가격 구하는 코드를 짤 수 있었다.
참고한 글: 링크
Vectorizing Adjusted Close with Python
Adjusted prices are essential when working with historical stock prices. Any time there is a corporate split or dividend, all stock prices prior to that event need to be adjusted to reflect the change.
joshschertz.com
위 글을 바탕으로 필요에 맞게 코드를 약간 수정하였다.
import datetime as dt
from dateutil.relativedelta import relativedelta
import pandas_datareader as web
import numpy as np
from pandas import DataFrame, Series
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (14,8)
plt.rcParams['font.size'] = 16
plt.rcParams['lines.linewidth'] = 1
plt.rcParams["axes.grid"] = True
plt.rcParams['axes.axisbelow'] = True
import mplfinance as mpf
먼저 패키지들을 import해오자.
datetime 및 relativedelta는 정확히 현재 시점으로 과거 10년의 데이터를 가져오기 위해서
가격 정보 수집을 위해서 pandas_datareader를,
벡터 연산을 위해 numpy를,
가격정보를 담을 자료구조를 위해 pandas DataFrame을,
차트를 그리기 위해 matplotlib과 mplfinance를 import했다.
plt.rcParams...부분은 앞으로 그릴 플롯의 형태를 미리 전역 설정해주는것이다. 자세한건 나중에 따로 공부해야지.
date_today = dt.date.today()
date_10yrs_ago = date_today - relativedelta(years=10)
10년 전 오늘의 날짜를 정확하게 가져오려면 이러한 방식을 사용해야 한다.
이해가 안 가는 것이 있다면 datetime 패키지의 timedelta 클래스를 사용해서는 1년 이상의 날짜 연산이 안된다.
timeutil의 relativedelta 클래스를 사용해줌으로써 해결.
av_apikey = 'YOUR API KEY'
df_AAPL_av = web.DataReader('AAPL', 'av-daily-adjusted', start=date_10yrs_ago, api_key=av_apikey)
중요한 부분이다. 애플의 10년 가격 정보와 수정 정보를 받아오는것인데, 그 전에 먼저 alpha-vantage(이하 av)에 무료 회원가입을 하고서 API 키를 받아야한다(링크).
가입하면서 알려주는 API 키는 한번 창을 끄면 다시는 알려주지 않으므로 반드시 따로 저장해놓도록 한다. 이렇게 받은 API키는 av_apikey 변수에 string으로 할당해놓자.
pandas_datareader의 DataReader 메서드를 통해 av API를 사용할 수 있다.
첫 번째 패러미터로 티커를, 두번째는 'av-daily-adjusted'를 입력해줌으로써 av의 일간 수정 가격을 받아오겠다고 명시하자. 세 번째에는 아까 받은 API key를 꼭 제공해야 한다.
df_AAPL_av.index = pd.to_datetime(df_AAPL_av.index)
df_AAPL_av.index.name = 'Date'
그리고 약간의 가공을 해주자. av에서는 데이터의 인덱스(날짜)를 string type으로 주는데, 호환성 높은 시계열 데이터로 만들기 위해서는 to_datetime을 사용해 datetime type으로 인덱스를 바꿔주는 게 좋을 것이다.
그러면 다음과 같이 10년치 애플 가격이 받아와진다.
처음에 여기서 한번 실망을 했다. 왜 수정 종가밖에 없는거지? 하고. 그런데 알고보니 오른쪽 두 열이 힌트였다.
dividend amount가 배당이고 split coefficient가 액면분할계수다.
참고로 기업의 주식배당(stock dividends)는 원 글을 참고하면, 주식 지출이 (회사 장부로부터 주주에게로) 이미 존재하는 주식의 소유권만 변경하기 때문에 과거의 가격 수정이 필요하지 않다고 한다. 즉 기업의 시가 총액이 변하는 것이 아니므로 가격을 수정할 필요가 없는 것이다.
배당 및 주식 액면분할 효과란 즉 액면분할하면 액면분할계수대로 나누고, 배당을 주면 주당 현금 배당금만큼 주가에서 빼면 된다. 상식적이다. 위는 그것을 공식화한 것인데, 주의할 점은 'S(Split ratio)'다. 1대 7 액면분할을 하면 S=1/7이 된다. 그래서 공식에서는 바로 위에서 말한 것과 다르게 '곱하기'로 표시되어 있는 것이다.
이제 이것을 코드화하는 일만 남았다.
def calculate_adjusted_prices(df: DataFrame, column: str, split_coefficient: str, dividend: str):
"""
Refer to https://joshschertz.com/2016/08/27/Vectorizing-Adjusted-Close-with-Python/
Vectorized approach for calculating the adjusted prices for the
specified column in the provided DataFrame. This creates a new column
called 'adj_<column name>' with the adjusted prices. This function requires
that the DataFrame have columns with dividend and split_ratio values.
:param df: DataFrame with raw prices along with dividend and split_ratio
values
:param column: String of which price column should have adjusted prices
created for it
:param split_coefficient: String of split coefficient column.
e.g. split_coefficient=7 means that 1 stock is split into 7 stocks.
:param dividend: String of dividend column
:return: DataFrame with the addition of the adjusted price column
"""
adj_column = 'adj_' + column
# Remove redundant column
if adj_column in df.columns:
del df[adj_column]
# Reverse the DataFrame order, sorting by date in descending order
df.sort_index(ascending=False, inplace=True)
# Extract values
price_col = df[column].values
split_col = df[split_coefficient].values
dividend_col = df[dividend].values
adj_price_col = np.zeros(len(df.index))
adj_price_col[0] = price_col[0]
# Calculate adjusted prices
for i in range(1, len(price_col)):
adj_price_col[i] = \
round(
number=(adj_price_col[i - 1] + adj_price_col[i - 1] * (((price_col[i] / split_col[i - 1]) - price_col[i - 1] - dividend_col[i - 1]) / price_col[i - 1])),
ndigits=4
)
df[adj_column] = adj_price_col
# Change the DataFrame order back to dates ascending
df.sort_index(ascending=True, inplace=True)
return df
위 공식을 벡터화하여 계산하는 방법이다. 사실 원작자는 처음엔 loop를 사용해서 현재부터 과거까지 하나하나 계산했는데, 긴 시계열에서 아주 오랜 시간이 걸렸다고 한다. 그래서 이 연산을 벡터화(링크)한 코드가 위의 코드다. 이론적인 부분까지 깊게 들어가긴 어렵다. 그의 헌신을 감사히 여기고 그의 코드를 사용하자. 위 코드는 원본에서 약간의 수정을 거쳤다. av의 split coefficient column에는 1대 n 액면분할시에 1/n이 아닌 n이 그대로 들어가있었다. 해당 부분을 수정했다. (나의 헌신도 한방울 포함되어있다.)
코드 자체는 이해하기 어렵지 않다. dataframe의 필요한 각 column을 numpy에 넣어서 열벡터로 각각 만들고, 위에 언급한 공식을 그대로 적용해주는 것이다. 인접한 가격이 수정되는것을 반영해주어야 하므로 시계열 길이만큼 반복해준다.
메서드는 만들어져있으니 적용해본다.
for price in ['open', 'high', 'low', 'close']:
ret_df = calculate_adjusted_prices(df_AAPL_av, price, 'split coefficient', 'dividend amount')
이런 dataframe이 만들어진다.
av에서 기본으로 제공하는 adjusted close column과 직접 계산한 adj_close column의 값을 비교해보면, 아주 먼 과거에서도 별로 큰 차이가 없다. 내가 FinanceDataReader를 아쉬워했던것이 이 부분이다. FinanceDataReader에서는 먼 과거 시점의 가격 정보가 꽤 틀어져있었기 때문이다. 원본 OHLC가격 정보, 배당금이나 액면분할계수는 그냥 과거의 사실 그 자체임에도 불구하고 계산한 값과 다르다는 것을 받아들이기 어려웠다.
df_APPL_adj_ohlc = ret_df.loc[:, ['adj_open', 'adj_high', 'adj_low', 'adj_close', 'volume']]
df_APPL_adj_ohlc.columns = ['Open', 'High', 'Low', 'Close', 'Volume']
mpf.plot(df_APPL_adj_ohlc, type='candle')
이제 수정 가격만을 가져와서 정리해준다.
눈에는 잘 안띄지만, 수정 OHLC 가격의 캔들 차트도 볼 수 있게 되었다. 끝!
'파이썬' 카테고리의 다른 글
파이썬 pandas-datareader로 네이버 금융 API를 사용할수 있다(?) (0) | 2020.07.29 |
---|---|
주가 수익률은 정말 정규분포를 따를까? (0) | 2020.07.27 |
파이썬 패키지를 이용한 주가 데이터 수집 방법_1 (1) | 2020.06.14 |