Skip to content

stock

4 posts with the tag “stock”

[2020-H1] 딥러닝 주식종목 랭킹모델 백테스팅

이번 포스트에서는 최근 구축한 주식종목 랭킹모델의 2020년 상반기 백테스팅 결과를 정리합니다.

백테스팅 환경은 이 포스트에 정리한 것과 같습니다.

비교 대상(baseline)은 코스피 인덱스로 잡았습니다.

백테스팅 결과

진한 선이 포트폴리오 가치(PV)이고 연한 선이 코스피입니다. 변화율을 y축으로 정했습니다. 0.2는 20%라는 뜻입니다.

2020년 상반기 백테스팅 결과를 보면 퀀티랩 PV 수익률은 약 **+20.3%**에 달하고 코스피 수익률은 약 -3.1% 입니다.

Maxium Drawdown(MDD)은 퀀티랩 딥러닝 모델은 약 -23.1%, 코스피는 **-35.7%**로 퀀티랩 딥러닝 모델의 위험이 더 작았습니다.

2020년 상반기 퀀티랩 딥러닝 주식투자 모델의 백테스팅의 매매 내역은 다음과 같습니다.

dateactioncodenamepriceamountprofitlosseval
20200102buy028300에이치엘비101,3159899,998,511
20200102buy005930삼성전자55,20018199,997,012
20200102buy140410메지온193,8005199,995,529
20200102buy196170알테오젠69,80014399,994,032
20200102buy032500케이엠더블유57,30017499,992,537
20200103buy000660SK하이닉스94,50010598,904,030
20200103buy068270셀트리온177,5005698,902,539
20200103buy002360SH에너지화학1,1258,88898,901,039
20200103buy053800안랩76,00013198,899,546
20200103buy005380현대자동차116,0008698,898,050
20200108sell002360SH에너지화학1,6208,88844.00%99,729,899
20200108buy078130국일제지5,3501,86999,728,400
20200219sell196170알테오젠98,50014341.12%107,025,215
20200219buy096530씨젠34,900286107,023,718
20200306sell096530씨젠48,35028638.54%104,062,865
20200313sell053800안랩49,750131-34.54%92,553,090
20200316buy140410메지온112,900191,114,960
20200318buy000660SK하이닉스73,100188,061,293
20200423buy196170알테오젠97,900102106,097,159
20200511sell196170알테오젠168,40010272.01%115,562,596
20200615sell078130국일제지5,4201,8691.31%116,858,954
20200619buy053800안랩51,400194119,638,858
20200625buy229000젠큐릭스21,650461120,262,761

[2019] 딥러닝 주식종목 랭킹모델 백테스팅

이번 포스트에서는 최근 구축한 주식종목 랭킹모델의 2019년도 1년치 백테스팅 결과를 정리합니다.

퀀티랩의 주식종목 랭킹모델은 PER/PBR/ROE 등의 기본적 분석 지표, OHLCV/MACD/RSI 등의 기술적 분석 지표, 수익률, 변동성, 거래규모 등을 종합적으로 고려하여 학습했습니다.

이렇게 학습한 모델의 출력 값이 높을 수록 매수 고려 대상 종목이 되는 매매 전략을 세웠습니다. 매수, 익절, 손절 등의 기준은 다음과 같습니다.

  • 매수: 종목 랭킹 결과 Top 5 종목
  • 익절: 60%
  • 손절: -60%
  • 청산: 과거 평가 수익 고점 대비 -20%

이러한 기준으로 종가 매매를 진행하며 최대 10개 종목을 보유할 수 있도록 제한했습니다.

자본금은 1억원으로 정하고 종목당 1천만원까지 매수할 수 있도록 했습니다.

매수/매도 거래 비용을 다음과 같이 현실적으로 적용했습니다.

  • 거래세: 0.25%
  • 매매 수수료: 0.015%

비교 대상(baseline)을 코스피 인덱스로 정하여 백테스팅 결과를 차트로 그려보면 다음과 같습니다.

백테스팅 결과

진한 검정 선이 포트폴리오 가치(PV)를, 연한 회색 선이 코스피 인덱스를 보여줍니다.

동일한 스케일로 표시하기 위해 PV, 코스피 인덱스의 처음 값을 0으로 두고 변화율을 표시했습니다.

그 결과 퀀티랩 주식 종목 랭킹 모델은 약 15% 수익을, 코스피 Buy & Hold 전략 수익률은 약 9% 로 구해졌습니다.

백테스팅 과정에서 매매한 종목들의 내역은 다음과 같습니다.

dateactioncodenamepriceamountprofitlosseval
20190102buy042110에스씨디1,4556,87299,998,500
20190102buy028300에이치엘비76,30013199,997,000
20190102buy095190이엠코리아7,4301,34599,995,501
20190102buy011320유니크7,7201,29599,994,002
20190103buy017900광전자2,2454,45499,188,832
20190103buy115440우리넷7,2701,37599,187,332
20190103buy225570넷게임즈9,5801,04399,185,834
20190103buy041960코미팜19,31051799,184,336
20190104buy004720우리들제약8,7701,140101,308,762
20190104buy041140넥슨지티9,7701,023101,307,262
20190110sell225570넷게임즈10,5001,0439.60%104,164,638
20190110buy039830오로라10,150985104,163,138
20190116sell041140넥슨지티12,7501,02330.50%110,248,684
20190116buy007720대명소노시즌2,5853,868110,247,184
20190117sell095190이엠코리아10,2001,34537.28%114,231,397
20190117sell011320유니크12,8001,29565.80%114,187,471
20190117buy108230톱텍9,9701,003114,185,971
20190117buy036200유니셈4,0952,442114,184,471
20190117buy007390네이처셀11,744851114,182,972
20190131sell039830오로라10,3009851.48%117,832,543
20190219sell042110에스씨디1,8956,87230.24%127,255,406
20190219buy008370원풍4,0502,469127,253,906
20190219buy250000보라티알8,6501,156127,252,406
20190308sell036200유니셈4,1252,4420.73%118,311,856
20190325sell028300에이치엘비78,0001312.23%120,028,982
20190325buy056080유진로봇4,4752,234120,027,483
20190325buy208860엔지스테크널러지6,9001,449120,025,983
20190411sell041960코미팜20,5405176.37%121,651,076
20190423sell007390네이처셀11,7448510.00%117,992,185
20190423buy097230한진중공업12,036830117,990,686
20190423buy000440중앙에너비스8,3801,193117,989,187
20190520sell017900광전자2,4354,4548.46%99,035,671
20190524sell097230한진중공업8,150830-32.29%103,109,920
20190524buy083640인콘3,2003,125103,108,420
20190524buy010660화천기계2,4904,016103,106,920
20190610sell115440우리넷9,9901,37537.41%106,821,419
20190628sell010660화천기계3,1204,01625.30%108,235,770
20190628buy004540깨끗한나라2,4154,140108,234,270
20190628buy004830덕성3,2003,125108,232,770
20190628buy019540일지테크3,1503,174108,231,270
20190708sell083640인콘2,1853,125-31.72%108,047,706
20190711sell019540일지테크4,2753,17435.71%112,418,803
20190725sell056080유진로봇3,0902,234-30.95%108,903,860
20190725buy230980솔트웍스3,8002,631108,902,361
20190725buy002070남영비비안1,5456,472108,900,861
20190726sell007720대명소노시즌1,7903,868-30.75%110,587,888
20190729sell004720우리들제약5,9601,140-32.04%112,485,768
20190729sell002070남영비비안2,6056,47268.61%112,441,090
20190729buy012700리드코프6,9501,438112,439,591
20190729buy000140하이트진로홀딩스12,000833112,438,091
20190729buy012610경인양행9,4001,063112,436,592
20190731sell004830덕성3,3003,1253.12%111,465,379
20190801sell250000보라티알9,0001,1564.05%107,712,924
20190801buy036030KTH5,3401,872107,711,424
20190801buy207760미스터블루7,4301,345107,709,925
20190801buy086980쇼박스4,0502,469107,708,425
20190805sell000440중앙에너비스5,6201,193-32.94%105,756,923
20190805sell230980솔트웍스2,4452,631-35.66%105,739,876
20190806sell208860엔지스테크널러지4,6901,449-32.03%105,802,627
20190806buy041960코미팜11,550865105,801,129
20190806buy007390네이처셀6,9001,449105,799,629
20190807sell004540깨끗한나라3,1804,14031.68%106,554,466
20190819sell207760미스터블루9,6901,34530.42%107,638,863
20190819buy006090사조오양9,6001,041107,637,364
20190819buy189860서전기전3,7902,638107,635,865
20191011sell189860서전기전3,9202,6383.43%109,339,291
20191024sell007390네이처셀9,4701,44937.25%115,558,718
20191024buy056080유진로봇3,0003,333115,557,218
20191024buy028300에이치엘비163,98460115,555,742
20191024buy039830오로라12,650790115,554,243
20191213sell028300에이치엘비87,04460-46.92%114,112,748
20191217sell012700리드코프7,1101,4382.30%113,575,584
20191219sell036030KTH5,4101,8721.31%113,394,701
20191219buy000440중앙에너비스8,6301,158113,393,202
20191219buy000520삼일제약22,700440113,391,704
20191223sell041960코미팜15,45086533.77%113,165,219

