Skip to content

블로그

기술적 분석 라이브러리 TA-LIB Python Wrapper

기술적 분석(Technical Analysis)는 과거 축적된 주가와 거래량 등을 분석하여 미래 주가의 방향성을 예측하는 분석 방법입니다. 대표적인 보조지표(Indicator)로 이동평균(Moving Average), 볼린저밴드(Bollinger Band), Moving Average Convergence Divergence(MACD), Relative Strength Index(RSI) 등이 있습니다.

보조지표들 외에도 차트에서 패턴을 알아내기도 합니다. 대표적인 패턴으로 Three Line Strike, Two Black Gapping, Three Black Crows, Evening Star, Abandoned Baby 등이 있습니다.

이번 포스트에서는 기술적 분석 라이브러리인 TA-LIB의 Python Wrapper에 대해서 소개하고자 합니다. TA-LIB를 이용하면 이동평균, 볼린저밴드, MACD, RSI 등의 대표적인 기술적 분석 보조지표를 쉽게 계산할 수 있고 다양한 차트 패턴을 포착할 수 있습니다. 자세한 기능은 이 문서에서 확인할 수 있습니다.

우분투 환경에서 TA-LIB 설치를 진행합니다.

Terminal window
wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar -xzf ta-lib-0.4.0-src.tar.gz
cd ta-lib/
./configure --prefix=/usr
make
sudo make install

다음과 같이 make가 없다는 메시지가 뜬다면 make를 먼서 설치하고 다시 make 명령을 실행하면 됩니다.

Command 'make' not found, but can be installed with:
sudo apt install make
sudo apt install make-guile

이제 TA-LIB 파이썬 라이브러리를 설치하면 됩니다. TA-LIB이 설치되어 있어야 TA-LIB 파이썬 라이브러리를 설치할 수 있습니다.

Terminal window
pip install ta-lib

TA-LIB 파이썬 라이브러리를 이용하여 볼린저밴드를 추가해 보겠습니다. 먼저 다음과 같은 OHLCV(Open-High-Low-Close-Volume) 데이터, 즉 봉차트가 있습니다.

import pandas as pd
df = pd.read_csv('chart.csv')
closecodedatediffdiffratiodiffsignhighlowopenpricevolume
46200.0593020180823100.0NaN246200.045700.046150.03093430000006730918
46150.0593020180824-50.0NaN546400.045550.045900.03022130000006582535
46300.0593020180827150.00.325027246550.046000.046100.02436960000005263782
46550.0593020180828250.00.539957246950.046300.046800.03433230000007365976
46800.0593020180829250.00.537057246800.046400.046750.02599640000005572374
47650.0593020180830850.01.816239247950.046700.046950.058025900000012193231
48450.0593020180831800.01.678909248450.047000.047100.065231800000013577454
43500.0593020190806-450.0-1.023891543800.042500.042500.065480500000015083824
43200.0593020190807-300.0-0.689655543900.043100.043600.043450000000010002533
42650.0593020190808-550.0-1.273148543500.042650.043250.072807400000016926881
43150.0593020190809500.01.172333243350.043050.043250.04187190000009685147
43700.0593020190812550.01.274623244000.043550.044000.03705310000008466169
43000.0593020190813-700.0-1.601831543500.042950.043500.03141910000007276979
43700.0593020190814700.01.601831244250.043500.043900.03834270000008750135

TA-LIB 파이썬 라이브러리 모듈 이름은 talib 입니다. talib을 임포트하고 볼린저밴드를 구하는 함수를 호출합니다. 이 문서에서 함수들의 Signature를 확인할 수 있습니다.

upperband, middleband, lowerband = BBANDS(close, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)

이렇게 Upper Bollinger Band, Middle Bollinger Band, Lower Bollinger Band를 쉽게 구할 수 있습니다.

import talib
ubb, mbb, lbb = talib.BBANDS(df.close, 20, 2)
df['ubb'] = ubb
df['mbb'] = mbb
df['lbb'] = lbb
closecodedatediffdiffratiodiffsignhighlowopenpricevolumeubbmbblbb
46200.0593020180823100.0NaN246200.045700.046150.03093430000006730918NaNNaNNaN
46150.0593020180824-50.0NaN546400.045550.045900.03022130000006582535NaNNaNNaN
46300.0593020180827150.00.325027246550.046000.046100.02436960000005263782NaNNaNNaN
46550.0593020180828250.00.539957246950.046300.046800.03433230000007365976NaNNaNNaN
46800.0593020180829250.00.537057246800.046400.046750.02599640000005572374NaNNaNNaN
47650.0593020180830850.01.816239247950.046700.046950.058025900000012193231NaNNaNNaN
48450.0593020180831800.01.678909248450.047000.047100.065231800000013577454NaNNaNNaN
43500.0593020190806-450.0-1.023891543800.042500.042500.06548050000001508382448098.40053746057.544016.599463
43200.0593020190807-300.0-0.689655543900.043100.043600.04345000000001000253348325.70744245940.043554.292558
42650.0593020190808-550.0-1.273148543500.042650.043250.07280740000001692688148540.42638545762.542984.573615
43150.0593020190809500.01.172333243350.043050.043250.0418719000000968514748592.45711345605.042617.542887
43700.0593020190812550.01.274623244000.043550.044000.0370531000000846616948538.70090545467.542396.299095
43000.0593020190813-700.0-1.601831543500.042950.043500.0314191000000727697948456.11615645275.042093.883844
43700.0593020190814700.01.601831244250.043500.043900.0383427000000875013548388.64143945157.541926.358561

파이썬으로 Maximum Drawdown (MDD) 확인하기

Maximum Drawdown (MDD)는 특정 기간동안 발생한 최대 낙폭을 의미하는 하방 리스크 지표 입니다. MDD가 클수록 투자 리스크가 크기 때문에 유의해야 합니다.

MDD = (기간 동안의 최저점 - 기간 동안의 최고점) / 기간 동안의 최고점으로 간단히 구할 수 있습니다.

파이썬에서 MDD를 다음과 같이 구할 수 있습니다.

import numpy as np
def get_mdd(x):
"""
MDD(Maximum Draw-Down)
:return: (peak_upper, peak_lower, mdd rate)
"""
arr_v = np.array(x)
peak_lower = np.argmax(np.maximum.accumulate(arr_v) - arr_v)
peak_upper = np.argmax(arr_v[:peak_lower])
return peak_upper, peak_lower, (arr_v[peak_lower] - arr_v[peak_upper]) / arr_v[peak_upper]
data = [['20190219', 125000.0, 127500.0, 123500.0, 126000.0, 57757], ['20190220', 125000.0, 127000.0, 124500.0, 126000.0, 68453], ['20190221', 125500.0, 126000.0, 124000.0, 125000.0, 43961], ['20190222', 125000.0, 125000.0, 123500.0, 125000.0, 31065], ['20190225', 125500.0, 126000.0, 124000.0, 125500.0, 45852], ['20190226', 125000.0, 127000.0, 124500.0, 126500.0, 37404], ['20190227', 126500.0, 127000.0, 124500.0, 126000.0, 36131], ['20190228', 126500.0, 127000.0, 124500.0, 125000.0, 69474], ['20190304', 124500.0, 125500.0, 122500.0, 123500.0, 65517], ['20190305', 123000.0, 125000.0, 122000.0, 124500.0, 35186], ['20190306', 124000.0, 124500.0, 122500.0, 123500.0, 35449], ['20190307', 123500.0, 124000.0, 122000.0, 123500.0, 34768], ['20190308', 122500.0, 122500.0, 120500.0, 121500.0, 35118], ['20190311', 121500.0, 122500.0, 120000.0, 122500.0, 39576], ['20190312', 123000.0, 124000.0, 122000.0, 123500.0, 24117], ['20190313', 123000.0, 123500.0, 121500.0, 123500.0, 37649], ['20190314', 123000.0, 124000.0, 122000.0, 123500.0, 95132], ['20190315', 123000.0, 128000.0, 123000.0, 126500.0, 107246], ['20190318', 127000.0, 131000.0, 126500.0, 131000.0, 74644], ['20190319', 130000.0, 134000.0, 129500.0, 133000.0, 68348], ['20190320', 132000.0, 133000.0, 129500.0, 131000.0, 42697], ['20190321', 130000.0, 132000.0, 127500.0, 128500.0, 54018], ['20190322', 127500.0, 129500.0, 126500.0, 127000.0, 32380], ['20190325', 125500.0, 126500.0, 124000.0, 124500.0, 37185], ['20190326', 124500.0, 125500.0, 123500.0, 124000.0, 45161], ['20190327', 124000.0, 125000.0, 123500.0, 124000.0, 34336], ['20190328', 124000.0, 124500.0, 120500.0, 121500.0, 43518], ['20190329', 122500.0, 125000.0, 122000.0, 124500.0, 39035], ['20190401', 124000.0, 126500.0, 124000.0, 126500.0, 22463], ['20190402', 125500.0, 126500.0, 123500.0, 126000.0, 31754], ['20190403', 124500.0, 128000.0, 124500.0, 128000.0, 36250], ['20190404', 128500.0, 128500.0, 126000.0, 128000.0, 34854], ['20190405', 127500.0, 129000.0, 126000.0, 127500.0, 33513], ['20190408', 127500.0, 128000.0, 126000.0, 128000.0, 39005], ['20190409', 128000.0, 129000.0, 127500.0, 128500.0, 33266], ['20190410', 128000.0, 129000.0, 126500.0, 128000.0, 64476], ['20190411', 128500.0, 129000.0, 125000.0, 125000.0, 84802], ['20190412', 126000.0, 127500.0, 125500.0, 127000.0, 39663], ['20190415', 126500.0, 128500.0, 126000.0, 127000.0, 61140], ['20190416', 127000.0, 129000.0, 126500.0, 128500.0, 40123], ['20190417', 128500.0, 129000.0, 127000.0, 128000.0, 30846], ['20190418', 128000.0, 128500.0, 124000.0, 124500.0, 55346], ['20190419', 124500.0, 125000.0, 122000.0, 123000.0, 53439], ['20190422', 123000.0, 123500.0, 121000.0, 122500.0, 30421], ['20190423', 122500.0, 123500.0, 120500.0, 121500.0, 54997], ['20190424', 122500.0, 122500.0, 119500.0, 120000.0, 63486], ['20190425', 121000.0, 121000.0, 118500.0, 119000.0, 36046], ['20190426', 118500.0, 119500.0, 117000.0, 119000.0, 43749], ['20190429', 119000.0, 119500.0, 117000.0, 119500.0, 33516], ['20190430', 122000.0, 123000.0, 119000.0, 119500.0, 94118], ['20190502', 118500.0, 122000.0, 118000.0, 121000.0, 56723], ['20190503', 121000.0, 122000.0, 119500.0, 120000.0, 35240], ['20190507', 118500.0, 119500.0, 117000.0, 117500.0, 44453], ['20190508', 116500.0, 117000.0, 115000.0, 116500.0, 52805], ['20190509', 117000.0, 117000.0, 113000.0, 113000.0, 116012], ['20190510', 113000.0, 114500.0, 110000.0, 111500.0, 86072], ['20190513', 110500.0, 110500.0, 107500.0, 108500.0, 70847], ['20190514', 107500.0, 108000.0, 105000.0, 106500.0, 92820], ['20190515', 107000.0, 107500.0, 105000.0, 107000.0, 68937], ['20190516', 107000.0, 107500.0, 104000.0, 105000.0, 64047]]
import pandas as pd
df = pd.DataFrame(data, columns=['date', 'open', 'high', 'low', 'close', 'volume'])
mdd = get_mdd(df['close'])
(19, 59, -0.21052631578947367)
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(8, 5))
fig.set_facecolor('w')
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
axes = []
axes.append(plt.subplot(gs[0]))
axes.append(plt.subplot(gs[1], sharex=axes[0]))
axes[0].get_xaxis().set_visible(False)
from mpl_finance import candlestick_ohlc
x = np.arange(len(df.index))
ohlc = df[['open', 'high', 'low', 'close']].astype(int).values
dohlc = np.hstack((np.reshape(x, (-1, 1)), ohlc))
# 봉차트
candlestick_ohlc(axes[0], dohlc, width=0.5, colorup='r', colordown='b')
# 거래량 차트
axes[1].bar(x, df.volume, color='k', width=0.6, align='center')
import datetime
_xticks = []
_xlabels = []
_wd_prev = 0
for _x, d in zip(x, df.date.values):
weekday = datetime.datetime.strptime(str(d), '%Y%m%d').weekday()
if weekday <= _wd_prev:
_xticks.append(_x)
_xlabels.append(datetime.datetime.strptime(str(d), '%Y%m%d').strftime('%m/%d'))
_wd_prev = weekday
axes[1].set_xticks(_xticks)
axes[1].set_xticklabels(_xlabels, rotation=45, minor=False)
# MDD 그리기
axes[0].plot(mdd[:2], df.loc[mdd[:2], 'close'], 'k')
plt.tight_layout()
plt.show()

mdd

이 차트에서는 약 -21%의 MDD가 발생했습니다. 포트폴리오를 구성할 때 보유하고자 하는 종목들의 MDD를 전체적으로 확인해보는 것이 좋습니다. 특히 보유하고자 하는 종목 수가 적을수록 MDD가 낮은 종목을 보유하는 것이 안전할 것입니다.

IMF에서 GDP 관련 지표를 파이썬으로 얻어오기

IMF에서 세계 지표들을 확인할 수 있습니다. 이번 포스트에서는 파이썬으로 IMF 사이트에서 GDP 관련 지표들을 파이썬으로 얻어오는 방법을 다루겠습니다.

국제통화기금(IMF, International Monetary Fund)는 선진국들이 출자하는 펀드이며 세계 경제 지표를 조사하여 공개하고 있습니다. IMF에 대한 자세한 사항은 나무위키 - IMF를 참고하세요.