모델을 지속적으로 발전시켜 나가며 종목 랭킹 결과 및 운용 시뮬레이션 결과를 퀀티랩에서 제공할 계획입니다.

파이썬으로 네이버 금융 뉴스 크롤링하기

네이버 주식에서 뉴스를 크롤링하는 간단한 파이썬 코드를 소개합니다.

  • Dependency
    • python 3
    • requests
    • BeautifulSoup
    • webob

뉴스 문서를 질의 (Query)나 종목코드로 크롤링 해와서 문서에 포함된 글과 종목코드들을 반환해 줍니다.

import json
import os
from bs4 import BeautifulSoup
import re
import requests
from webob.compat import urlparse
class NaverFinanceNewsCrawler:
URL_NAVER_FINANCE = "http://finance.naver.com"
URL_NAVER_FINANCE_NEWS_QUERY = "http://finance.naver.com/news/news_search.nhn?q=%s&x=0&y=0" # params: query
URL_NAVER_FINANCE_NEWS_CODE = "http://finance.naver.com/item/news_news.nhn?code=%s&page=%s" # params: (code, page)
URL_NAVER_NEWS_FLASH = "http://finance.naver.com/news/news_list.nhn?mode=LSS2D&section_id=101&section_id2=258"
URL_NAVER_STOCK_NOTICE = "http://finance.naver.com/item/news_notice.nhn?code=%s&page=%s" # params: (code, page)
def __init__(self):
pass
def crawl(self, query=None, code=None, page=1):
"""
:param query:
:param code:
:param page:
:return:
"""
if query:
return self._crawl_by_query(query)
elif code:
return self._crawl_by_code(code, page=page)
else:
raise Exception("[Error] 'query' or 'code' should be entered.")
def _crawl_by_query(self, query):
"""
Crawl Naver Finance News
:param query: string; search keywords
:return: generator; [{title, summary, url, articleId, content, codes}, ...]
"""
# Convert the query to euc-kr string
q = ""
for c in query.encode('euc-kr'):
q += "%%%s" % format(c, 'x').capitalize()
r_url = NaverFinanceNewsCrawler.URL_NAVER_FINANCE_NEWS_QUERY % (q)
r = requests.get(r_url)
soup = BeautifulSoup(r.text, "lxml")
news = soup.find('div', class_='newsSchResult').find('dl', class_='newsList')
news_title = news.find_all('dt', class_='articleSubject')
news_summary = news.find_all('dd', class_='articleSummary')
for title, summary in zip(news_title, news_summary):
url = NaverFinanceNewsCrawler.URL_NAVER_FINANCE + title.a.get("href")
res = {
"title": title.a.text,
"summary": summary.find(text=True).strip(' \t\n\r'),
"url": url,
"articleId": urlparse.parse_qs(urlparse.urlparse(url).query)["article_id"][0]
}
res.update(self._crawl_content(url))
yield res
def _crawl_by_code(self, code, page=1):
"""
Crawl Naver Stock News
:param code: string; a stock code
:return: generator;
"""
r_url = NaverFinanceNewsCrawler.URL_NAVER_FINANCE_NEWS_CODE % (code, page)
r = requests.get(r_url)
soup = BeautifulSoup(r.text, "lxml")
news_rows = soup.find('table', class_='type2').find_all('td', class_='title')
for row in news_rows:
yield {"title": row.a.text.strip(' \t\n\r'), "url": row.a.get('href')}
def _crawl_content(self, url):
r = requests.get(url)
soup = BeautifulSoup(r.text, "lxml")
content = soup.find('div', id="content", class_='articleCont')
codes = re.findall(r"\d{6}", content.text)
return {"content": content.text.strip(' \t\n\r'), "codes": codes}

삼성전자에 관한 뉴스를 가져와 보겠습니다.

if __name__ == "__main__":
crawler = NaverFinanceNewsCrawler()
docs = crawler.crawl(query='삼성전자')
for i, d in enumerate(docs):
print("{i}번째 문서".format(i=i+1), end=" " + "-" * 50)
print("-" * 50)
print("내용: {content}".format(content=d["content"]))
print("문서에 포함된 종목 코드: {codes}".format(codes=d["codes"]))

이렇게 돌리면 결과는 다음과 같습니다.