국내총생산(GDP, Gross Domestic Product)에는 명목 GDP(Nominal GDP)와 실질 GDP(Real GDP)가 있습니다. 명목 GDP는 당해의 시장가격으로 가치를 계산하고 실질 GDP는 물가를 고려한 가격으로 가치를 계산합니다. 따라서 경제성장률은 실질 GDP의 성장률을 의미합니다. 한국의 GDP는 나라지표 사이트에서 확인할 수 있습니다.

한국 뿐만 아니라 세계 주요 국가들의 GDP 관련 지표들을 확인해 보겠습니다. IMF - World Econimic Outlook Databases에서 최신 발표 내용을 확인하면 됩니다.

먼저 By Countries (country-level data)를 선택합니다.

imf weo

그리고 데이터를 얻고자 하는 국가를 선택합니다. 여기서는 모든 국가를 선택하겠습니다.

imf weo

다음으로 얻고자 하는 데이터 종류를 선택합니다. 이 포스트에서 확인하고자 하는 데이터는 Gross domestic product, constant prices (Percent change)Output gap in percent of potential GDP (Percent of potential GDP)이긴 하지만 전체 다 선택하겠습니다.

imf weo

이제 다음 화면에서 Start Year를 1980으로 변경하고 나머지는 기본 설정으로 둔 상태에서 Prepare Report 버튼을 누릅니다.

imf weo

조금 기다리면 다음과 같은 화면이 나옵니다. 화면에 표시하기에 데이터가 너무 크다는 문구가 나오는데, 그 아래에 다운로드 링크가 있습니다. 이 링크를 클릭하면 WEO_Data.xls 파일을 다운받을 수 있습니다. 이 파일은 사실 TSV(Tab-separated Values) 입니다.

imf weo

이제 파이썬으로 이 파일을 다운받고 읽어 보겠습니다. 먼저 다음처럼 requests 라이브러리를 사용해서 IMF 데이터를 요청하고 파일을 저장합니다.

import requests
url = 'https://www.imf.org/external/pubs/ft/weo/2019/01/weodata/weoreptc.aspx?pr.x=49&pr.y=15&sy=1980&ey=2024&scsm=1&ssd=1&sort=country&ds=.&br=1&c=512%2C668%2C914%2C672%2C612%2C946%2C614%2C137%2C311%2C546%2C213%2C674%2C911%2C676%2C314%2C548%2C193%2C556%2C122%2C678%2C912%2C181%2C313%2C867%2C419%2C682%2C513%2C684%2C316%2C273%2C913%2C868%2C124%2C921%2C339%2C948%2C638%2C943%2C514%2C686%2C218%2C688%2C963%2C518%2C616%2C728%2C223%2C836%2C516%2C558%2C918%2C138%2C748%2C196%2C618%2C278%2C624%2C692%2C522%2C694%2C622%2C962%2C156%2C142%2C626%2C449%2C628%2C564%2C228%2C565%2C924%2C283%2C233%2C853%2C632%2C288%2C636%2C293%2C634%2C566%2C238%2C964%2C662%2C182%2C960%2C359%2C423%2C453%2C935%2C968%2C128%2C922%2C611%2C714%2C321%2C862%2C243%2C135%2C248%2C716%2C469%2C456%2C253%2C722%2C642%2C942%2C643%2C718%2C939%2C724%2C734%2C576%2C644%2C936%2C819%2C961%2C172%2C813%2C132%2C726%2C646%2C199%2C648%2C733%2C915%2C184%2C134%2C524%2C652%2C361%2C174%2C362%2C328%2C364%2C258%2C732%2C656%2C366%2C654%2C144%2C336%2C146%2C263%2C463%2C268%2C528%2C532%2C923%2C944%2C738%2C176%2C578%2C534%2C537%2C536%2C742%2C429%2C866%2C433%2C369%2C178%2C744%2C436%2C186%2C136%2C925%2C343%2C869%2C158%2C746%2C439%2C926%2C916%2C466%2C664%2C112%2C826%2C111%2C542%2C298%2C967%2C927%2C443%2C846%2C917%2C299%2C544%2C582%2C941%2C474%2C446%2C754%2C666%2C698&s=NGDP_R%2CNGDP_RPCH%2CNGDP%2CNGDPD%2CPPPGDP%2CNGDP_D%2CNGDPRPC%2CNGDPRPPPPC%2CNGDPPC%2CNGDPDPC%2CPPPPC%2CNGAP_NPGDP%2CPPPSH%2CPPPEX%2CNID_NGDP%2CNGSD_NGDP%2CPCPI%2CPCPIPCH%2CPCPIE%2CPCPIEPCH%2CFLIBOR6%2CTM_RPCH%2CTMG_RPCH%2CTX_RPCH%2CTXG_RPCH%2CLUR%2CLE%2CLP%2CGGR%2CGGR_NGDP%2CGGX%2CGGX_NGDP%2CGGXCNL%2CGGXCNL_NGDP%2CGGSB%2CGGSB_NPGDP%2CGGXONLB%2CGGXONLB_NGDP%2CGGXWDN%2CGGXWDN_NGDP%2CGGXWDG%2CGGXWDG_NGDP%2CNGDP_FY%2CBCA%2CBCA_NGDPD&grp=0&a='
res = requests.get(url)
if res.status_code == 200:
with open('data/2019-06-09-imf_gdp/WEO_Data_2019.xls', 'wb') as fout:
fout.write(res.content)

그리고 저장된 파일을 pandas 라이브러리를 사용하여 읽고 활용합니다. 확장자가 xls이기는 하지만 실제 내용은 TSV 형태이므로 read_csv() 함수를 사용할 수 있습니다.

그리고 저장된 파일을 pandas 라이브러리를 사용하여 읽고 활용합니다. 확장자가 xls이기는 하지만 실제 내용은 TSV 형태이므로 read_csv() 함수를 사용할 수 있습니다.

import pandas as pd
df = pd.read_csv('data/2019-06-09-imf_gdp/WEO_Data_2019.xls', sep='\t', encoding='ISO-8859-1')
df.head()
CountrySubject DescriptorUnitsScaleCountry/Series-specific Notes19801981198219831984201620172018201920202021202220232024Estimates Start After
AfghanistanGross domestic product, constant pricesNational currencyBillionsSource: National Statistics Office Latest actu…NaNNaNNaNNaNNaN493.073506.215517.858533.394552.063574.127599.933629.880664.4522017.0
AfghanistanGross domestic product, constant pricesPercent changeNaNSee notes for: Gross domestic product, consta…NaNNaNNaNNaNNaN2.1642.6652.3003.0003.5003.9974.4954.9925.4892017.0
AfghanistanGross domestic product, current pricesNational currencyBillionsSource: National Statistics Office Latest actu…NaNNaNNaNNaNNaN1,318.4781,377.5351,418.1281,488.8611,595.0531,733.5391,902.1092,096.9512,322.6502017.0
AfghanistanGross domestic product, current pricesU.S. dollarsBillionsSee notes for: Gross domestic product, curren…NaNNaNNaNNaNNaN19.42820.23519.58519.99020.68221.92823.57725.45027.6082017.0
AfghanistanGross domestic product, current pricesPurchasing power parity; international dollarsBillionsSee notes for: Gross domestic product, curren…NaNNaNNaNNaNNaN66.38469.44972.64876.15880.47085.42691.11397.643105.1582017.0

여기서 한국의 GDP 성장률과 GDP Gap 데이터만 뽑아서 차트로 가시화해보면 다음과 같습니다.

df_gdp = df[(df['Country'] == 'Korea') & (df['Subject Descriptor'] == 'Gross domestic product, constant prices') & (df['Units'] == 'Percent change')].iloc[:, 5:-1]
df_gdpgap = df[(df['Country'] == 'Korea') & (df['Subject Descriptor'] == 'Output gap in percent of potential GDP') & (df['Units'] == 'Percent of potential GDP')].iloc[:, 5:-1]
x_gdp = df_gdp.columns.astype(int)
y_gdp = df_gdp.values[0].astype(float)
x_gdpgap = df_gdpgap.columns.astype(int)
y_gdpgap = df_gdpgap.values[0].astype(float)
import matplotlib.pyplot as plt
import matplotlib
%matplotlib inline
plt.plot(x_gdp, y_gdp, label='GDP')
plt.plot(x_gdpgap, y_gdpgap, label='GDP Gap')
plt.hlines(0, xmin=x_gdp[0]-3, xmax=x_gdp[-1]+3)
plt.legend()

res

bokeh로 봉차트(Candlestick Chart) 그리기

파이썬에서 봉차트를 제공하는 라이브러리가 많지 않습니다. 필자는 matplotlib에서 떨어져 나온 mpl_finance와 bokeh 정도로 알고 있습니다. 이미 mpl_finance로 봉차트를 그리는 방법은 이전 포스트 Matplotlib으로 봉차트(Candlestick Chart) 그리기 에서 다루었습니다.

이번 포스트에서 bokeh로 봉차트를 그리는 방법에 대해서 다루겠습니다. bokeh는 Anaconda에서 개발 중인 차트 라이브러리 입니다. matplotlib과 matplotlib 기반인 seaborn과는 다르게 독자 노선을 가지는 라이브러리 입니다. bokeh는 웹에 최적화 되어있는 라이브러리 입니다. matplotlib을 웹브라우저에 띄우려면 차트를 png 등의 그림으로 변환하여 보여줘야 해서 응답형(reponsible) 차트를 웹에 띄우기가 어렵습니다. 그래서 보통 웹에 차트를 띄울 때는 데이터만 서버에서 받고 Chart.jsD3.js 같은 자바스크립트 라이브러리를 사용하여 데이터를 가시화 합니다. bokeh는 파이썬에서 차트를 가시화하고 그 결과를 쉽게 웹에 띄울 수 있는 여러 방법을 제공합니다. 이 포스트에서는 다루지 않겠습니다.

bokeh에서 봉차트를 제공하고 있습니다. bokeh 봉차트 문서에서 상세한 정보를 확인해 보세요.

먼저 Anaconda3가 설치되어 있는 상태라 가정하고 글을 이어나가겠습니다. Jupyter Lab(Notebook)에서 bokeh가 잘 설치되어 있는지 확인합니다.

from bokeh.io import output_notebook, show
from bokeh.plotting import figure, gridplot
output_notebook()

BokehJS 0.12.16 successfully loaded.

이렇게 로딩이 성공되었다는 메시지가 뜨는지 확인합니다. 만약 다음과 같은 메시지가 뜬다면 Jupyter Lab의 Extension을 설치해야 합니다.

Loading BokehJS ...
JavaScript output is disabled in JupyterLab

Anaconda Prompt에서 다음과 같이 jupyterlab_bokeh 확장을 설치합니다.

Terminal window
jupyter labextension install jupyterlab_bokeh

설치가 되지 않는다면 아마도 node.js가 설치되어 있지 않아서일 것입니다. 그렇다면 conda install nodejs를 통해 먼저 node.js를 설치하고 다시 jupyterlab_bokeh 확장을 설치합니다.

bokeh가 잘 설치되어 있다면 이제 봉차트를 그릴 준비가 되었습니다. 차트 데이터는 다음과 같이 ['date', 'open', 'high', 'low', 'close', 'volume']의 리스트로 있습니다.