1번째 문서 ----------------------------------------------------------------------------------------------------
내용: 【서울=뉴시스】 김지은 기자 = 코스피가 8일 사흘 만에 반등에 성공하며 2550선을 회복했다. 코스닥은 연중 최고치를 경신했다. 이날 코스피 지수는 전 거래일(2545.44)보다 6.96포인트(0.27%) 오른 2552.40에 장을 마쳤다.지수는 미국 증시 혼조세와 한미 자유무엽협정(FTA) 재협상에 대한 경계감에 전 거래일보다 10.96포인트(0.43%) 내린 2534.21로 출발했다. 하지만 이내 장중 낙폭을 줄이고 상승 전환해 2540선에서 등락을 거듭하다 개인의 매수세에 2550를 넘었다.이날 개인투자자는 618억원 어치의 주식을 사며 지수 상승을 이끌었다. 기관은 1055억, 외국인은 38억원 순매도했다. 간밤 미국 증시는 세제개편안 통과에 대한 불확실성이 부각되며 혼조세로 마감했다.뉴욕증권거래소(NYSE)에서 다우존스30 산업평균지수는 전날보다 8.81포인트(0.04%) 오른 2만3557.23으로 거래를 마쳤다. 스탠더드앤드푸어스(S&P) 500지수는 전날보다 0.49포인트(0.02%) 하락한 2590.64에 장을 닫았다. 기술주 중심의 나스닥은 18.65포인트(0.27%) 내린 6767.78을 기록했다. 키움증권 서상영 연구원은 "세제개편안 통과에 대한 불확실성의 부각으로 인한 미국 증시의 혼조세와 한미 자유무역협정(FTA) 재협상 요인 등은 한국 증시 하락 요인으로 작용할 가능성이 높다"면서도 "전날 트럼프가 북한과의 대화를 언급하는 등 지정학적 우려 완화에 대한 기대감을 높인 점은 긍정적"이라고 말했다.업종별로는 등락이 엇갈렸다.의약품(1.13%), 전기전자(1.05%), 섬유의복(0.95%), 비금속광물(0.77%), 종이목재(0.71%), 제조업(0.62%), 서비스업(0.60%), 전기가스업(0.53%) 등은 상승했다.반면 철강금속(-1.51%), 보험(-1.17%) 통신업(-0.99%), 은행(-0.76%), 의료정밀(-0.76%), 운수창고(-0.73%), 금융업(-0.69%) 등은 하락했다.시가총액 상위종목을 보면 '대장주' 삼성전자는 2만원(0.71%) 오른 282만5000원에 거래를 마쳤다. 2등주 SK하이닉스도 1200원(1.46%) 오른 8만3600원을 기록했다.이에 반해 현대차(-0.32%), 네이버(-1.63%), 포스코(-2.62%), 삼성물산(-0.34%), 삼성생명(-1.87%), 현대모비스(-0.19%), 삼성바이오로직스(-0.64%) 등은 떨어졌다.코스닥 지수는 전 거래일(701.14)보다 7.97포인트(1.14%) 오른 709.11로 마감, 연중 최고치를 기록했다.전년에 견줘서는 12.3% 상승했다.개인은 808억원 매도 우위를 보였지만 외국인과 기관은 각각 25억원, 900억원 어치 사들였다.정부의 코스닥 활성화 정책에 대한 기대감과 연기금의 코스닥 투자 확대 논의에 힘입어 상승세가 이어진 것으로 풀이된다.시가총액은 248조6000억원으로 전년 대비 23.4%늘었다. kje1321@newsis.com▶ 뉴시스 빅데이터 MSI 주가시세표 바로가기 ▶ 네이버 채널에서 뉴시스를 구독해주세요뉴시스 관련뉴스언론사 페이지로 이동합니다.[빅데이터MSI]8일 주식시장 심리 1→1→1→1단계 '매우 나쁨'시장심리 톱5…신세계·한국토지신탁·영풍·삼성중공업·한미반도체8일 빅데이터 '핫 키워드'…기능·사람·본문·복사·제품코스피, 사흘 만에 반등 2550선 회복…2552.40 마감김동연 "한미 정상회담서 FTA 깊은 논의 없어"
문서에 포함된 종목 코드: []
2번째 문서 ----------------------------------------------------------------------------------------------------
내용: - 대부분 해외 투자 집중…코스피 수익률 못따라잡아- 국내 투자도 비용 이유로 ETF 투자 중심- 종목 투자는 삼성전자 쏠림 현상으로 재미 못봐[이데일리 안혜신 기자] 로보어드바이저펀드에 투자하면 안정적으로 수익을 올릴 수 있다는 말에 적지 않은 자금을 투자했던 투자자들이 부진한 수익률때문에 한숨을 쉬고 있다.◇로보어드바이저 펀드 대부분 해외 투자 집중우후죽순 격으로 늘어나는 로보어드바이저펀드를 운용하는 ‘로봇 펀드매니저’가 고른 투자대상을 들여다보면 그 대답의 단초를 발견할 수 있다. 우선 로보어드바이저펀드의 투자 유형을 살펴봐야 하는데 펀드평가사인 KG제로인에 따르면 현재 운용순자산 10억원 이상의 로보어드바이저펀드 17개 가운데 해외채권이나 주식에 투자하는 상품이 12개로 압도적이다. 로보어드바이저펀드 내에 해외투자상품이 유독 많은 이유는 미국을 중심으로 한 전세계 로보어드바이저 상품 자체가 글로벌하게 자산을 배분하는 상품이 대부분이라는 점이 꼽힌다. 로보어드바이저펀드 후발주자인 우리 입장에서는 해외 사례를 벤치마킹한 측면이 강하다는 얘기다.아울러 국내 주식에 대한 기대 투자수익률이 낮아진 점도 해외투자상품이 많은 이유 중 하나다. 과거 7% 경제성장률을 달성할 때와 현재 2~3% 수준의 경제성장률을 이어가고 있는 상황에서의 주식시장에 대한 기대감은 다를 수 밖에 없다는 것. 업계 관계자는 “국내 자산만으로는 자산 배분에 한계를 느끼고 있다”며 “전세계적으로 놓고 보면 국내뿐만 아니라 다양한 투자 기회가 생길 수 있다”고 설명했다.하지만 해외투자 비중 증가는 아이러니하게도 로보어드바이저펀드 수익률 부진으로 이어졌다. 올해 국내 증시 상승률이 두드러지면서 상대적으로 해외투자 수익률이 이를 따라잡지 못한 것. 실제 올들어 전세계 주가지수는 지난 10월까지 평균 17% 상승했다. 반면 코스피 상승률은 22.9%로 홍콩, 브라질에 이어 상승률에서 3위를 기록하고 있다.◇국내 종목 투자는 삼성전자 쏠림에 재미 못봐그렇다면 국내 투자에 집중하는 로보어드바이저펀드의 선택 종목은 무엇일까. 대부분 펀드의 포트폴리오는 상장지수펀드(ETF)가 차지하고 있다. 분산투자로 상대적으로 안정성이 높은 ETF의 특성상 주식 직접투자보다 체감 수익률이 낮을 수밖에 없는 것이다. 최근 로보어드바이저펀드 중 가장 수익률이 높은 ‘미래에셋AI스마트베타마켓헤지자(주식-재간접)종류F’의 경우 펀드 내 비중이 21.37%로 압도적인 삼성전자(005930)를 제외하고는 일시적으로 낙폭이 큰 우량주 30종목에 투자하는 ‘TIGER 가격조정’(13.96%), 최근 성과가 좋은 종목에 투자하는 ‘TIGER 모멘텀’(13.09%), 변동석이 적은 종목에 투자하는 ‘TIGER 로우볼’(12.55%) 등 7개 ETF를 담고 있다. ‘대신로보어드바이저자산배분성과보수 1[혼합-재간접]Class C’ 역시 ‘KODEX 200’(25.45%), ‘아리랑 배당주’(7.31%), ‘KODEX 코스닥 150’(4.2%) 등 주식에 투자한 10개 종목이 모두 ETF다. ‘하이ROKI1글로벌로보어드바이저증권투자신탁 H[혼합-재간접형]’도 ‘TIGER200’(14.86%)를 비롯해 총 9개의 국내외 ETF로 포트폴리오를 채우고 있다.로보어드바이저펀드가 주로 ETF에 투자하는 이유는 간단하다. 일단 투자비용이 상대적으로 저렴하다. 업계 관계자는 “과거에는 펀드 투자성과가 잘 나오는 편이었지만 최근은 수익 내기가 쉽지 않아졌다”며 “초과 성과도 잘 나오지 않는 상황에서 비용이 더 싼 ETF를 담을 수밖에 없다”고 말했다.ETF보다 주식에 주로 투자하는 펀드도 있다. 다만 최근 코스피 정보기술(IT)주 쏠림이 심화하면서 수익률이 좋지 않다. ‘에셋플러스알파로보코리아그로스증권자투자신탁 1-1(주식)’는 LG디스플레이(034220)·NAVER(035420)·현대건설(000720)·현대산업(012630)·GS리테일(007070)·LG이노텍(011070)·롯데케미칼(011170)·현대중공업(009540)·LG전자(066570)·사람인에이치알(143240) 등 80개 종목에 투자 중이며 ‘에셋플러스알파로보글로벌인컴성과보수증권자투자신탁 1-2(주식)’는 LG디스플레이(034220)·현대건설(000720)·롯데케미칼(011170)·아모레G(002790)·엔씨소프트(036570)·한화(000880)·농심(004370)·GS(078930)·롯데칠성(005300)·한국타이어월드와이드(000240) 등 총 80개 종목에 투자중이다. 에셋플러스자산운용 관계자는 “성장에 중심을 두는 그로스펀드와 안정적인 수익 추구를 하는 인컴펀드의 투자 종목이 다르다”며 “ETF보다 액티브펀드에 종점을 두고 있는 회사인만틈 ETF 투자가 아닌 종목 투자로 로보어드바이저펀드를 연구했다”고 설명했다.‘DB밸류아이로보어드바이저증권자투자신탁(H)[채권혼합-재간접형]’ 역시 개별종목 투자비중이 높은 펀드 중 하나다. 이 펀드는 SK하이닉스(000660)·고려아연(010130)·LG(003550)·두산인프라코어(042670)·대우건설(047040)·롯데하이마트(071840)·롯데정밀화학(004000) 등에 투자하고 있다.이창헌 미래에셋자산운용 패시브솔루션팀장은 “로보어드바이저펀드는 자산배분 전략을 사용하고 있어 단순히 코스피와 수익률을 비교하는 것은 맞지 않다고 본다”며 “단기 투자 실적에 투자자들이 민감하다는 것은 알지만 로보어드바이저펀드는 꾸준히 장기적인 관점에서 수익을 봐야 한다”고 말했다.안혜신 (ahnhye@edaily.co.kr)▶이데일리 개편기념 이벤트 - 갤럭시 노트8 쏩니다▶[카카오톡] 플친 맺으면 스타벅스 쿠폰 팡팡~♥<ⓒ종합 경제정보 미디어 이데일리 - 무단전재 & 재배포 금지>이데일리 관련뉴스언론사 페이지로 이동합니다.미소 띤 트럼프, 한미 FTA '돌출 발언' 사라진 이유[단독]종교인 과세 50년만에 내년 시행 '가닥'…기독교계는 반발조두순 '재심'은 불가능..출소 전 타이트한 '보안 처분' 이뤄져야멜라니아, '델포조' 코트 벗고 '제이멘델' 시스루 드레스 입어'비행기 사고로 사망' 할러데이에 메이저리그 애도 물결
문서에 포함된 종목 코드: ['005930', '034220', '035420', '000720', '012630', '007070', '011070', '011170', '009540', '066570', '143240', '034220', '000720', '011170', '002790', '036570', '000880', '004370', '078930', '005300', '000240', '000660', '010130', '003550', '042670', '047040', '071840', '004000']
3번째 문서 ----------------------------------------------------------------------------------------------------
내용: - 코스피지수 2550선 회복…삼성전자·SK하이닉스등 IT관련주 상승전환- 트럼프, 北이슈 완화발언·FTA 언급자제…“긍정적으로 평가”- “IT업종 하반기 랠리 주도…반도체 업종 상승추세 여전”[이데일리 윤필호 기자] 도널드 트럼프 미국 대통령의 방한에 잔뜩 긴장했던 국내 주식시장이 안도랠리를 보였다. 우려와 달리 돌발변수가 발생하지 않자 투자심리가 안정을 되찾은 것. 특히 올해 상승장을 이끌고 있는 정보기술(IT) 관련주(株)와 제약·바이오주 등 기존 주도주들이 최근 조정세를 접고 다시 힘을 내고 있는 양상이다.◇IT·바이오주 주도로 증시 반등…트럼프 리스크 해소8일 마켓포인트에 따르면 코스피지수는 전거래일대비 6.96포인트(0.27%) 오른 2552.40에 장을 마감했다. 코스피지수는 이번 주 들어 트럼프 방한을 앞두고 부진한 모습을 보였다. 지난 6일부터 8.56포인트, 7일 3.97포인트 각각 빠지며 조정국면에 들어가는 것이 아니냐는 전망도 나왔지만 별다른 충격없이 트럼프 방한 일정이 마무리되자 시장은 기존 상승추세를 회복하는 모습이다. 특히 기존 주도주였던 IT관련주가 재차 상승세를 타면서 전기전자업종지수도 5거래일 만에 반등했다. 대장주인 삼성전자(005930)는 5거래일 만에 상승세를 보였고 SK하이닉스(000660)도 3거래일만에 상승 전환했다. 코스닥지수의 경우 제약·바이오의 약진이 돋보였다. 지난 3일 700선을 넘긴 이후 방한 이슈와는 별도로 꾸준한 상승세를 이어가고 있다.트럼프 대통령 방한으로 비롯된 불확실성이 해소된 데 따른 것으로 보인다. 트럼프 대통령은 이날 국회 연설에서 북한 핵미사일 이슈와 관련해 “북한은 미국을 유약하다고 해석했는데 이것은 치명적인 오산이 될 것“이라고 경고하며 강한 어조로 비판했다. 하지만 전날인 7일 문재인 대통령과의 공동 기자회견에서는 “북한은 협상 테이블에 나와 우리와 합의를 도출하는 것이 좋을 것”이라고 밝혀 그간 초강경 기조보다 완화됐다는 평가를 받았다.한국과 미국간 자유무역협정(FTA) 개정 문제에 대해서도 압박의 강도가 높지 않았다는 평가다. 트럼프 대통령은 국회 연설에서 이에 대해 한 차례 언급을 하는데 그쳤다. 전날 공동기자회견에서도 “현재 협정은 성공적이지 못했고 미국에는 그렇게 좋은 협상은 아니었다고 말씀드리고 싶다”고 말해 원론적인 입장을 강조하는데 그쳤다.◇“트럼프 방한 긍정적…IT관련주, 다시 상승장 이끌 것”증권가에서는 트럼프 대통령의 방한에 대체로 긍정적 평가를 내놓았다. 아울러 증시에서 IT주 등 기존 주도주들이 다시 상승랠리를 이끌 것이라고 전망했다. 박중제 메리츠종금증권 투자전략팀장은 “트럼프 대통령 방문에서 북한 문제에 완화된 모습은 긍정적”이라면서도 “FTA 재협상 문제의 경우 아직 어떤 방향으로 합의가 되고 얼마나 시간이 걸릴지 등에 대한 정보가 부족하기 때문에 당장 반영되는 이슈가 아니다”고 말했다. 그는 “IT업종은 올해 주도주였고 실적도 좋았다”며 “최근 상승세가 주춤한 것은 다른 업종과의 수익률 격차가 크게 벌어진데 따른 단기적 숨고르기 정도로 보인다”며 “하지만 전망도 여전히 좋고 이익도 증가하고 있어서 연말까지는 순환매가 나올 수 있다고 보고 있다”고 진단했다.이상재 유진투자증권 투자전략팀장은 최근 코스피지수 부진에 대해 “상승국면에서 잠시 쉬어가는 흐름으로 시장 방향성과 관련해 논란이 제기될 상황은 아니다”고 판단했다. 그러면서 트럼프 대통령의 북한 관련 발언에 대해서는 “북한 리스크와 관련해 종전의 강한 기조에서 물러나 대화를 시사하는 등 비교적 우호적 모습을 보였다”고 평가했다. 이 팀장은 IT관련주 부진에 대해 “IT주는 잠시 쉬어가는 정도지 조정이라고 말할 순 없다”면서 “단기 급등에 따른 조정은 있을 수 있지만 반도체 업황에 대한 긍정적 기대가 여전한 만큼 상승추세는 유효하다”고 점쳤다.윤필호 (nothing@edaily.co.kr)▶이데일리 개편기념 이벤트 - 갤럭시 노트8 쏩니다▶[카카오톡] 플친 맺으면 스타벅스 쿠폰 팡팡~♥<ⓒ종합 경제정보 미디어 이데일리 - 무단전재 & 재배포 금지>이데일리 관련뉴스언론사 페이지로 이동합니다.미소 띤 트럼프, 한미 FTA '돌출 발언' 사라진 이유[단독]종교인 과세 50년만에 내년 시행 '가닥'…기독교계는 반발조두순 '재심'은 불가능..출소 전 타이트한 '보안 처분' 이뤄져야멜라니아, '델포조' 코트 벗고 '제이멘델' 시스루 드레스 입어'비행기 사고로 사망' 할러데이에 메이저리그 애도 물결
문서에 포함된 종목 코드: ['005930', '000660']
4번째 문서 ----------------------------------------------------------------------------------------------------
내용: [서울경제] 코스피 지수가 사흘 만에 상승세로 전환했다. 8일 코스피 지수는 전 거래일 대비 6.96포인트(0.27%) 오른 2,552.40에 장을 마쳤다. 이 날 지수는 전일 대비 0.43% 하락한 2,534.48에 장을 시작했으나 외국인과 개인의 매수세에 힘입어 장중 반등에 성공했다. 장중 국내 주식을 사들이던 외국인은 오후 장에서 매도세로 전환했다. 외국인과 기관은 각각 38억원 1,059억원 어치 주식을 팔아치웠으며 개인만 홀로 매수 우위를 나타냈다. 도널드 트럼프 미국 대통령 방문으로 일부 불확실성이 해소되면서 일부 업종이 상승 전환한 게 주효했다. 업종별로는 의약품이 1.13% 상승했으며 전기전자(1.05%), 음식료품(0.59%), 섬유의복(0.95%), 종이목재(0.71%), 화학(0.40%), 비금속광물(0.77%) 등이 올랐다. 철강금속은 한미FTA 재협상 우려로 1.51% 하락했고 기계(-0.51%), 의료정밀(-0.76%), 운송장비(-0.18%), 통신업(-0.99%), 보험(-1.17%) 등이 약세를 나타냈다. 시가총액 상위 종목 중에는 삼성전자(005930)와 SK하이닉스(000660)가 각각 1.18%, 0.97% 상승하며 지수 상승을 이끌었다. 다른 상위 종목은 대개 약세로 마감했으며 한미FTA 재협상 가능성이 야기되면서 포스코가 2.31% 하락 마감했다. 코스닥은 전일 대비 7.97포인트(1.14%) 오른 709.11에 마감했다./서지혜기자 wise@sedaily.com[서울경제 바로가기]1997년 이래 대한민국 최고의 과학자 DB [바로가기]서울경제의 모든 연재물!... 서경Cube [바로가기▶]저작권자 ⓒ 서울경제, 무단 전재 및 재배포 금지서울경제 관련뉴스언론사 페이지로 이동합니다.[단독] 트럼프 맞아 청와대가 공수해 건 그림은?北에 등돌리는 중국? 갑자기 평양관광 금지한 이유멜라니아 트럼프가 샤이니 민호에게 활짝 웃어준 까닭? "훈훈한 분위기"조두순 사건 영화 '소원' 재조명...이준익 감독이 영화를 만든 이유는?장훈 감독 입장은? "조덕제 나를 만신창이 만들어" 메이킹 영상 "처절하게 강간당하는 모습
문서에 포함된 종목 코드: ['005930', '000660']