import pandas as pd
data = [['20190219', 125000.0, 127500.0, 123500.0, 126000.0, 57757], ['20190220', 125000.0, 127000.0, 124500.0, 126000.0, 68453], ['20190221', 125500.0, 126000.0, 124000.0, 125000.0, 43961], ['20190222', 125000.0, 125000.0, 123500.0, 125000.0, 31065], ['20190225', 125500.0, 126000.0, 124000.0, 125500.0, 45852], ['20190226', 125000.0, 127000.0, 124500.0, 126500.0, 37404], ['20190227', 126500.0, 127000.0, 124500.0, 126000.0, 36131], ['20190228', 126500.0, 127000.0, 124500.0, 125000.0, 69474], ['20190304', 124500.0, 125500.0, 122500.0, 123500.0, 65517], ['20190305', 123000.0, 125000.0, 122000.0, 124500.0, 35186], ['20190306', 124000.0, 124500.0, 122500.0, 123500.0, 35449], ['20190307', 123500.0, 124000.0, 122000.0, 123500.0, 34768], ['20190308', 122500.0, 122500.0, 120500.0, 121500.0, 35118], ['20190311', 121500.0, 122500.0, 120000.0, 122500.0, 39576], ['20190312', 123000.0, 124000.0, 122000.0, 123500.0, 24117], ['20190313', 123000.0, 123500.0, 121500.0, 123500.0, 37649], ['20190314', 123000.0, 124000.0, 122000.0, 123500.0, 95132], ['20190315', 123000.0, 128000.0, 123000.0, 126500.0, 107246], ['20190318', 127000.0, 131000.0, 126500.0, 131000.0, 74644], ['20190319', 130000.0, 134000.0, 129500.0, 133000.0, 68348], ['20190320', 132000.0, 133000.0, 129500.0, 131000.0, 42697], ['20190321', 130000.0, 132000.0, 127500.0, 128500.0, 54018], ['20190322', 127500.0, 129500.0, 126500.0, 127000.0, 32380], ['20190325', 125500.0, 126500.0, 124000.0, 124500.0, 37185], ['20190326', 124500.0, 125500.0, 123500.0, 124000.0, 45161], ['20190327', 124000.0, 125000.0, 123500.0, 124000.0, 34336], ['20190328', 124000.0, 124500.0, 120500.0, 121500.0, 43518], ['20190329', 122500.0, 125000.0, 122000.0, 124500.0, 39035], ['20190401', 124000.0, 126500.0, 124000.0, 126500.0, 22463], ['20190402', 125500.0, 126500.0, 123500.0, 126000.0, 31754], ['20190403', 124500.0, 128000.0, 124500.0, 128000.0, 36250], ['20190404', 128500.0, 128500.0, 126000.0, 128000.0, 34854], ['20190405', 127500.0, 129000.0, 126000.0, 127500.0, 33513], ['20190408', 127500.0, 128000.0, 126000.0, 128000.0, 39005], ['20190409', 128000.0, 129000.0, 127500.0, 128500.0, 33266], ['20190410', 128000.0, 129000.0, 126500.0, 128000.0, 64476], ['20190411', 128500.0, 129000.0, 125000.0, 125000.0, 84802], ['20190412', 126000.0, 127500.0, 125500.0, 127000.0, 39663], ['20190415', 126500.0, 128500.0, 126000.0, 127000.0, 61140], ['20190416', 127000.0, 129000.0, 126500.0, 128500.0, 40123], ['20190417', 128500.0, 129000.0, 127000.0, 128000.0, 30846], ['20190418', 128000.0, 128500.0, 124000.0, 124500.0, 55346], ['20190419', 124500.0, 125000.0, 122000.0, 123000.0, 53439], ['20190422', 123000.0, 123500.0, 121000.0, 122500.0, 30421], ['20190423', 122500.0, 123500.0, 120500.0, 121500.0, 54997], ['20190424', 122500.0, 122500.0, 119500.0, 120000.0, 63486], ['20190425', 121000.0, 121000.0, 118500.0, 119000.0, 36046], ['20190426', 118500.0, 119500.0, 117000.0, 119000.0, 43749], ['20190429', 119000.0, 119500.0, 117000.0, 119500.0, 33516], ['20190430', 122000.0, 123000.0, 119000.0, 119500.0, 94118], ['20190502', 118500.0, 122000.0, 118000.0, 121000.0, 56723], ['20190503', 121000.0, 122000.0, 119500.0, 120000.0, 35240], ['20190507', 118500.0, 119500.0, 117000.0, 117500.0, 44453], ['20190508', 116500.0, 117000.0, 115000.0, 116500.0, 52805], ['20190509', 117000.0, 117000.0, 113000.0, 113000.0, 116012], ['20190510', 113000.0, 114500.0, 110000.0, 111500.0, 86072], ['20190513', 110500.0, 110500.0, 107500.0, 108500.0, 70847], ['20190514', 107500.0, 108000.0, 105000.0, 106500.0, 92820], ['20190515', 107000.0, 107500.0, 105000.0, 107000.0, 68937], ['20190516', 107000.0, 107500.0, 104000.0, 105000.0, 64047]]
df = pd.DataFrame(data, columns=['date', 'open', 'high', 'low', 'close', 'volume'])
dateopenhighlowclosevolume
20190219125000.0127500.0123500.0126000.057757
20190220125000.0127000.0124500.0126000.068453
20190221125500.0126000.0124000.0125000.043961
20190222125000.0125000.0123500.0125000.031065
20190225125500.0126000.0124000.0125500.045852
20190226125000.0127000.0124500.0126500.037404
20190227126500.0127000.0124500.0126000.036131
20190228126500.0127000.0124500.0125000.069474
20190304124500.0125500.0122500.0123500.065517
20190305123000.0125000.0122000.0124500.035186
20190306124000.0124500.0122500.0123500.035449
20190307123500.0124000.0122000.0123500.034768
20190308122500.0122500.0120500.0121500.035118
20190311121500.0122500.0120000.0122500.039576
20190312123000.0124000.0122000.0123500.024117
20190313123000.0123500.0121500.0123500.037649
20190314123000.0124000.0122000.0123500.095132
20190315123000.0128000.0123000.0126500.0107246
20190318127000.0131000.0126500.0131000.074644
20190319130000.0134000.0129500.0133000.068348
20190320132000.0133000.0129500.0131000.042697
20190321130000.0132000.0127500.0128500.054018
20190322127500.0129500.0126500.0127000.032380
20190325125500.0126500.0124000.0124500.037185
20190326124500.0125500.0123500.0124000.045161
20190327124000.0125000.0123500.0124000.034336
20190328124000.0124500.0120500.0121500.043518
20190329122500.0125000.0122000.0124500.039035
20190401124000.0126500.0124000.0126500.022463
20190402125500.0126500.0123500.0126000.031754
20190403124500.0128000.0124500.0128000.036250
20190404128500.0128500.0126000.0128000.034854
20190405127500.0129000.0126000.0127500.033513
20190408127500.0128000.0126000.0128000.039005
20190409128000.0129000.0127500.0128500.033266
20190410128000.0129000.0126500.0128000.064476
20190411128500.0129000.0125000.0125000.084802
20190412126000.0127500.0125500.0127000.039663
20190415126500.0128500.0126000.0127000.061140
20190416127000.0129000.0126500.0128500.040123
20190417128500.0129000.0127000.0128000.030846
20190418128000.0128500.0124000.0124500.055346
20190419124500.0125000.0122000.0123000.053439
20190422123000.0123500.0121000.0122500.030421
20190423122500.0123500.0120500.0121500.054997
20190424122500.0122500.0119500.0120000.063486
20190425121000.0121000.0118500.0119000.036046
20190426118500.0119500.0117000.0119000.043749
20190429119000.0119500.0117000.0119500.033516
20190430122000.0123000.0119000.0119500.094118
20190502118500.0122000.0118000.0121000.056723
20190503121000.0122000.0119500.0120000.035240
20190507118500.0119500.0117000.0117500.044453
20190508116500.0117000.0115000.0116500.052805
20190509117000.0117000.0113000.0113000.0116012
20190510113000.0114500.0110000.0111500.086072
20190513110500.0110500.0107500.0108500.070847
20190514107500.0108000.0105000.0106500.092820
20190515107000.0107500.0105000.0107000.068937
20190516107000.0107500.0104000.0105000.064047

bokeh를 이용해 봉차트를 그려봅니다. 먼저 데이터에서 양봉과 음봉에 대한 mask를 저장합니다.

inc = df.close >= df.open
dec = df.open > df.close

inc는 양봉, dec는 음봉에 해당합니다. 이제 봉들을 그려줍니다. 하나의 봉은 bokeh에서 segmentvbar로 구성되어 있습니다. segment는 high와 low까지 선으로 그은 봉의 꼬리를 그리는데 사용됩니다. vbar로는 봉의 몸통을 그립니다.

p_candlechart = figure(plot_width=1050, plot_height=200, x_range=(-1, len(df)), tools="crosshair")
p_candlechart.segment(df.index[inc], df.high[inc], df.index[inc], df.low[inc], color="red")
p_candlechart.segment(df.index[dec], df.high[dec], df.index[dec], df.low[dec], color="blue")
p_candlechart.vbar(df.index[inc], 0.5, df.open[inc], df.close[inc], fill_color="red", line_color="red")
p_candlechart.vbar(df.index[dec], 0.5, df.open[dec], df.close[dec], fill_color="blue", line_color="blue")

봉차트 밑에 거래량 막대차트(Bar Chart)까지 그려 줍니다. 이 때도 vbar를 사용하면 됩니다.

p_volumechart = figure(plot_width=1050, plot_height=100, x_range=p_candlechart.x_range, tools="crosshair")
p_volumechart.vbar(df.index, 0.5, df.volume, fill_color="black", line_color="black")

그리고 마지막으로 위 차트들을 gridplot으로 배치하여 가시화합니다.

p = gridplot([[p_candlechart], [p_volumechart]], toolbar_location=None)
show(p)

이 코드들을 모아서 다음과 같은 봉차트를 그릴 수 있습니다.

from bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.layouts import gridplot
inc = df.close >= df.open
dec = df.open > df.close
p_candlechart = figure(plot_width=1050, plot_height=200, x_range=(-1, len(df)), tools="crosshair")
p_candlechart.segment(df.index[inc], df.high[inc], df.index[inc], df.low[inc], color="red")
p_candlechart.segment(df.index[dec], df.high[dec], df.index[dec], df.low[dec], color="blue")
p_candlechart.vbar(df.index[inc], 0.5, df.open[inc], df.close[inc], fill_color="red", line_color="red")
p_candlechart.vbar(df.index[dec], 0.5, df.open[dec], df.close[dec], fill_color="blue", line_color="blue")
p_volumechart = figure(plot_width=1050, plot_height=100, x_range=p_candlechart.x_range, tools="crosshair")
p_volumechart.vbar(df.index, 0.5, df.volume, fill_color="black", line_color="black")
p = gridplot([[p_candlechart], [p_volumechart]], toolbar_location=None)
show(p)

chart1

그러나 여기서 각 axis에 표시된 레이블들이 유용한 정보를 주지 못하고 있습니다. 좀 더 보기 좋게 레이블들을 포메팅 해보겠습니다.

p_candlechart.yaxis[0].formatter = NumeralTickFormatter(format='0,0')
p_candlechart.xaxis.visible = False

p_candlechart의 y 레이블을 다음과 같이 천단위로 콤마를 붙인 숫자로 표현합니다.

여기서 x 레이블은 거래량 차트에만 표시하기 위해서 숨기도록 합니다.

p_volumechart의 x 레이블을 날짜로 표현하고 y 레이블을 숫자로 표현합니다.

major_label = {
i: date.strftime('%Y%m%d') for i, date in enumerate(pd.to_datetime(df["date"]))
}
major_label.update({len(df): ''})
p_volumechart.xaxis.major_label_overrides = major_label
p_volumechart.yaxis[0].formatter = NumeralTickFormatter(format='0,0')

x 레이블의 마지막 값은 잘려서 나와서 숨겼습니다. 이제 이 코드들을 추가해서 다시 봉차트를 그려봅니다.

from bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.layouts import gridplot
from bokeh.models.formatters import NumeralTickFormatter
inc = df.close >= df.open
dec = df.open > df.close
p_candlechart = figure(plot_width=1050, plot_height=200, x_range=(-1, len(df)), tools="crosshair")
p_candlechart.segment(df.index[inc], df.high[inc], df.index[inc], df.low[inc], color="red")
p_candlechart.segment(df.index[dec], df.high[dec], df.index[dec], df.low[dec], color="blue")
p_candlechart.vbar(df.index[inc], 0.5, df.open[inc], df.close[inc], fill_color="red", line_color="red")
p_candlechart.vbar(df.index[dec], 0.5, df.open[dec], df.close[dec], fill_color="blue", line_color="blue")
p_candlechart.yaxis[0].formatter = NumeralTickFormatter(format='0,0')
p_candlechart.xaxis.visible = False
p_volumechart = figure(plot_width=1050, plot_height=100, x_range=p_candlechart.x_range, tools="crosshair")
p_volumechart.vbar(df.index, 0.5, df.volume, fill_color="black", line_color="black")
major_label = {
i: date.strftime('%Y%m%d') for i, date in enumerate(pd.to_datetime(df["date"]))
}
major_label.update({len(df): ''})
p_volumechart.xaxis.major_label_overrides = major_label
p_volumechart.yaxis[0].formatter = NumeralTickFormatter(format='0,0')
p = gridplot([[p_candlechart], [p_volumechart]], toolbar_location=None)
show(p)

chart2

이렇게 x축은 날짜가, y축은 숫자가 보기좋게 나오게 됩니다. 이 차트를 html, json 등으로 웹에 넣어줄 수 있습니다. 자세한 사항은 bokeh 문서 Embedding Plots and Apps를 확인해 주세요.

아나콘다(Anaconda) 32bit 환경 설치하기

아나콘다는 파이썬 및 데이터 과학(data science) 패키지가 포함된 배포판 입니다. 기본적으로 아나콘다는 64bit 버전을 제공하고 있습니다.

anaconda

그러나 시스템 트레이딩과 같이 Windows 환경에서 다른 프로그램들과 통신할 때 32bit 버전이 필요한 경우가 종종 있습니다. 이번 포스트에서는 아나콘다에서 32bit 파이썬 환경을 구성하는 방법에 대해서 다룹니다.

먼저 64bit 버전의 아나콘다를 다운받아서 설치합니다. 아나콘다 다운로드 페이지에서 최신 버전의 아나콘다를 받을 수 있습니다.

Anaconda Prompt를 열고 다음과 같이 명령을 입력합니다.

Terminal window
set CONDA_FORCE_32BIT=1
conda create -n py37_32 python=3.7 anaconda

설치하는데 시간이 꽤 걸릴 것이니 차분히 기다립니다. 여기서 python=3.7부분을 수정하여 원하는 버전을 설치할 수 있습니다.

설치가 완료되면 이제 32bit 버전의 파이썬을 사용할 수 있습니다. 다음과 같이 좀전에 설치한 파이썬 32bit 환경을 활성화 시키면 됩니다.

Terminal window
activate py37_32

프롬프트 커맨드 라인 처음에 (py37_32)가 표시되어 있으면 활성화가 제대로 된 것입니다. 이 환경을 비활성화 시키려면 다음과 같이 명령하면 됩니다.

Terminal window
deactivate

참고로 리눅스 운영체제에서는 source activate py37_32, source deactivate와 같이 명령을 줘야 합니다.

파이썬으로 크레온 플러스(Creon Plus) 자동 로그인하기

시스템 트레이딩에 관심 있는 분들이라면 주식투자 자동화에도 관심이 있을 것입니다. 주식투자 자동화의 첫걸음은 증권사 HTS API 자동 로그인 입니다. 증권사 HTS마다 그 방법이 다르며 이번 포스트에서는 대신증권의 크레온 플러스를 파이썬에서 자동으로 로그인하는 방법을 다루겠습니다.

크레온 다운로드센터에서 CREON HTS를 다운받습니다. CREON HTSCREON Plus가 포함되어 있습니다. 설치 과정은 생략하겠습니다.

크레온 HTS를 실행하면 다음과 같은 로그인 창이 나타날 것입니다. 직접 크레온 HTS에 로그인하여 API를 사용할 때는 로그인 창 상단에 creon plus 버튼을 누르고 ID, 비밀번호, 공인인증 비밀번호(조회전용이 아닌 경우)를 입력하여 로그인 버튼을 누르면 됩니다.