파이썬을 이용한 자동 주식투자 시스템 개발 튜토리얼 - 키움증권편

많은 분들이 주식투자를 해보셨거나 적어도 관심은 있으실 것입니다. 그러나 막상 주식투자를 해서 수익을 내는 분들은 많지 않은 것이 현실입니다. 많은 분들이 주식시장에서 이기지 못합니다. 그 이유는 욕심때문에 종목 매매를 객관적으로 하지 못해서, 계속해서 주식시장을 들여다볼 시간이 없어서일 수 있습니다. 시스템 트레이딩은 정해진 규칙으로 객관적으로 종목을 매매하여 객관성을 유지하고, 시스템이 자동으로 투자하기 때문에 주식시장을 계속 들여다보지 않아도 되는 등의 다양한 장점을 가집니다. 이 책을 통해 자신만의 자동 투자 시스템을 구현하여 모두 주식시장에서 성공적인 투자자가 될 수 있기를 바라봅니다.

이 책은 파이썬으로 키움증권 Open API를 활용하여 시스템 트레이딩 프로그램 개발의 전반을 설명합니다. 파이썬에 대한 기초적인 지식이 있다면 이 책을 읽기가 더욱 수월할 것입니다. 파이썬을 전혀 몰라도 이 책의 내용을 충분히 이해할 수 있도록 소스 코드에 대해 상세히 설명하며 필요한 파이썬 기초 문법도 다룹니다.