크레온 로그인 창

이제 파이썬에서 크레온 HTS에 자동으로 로그인하기 위한 준비를 합니다.

파이썬 32bit 환경은 아나콘다를 설치 후에 32bit 환경을 설치하는 것을 권장합니다. 그 방법은 이 포스트를 참고 바랍니다.

커맨드 창에서 다음 pip 명령으로 pywinautopywin32를 설치합니다.

Terminal window
pip install pywinauto
pip install pywin32

크레온 API를 감싸는(wrapping) 클래스를 만들겠습니다.

import win32com.client
from pywinauto import application
class Creon:
def __init__(self):
self.obj_CpUtil_CpCybos = win32com.client.Dispatch('CpUtil.CpCybos')
def kill_client(self):
os.system('taskkill /IM coStarter* /F /T')
os.system('taskkill /IM CpStart* /F /T')
os.system('taskkill /IM DibServer* /F /T')
os.system('wmic process where "name like \'%coStarter%\'" call terminate')
os.system('wmic process where "name like \'%CpStart%\'" call terminate')
os.system('wmic process where "name like \'%DibServer%\'" call terminate')
def connect(self, id_, pwd, pwdcert):
if not self.connected():
self.disconnect()
self.kill_client()
app = application.Application()
app.start(
'C:\CREON\STARTER\coStarter.exe /prj:cp /id:{id} /pwd:{pwd} /pwdcert:{pwdcert} /autostart'.format(
id=id_, pwd=pwd, pwdcert=pwdcert
)
)
while not self.connected():
time.sleep(1)
return True
def connected(self):
b_connected = self.obj_CpUtil_CpCybos.IsConnect
if b_connected == 0:
return False
return True
def disconnect(self):
if self.connected():
self.obj_CpUtil_CpCybos.PlusDisconnect()

Creon 클래스는 자동 로그인 기능을 위해 다음 함수들을 가집니다.

  • kill_client: 실행중인 크레온 HTS 프로그램을 종료합니다.
  • connect: 크레온 ID, 비밀번호, 공인인증서 비밀번호를 입력받아서 크레온 HTS에 로그인 합니다.
  • connected: 크레온 HTS에 연결되었는지 확인합니다.
  • disconnect: 크레온 HTS와의 연결을 해제합니다.

여기서 connect 함수를 호출하면 기존 크레온을 종료하고 로그인을 시도하게 되어 있습니다.

Windows 10에서 아나콘다 32bit 사용시 에러 해결

Anaconda3 x86(32bit) 버전을 아무 문제 없이 잘 쓰고 있었다. 굳이 32bit 버전을 사용한 이유는 HTS API를 사용하기 위해서 였습니다. 증권사 장단점 비교 글에서 그 이유를 설명했습니다. 그런데 윈도우 업데이트가 되었는지 갑자기 Anaconda3 x86의 python.exe를 실행하려 하면 다음과 같은 에러 메시지가 표시되며 실행되지 않았습니다.

error

먼저 이 에러 메시지를 검색해서 이런 저런 방법을 써봤지만 해결되지 않았습니다. 관리자 권한으로 실행해봐도 안되고, “기본 제공 관리자 계정에 대한 관리자 승인 모드” 사용을 해봐도 안됐습니다. Anaconda 버전이 낮아서 그런가 해서 다시 설치해 봤는데도 안되었습니다. 결국 Anaconda3 x64 버전에 x86 환경을 생성하는 것으로 해결는데 그 방법은 다음과 같습니다. 먼저 cmd prompt를 엽니다. 그리고 다음과 같이 환경 설정 후 x86 환경을 설치합니다.

Terminal window
set CONDA_FORCE_32BIT=1
conda create -n py37_32 python=3.7 anaconda
activate py37_32

파이썬 메인 모듈에서 인자(argument) 파싱

파이썬 main을 실행할 때 인자(argument)를 주고싶은 경우 다음과 같이 할 수 있습니다. 인자는 positional argument와 optional argument로 구성할 수 있습니다. (자세한 정보는 https://docs.python.org/3.7/library/argparse.html 참고하세요.)

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('parg1') # positional argument
parser.add_argument('parg2') # positional argument
parser.add_argument('--oarg1') # optional argument
parser.add_argument('--oarg2') # optional argument
args = parser.parse_args()
print(args)

다음은 실행 커맨드에 따라 달라지는 args를 보여줍니다.

  • python main.py 1 2
    • Namespace(oarg1=None, oarg2=None, parg1='1', parg2='2')
  • python invalidanc.py 1 2 --oarg1=3
    • Namespace(oarg1='3', oarg2=None, parg1='1', parg2='2')
  • python invalidanc.py 1 2 --oarg1=3 --oarg2=4
    • Namespace(oarg1='3', oarg2='4', parg1='1', parg2='2')

증권사 API 장단점 비교

이 포스트에서는 다음 세 가지 증권사 API의 활용성에 대해 정리하고자 합니다.

  • 키움증권 Open API
  • 대신증권 크레온 API
  • 이베스트투자증권 Xing API

증권사 API들은 제가 아는 바로는 모두 윈도우 환경에서만 제공됩니다. 공인인증서 등의 보안 장치 때문으로 보이는데, 개발 환경이 주로 리눅스나 맥인 경우 이는 상당한 단점이 됩니다.

주요 개발환경을 리눅스나 맥으로 유지하면서 증권사 API를 활용하려면 어떻게 해야 할까요? 윈도우 환경으로 증권사 API를 호출하고 결과를 그대로 전달해주는 간단한 Bridge 서버를 구성하는 것이 유일한 방법으로 보입니다.

arch

리눅스, 맥 환경의 클라이언트가 윈도우 환경의 Brige 서버에 요청을 보내고, 이 Bridge 서버는 요청에 맞게 HTS API를 호출하여 결과를 다시 클라이언트에게 전송하는 구조입니다.

Bridge 서버는 HTTP, Socket 등의 통신 방식으로 구성할 수 있습니다. Request-Response 방식의 요청만 필요하면 HTTP 서버로, 실시간 데이터 수신이 필요하면 Socket 서버를 사용하는 것이 효과적일 것입니다.

증권사들이 윈도우 환경에서 OCX, COM, DLL 방식으로 API를 제공합니다.

  • 키움증권 Open API: OCX 방식
  • 대신증권 크레온 API: COM 방식
  • 이베스트투자증권 Xing API: COM 방식, DLL 방식

개인적으로 API 제공 방식은 OCX 보다는 COM 방식을 선호합니다. 파이썬에서 OCX 방식의 API를 사용하기 위해서 PyQt를 이용해야 하지만 COM 방식은 win32com을 사용하여 보다 간편합니다. (DLL 방식으로는 개발해보지 않았습니다.)

API가 잘 설계되어 있으면 그만큼 사용하기 편합니다. 세 증권사 API를 모두 사용해본 소감을 다음과 같이 정리합니다.

  • 키움증권 Open API: 문서화가 잘 되어 있고 API의 Signature는 설계가 잘 되어 있으나, 요청 결과를 비동기 방식으로 제공하여 불편함이 있습니다.
  • 대신증권 크레온 API: 문서화가 잘 되어 있고 API의 Signature도 잘 설계되어 있으며 요청 결과를 동기적으로 받을 수 있습니다.
  • 이베스트투자증권 Xing API: 문서화가 잘 되어 있지만 API Signature가 직관적이지가 않습니다. 요청 결과는 동기적으로 받을 수 있습니다.

즉 제가 느낀 사용성은 크레온 >> 키움 = 씽 입니다.

증권사 API들은 원활한 서비스 제공을 위해 API를 통한 과도한 요청을 막고 있습니다.

  • 키움증권 Open API: 1초에 5회로 제한하며, 제 경험상 1시간에 1000회로 제한하고 있습니다.
  • 대신증권 크레온 API: 시세 조회의 경우 15초에 60건, 실시간 조회의 경우 최대 400건으로 제한되어 있고, 주문 관련 조희의 경우 15초에 최대 20건으로 제한됨.
  • 이베스트투자증권 Xing API: 모의서버의 경우 1초에 10건으로 제한되어 있습니다.

이로 봤을 때 요청 제한은 Xing API가 가장 제한이 약하고 나머지 두 증권사는 비슷한 수준으로 보입니다.

세 증권사 API 모두 자동로그인이 가능합니다. 다만 사용성과 보안성 측면에서 차이점이 있습니다.

  • 키움증권 Open API: 코드 상에서 아이디, 비밀번호, 공인인증서 비밀번호가 노출되지 않으므로 보안이 강합니다.
  • 대신증권 크레온 API: 코드 상에서 크레온 프로그램을 실행하여 자동로그인 기능을 구현하면 아이디, 비밀번호, 공인인증서 비밀번호가 노출될 수 있습니다. 스크립트 등으로 로그인하고 프로그램을 실행하는 것이 보안상 좋습니다.
  • 이베스트투자증권 Xing API: 코드 상에서 아이디, 비밀번호, 공인인증서 비밀번호가 노출될 수 있습니다.

즉 보안성은 키움증권이 제일 좋아 보이며 크레온, Xing을 사용할 경우 코드가 유출되지 않도록 각별히 유의해야 할 것입니다.

키움증권크레온Xing
환경윈도우윈도우윈도우
API 연결 방식OCXCOMCOM, DLL
API 사용성보통좋음보통
요청 제한5/s4/s10/s
자동로그인 보안성좋음보통보통

Matplotlib으로 봉차트(Candlestick Chart) 그리기

봉차트(Candlestick Chart)는 한국 금융권에서 아주 친숙하게 사용되고 있는 차트 입니다. 하나의 봉(Candle)은 특정 기간 동안에서의 시가, 고가, 저가, 종가 (Open, High, Low, Close; OHLC)를 한번에 표현해 줍니다. 보통 일봉, 즉 하루 동안의 OHLC를 표현한 봉으로 차트를 그립니다.

파이썬에서 봉차트를 그릴 수 있는 도구는 다양하겠지만 대표적인 파이썬 차트 라이브러리인 (Matplotlib)[https://matplotlib.org/index.html]을 활용하여 봉차트를 그리는 법을 이 포스트에서 다루겠습니다.

Matplotlib 1.X 버전에서는 matplotlib.finance 모듈에서 candlestick_ohlc() 함수를 제공하고 있었으나 이 모듈은 2.0 버전부터 Deprecated 되었고, 이 모듈은 따로 (Gihub Repository)[https://github.com/matplotlib/mpl_finance]로 유지되고 있습니다. 설치는 다음 명령으로 할 수 있습니다.

pip install https://github.com/matplotlib/mpl_finance/archive/master.zip

이제 가시화할 데이터를 준비합니다. 다음과 같은 20일치 OHLC와 거래량 데이터를 봉차트로 그려보겠습니다.

dateopenhighlowclosevolume
02018080714250014550014200014500063103
12018080814400014500014150014350049191
22018080914350014400014200014200063800
32018081014200014650014200014600057283
42018081314500014600014100014200051095
52018081414150014600014150014250042579
62018081614150014200013850014100066262
720180817140500141000137000138500100717
82018082013850014000013750013750053507
92018082113750014000013750014000040399
102018082213850014000013850013900034023
112018082313950014000013500013600095824
1220180824135500137000132000134500118459
132018082713500013650013400013500067489
142018082813550013600013300013500066626
152018082913500013600013400013550064548
1620180830135500136000131500132500141496
1720180831132500133500131000132000100410
182018090313250013350013200013200037159
192018090413250013350013150013350035215

이렇게 데이터가 준비되었으니, 차트 레이아웃을 구성하겠습니다.

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(8, 5))
fig.set_facecolor('w')
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
axes = []
axes.append(plt.subplot(gs[0]))
axes.append(plt.subplot(gs[1], sharex=axes[0]))
axes[0].get_xaxis().set_visible(False)

결과는 다음과 두 개의 차트 영역이 위아래에 구성됩니다. 위에는 봉차트를 아래에는 거래량 바차트(Bar Chart)를 그리겠습니다. 위 아래 차트 모두 x 축은 같기 때문에 아래쪽 차트에만 x 축의 틱 레이블(Tick Label)을 표시합니다.

layout

이제 여기에 봉차트와 거래량 차트를 그리면 됩니다. mpl_finance 모듈에서 candlestick_ohlc() 함수를 임포트하고 x 축 데이터를 준비한 다음 봉차트와 거래량 차트를 그립니다.

from mpl_finance import candlestick_ohlc
x = np.arange(len(df.index))
ohlc = df[['open', 'high', 'low', 'close']].astype(int).values
dohlc = np.hstack((np.reshape(x, (-1, 1)), ohlc))
# 봉차트
candlestick_ohlc(axes[0], dohlc, width=0.5, colorup='r', colordown='b')
# 거래량 차트
axes[1].bar(x, df.volume, color='k', width=0.6, align='center')
plt.tight_layout()
plt.show()

이 파이썬 코드의 결과는 다음과 같습니다.

layout

여기서 x 축의 틱 레이블이 0에서 59까지 index 값으로 표시되는데, 일자로 표시되도록 해보겠습니다. 아래와 같이 plt.show() 전에 틱 레이블을 추가합니다. 여기서는 월요일에 해당하는 날짜만 표시하도록 했습니다.

# xlabels에 요일 추가
import datetime
_xticks = []
_xlabels = []
_wd_prev = 0
for _x, d in zip(x, df.date.values):
weekday = datetime.datetime.strptime(str(d), '%Y%m%d').weekday()
if weekday <= _wd_prev:
_xticks.append(_x)
_xlabels.append(datetime.datetime.strptime(str(d), '%Y%m%d').strftime('%m/%d'))
_wd_prev = weekday
axes[1].set_xticks(_xticks)
axes[1].set_xticklabels(_xlabels, rotation=45, minor=False)

이렇게 해주면 아래와 같이 index 값 대신 날짜가 x축 틱의 레이블로 표시됩니다.

layout