진행하다 막히는 부분이 있으면 Github 리포지토리(https://github.com/quantylab/systrader) 또는 이메일(quantylab@gmail.com)을 통해 트러블슈팅(Trouble Shooting)을 도와드립니다.

파이썬에서 키움증권 API를 사용하여 자동투자 시스템을 개발하는데 다음 환경을 권장합니다.

  • 윈도우 7+
  • 파이썬 3.5+ x86(32 비트)
  • PyCharm
  • 키움증권 API

여기서는 파이썬 3.5+ 32비트 버전을 사용합니다. 파이썬 2를 사용해도 무방하지만 32비트를 사용해야 합니다. 그리고 꼭 파이썬 2를 사용해야 하는 것이 아니라면 파이썬 3를 사용할 것을 권장합니다.

추가로 파이썬 라이브러리들을 설치해야 하는데, 본 책에서 활용하는 주요 라이브러리들은 다음과 같습니다.

  • PyQt5
  • NumPy
  • Pandas
  • Bokeh

Anaconda3는 파이썬 3와 대부분의 주요 라이브러리들을 포함하고 있습니다. 번거로움을 줄이고자 이들을 별도로 설치하는 것 보다 Anaconda3를 설치하기를 권장합니다.

다음 사이트에서 무료로 Anaconda3를 받을 수 있습니다.

https://www.continuum.io/downloads

Anaconda3 다운로드 사이트 화면

왼쪽 아래의 빨간 박스로 표시한 링크를 다운받기를 권장합니다.

다운받은 파일로 설치를 진행합니다. 별도의 설정 변경 없이 설치를 진행하면 되기 때문에 어려운 부분은 없습니다.

여기서는 VSCode를 주 IDE(Integrated Development Environment)로 사용합니다. VSCode는 마이크로소프트에서 만든 오픈소스 코드 편집기 입니다. https://code.visualstudio.com/에서 VSCode를 무료로 다운받을 수 있습니다.

파이썬 IDE로 대표적인 PyCharm 또한 SysTrader 개발에 사용하기 적합합니다. 다음 사이트에서 무료로 PyCharm Community 버전을 다운받을 수 있습니다.

https://www.jetbrains.com/pycharm/

Professional 버전은 유료이니 Community 버전을 받으시면 됩니다. Community 버전도 자동 투자 시스템을 만드는데 충분합니다.

이제 키움증권의 Open API를 설치할 차례입니다. 먼저 브라우저를 통해 키움증권 홈페이지(https://www1.kiwoom.com)에 들어갑니다. 메인화면의 최하단을 보시면 다음과 같이 Open API 메뉴가 있습니다.

openapi

클릭하여 들어가시면 다음과 같은 화면을 볼 수 있습니다.

openapi

여기서 표시해 놓은 사용 신청하러 가기키움 Open API+ 모듈 다운로드를 진행해 주시면 됩니다.

이렇게 다운받은 OpenAPISetup.exe파일을 실행시켜서 설치하시면 키움증권 API 설치가 완료됩니다.

추가로 KOA Studio를 설치하여 키움증권 API를 살펴볼 수 있습니다. KOA Studio를 실행했을 때 mfc100.dll을 찾을 수 없다는 에러화면을 보게 될 수도 있습니다.

koastudio 에러

이 때는 MS Visual C++ 2010 x86을 설치하면 에러가 해결됩니다.

MS VC++ 다운로드 페이지: https://www.microsoft.com/en-us/download/details.aspx?id=5555

키움증권을 통해 주식 투자를 하기 위해 우선 키움증권 계좌를 개설해야 합니다. 키움증권 홈페이지 메인화면의 우측에 다음 화면이 있습니다.

계좌개설

여기서 키움 계좌 개설 메뉴를 클릭하시고 계좌 개설을 진행해 주시면 됩니다.

자동투자 시스템을 구현하면서는 모의투자 계좌를 이용하셔야 합니다. 키움증권 홈페이지 메인화면의 최하단을 보시면 다음과 같이 상시 모의투자 메뉴가 있습니다.

모의투자

클릭하여 들어가시면 왼쪽 메뉴 리스트에 참가신청 메뉴가 있고, 클릭하여 들어가시면 다음과 같이 상시모의투자 참가신청란이 있습니다.

모의투자

신청 완료하시면 왼쪽 메뉴 리스트의 계좌정보 메뉴를 통해 주식계좌를 확인할 수 있습니다.

키움증권 API는 크게 TR과 REAL로 구분되는 API 집합으로 구성되어 있습니다.

  • TR은 요청을 보내면 그 결과가 콜백 함수로 전달되는 API들로, 시세조회, 관심종목조회, 조건검색 등을 포함합니다.
  • REAL은 키움증권 시스템에서 콜백 함수에 실시간으로 데이터를 전달하는 API들로, 매매 체결, 업종/종목의 실시간 시세, 실시간 조검검색 등을 포함합니다.

KOA Studio프로그램을 이용하면 API에 대한 상세 정보를 확인할 수 있습니다.

앞으로 활용할 주요 API들을 살펴보겠습니다. 여기서는 API에 포함되어 있는 함수들의 형태 (Signature)만 보여주고 파이썬에서의 호출 방법은 뒤에서 설명합니다.

로그인 함수는 아주 간단합니다. CommConnect()를 호출하면 키움증권 로그인 창이 뜨고 로그인 성공시 1, 로그인 실패시 0을 반환합니다. 키움증권 로그인 창이 뜨면 아이디, 비밀번호, 공인인증서비밀번호 입력란이 있습니다. 개발 동안에는 모의투자를 체크하여 아이디와 비밀번호만 입력하시길 강력 권장합니다.

키움증권의 로그인 창은 다음과 같습니다. 여기서 아이디를 저장하고, 모의투자 모드로 설정할 수 있습니다. 이 창을 파이썬으로 띄우는 방법은 개발 관련 장에서 설명합니다.

로그인

그렇다면 매번 아이디, 비밀번호, 공인인증서번호를 입력해야 할까요? 다행히 자동로그인 기능이 있습니다. 먼저 계좌 비밀번호를 저장해야 합니다. 이를위해 다음과 같이 Windows 우측 하단의 트레이 아이콘을 우클릭하여 계좌비밀번호 저장 메뉴를 통해 계좌 비밀번호를 저장할 수 있습니다. 트레이 아이콘이 숨어져 있을 수도 있으니, 화살표를 눌러서 확인해보시기 바랍니다. 물론 로그인이 되어 있는 상태에서만 아이콘을 확인할 수 있으니, 개발 관련 장에서 확인하시면 됩니다.

계좌비밀번호저장

다음과 같이 자동로그인을 활성화할 수 있습니다. 먼저 계좌 비밀번호를 등록하고, 계좌번호 바로 밑에 있는 AUTO를 체크합니다. 이렇게 하면 앞으로 로그인할 때 비밀번호를 입력하지 않아도 자동으로 로그인이 됩니다.

계좌비밀번호저장

이제 키움증권 API를 사용할 환경이 갖추어 졌습니다. 로그인한 계좌의 현황을 확인할 수 있는 API들을 살펴보겠습니다. 살펴볼 주요 API는 다음과 같습니다.

  • 현재 잔고 (Balance) 확인
  • 보유 종목 확인

현재 잔고를 확인하는 API는 예수금상세현황요청 기능을 이용하시면 됩니다. TR 목록opw00001을 활용하면 됩니다. 호출 방법은 다음과 같습니다.

SetInputValue("계좌번호", <계좌번호>)
CommRqData("예수금상세현황요청", "opw00001", 0, <화면번호>)

여기서 <계좌번호>에는 여러분의 모의투자 또는 실계좌 계좌번호를 입력하시면되고, 화면번호에는 임의의 값을 정해주시면 됩니다. 계좌번호가 8자리라면 11을 붙여서 10자리로 만들어 줍니다. 이렇게 호출하면 OnReceiveTrData() 함수가 콜백됩니다. 콜백이란 필요한 처리 후 시스템에서 함수를 호출해 준다는 의미를 가지는 컴퓨터 용어입니다. 주로 이름이 On으로 시작하는 함수들이 콜백 합수입니다.

화면번호의 값을 상수화하여 사용하시길 권장합니다. 키움증권에서 사용할 수 있는 화면번호가 200개로 제한되어 있기 때문입니다.

OnReceiveTrData()의 형태는 다음과 같습니다.

OnReceiveTrData(sScrNo, sRQName, sTRCode, sRecordName, sPreNext, nDataLength, sErrorCode, sMessage, sSPlmMsg)
  • sScrNo: 화면번호
  • sRQName: 사용자 구분명
  • sTRCode: TR이름
  • sRecordName: 레코드 이름
  • sPreNext: 연속조회 유무를 판단하는 값 0: 연속(추가조회)데이터 없음, 2:연속(추가조회) 데이터 있음
  • nDataLength: 사용안함
  • sErrorCode: 사용안함
  • sMessage: 사용안함
  • sSPlmMsg: 사용안함

이 콜백 함수 내에서 sRQName예수금상세현황요청인지 확인하고, 다음과 같이 주문가능금액을 얻어올 수 있습니다.

GetCommData(sTRCode, sRQName, 0, "주문가능금액")

매수 주문을 넣고, 주문이 체결되면 보유 종목이 생깁니다. 보유 종목은 주로 매도의 관점에서 모니터링 해야 합니다. 물론 추가 매수를 할 수도 있습니다. 보유 종목의 수익률을 확인하는 API를 살펴보겠습니다. 보유종목 정보를 받아오는 TR 코드는 OPT10085 입니다.

SetInputValue("계좌번호", <계좌번호>)
CommRqData("계좌수익률요청", "opt10085", 0, <화면번호>)

여기서 화면번호를 임의의 문자열을 넣어주면 됩니다. 화면번호는 다른 요청과 겹치지 않는 것이 좋습니다.

실제로 투자를 할때 하는 일들을 살펴볼 필요가 있습니다. 다음과 같이 정리해 보겠습니다.

  • 시장의 상황을 파악합니다. 궂이 불구덩이에 뛰어들 필요는 없습니다. 약세장일때는 피하는게 상책이죠.
  • 계좌의 현재 상태를 파악합니다. 매수 가능 금액이 얼마나 있는지 알아야 합니다.
  • 매수를 고려할 종목을 선택 합니다. 아무리 강세장에서도 오를 종목만 오릅니다.
  • 보유종목을 모니터링 합니다. 자신의 규칙에 따라 익절, 손절하는 것이 필요하죠.
  • 매수할 종목을 선택합니다.
  • 매도할 보유종목을 선택합니다.
  • 투자 손익을 파악합니다.

자동 투자 시스템을 구현할 때, 위 일들을 별도의 모듈로 구현하는 것이 유지보수 측면에서 좋을 것입니다. 위에서 나열한 일들을 수행하는 모듈들을 정해 보겠습니다.

  • 시장모니터 (Markets Monitor): 코스피, 코스닥 시장을 모니터링하여 강세장, 약세장을 판단하는 모듈
  • 계좌관리자 (Account Manager): 잔고를 파악하는 모듈
  • 보유종목모니터 (Holdings Monitor): 보유종목의 수익률 등을 모니터링 하는 모듈
  • 종목모니터 (Stocks Monitor): 실시간 종목 검색, 종목 상세 정보 획득 등을 수행하는 모듈
  • 매매판단자 (Decision Maker): 종목의 매수, 매도를 판단하는 모듈

파이썬은 컴퓨터 비전공자도 쉽게 접근할 수 있으면서 강력한 고급 프로그래밍 언어입니다. 전세계적으로 사용자가 크게 늘고 있는 추세이고, 구글을 비롯한 여러 업체의 실서비스에서도 사용하고 있습니다. 본 튜토리얼에서는 이후에 나오는 소스코드들을 따라할 수 있을 정도의 문법만 다룹니다.

우선 프로그래밍 언어를 처음 배울 때 꼭 한번씩 해보는 “Hello World!” 문자열을 출력해 보겠습니다.

if __name__ == "__main__":
print("Hello World!")

실행해 보면 Hello World!가 출력되는 것을 확인할 수 있습니다.

파이썬으로 객체지향 프로그램을 작성할 수 있습니다. 이를 위해서 파이썬에는 모듈과 클래스 개념이 들어있습니다. 파이썬에서 모듈은 하나의 .py 파일이라고 볼 수 있습니다. 하나의 파이썬 파일에 여러 클래스들이 포함될 수 있습니다.

이제 main.py 모듈을 하나 만들겠습니다. 그리고 아래의 코드를 작성합니다.

class SysTrader():
def __init__(self):
"""자동투자시스템 메인 클래스
"""
self.hello()
def hello(self):
print("Hello World!")
if __name__ == "__main__":
trader = SysTrader()

이렇게 클래스 하나를 선언하고 클래스의 객체를 생성하고 hello() 함수를 호출하여 똑같이 Hello World!를 출력했습니다. 앞으로 이 SysTrader 클래스를 차근차근 이름값 하게 만들어 보겠습니다.

로거는 시스템이 동작하는 동안 발생하는 정보를 기록하기 위한 모듈입니다. 단순히 print() 함수를 써서 출력하는 것보다 로거를 쓰는 것이 정보를 콘솔, 파일로 관리할 수 있어서 효과적입니다. 또한 로거는 출력할 정보에 레벨 (Level)을 부여하여 정보의 중요 정도를 정할 수 있습니다.

이제 시스템에 로거를 추가해 보겠습니다.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
from logging.handlers import TimedRotatingFileHandler
class SysTrader():
def __init__():
"""자동투자시스템 메인 클래스
"""
pass
if __name__ == "__main__":
# --------------------------------------------------
# 로거 (Logger) 준비하기
# --------------------------------------------------
# 로그 파일 핸들러
fh_log = TimedRotatingFileHandler('logs/log', when='midnight', encoding='utf-8', backupCount=120)
fh_log.setLevel(logging.DEBUG)
# 콘솔 핸들러
sh = logging.StreamHandler()
sh.setLevel(logging.DEBUG)
# 로깅 포멧 설정
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
fh_log.setFormatter(formatter)
sh.setFormatter(formatter)
# 로거 생성 및 핸들러 등록
logger = logging.getLogger(__file__)
logger.setLevel(logging.DEBUG)
logger.addHandler(fh_log)
logger.addHandler(sh)
# --------------------------------------------------
# 자동투자시스템 시작
# --------------------------------------------------
trader = SysTrader()

여기서는 로그를 파일과 콘솔에 출력합니다. 이를 위해 먼저 logging 모듈을 임포트합니다. 그리고 일별로 로그파일을 관리하기 위해 TimedRotatingFileHandler 클래스를 임포트 했습니다. TimedRotatingFileHandler의 생성자 파라미터로 파일 경로, 새로운 파일을 생성할 기준 (여기서는 자정 midnight으로 설정했습니다.), 파일 인코딩, 최대 파일 개수 backupCounting을 정해줍니다. 이렇게 하면 현재 로그는 logs/log 파일에 출력되고, 파일이 새로 생성될때 이전에 쌓였던 로그는 logs/log.2017-08-15와 같은 이름의 파일로 생성됩니다.

콘솔에서도 로그를 확인하기 위해 StreamHandler 클래스도 사용합니다.

각 핸들러에 setLevel()을 호출하는데, 레벨의 개념을 간략히 설명하겠습니다.

로깅 레벨과 그 값은 다음과 같습니다.

레벨
CRITICAL50
ERROR40
WARNING30
INFO20
DEBUG10
NOTSET0

이 중에서 하나를 지정하여 사용합니다. (자신만의 레벨을 만들수도 있지만, 미리 정의되어 있는 것을 사용하시는 것을 권장합니다.) 로깅 핸들러는 지정한 레벨보다 값이 높은 레벨의 로그들을 출력합니다. 주로 DEBUG, INFO, ERROR를 사용합니다.

이렇게 준비한 핸들러들을 logger에 등록합니다. 코드에서 볼 수 있듯이, 로거 수준에서 레벨을 지정할 수도 있습니다. 로그를 남기는 방법은 다음과 같습니다.

  • DEBUG 레벨 로그: logger.debug()
  • INFO 레벨 로그: logger.info()
  • ERROR 레벨 로그: logger.error()

키움증권 API는 시세조회, 관심종목조회, 조건검색 등의 대부분 TR 조회를 아울러 1초당 5회로 제한하고 있습니다. 즉 TR 요청을 동시에 보내지 않고 0.2초 간격으로 순차적(동기적)으로 보내는 것이 좋습니다.

이를 위해 본 튜토리얼에서는 파이썬의 고급 기능을 활용하여 요청들을 줄을 세우고, 0.2초의 간격으로 하나씩 처리하는 자료구조와 프로세스를 쓰레드로 구현합니다.

큐(Queue)는 선입선출(FIFO, First In First Out) 자료구조 입니다. 요청큐

쓰레드는 독자적으로 알고리즘을 수행하는 프로그램 속의 작은 독립적인 프로그램이라고 생각하시면 됩니다. 수행이 독립적으로 일어나기는 하지만 쓰레드 간에 데이터를 공유할 수는 있습니다.

구현은 다음과 같은 단계로 진행됩니다.

  • 단계 1. RequestThreadWorker 클래스 구현
  • 단계 2. SysTrader 클래스의 __init__() 함수에서 RequestThreadWorker 클래스의 객체 생성, 요청 쓰레드 객체 생성, 요청 쓰레드에 생성한 RequestThreadWorker 클래스의 객체 할당, 요청 쓰레드 객체 실행
  • 단계 3. SyncRequestDecorator 데코레이터 클래스 구현
  • 단계 4. 키움증권 API 요청 및 콜백 함수에 SyncRequestDecorator 데코레이터 설정

RequestThreadWorker는 요청들을 줄세울 수 있도록 큐(Queue) 자료구조를 가지고, 여기서 요청들을 하나씩 빼내서 0.2초 이상의 간격으로 호출하는 프로세스를 가지고 있습니다.

from collections import deque
from threading import Lock
class RequestThreadWorker(QObject):
def __init__(self):
"""요청 쓰레드
"""
super().__init__()
self.request_queue = deque() # 요청 큐
self.request_thread_lock = Lock()
# 간혹 요청에 대한 결과가 콜백으로 오지 않음
# 마지막 요청을 저장해 뒀다가 일정 시간이 지나도 결과가 안오면 재요청
self.retry_timer = None
def retry(self, request):
logger.debug("키움 함수 재시도: %s %s %s" % (request[0].__name__, request[1], request[2]))
self.request_queue.appendleft(request)
def run(self):
while True:
# 큐에 요청이 있으면 하나 뺌
# 없으면 블락상태로 있음
try:
request = self.request_queue.popleft()
except IndexError as e:
time.sleep(요청주기)
continue
# 요청 실행
logger.debug("키움 함수 실행: %s %s %s" % (request[0].__name__, request[1], request[2]))
request[0](trader, *request[1], **request[2])
# 요청에대한 결과 대기
if not self.request_thread_lock.acquire(blocking=True, timeout=5):
# 요청 실패
time.sleep(요청주기)
self.retry(request) # 실패한 요청 재시도
time.sleep(요청주기) # 0.2초 이상 대기 후 마무리

__init__() 함수는 RequestThreadWorker 클래스의 객체가 초기화될 때 실행됩니다. 여기서 request_queue 속성를 deque 객체로 할당합니다. 이 속성에 요청을 넣으면

SysTrader 클래스의 __init__() 함수에서 RequestThreadWorker를 실행할 요청 쓰레드인 request_thread 속성을 생성합니다. RequestThreadWorker 클래스의 객체를 request_thread에 할당하고, 요청 쓰레드가 시작될 때 RequestThreadWorkerrun() 함수가 실행되도록 설정합니다. 최종적으로 요청쓰레드를 시작합니다.

class SysTrader(QObject):
def __init__(self):
# (중략)
# 요청 쓰레드
self.request_thread_worker = RequestThreadWorker()
self.request_thread = QThread()
self.request_thread_worker.moveToThread(self.request_thread)
self.request_thread.started.connect(self.request_thread_worker.run)
self.request_thread.start()

QThread 클래스는 Qt 애플리케이션에서 사용하는 쓰레드 구현체입니다.

키움증권 API의 요청 결과는 대부분 콜백 함수로 전달됩니다. 그러므로 요청을 동기적으로 하기 위해서는 이전에 호출한 요청의 결과가 콜백 함수로 전달될때까지 기다려야 합니다. 이를 위해 SyncRequestDecorator 클래스를 구현합니다. SyncRequestDecorator 클래스는 키움증권 API를 호출하는 요청 함수들에 적용할 데코레이터와 콜백 함수들에 적용할 데코레이터를 가집니다.

kiwoom_sync_request() 데코레이터에서는 요청 함수, 함수에 입력된 인자값들을 요청 큐(request_queue)에 넣어주는 역할을 합니다.

kiwoom_sync_callback()은 콜백 함수를 실행한 후 요청 쓰레드의 잠금을 해제합니다.

class SyncRequestDecorator:
'''키움 API 동기화 데코레이터
'''
@staticmethod
def kiwoom_sync_request(func):
def func_wrapper(self, *args, **kwargs):
self.request_thread_worker.request_queue.append((func, args, kwargs))
return func_wrapper
@staticmethod
def kiwoom_sync_callback(func):
def func_wrapper(self, *args, **kwargs):
logger.debug("키움 함수 콜백: %s %s %s" % (func.__name__, args, kwargs))
func(self, *args, **kwargs) # 콜백 함수 호출
if self.request_thread_worker.request_thread_lock.locked():
self.request_thread_worker.request_thread_lock.release() # 요청 쓰레드 잠금 해제
return func_wrapper

이제 키움증권 API를 호출하는 함수들에 단계 3에서 만든 데코레이터를 달아 줍니다. 키움증권 API 관련 함수들은 뒤에서 기능별로 소개할 것이기 때문에 여기서는 데코레이터를 함수에 적용시키는 방법만 다루겠습니다.

SysTrader에서 키움증권의 요청 API는 kiwoom_로 시작하고 콜백은 kiwoom_On으로 시작합니다. 데코레이터는 함수 선언 위에 @로 적용합니다.

@SyncRequestDecorator.kiwoom_sync_request~
def kiwoom_REQUESTNAME(self, x):
pass
@SyncRequestDecorator.kiwoom_sync_callback
def kiwoom_OnCALLBACKNAME(self, x):
pass

이렇게 하면 kiwoom_REQUESTNAME()가 호출될 때 kiwoom_sync_request가, kiwoom_OnCALLBACKNAME()가 호출될 때 kiwoom_sync_callback가 먼저 호출됩니다.

이제 키움증권 API를 이용하여 투자 시스템에 로그인 해보겠습니다. 우선 Qt 애플리케이션을 생성하고 키움증권 API의 컨트롤 객체를 생성합니다. 여기서 Qt 관련 클래스들을 자세히 설명하지는 않겠습니다. 이들 클래스에 대한 깊은 이해가 없어도 자동투자 시스템을 개발하기에 무리가 없습니다.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import logging
from logging.handlers import TimedRotatingFileHandler
from PyQt5.QAxContainer import QAxWidget
from PyQt5.QtCore import QObject
from PyQt5.QtWidgets import QApplication
class SysTrader(QObject):
def __init__():
"""자동투자시스템 메인 클래스
"""
self.kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
self.kiwoom.OnEventConnect.connect(self.kiwoom_OnEventConnect) # 로그인 결과를 받을 콜백함수 연결
# -------------------------------------
# 로그인 관련함수
# -------------------------------------
@SyncRequestDecorator.kiwoom_sync_request
def kiwoom_CommConnect(self):
"""로그인 요청
키움증권 로그인창 띄워주고, 자동로그인 설정시 바로 로그인 진행됨.
OnEventConnect()으로 콜백 전달됨.
:param kwargs:
:return: 1: 로그인 요청 성공, 0: 로그인 요청 실패
"""
lRet = self.kiwoom.dynamicCall("CommConnect()")
return lRet
@SyncRequestDecorator.kiwoom_sync_callback
def kiwoom_OnEventConnect(self, nErrCode):
"""로그인 결과 수신
:param nErrCode: 0: 로그인 성공, 100: 사용자 정보교환 실패, 101: 서버접속 실패, 102: 버전처리 실패
:param kwargs:
:return:
"""
if nErrCode == 0:
logger.debug("로그인 성공")
elif nErrCode == 100:
logger.debug("사용자 정보교환 실패")
elif nErrCode == 101:
logger.debug("서버접속 실패")
elif nErrCode == 102:
logger.debug("버전처리 실패")
if __name__ == "__main__":
# (중략)
# --------------------------------------------------
# 자동투자시스템 시작
# --------------------------------------------------
app = QApplication(sys.argv) # Qt 애플리케이션 생성
trader = SysTrader() # QObject를 상속하는 자동투자시스템 객체 생성
trader.kiwoom_CommConnect() # 로그인
sys.exit(app.exec_())

kiwoom_CommConnect() 함수에서 키움증권 API 컨트롤을 통해 CommConnect() API를 호출하여 로그인 요청을 합니다. 처음에는 키움증권 로그인 창이 나타나고 아이디, 비밀번호 등을 입력하여 로그인합니다. 로그인 결과는 콜백으로 kiwoom_OnEventConnect() 함수에 전달됩니다. 이때 파라미터로 넘어오는 nErrCode의 값으로 로그인 결과를 판단합니다. 이 값이 0일 경우 로그인 성공입니다.

자동로그인을 설정해 놓은 경우 아이디, 비밀번호 입력 없이 자동으로 로그인이 진행되고, 그 결과가 콜백 함수로 들어옵니다.

키움증권 API에 로그인을 했으면 이제 계좌의 잔고를 확인해 보겠습니다. 다음 스텝으로 구현합니다.

  • 단계 1. SysTrader 클래스에 kiwoom_SetInputValue(), kiwoom_CommRqData(), kiwoom_TR_OPW00001_예수금상세현황요청(), kiwoom_OnReceiveTrData() 함수 추가
  • 단계 2.

먼저 다음 함수를 SysTrader 클래스에 추가합니다.

def kiwoom_SetInputValue(self, sID, sValue):
"""
:param sID:
:param sValue:
:return:
"""
res = self.kiwoom.dynamicCall("SetInputValue(QString, QString)", [sID, sValue])
return res
def kiwoom_CommRqData(self, sRQName, sTrCode, nPrevNext, sScreenNo):
"""
:param sRQName:
:param sTrCode:
:param nPrevNext:
:param sScreenNo:
:return:
"""
res = self.kiwoom.dynamicCall("CommRqData(QString, QString, int, QString)",
[sRQName, sTrCode, nPrevNext, sScreenNo])
return res
@SyncRequestDecorator.kiwoom_sync_request
def kiwoom_TR_OPW00001_예수금상세현황요청(self, 계좌번호, **kwargs):
"""계좌수익률요청
:param 계좌번호: 계좌번호
:param kwargs:
:return:
"""
res = self.kiwoom_SetInputValue("계좌번호", 계좌번호)
res = self.kiwoom_CommRqData("예수금상세현황요청", "opw00001", 0, 화면번호_예수금상세현황)
@SyncRequestDecorator.kiwoom_sync_callback
def kiwoom_OnReceiveTrData(self, sScrNo, sRQName, sTRCode, sRecordName, sPreNext, nDataLength, sErrorCode, sMessage, sSPlmMsg, **kwargs):
"""TR 요청에 대한 결과 수신
데이터 얻어오기 위해 내부에서 GetCommData() 호출
GetCommData(
BSTR strTrCode, // TR 이름
BSTR strRecordName, // 레코드이름
long nIndex, // TR반복부
BSTR strItemName) // TR에서 얻어오려는 출력항목이름
:param sScrNo: 화면번호
:param sRQName: 사용자 구분명
:param sTRCode: TR이름
:param sRecordName: 레코드 이름
:param sPreNext: 연속조회 유무를 판단하는 값 0: 연속(추가조회)데이터 없음, 2:연속(추가조회) 데이터 있음
:param nDataLength: 사용안함
:param sErrorCode: 사용안함
:param sMessage: 사용안함
:param sSPlmMsg: 사용안함
:param kwargs:
:return:
"""
if sRQName == "예수금상세현황요청":
self.int_주문가능금액 = int(self.kiwoom_GetCommData(sTRCode, sRQName, 0, "주문가능금액"))
logger.debug("주문가능금액: %s" % (self.int_주문가능금액,))

먼저 계좌번호SetInputValue() 함수로 입력하고, 예수금상세현황요청CommRqData() 함수로 호출합니다. 이 요청에 대한 결과는 OnReceiveTrData() 콜백 함수를 통해 확인할 수 있습니다.

이 콜백 함수를 키움증권 API 컨트롤에 연결해 주어야 합니다.

def __init__():
"""자동투자시스템 메인 클래스
"""
self.kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
self.kiwoom.OnEventConnect.connect(self.kiwoom_OnEventConnect) # 로그인 결과를 받을 콜백함수 연결
self.kiwoom.OnReceiveTrData.connect(self.kiwoom_OnReceiveTrData)
self.kiwoom_CommConnect() # 로그인

조건 종목 검색을 위해 우선 다음 두 함수 kiwoom_GetConditionLoad(), kiwoom_OnReceiveConditionVer()를 정의합니다.

# -------------------------------------
# 조건검색 관련함수
# GetConditionLoad(), OnReceiveConditionVer(), SendCondition(), OnReceiveRealCondition()
# -------------------------------------
@SyncRequestDecorator.kiwoom_sync_request
def kiwoom_GetConditionLoad(self, **kwargs):
"""
조건검색의 조건목록 요청
:return:
"""
lRet = self.kiwoom.dynamicCall("GetConditionLoad()")
return lRet
@SyncRequestDecorator.kiwoom_sync_callback
def kiwoom_OnReceiveConditionVer(self, lRet, sMsg, **kwargs):
"""
조건검색의 조건목록 결과 수신
GetConditionNameList() 실행하여 조건목록 획득.
첫번째 조건 이용하여 [조건검색]SendCondition() 실행
:param lRet:
:param sMsg:
:param kwargs:
:return:
"""
if lRet:
sRet = self.kiwoom.dynamicCall("GetConditionNameList()")
pairs = [idx_name.split('^') for idx_name in [cond for cond in sRet.split(';')]]
if len(pairs) > 0:
nIndex = pairs[0][0]
strConditionName = pairs[0][1]
self.kiwoom_SendCondition(strConditionName, nIndex)