콘텐츠로 이동

블로그

KRX에서 공매도 데이터 수집하기

이번 포스트에서는 KRX에서 개별종목 공매도 거래 및 개별종목 공매도 잔고 데이터를 파이썬으로 수집하는 방법을 다룹니다.

필요한 파이썬 라이브러리는 다음과 같습니다.

  • pip install requests
  • pip install numpy
  • pip install pandas

먼저 KRX 정보데이터시스템에서 다음 두 메뉴를 확인해 주세요.

  • 통계 / 공매도 통계 / 공매도 거래 / 개별종목 공매도 거래
  • 통계 / 공매도 통계 / 공매도 잔고 / 개별종목 공매도 잔고

개별종목 공매도 거래 데이터 수집하기

섹션 제목: “개별종목 공매도 거래 데이터 수집하기”

먼저 개별종목 공매도 거래 데이터를 받아오겠습니다. 크롬이나 웨일 브라우저에서 F12 키를 눌러 개발자 도구를 열어줍니다. 그리고 다음과 같이 Network 탭에서 Fetch/XHR 하위 탭을 확인합니다. 그리고 조회 버튼을 다시 누르면 아래에 getJsonData.cmd 내역이 뜨고 여기서 요청 정보를 확인할 수 있습니다.

1

여기서 Request URL, Request Headers, Form Data를 확인합니다. 다음과 같이 확인한 정보로 요청 준비를 합니다.

url = 'http://data.krx.co.kr/comm/bldAttendant/getJsonData.cmd'
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Host': 'data.krx.co.kr',
'Origin': 'http://data.krx.co.kr',
'Referer': 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader/index.cmd?menuId=MDC0203',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.118 Whale/2.11.126.23 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
}
data = [
('bld', 'dbms/MDC/STAT/srt/MDCSTAT30101'),
('searchType', '1'),
('mktId', 'STK'),
('secugrpId', 'STMFRTSCIFDRFS'),
('secugrpId', 'SRSW'),
('secugrpId', 'BC'),
('inqCond', 'STMFRTSCIFDRFSSRSWBC'),
('trdDd', '20211105'),
('share', '1'),
('money', '1'),
('csvxls_isNo', 'false'),
]

이렇게 준비한 url, headers, data로 requests 요청을 보냅니다. Request Method를 보면 POST를 확인할 수 있습니다.

import requests
res = requests.post(url, headers=headers, data=data)
res
Output: <Response [200]>

Status Code가 200으로 요청이 성공했음을 확인했습니다. 다음으로 받아온 데이터를 DataFrame으로 저장합니다.

import json
short_list = json.loads(res.text)['OutBlock_1']
import pandas as pd
df = pd.DataFrame(short_list)
df.columns = ['종목코드', '종목명', '증권구분', '공매도거래량', '공매도거래량업틱룰', '공매도거래량업틱룰예외', '거래량', '거래량비중', '공매도거래대금', '공매도거래대금업틱룰', '공매도거래대금업틱룰예외', '거래대금', '거래대금비중']
df
index종목코드종목명증권구분공매도거래량공매도거래량업틱룰공매도거래량업틱룰예외거래량거래량비중공매도거래대금공매도거래대금업틱룰공매도거래대금업틱룰예외거래대금거래대금비중
0095570AJ네트웍스주권00059,7390.00000335,609,2800.00
1006840AK홀딩스주권00020,2250.00000508,366,0000.00
2027410BGF주권147,2900147,2908,276,9041.78959,076,1300959,076,13054,013,149,7701.78
3282330BGF리테일주권1,4801,4027822,4896.58244,731,000231,828,50012,902,5003,734,658,0006.55
4138930BNK금융지주주권54,96254,96202,228,7702.47463,644,520463,644,520018,819,972,0802.46
……………………………………
1028069260휴켐스주권72,49872,0734251,137,3256.371,919,680,5501,908,522,05011,158,50030,290,522,8506.34
1029000540흥국화재주권00083,2810.00000318,332,1400.00
1030000547흥국화재2우B주권0001920.000005,323,0000.00
1031000545흥국화재우주권0009880.000008,514,7700.00
1032003280흥아해운주권0001,114,0590.000003,560,429,5450.00

거래량비중으로 내림차순 정렬을 해보면 공매도 비중이 높은 종목 순위를 확인할 수 있습니다.

df.sort_values('거래량비중', ascending=False)
index종목코드종목명증권구분공매도거래량공매도거래량업틱룰공매도거래량업틱룰예외거래량거래량비중공매도거래대금공매도거래대금업틱룰공매도거래대금업틱룰예외거래대금거래대금비중
51294870HDC현대산업개발주권54,93554,9350562,4339.771,353,057,7501,353,057,750013,880,230,4009.75
1004008770호텔신라주권66,04056,3889,652684,4589.655,269,414,8004,501,247,500768,167,30054,557,366,5009.66
800039490키움증권주권4,5614,07149047,2999.64484,180,000432,296,50051,883,5005,018,881,5009.65
813003240태광산업주권676706979.6166,460,00066,460,0000692,149,0009.60
940180640한진칼주권5,5874,96162658,8329.50315,127,600279,921,10035,206,5003,317,193,8009.50
……………………………………
433007210벽산주권2023,489,8850.008,06008,06014,442,649,3350.00
434002760보락주권000230,6080.00000490,745,8650.00
436000890보해양조주권0001,282,0730.000001,233,822,7750.00
438001270부국증권주권0006,8280.00000166,036,9500.00
1032003280흥아해운주권0001,114,0590.000003,560,429,5450.00

그런데 조금 이상합니다. 거래량비중이 생각보다 적고 9.XX 값이 상위에 랭크됩니다. 이는 데이터 형식이 문자열(str)로 되어 있을 때 자주 겪는 문제입니다. 다음과 같이 거래량비중 컬럼의 데이터 형식을 float로 변경합니다.

df['거래량비중'] = df['거래량비중'].astype(float)

다시 정렬 결과를 확인하면 다음과 같이 제대로된 랭킹이 된 것을 확인할 수 있습니다.

df.sort_values('거래량비중', ascending=False)
index종목코드종목명증권구분공매도거래량공매도거래량업틱룰공매도거래량업틱룰예외거래량거래량비중공매도거래대금공매도거래대금업틱룰공매도거래대금업틱룰예외거래대금거래대금비중
539068270셀트리온주권150,827133,03017,797488,77930.8631,363,423,00027,715,592,5003,647,830,500101,280,970,50030.97
1024081660휠라홀딩스주권161,370160,878492665,89024.235,958,633,4505,940,402,90018,230,55024,692,808,60024.13
673214320이노션주권6,9946,85613830,81022.70403,284,700395,322,1007,962,6001,776,867,00022.70
382011170롯데케미칼주권45,95634,17611,780206,11622.3010,110,596,0007,520,623,0002,589,973,00045,414,691,50022.26
240002350넥센타이어주권27,71225,1762,536125,20422.13208,348,430189,322,06019,026,370941,065,64022.14
……………………………………
433007210벽산주권2023,489,8850.008,06008,06014,442,649,3350.00
434002760보락주권000230,6080.00000490,745,8650.00
436000890보해양조주권0001,282,0730.000001,233,822,7750.00
438001270부국증권주권0006,8280.00000166,036,9500.00
1032003280흥아해운주권0001,114,0590.000003,560,429,5450.00

개별종목 공매도 잔고 데이터 수집하기

섹션 제목: “개별종목 공매도 잔고 데이터 수집하기”

이제 개별종목 공매도 잔고 데이터를 받아 보겠습니다. 비슷하게 요청 준비를 합니다.

url = 'http://data.krx.co.kr/comm/bldAttendant/getJsonData.cmd'
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'http://data.krx.co.kr',
'Referer': 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader/index.cmd?menuId=MDC0201',
}
data = [
('bld', 'dbms/MDC/STAT/srt/MDCSTAT30501'),
('searchType', '1'),
('mktTpCd', '1'),
('trdDd', '20211102'),
('share', '1'),
('money', '1'),
('csvxls_isNo', 'false'),
]
res = requests.post(url, headers=headers, data=data)
res
Output: <Response [200]>

다음과 같이 받아온 데이터를 DataFrame으로 변환합니다.

df_shortbalance = pd.DataFrame(json.loads(res.text)['OutBlock_1'])
df_shortbalance
indexISU_CDISU_ABBRVBAL_QTYLIST_SHRSBAL_AMTMKTCAPBAL_RTO
0095570AJ네트웍스34,25546,822,295190,115,250259,863,737,2500.07
1006840AK홀딩스4,59313,247,561121,484,850350,397,988,4500.03
2027410BGF48,44095,716,791279,014,400551,328,716,1600.05
3282330BGF리테일5,01617,283,906825,132,0002,843,202,537,0000.03
4138930BNK금융지주524,715325,935,2464,601,750,5502,858,452,107,4200.16
……………………
934069260휴켐스953,65840,878,58826,320,960,8001,128,249,028,8002.33
935000540흥국화재33,30164,242,645129,540,890249,903,889,0500.05
936000547흥국화재2우B0153,60004,331,520,0000.00
937000545흥국화재우0768,00006,673,920,0000.00
938003280흥아해운84,187240,424,899311,070,965888,370,001,8050.04
df_shortbalance.columns = ['종목코드', '종목명', '공매도잔고수량', '상장주식수', '공매도잔고금액', '시가총액', '비중']
df_shortbalance
index종목코드종목명공매도잔고수량상장주식수공매도잔고금액시가총액비중
0095570AJ네트웍스34,25546,822,295190,115,250259,863,737,2500.07
1006840AK홀딩스4,59313,247,561121,484,850350,397,988,4500.03
2027410BGF48,44095,716,791279,014,400551,328,716,1600.05
3282330BGF리테일5,01617,283,906825,132,0002,843,202,537,0000.03
4138930BNK금융지주524,715325,935,2464,601,750,5502,858,452,107,4200.16
……………………
934069260휴켐스953,65840,878,58826,320,960,8001,128,249,028,8002.33
935000540흥국화재33,30164,242,645129,540,890249,903,889,0500.05
936000547흥국화재2우B0153,60004,331,520,0000.00
937000545흥국화재우0768,00006,673,920,0000.00
938003280흥아해운84,187240,424,899311,070,965888,370,001,8050.04

이번에는 비중 컬럼으로 내림차순 정렬을 해봅니다. 마찬가지로 문자열 정렬 문제가 있을 수 있으므로 이 컬럼을 float으로 변환하고 정렬합니다.

df_shortbalance['비중'] = df_shortbalance['비중'].astype(float)
df_shortbalance.sort_values('비중', ascending=False)
index종목코드종목명공매도잔고수량상장주식수공매도잔고금액시가총액비중
78034220LG디스플레이28,935,291357,815,700568,578,468,1507,031,078,505,0008.09
339032350롯데관광개발4,726,86769,275,66294,300,996,6501,382,049,456,9006.82
910008770호텔신라2,462,55739,248,121198,974,605,6003,171,248,176,8006.27
46011200HMM20,968,085405,392,487574,525,529,00011,107,754,143,8005.17
523019170신풍제약2,499,52752,984,990128,225,735,1002,718,129,987,0004.72
……………………
313030720동원수산04,653,805045,886,517,3000.00
315014825동원시스템즈우0265,35808,809,885,6000.00
644000230일동홀딩스011,540,4000156,949,440,0000.00
316163560동일고무벨트013,900,0000116,760,000,0000.00
261006340대원전선071,016,9670142,033,934,0000.00

공매도 거래와 공매도 잔고 데이터 합치기

섹션 제목: “공매도 거래와 공매도 잔고 데이터 합치기”

이제 두 데이터를 합쳐서 확인해 보겠습니다. df는 공매도 거래 데이터이고 df_shortbalance는 공매도 잔고 데이터입니다. 이들 둘을 pandas merge() 메소드로 합치겠습니다.

_df = df.merge(df_shortbalance, how='outer', on='종목코드', suffixes=('', '_dup'))

이 데이터로 다양한 분석을 해볼 수 있겠습니다. 여기서는 공매도 잔고는 많으면서 공매도 거래는 적은 순으로 정렬해 봅니다.

_df['score'] = _df['비중'] / (_df['거래량비중'] + 1)
_df.sort_values('score', ascending=False)
index종목코드종목명증권구분공매도거래량공매도거래량업틱룰공매도거래량업틱룰예외거래량거래량비중공매도거래대금공매도거래대금업틱룰공매도거래대금업틱룰예외거래대금거래대금비중종목명_dup공매도잔고수량상장주식수공매도잔고금액시가총액비중score
97034220LG디스플레이주권4,4202,2902,1301,949,1940.2389,668,95046,585,15043,083,80039,626,663,8000.23LG디스플레이28,935,291357,815,700568,578,468,1507,031,078,505,0008.096.577236
620097520엠씨넥스주권3003083,6760.041,333,05001,333,0503,713,085,8500.04엠씨넥스435,40917,963,28219,332,159,600797,569,720,8002.422.326923
370032350롯데관광개발주권17,56716,928639882,3321.99324,702,700312,941,65011,761,05016,281,919,2501.99롯데관광개발4,726,86769,275,66294,300,996,6501,382,049,456,9006.822.280936
699006490인스코비주권67506751,384,4220.052,285,32002,285,3204,702,224,2550.05인스코비1,824,640109,848,1006,541,334,400393,805,438,5001.661.580952
578003620쌍용차주권00000.0000000.00쌍용차2,180,965149,840,0026,041,273,050415,056,805,5401.461.460000
………………………………………………………
98375701B93현대유퍼스트부동산25호 A수익증권00000.0000000.00NaNNaNNaNNaNNaNNaNNaN
98475702B93현대유퍼스트부동산25호 C-i수익증권00000.0000000.00NaNNaNNaNNaNNaNNaNNaN
98575701BA7현대유퍼스트부동산30호[파생형] A수익증권00000.0000000.00NaNNaNNaNNaNNaNNaNNaN
98675702BA7현대유퍼스트부동산30호[파생형] C-i수익증권00000.0000000.00NaNNaNNaNNaN

공매도 잔고가 많다는 것은 다시 상환해야 할 물량이 많다는 것이고 동시에 공매도 거래 비중이 적다면 주가가 바닥에 가까워 졌다고 추측해 볼 여지가 있습니다. 주식 거래에서 확실한 것은 없습니다. 다양한 금융데이터 분석을 통해 성공적인 투자에 한걸음씩 다가가는 것이 중요하다고 봅니다.

퀀티랩 Long-Term Trading (QL) 모델 개선 작업

퀀티랩 주식투자 대표 모델인 Quantylab Long-Term Trading (QL) 모델을 업데이트 했습니다. 기존 모델에 비해 다양한 시장지표 및 종목지표를 추가하고 레이블링을 조정하는 작업이었습니다.

Feature 수는 412개로 326개 시장지표 자질과 86개 종목지표 자질로 구성했습니다. 각종 지표에 Moving Average 등을 적용하여 자질 수는 부풀려진 면이 있습니다.

시장지표는 코스피/코스닥 지수, 해외 주식 시장 지수, 환율, 채권, 금리, 원자재, GSCI, 달러 지수, BDI, 필라델피아 반도체 지수, 미국시장 변동성 지수, MSCI 지수 등을 포함합니다. 종목지표는 종목 주가, 거래량, 시가총액, Bollinger Band, MACD, RSI, 수급, PER, PBR, ROE, 배당수익률, 기관/외국인/개인 수급, 공매도 비율, 대차잔고액 등을 포함합니다. 이들 시장지표 및 종목지표 자질들을 모아서 표준화 (Standardization) 했습니다.

레이블링은 수익성, 변동성, 거래규모 지표를 만들어 조합했습니다. 수익성 지표는 중장기 기간에서 종가의 변곡점을 찾아서 현재 종가와 다음 변곡점과의 차이를 기대 수익률로 삼았습니다. 변동성 지표는 변곡점 사이에서의 표준편차로 구했습니다. 거래규모 지표는 종목의 거래대금에 log를 취해 사용했습니다.

이렇게 자질과 레이블링을 마치고 적당히 심층신경망 레이어를 구성하여 학습했습니다. 중간에 이런저런 실수로 시행착오를 겪었지만 다 생략하고 결과를 바로 공유하려 합니다.

학습에는 2015~2019년 기간내의 데이터를 사용했습니다. 이렇게 학습한 모델로 2020년부터 현재까지 일단위로 종목 점수를 구하여 매수/매도를 판단해서 종가매매 백테스팅을 수행했습니다. 즉, 학습과 백테스팅 데이터가 겹쳐지지 않게하여 의미있는 백테스팅이 되도록 했습니다.

백테스팅

백테스팅 결과 2020년 부터 2021-09-17 까지의 평가 수익률은 +73.07%로 동기간 코스피 대비 30% 정도 높았습니다. QL 모델로 매수/매도를 판단한 내역은 다음과 같습니다.

날짜매매종목명단가수량수익률평가총액
2021-08-19매도현대자동차206,50058+20.41%173,831,949
2021-05-06매수LG전자151,50066177,219,388
2021-05-06매수SK케미칼256,00039177,220,888
2021-05-06매수LG화학938,00010177,222,386
2021-05-06매도포스코395,50051+103.87%177,223,793
2021-03-23매도현대모비스286,50049+40.79%172,520,635
2021-03-23매수셀트리온292,50034172,517,645
2021-03-23매수삼성전기185,00054172,519,136
2021-01-13매도삼성SDI754,00026+100.80%178,728,737
2021-01-08매도삼성전기193,000112+116.37%179,837,188
2020-11-16매도NAVER278,00055+53.17%148,783,070
2020-09-24매수SK텔레콤230,50043136,101,988
2020-09-24매수현대자동차171,50058136,103,475
2020-09-24매수케이티앤지80,400124136,104,967
2020-09-24매도셀트리온245,50261+51.06%136,106,463
2020-08-03매도LG화학635,00031+102.23%137,605,846
2020-07-24매수현대모비스203,50049131,218,600
2020-07-24매수포스코194,00051131,215,651
2020-07-24매수삼성SDI375,50026131,217,135
2020-07-24매도엔씨소프트802,00017+41.95%131,220,096
2020-06-29매도SK케미칼134,500163+120.49%128,438,310
2020-03-23매수삼성전기89,20011281,295,074
2020-03-23매도케이티앤지63,600108-31.24%81,296,573
2020-03-17매수SK케미칼61,00016385,736,761
2020-03-17매도현대자동차80,10084-32.12%85,738,252
2020-03-16매도삼성SDI253,00043+10.48%86,948,811
2020-03-16매도포스코162,00042-31.36%86,977,640
2020-03-16매수삼성전자48,90020486,945,828
2020-03-16매수셀트리온162,5226186,947,324
2020-03-13매도현대모비스171,50040-30.71%91,169,271
2020-01-03매수이마트122,5008199,845,350
2020-01-03매수NAVER181,5005599,846,838
2020-01-03매수케이티앤지92,50010899,848,336
2020-01-03매수삼성SDI229,0004399,849,834
2020-01-03매수엔씨소프트565,0001799,851,311
2020-01-02매수LG화학314,0003199,994,081
2020-01-02매수현대모비스247,5004099,995,541
2020-01-02매수현대자동차118,0008499,997,026
2020-01-02매수LG생활건강1,266,000799,992,752
2020-01-02매수포스코236,0004299,998,513

퀀티랩에서 매일 올라오는 QL 모델의 매매내역과 보유종목을 확인해 보세요.

[2021-08-13] 공매도 상위 종목 살펴보기

공매도는 주식을 빌려서 파는 행위를 말합니다. 주가가 하락한다는 쪽에 베팅하는 것입니다. 이러한 베팅을 숏(Short) 포지션 취한다고 합니다. 주가가 하락할 것이라고 예상하고 주식을 빌려서 현재 가격에 매도하고 주가가 떨어졌을 때 다시 매수해서 빌린 주식을 갚는 것입니다.

공매도는 2020년 3월에 주가 급락을 막기 위해 금지됐었습니다. 그렇게 1년 2개월간 금지된 공매도가 2021년 5월에 재개됐습니다. 공매도는 개인투자자들이 하기 어렵고 그만큼 개인투자자들의 공매도에 대한 부정적인 인식이 강합니다.

공매도가 많다고 해서 주가가 꼭 하락한다는 보장은 없지만 공매도의 현황을 파악해 놓는 것은 중요하다고 생각합니다. 공매도의 추세가 증가하면 가급적 매수를 자제하는 것이 좋다고 생각합니다. 다만 공매도가 많다가 줄어드는 추세이고 대차잔고가 많은 상태면 주가가 바닥권이라는 예측을 해볼 수도 있습니다. 대차잔고는 주식을 빌리고 아직 갚지 않은 주식 수를 의미합니다. 즉, 대차잔고만큼 앞으로 매수가 이뤄질 것이란 의미입니다.

퀀티랩 종목지표/공매도 메뉴에서 종목별 공매도 현황을 표로 확인할 수 있습니다.

이 포스트에서는 2021-08-13 기준으로 공매도율, 대차잔고율, 대차잔고액 순위를 살펴보겠습니다.

종목명주가등락율(%)공매도액(만원)공매도율(%)대차잔고액(만원)대차잔고율(%)
티씨케이173,400-1.64137,55625.57  
넷마블127,000-3.791,398,73025.519,808,5330.85
하림지주9,580-1.5465,51022.83  
SK머티리얼즈432,000-1.5508,95921.26  
휴온스61,400-1.7676,52719.85  
금호석유화학192,500-2.531,408,19318.8814,948,0292.48
엔씨소프트786,000-2.481,563,72318.77,751,7090.43
리노공업172,4000.17151,01818.69  
CJ대한통운165,500-2.07189,09718.43845,5910.22
GS리테일33,200-1.92443,43218.12711,7630.2
고영23,450-2.0984,65917.43  
주성엔지니어링13,050-1.14326,58817.19  
SK텔레콤306,000-2.081,344,42217.128,681,7190.4
동국제약24,700-1.7971,29817.11  
팬오션7,410-4.391,044,21716.944,016,5251.03
하나투어75,600-2.45265,63316.831,204,7801.09
S-Oil93,800-1.37360,71916.512,908,9250.28
제일기획23,200-0.22108,40615.71429,8280.16
이오테크닉스112,700-3.1153,84315.5  
한화솔루션38,700-2.27564,86715.4112,955,9341.7
종목명주가등락율(%)공매도액(만원)공매도율(%)대차잔고액(만원)대차잔고율(%)
롯데관광개발18,900-2.0756,8846.669,874,8717.25
LG디스플레이20,800-3.48971,07811.9750,027,6426.31
신풍제약66,800-6.96867,6693.5914,668,3194.31
HMM39,150-3.69100,3460.4564,919,6674.13
엠씨넥스45,500-1.62270.003,234,9843.81
삼성중공업6,170-2.68179,1775.7315,810,5893.73
OCI107,000-3.60178,2872.698,522,1113.18
셀트리온275,500-2.13960,1994.45112,924,1253.03
부광약품20,400-2.8665,7163.804,234,1882.84
일양약품34,150-1.7331,3177.041,721,0902.58
호텔신라90,000-2.39313,5536.939,290,9012.57
금호석유화학192,500-2.531,408,19318.8814,948,0292.48
두산퓨얼셀48,400-2.2238,5711.538,021,7492.43
한전기술48,150-3.8942,4562.393,857,6091.99
GKL15,700-1.2643,6218.021,931,8801.93
삼양식품87,800-0.7919,8586.281,279,7201.90
인스코비4,32514.578800.01686,1711.79
씨에스윈드80,100-2.44300,5558.815,912,2981.77
CJ CGV28,750-2.8746,4652.571,942,7591.72
한화솔루션38,700-2.27564,86715.4112,955,9341.70
종목명주가등락율(%)공매도액(만원)공매도율(%)대차잔고액(만원)대차잔고율(%)
셀트리온275,500-2.13960,1994.45112,924,1253.03
HMM39,150-3.69100,3460.4564,919,6674.13
LG디스플레이20,800-3.48971,07811.9750,027,6426.31
현대자동차217,000-0.91654,3723.5132,678,4790.70
삼성바이오로직스983,0001.13683,4054.8120,547,0590.33
아모레퍼시픽220,500-1.78388,53610.8317,527,9871.32
삼성중공업6,170-2.68179,1775.7315,810,5893.73
금호석유화학192,500-2.531,408,19318.8814,948,0292.48
LG생활건강1,444,0001.62498,0127.0614,773,6560.66
신풍제약66,800-6.96867,6693.5914,668,3194.31
SK이노베이션248,5002.691,440,1985.3214,506,4950.65
LG화학896,0002.051,345,0333.7914,318,3710.24
한화솔루션38,700-2.27564,86715.4112,955,9341.70
카카오146,000-1.022,591,3428.2912,739,6100.20
포스코케미칼155,0001.311,070,66114.5512,592,2281.05
한국조선해양119,000-0.8393,6402.2510,713,9721.22
롯데관광개발18,900-2.0756,8846.669,874,8717.25
두산중공업20,800-4.15215,1781.679,809,8390.89
넷마블127,000-3.791,398,73025.519,808,5330.85
삼성전자74,400-3.3813,590,0493.009,771,2790.02

여기서 대차잔고액은 높은데 공매도율은 낮은 종목으로는 HMM이 있습니다. HMM의 공매도 흐름을 살펴보면 다음과 같습니다.

HMM 공매도

7월달을 보면 공매도율이 무려 25% 가까이 있었다는 것을 알 수 있습니다. 이 공매도 물량을 언제 상환할 지는 모르겠지만 최근 공매도율은 낮아진 것으로 보입니다.

여기에 BDI 역시 높은 상황이라 HMM이 다시 올라갈 여지도 있어 보입니다.

BDI

주요 MSCI 지수 차트 한꺼번에 보기

MSCI 지수는 모건스탠리캐피탈인터내셔널(MSCI)에서 발표하는 선진국/신흥국, 지역, 국가 기준의 주식 종합 지수입니다. MSCI 지수는 많은 펀드들이 추종하고 있어서 주식투자에 있어서 매우 중요한 지수입니다.

한국의 코스피, 미국의 S&P 500, 일본의 닛케이, 중국의 상해종합지수, 홍콩의 항셍지수 등 각 국가별로 주식 종합 지수가 존재합니다. 그럼 “왜 굳이 MSCI 지수가 필요하지?”라는 의문이 생길 수 있습니다. 각 국가에서 주식종합지수를 만드는 방법이 다르기 때문에 획일화된 방법으로 각 국가의 지수를 만들어야할 필요가 생겼습니다. 이에 모건스탠리캐피탈인터내셔널(MSCI)에서 이 역할을 수행하는 것입니다.

MSCI는 국가별 뿐만 아니라 지역(아시아, 아메리카, 유럽 등) 별로 또는 선진국/신흥국 별로 주식종합지수를 만들기도 합니다.

매우 다양한 MSCI 지수들 중에서 한국 주식 투자에 주로 참고해야 할 주요 MSCI 지수들의 차트를 퀀티랩에서 한꺼번에 볼 수 있습니다. 시장지표 / MSCI 메뉴에서 다은과 같은 차트들을 볼 수 있습니다.

MSCI

  • MSCI WORLD는 23개 선진 국가 주식 시장을 대상으로 합니다.
  • MSCI WCWI는 23개 선진 국가와 27개 신흥 국가 주식 시장을 대상으로 합니다.
  • MSCI EM은 26개 신흥 국가 주식 시장을 대상으로 합니다.
  • 그 외는 각 국가별 지수 입니다.

각 지수에 포함된 국가 수, 종목, 비중 등은 변경될 수 있습니다.

한국/미국 금리 차트 보기

앞서 퀀티랩에서 해외증시, 환율, 원자재 차트를 볼 수 있다고 소개 드렸는데, 중요한 지표인 금리가 빠져 있었습니다. 이제 한국과 미국 금리 차트도 퀀티랩에서 확인할 수 있습니다.

시장지표 / 금리 메뉴에서 금리 관련 차트들을 볼 수 있습니다.

금리

현재는 한국 기준 금리, 미국 기준 금리, 미국 10년물 국채 금리를 볼 수 있는데 앞으로 한국 국채 금리를 포함해 다양한 금리 데이터도 추가될 예정입니다.

추가되었으면 하는 데이터가 있다면 메일, 카페 등의 채널을 통해 요청해 주시면 최대한 서비스에 반영하도록 하겠습니다.

해외증시, 환율, 원자재 차트 한눈에 보기

퀀티랩에서 해외증시, 환율, 원자재 차트들을 볼 수 있습니다. 이러한 시장 지표들의 추세를 잘 확인하면 주식 투자에 큰 도움이 될 것입니다.

퀀티랩의 시장지표 메뉴를 누르면 나오는 해외증시, 환율, 원자재 하위 메뉴로 차트들을 볼 수 있습니다.

해외증시는 변동성지수 VIX와 다우산업, 나스닥종합, S&P 500, 니케이 225, 상해종합, 항셍, 영국 FTSE 100, 프랑스 CAC 40, 독일 DAX 30을 볼 수 있습니다. VIX는 S&P 500 지수 옵션의 30일 뒤의 기대 변동성을 입니다.

해외증시

환율 페이지에서는 미달러, 유로, 엔, 위안, 홍콩달러 환율을 볼 수 있습니다.

환율

원자재 페이지에서는 대표적인 원자재지수인 GSCI를 비롯해 다양한 원자재 선물의 차트들을 한눈에 보여줍니다.

원자재

대신증권 크레온 API로 매수/매도 주문 넣고 확인하기

이번 포스트에서는 대신증권 크레온 API로 주식 주문을 내고 실시간으로 체결을 확인하고 주문 내역을 조회하는 방법을 다룹니다.

먼저 매수 또는 매도 주문을 내는 방법을 알아보겠습니다. 주문은 크레온 API의 CpTrade.CpTd0311 모듈을 사용하면 됩니다.

다음과 같이 order() 함수를 선언하고, action, code, amount를 매수/매도 구분, 주문할 종목 코드, 주식 수로 각각 입력 받도록 했습니다. 물론 더 다양한 입력을 받을 수 있게 이 함수를 수정할 수 있습니다.

import win32com.client
class Creon:
def __init__(self):
self.orderevent_handler = None
def order(self, action, code, amount):
if not code.startswith('A'):
code = 'A' + code
account_no, account_gflags = self.init_trade()
self.obj_CpTrade_CpTd0311.SetInputValue(0, action) # 1: 매도, 2: 매수
self.obj_CpTrade_CpTd0311.SetInputValue(1, account_no) # 계좌번호
self.obj_CpTrade_CpTd0311.SetInputValue(2, account_gflags[0]) # 상품구분
self.obj_CpTrade_CpTd0311.SetInputValue(3, code) # 종목코드
self.obj_CpTrade_CpTd0311.SetInputValue(4, amount) # 매수수량
self.obj_CpTrade_CpTd0311.SetInputValue(8, '03') # 시장가
result = self.obj_CpTrade_CpTd0311.BlockRequest()
if result != 0:
print('order request failed.', file=sys.stderr)
status = self.obj_CpTrade_CpTd0311.GetDibStatus()
msg = self.obj_CpTrade_CpTd0311.GetDibMsg1()
if status != 0:
print('order failed. {}'.format(msg), file=sys.stderr)
def buy(self, code, amount):
return self.order('2', code, amount)
def sell(self, code, amount):
return self.order('1', code, amount)

매수할 때는 buy() 함수를 호출해서 action을 2로 지정하고, 매도할 때는 sell() 함수를 호출해서 action을 1로 지정하도록 했습니다.

여기서는 시장가로 매수/매도를 하도록 했는데 지정가, 최유리지정가, 최우선지정가 등으로 변경할 수 있습니다. 이 부분은 CpTrade.CpTd0311 문서에서 SetInputValue의 8 - (string) 주문호가구분코드 항목을 함고하시기 바랍니다.

이렇게 매수 또는 매도 주문을 넣고 체결 되었을 때 다음과 같이 이벤트를 받을 수 있습니다.

class Creon:
def subscribe_orderevent(self, cb):
obj = win32com.client.Dispatch('Dscbo1.CpConclusion')
handler = win32com.client.WithEvents(obj, OrderEventHandler)
handler.set_attrs(obj, cb)
self.orderevent_handler = obj
obj.Subscribe()
def unsubscribe_orderevent(self):
if self.orderevent_handler is not None:
self.orderevent_handler.Unsubscribe()
self.orderevent_handler = None
class OrderEventHandler(EventHandler):
def OnReceived(self):
item = {
'계좌명': self.obj.GetHeaderValue(1),
'name': self.obj.GetHeaderValue(2),
'체결수량': self.obj.GetHeaderValue(3),
'체결가격': self.obj.GetHeaderValue(4),
'주문번호': self.obj.GetHeaderValue(5),
'원주문번호': self.obj.GetHeaderValue(6),
'계좌번호': self.obj.GetHeaderValue(7),
'상품관리구분코드': self.obj.GetHeaderValue(8),
'종목코드': self.obj.GetHeaderValue(9),
'매매구분코드': self.obj.GetHeaderValue(12),
'체결구분코드': self.obj.GetHeaderValue(14),
'체결구분코드': self.obj.GetHeaderValue(14),
'체결구분코드': self.obj.GetHeaderValue(14),
'현금신용대용구분코드': self.obj.GetHeaderValue(17),
}
self.cb(item)

subscribe_orderevent() 함수를 호출해서 체결 이벤트를 실시간으로 받아올 수 있습니다. 매수/매도 주문이 체결되면 지정된 콜백 함수 cb로 체결 정보를 전송합니다. 이를 위해서 OrderEventHandler 클래스를 선언했고, 이 클래스의 OnReceived() 함수에서 체결 정보를 cb에 전송합니다.

다음과 같이 지금까지 주문한 내역을 확인할 수도 있습니다.

class Creon:
def __init__(self, account_no=''):
self.obj_CpTrade_CpTd5341 = win32com.client.Dispatch('CpTrade.CpTd5341')
def get_trade_history(self):
account_no, account_gflags = self.init_trade()
self.obj_CpTrade_CpTd5341.SetInputValue(0, account_no)
self.obj_CpTrade_CpTd5341.SetInputValue(1, account_gflags[0]) # 상품구분
_fields = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 22, 24]
_keys = [
'상품관리구분코드', '주문번호', '원주문번호', '종목코드', '종목이름',
'주문내용', '주문호가구분코드내용', '주문수량', '주문단가', '총체결수량',
'체결수량', '체결단가', '확인수량', '정정취소구분내용 ', '거부사유내용',
'채권매수일', '거래세과세구분내용', '현금신용대용구분내용', '주문입력매체코드내용',
'정정취소가능수량', '매매구분',
]
result = self.request(self.obj_CpTrade_CpTd5341, dict(zip(_fields, _keys)), cntidx=6)
return result

주문 이후에 get_trade_history() 함수를 호출하면 다음과 같이 매수/매도 주문에 대한 정보들을 받아올 수 있습니다. 여기서는 삼성전자를 10주씩 매수->매도->매수한 다음 get_trade_history() 함수를 호출한 결과 입니다.

{'data': [{'상품관리구분코드': '10', '주문번호': 6095, '원주문번호': 0, '종목코드': 'A005930', '종목이름': '삼성전자', '주문내용': '정규장현금매수', '주문호가구분코드내용': '시장가', '주문수량': 10, '주문단가': 0, '총체결수량': 10, '체결수량': 10, '체결단가': 82430, '확인수량': 0, '정정취소구분내용 ': '정상주문', '거부사유내용': '', '채권매수일': '', '거래세과세구분내용': '', '현금신용대용구분내용': '', '주문입력매체코드내용': 'C8', '정정취소가능수량': 0, '매매구분': '매수'}, {'상품관리구분코드': '10', '주문번호': 6099, '원주문번호': 0, '종목코드': 'A005930', '종목이름': '삼성전자', '주문내용': '정규장현금매도', '주문호가구분코드내용': '시장가', '주문수량': 10, '주문단가': 0, '총체결수량': 10, '체결수량': 10, '체결단가': 82500, '확인수량': 0, '정정취소구분내용 ': '정상주문', '거부사유내용': '', '채권매수일': '', '거래세과세구분내용': '', '현금신용대용구분내용': '', '주문입력매체코드내용': 'C8', '정정취소가능수량': 0, '매매구분': '매도'}, {'상품관리구분코드': '10', '주문번호': 6121, '원주문번호': 0, '종목코드': 'A005930', '종목이름': '삼성전자', '주문내용': '정규장현금매수', '주문호가구분코드내용': '시장가', '주문수량': 10, '주문단가': 0, '총체결수량': 10, '체결수량': 10, '체결단가': 82500, '확인수량': 0, '정정취소구분내용 ': '정상주문', '거부사유내용': '', '채권매수일': '', '거래세과세구분내용': '', '현금신용대용구분내용': '', '주문입력매체코드내용': 'C8', '정정취소가능수량': 0, '매매구분': '매수'}]}

퀀티랩 systrader 프로젝트에서 전반적인 시스템 트레이딩 코드를 공개하고 있습니다. 앞으로 시스템 트레이딩에 필요한 주요 기능들을 퀀티랩 Github systrader 리포지토리에서 오픈소스로 개발해 나갈 예정입니다.

SysTrader의 주요 TODO 태스크는 다음과 같습니다.

  • 주요 시스템 트레이딩 기능 구현
  • 딥러닝 모델 기반 자동 매매
  • PyQt5 기반의 GUI 개발
  • 매매 현황 리포팅

관심 있으신 모든 분들께서 이 프로젝트에 참여해 주시면 감사하겠습니다. SysTrader는 모든 개미들을 위한 견고한 시스템 트레이딩 솔루션이 되는 것을 목표로 합니다.

2021년 신규 상장 공모주

2021년은 많은 대어급 기업들의 IPO(기업공개, initial public offering)가 예정되어 있습니다. 며칠 전 있었던 SK아이이테크놀로지의 청약은 엄청난 흥행으로 마쳤습니다. 확정 공모가는 105,000원이고 청약 경쟁률은 288:1에 달했습니다.

이번 포스트에서는 2021년에 공모일정이 나와있고 앞으로 공모가 예정되어 있는 기업들을 알아보겠습니다.

다음 표는 공모일정이 나와있는 기업들 입니다. SK바이오사이언스와 SK아이이테크놀로지가 엄청난 흥행으로 공모를 마친 바 있습니다.

종목명신규상장일공모시작공모종료희망공모가확정공모가청약경쟁률
아모센스202106152021061613,300~16,300
엘비루셈20210611202106022021060312,000~14,000
에이디엠코리아20210525202105262,900~3,300
제주맥주20210513202105142,600~2,900
진시스템202105132021051416,000~20,000
삼영에스앤씨20210511202105127,800~10,000
NH스팩19호(유가)20210511202105122,000~2,000
삼성스팩4호20210511202105122,000~2,000
샘씨엔에스20210510202105115,000~5,700
라온테크202105102021051112,800~15,800
씨앤씨인터내셔널20210517202105062021050735,000~47,500
에이치피오20210514202105032021050422,200~25,40022,200
하이스팩6호2021051020210428202104292,000~2,0002,0007.63:1
SK아이이테크놀로지(유가)20210511202104282021042978,000~105,000105,000288.17:1
쿠콘202104192021042031,000~40,00045,0001596.32:1
해성티피씨20210412202104139,500~11,50013,0002053.27:1
이삭엔지니어링20210412202104139,500~11,50011,5002262.09:1
유진스팩6호20210330202103312,000~2,0002,000236.23:1
유안타스팩8호20210325202103262,000~2,0002,00084.91:1
엔시스202103222021032313,000~16,50019,0002573.60:1
자이언트스텝20210315202103169,000~11,00011,0002342:1
제노코202103152021031627,000~33,00036,0002095.45:1
라이프시맨틱스20210311202103129,000~12,50012,5001774.57:1
SK바이오사이언스(유가)202103092021031049,000~65,00065,000335.36:1
바이오다인202103082021030922,500~28,70030,00048.36:1
디비금융스팩9호20210305202103082,000~2,0008.56:1
네오이뮨텍20210304202103055,400~6,400664.65:1
프레스티지바이오로직스20210302202103038,700~12,400126.06:1
싸이버원20210302202103037,600~8,3001880.43:1
나노씨엠에스202102252021022614,500~18,5001243.48:1
하나머스트스팩7호20210223202102242,000~2,000237.46:1
IBKS스팩15호20210218202102192,000~2,000101.73:1
뷰노202102162021021715,000~19,5001102.7:1
씨이랩202102152021021623,000~31,000195.53:1
유일에너테크202102152021021611,000~14,000683.55:1
오로스테크놀로지202102152021021617,000~21,0001033.82:1
피엔에이치테크202102032021020414,000~17,0001528.76:1
하나금융스팩17호20210129202102012,000~2,000168.68:1
아이퀘스트20210127202101289,200~10,6002853.34:1
프레스티지바이오파마(유가)202101252021012625,000~32,000237.13:1
와이더플래닛202101252021012612,000~15,0001357.98:1
레인보우로보틱스20210125202101267,000~9,0001201.26:1
신한스팩7호20210125202101262,000~2,0007.45:1
솔루엠(유가)202101212021012213,700~15,5001147.76:1
핑거202101212021012213,000~15,000939.39:1
씨앤투스성진202101192021012026,000~32,000674.04:1
모비릭스202101192021012010,500~14,0001485.51:1
선진뷰티사이언스202101182021011910,100~11,5001987.74:1
엔비티202101122021011313,200~17,6004397.67:1
한국스팩9호20210112202101132,000~2,00046.54:1

SK바이오사이언스의 경우 따상에 성공하고 이후 주가가 큰폭으로 하락했다가 일부 회복한 상태입니다. 이번 SK아이이테크놀로지도 따상할 것이란 예측이 많은 상황입니다.

기업공개가 예정된 종목들도 이렇게 많이 있습니다. 이들 중에서 특히 크래프톤, 카카오벵크, 한컴라이프케어, 아주스틸, 카카오페이에 관심이 갑니다.

기업명청구일당기순이익(백만)매출액(백만)자본금(백만)주업종
에스앤디202104307,35956,5371,518기타 식품 제조업
비트나인20210428-10,22611,5764,110응용소프트웨어 개발 및소프트웨어
에이비온20210428-10,4761,9947,168자연과학 및 공학 연구개발업
라이콤202104273,47530,6041,469통신 및 방송 장비 제조업
차백신연구소20210426-6,2407811,194의약품 제조업
카카오페이(유가)20210426-25,051284,37156,683기타 금융업
리파인2021042316,06754,1641,300부동산 중개, 자문 및 감정평가업
바이오플러스202104239,70324,3676,239기초 의약물질 및 생물학적 제제
실리콘투202104235,71699,3984,214상품 종합 도매업
와이엠텍202104233,86617,0372,387전동기, 발전기 및 전기 변환 ·
엠로202104233,38940,3022,687소프트웨어 개발 및 공급업
아주스틸(유가)2021042211,709569,79411,2261차 철강 제조업
한컴라이프케어(유가)2021041627,690151,8423,937기타 의료용 기기 제조업
원티드랩20210416-9,17414,6481,988소프트웨어 개발 및 공급업
카카오뱅크(유가)20210415113,636804,1572,048,251은행 및 저축기관
브레인즈컴퍼니202104153,73414,8291,742시스템 소프트웨어 개발 및 공급업
엑셀세라퓨틱스20210413-10,1245404,368의료용품 및 기타 의약 관련제품
바이젠셀20210409-10,45503,772의약품 제조업
크래프톤(유가)20210408556,2671,670,4274,278소프트웨어 개발 및 공급업
일진하이솔루스(구.일진복합소재)(유가)2021040715,579113,52514,525기타 운송장비 제조업
큐라클20210401-28,61005,657자연과학 및 공학 연구개발업
플래티어202103312,68539,3763,207소프트웨어 개발 및 공급업
에브리봇20210330-90249,1652,614가정용 기기 제조업
오에스피202103223,31315,5673,458동물용 사료 및 조제식품 제조업
맥스트20210309-8,9211,9833,751소프트웨어 개발 및 공급업
루켄테크놀러지스20210204-1,49132,6675,494기타 전기장비 제조업
셀비온20210204-3,5183504,434의약품 제조업
에스디바이오센서(유가)202101261,39973,72846,659의료용 기기 제조업
레몬헬스케어20201228-22,3652,0424,458소프트웨어 개발 및 공급업
딥노이드20201214-8,6062352,156소프트웨어 개발 및 공급업
디앤디파마텍20201013-6,42104,018자연과학 및 공학 연구개발업

특히 크래프톤, 카카오뱅크의 경우 각각 5,500억, 1,100억의 당기순이익을 기록하고 있는 만큼 현재도 이익이 높은 회사라 청약 공모 시 엄청난 경쟁이 예상됩니다. 남아있는 IPO들 잘 챙겨서 수익 얻으시길 응원합니다.

대신증권 크레온 API로 실시간 주가 데이터 받기

이번 포스트에서는 대신증권 크레온 API를 이용한 실시간 주가를 획득하는 방법을 다룹니다. 여느 때와 같이 언어는 파이썬으로 진행합니다.

크레온 API는 문서화가 비교적 잘되어 있고 사용성이 좋습니다. [증권사 API 장단점 비교][/htsapi.html] 포스트에서 주요 증권사 API의 장단점을 비교해 놓았으니 참고하시기 바랍니다.

실시간 주가를 얻어오기 위한 모듈은 Dscbo1.StockCur이며 이에 대한 문서는 여기에서 확인할 수 있습니다.

실시간 데이터는 Publish/Subscribe 방식으로 데이터를 획득합니다. 크레온에서 실시간 주가 데이터를 발행(Publish)하고 사용자는 실시간 주가를 원하는 종목을 구독(Subscribe)합니다. 사용자는 크레온이 발행한 데이터를 콜백(callback)을 통해 받습니다.

그럼 파이썬으로 실시간 주가 데이터를 구독하는 함수와 데이터를 획득하는 콜백 함수를 구현해 보겠습니다.

import win32com.client
class Creon:
def __init__(self):
self.stockcur_handlers = {} # 주식/업종/ELW시세 subscribe event handlers
def subscribe_stockcur(self, code, cb):
if not code.startswith('A'):
code = 'A' + code
if code in self.stockcur_handlers:
return
obj = win32com.client.Dispatch('DsCbo1.StockCur')
obj.SetInputValue(0, code)
handler = win32com.client.WithEvents(obj, StockCurEventHandler)
handler.set_attrs(obj, cb)
self.stockcur_handlers[code] = obj
obj.Subscribe()
def unsubscribe_stockcur(self, code=None):
lst_code = []
if code is not None:
if not code.startswith('A'):
code = 'A' + code
if code not in self.stockcur_handlers:
return
lst_code.append(code)
else:
lst_code = list(self.stockcur_handlers.keys()).copy()
for code in lst_code:
obj = self.stockcur_handlers[code]
obj.Unsubscribe()
del self.stockcur_handlers[code]
class StockCurEventHandler:
def set_attrs(self, obj, cb):
self.obj = obj
self.cb = cb
def OnReceived(self):
item = {
'code': self.obj.GetHeaderValue(0),
'name': self.obj.GetHeaderValue(1),
'diffratio': self.obj.GetHeaderValue(2),
'timestamp': self.obj.GetHeaderValue(3),
'price_open': self.obj.GetHeaderValue(4),
'price_high': self.obj.GetHeaderValue(5),
'price_low': self.obj.GetHeaderValue(6),
'bid_sell': self.obj.GetHeaderValue(7),
'bid_buy': self.obj.GetHeaderValue(8),
'cum_volume': self.obj.GetHeaderValue(9), # 주, 거래소지수: 천주
'cum_trans': self.obj.GetHeaderValue(10),
'price': self.obj.GetHeaderValue(13),
'contract_type': self.obj.GetHeaderValue(14),
'cum_sellamount': self.obj.GetHeaderValue(15),
'buy_sellamount': self.obj.GetHeaderValue(16),
'contract_amount': self.obj.GetHeaderValue(17),
'second': self.obj.GetHeaderValue(18),
'price_type': chr(self.obj.GetHeaderValue(19)), # 1: 동시호가시간 예상체결가, 2: 장중 체결가
'market_flag': chr(self.obj.GetHeaderValue(20)), # '1': 장전예상체결, '2': 장중, '4': 장후시간외, '5': 장후예상체결
'premarket_volume': self.obj.GetHeaderValue(21),
'diffsign': chr(self.obj.GetHeaderValue(22)),
'LP보유수량':self.obj.GetHeaderValue(23),
'LP보유수량대비':self.obj.GetHeaderValue(24),
'LP보유율':self.obj.GetHeaderValue(25),
'체결상태(호가방식)':self.obj.GetHeaderValue(26),
'누적매도체결수량(호가방식)':self.obj.GetHeaderValue(27),
'누적매수체결수량(호가방식)':self.obj.GetHeaderValue(28),
}
self.cb(item)

이렇게 Creon 클래스에 실시간 주가를 구독하는 함수인 subscribe_stockcur와 구독을 취소하는 unsubscribe_stockcur 함수를 구현했습니다. subscribe_stockcur 함수에서 발행되는 주가 데이터를 처리하기 위한 StockCurEventHandler 클래스를 이어서 정의하였습니다.

subscribe_stockcur 함수에서 구독하는 종목마다 StockCurEventHandler를 할당해 주었습니다. 크레온이 주식 데이터를 발행하면 StockCurEventHandler의 OnReceived 함수가 호출됩니다.

여기서 필요한 데이터를 획득할 수 있습니다. 여기서는 이렇게 획득한 데이터를 StockCurEventHandler 객체를 생성할 때 인자로 넘겨준 cb 함수로 전달하고 있습니다.

다음과 같이 종목명과 가격을 출력하는 간단한 cb 함수를 선언하고 NAVER와 미래에셋증권 두 종목의 실시간 주가를 구독해 보았습니다.

from quantylab.systrader.creon import Creon
c = Creon()
def cb(item):
print(item['name'], item['price'])
c.subscribe_stockcur('035420', cb)
c.subscribe_stockcur('006800', cb)
# ...
c.unsubscribe_stockcur()

그러면 이런식으로 종목명과 주가가 출력되는 것을 볼 수 있습니다.

NAVER 389500
미래에셋증권 9990
미래에셋증권 10000
NAVER 390000
NAVER 389500
NAVER 389500
미래에셋증권 9990
NAVER 389500
NAVER 389500
미래에셋증권 9990
미래에셋증권 9990
미래에셋증권 9990
미래에셋증권 10000
미래에셋증권 9990
NAVER 390000
NAVER 389500
NAVER 389500
미래에셋증권 9990

퀀티랩 systrader 프로젝트에서 실시간 주가 확인 외에도 다양한 크레온 API 기능을 연결하고 있습니다. 퀀티랩 Github systrader 리포지토리에서 전체 코드를 확인할 수 있습니다. 문의는 이 리포지토리 이슈로 또는 네이버 카페로 주세요.

파이썬으로 DART에서 재무제표 수집하기

이번 포스트에서는 전자공시시스템(DART)에서 제공하는 재무제표를 파이썬으로 다운로드하고 파싱하는 방법을 다룹니다.

DART에서 재무제표를 얻어오는 방법은 다양한데 여기서는 두 가지 방법을 다룹니다.

첫 번째로 단일회사 주요계정 API를 활용한 방법입니다.

DART Open API를 사용하려면 인증키가 필요합니다. 인증키 획득 방법은 이 포스트에서 확인하세요.

다음은 단일회사 주요계정 API를 호출하여 재무제표 정보를 획득하는 함수 입니다. 인증키, 회사 고유번호, 사업연도, 보고서 코드를 입력하여 재무제표를 받아올 수 있습니다.

def get_fs(crtfc_key, corp_code, bsns_year, reprt_code):
# 단일회사 주요계정
# crtfc_key API 인증키 STRING(40) Y 발급받은 인증키(40자리)
# corp_code 고유번호 STRING(8) Y 공시대상회사의 고유번호(8자리)
# ※ 개발가이드 > 공시정보 > 고유번호 API조회 가능
# bsns_year 사업연도 STRING(1) Y 사업연도(4자리)
# ※ 2015년 이후 부터 정보제공
# reprt_code 보고서 코드 STRING(5) Y 1분기보고서 : 11013
# 반기보고서 : 11012
# 3분기보고서 : 11014
# 사업보고서 : 11011
api = 'https://opendart.fss.or.kr/api/fnlttSinglAcnt.json'
params = {
'crtfc_key': crtfc_key,
'corp_code': corp_code,
'bsns_year': bsns_year,
}
data = []
reprt_name = REPRT_NAMES.get(reprt_code, "")
params['reprt_code'] = reprt_code
res = self._request(api, params=params)
if res is None:
return None
if res.status_code != 200:
return None
item = json.loads(res.text)
if item.get('status', '') != '000':
return None
for _item in item.get('list'):
_item['corp_code'] = corp_code
_item['reprt_code'] = reprt_code
_item['reprt_name'] = reprt_name
data.append(_item)
return data

get_fs(crtfc_key, '00266961', 2015, '11011')와 같이 호출하면 다음과 같은 결과를 받아올 수 있습니다.

[{'account_nm': '자산총계', 'bfefrmtrm_amount': '9,881,190,909,324', 'bfefrmtrm_dt': '2018.12.31 현재', 'bfefrmtrm_nm': '제 20 기', 'bsns_year': '2020', 'corp_code': '00266961', 'frmtrm_amount': '12,299,527,120,786', 'frmtrm_dt': '2019.12.31 현재', 'frmtrm_nm': '제 21 기', ...}, {'account_nm': '부채총계', 'bfefrmtrm_amount': '3,932,050,396,031', 'bfefrmtrm_dt': '2018.12.31 현재', 'bfefrmtrm_nm': '제 20 기', 'bsns_year': '2020', 'corp_code': '00266961', 'frmtrm_amount': '5,795,601,052,206', 'frmtrm_dt': '2019.12.31 현재', 'frmtrm_nm': '제 21 기', ...}, ...]

DART Open API 재무정보 일괄 다운로드

섹션 제목: “DART Open API 재무정보 일괄 다운로드”

두 번째는 재무정보 일괄 다운로드로 한꺼번에 전체 기업의 재무제표 데이터를 받아오는 방법입니다. Open DART 사이트에서 공시정보 활용마당 / 재무정보 일괄다운로드를 클릭하면 다음과 같은 테이블을 볼 수 있습니다.

재무정보 일괄다운로드

여기서 다운로드 하면 해당 연도와 분기의 네 종류의 재무제표를 압축파일로 다운 받을 수 있습니다. 여기서는 다운로드, 압축해제를 포함하여 파이썬에서 재무제표를 읽어오는 방법을 다룹니다.

다음은 Selenium으로 다운로드 버튼을 클릭하여 파일을 다운로드하고 압축을 해제하는 코드입니다.

options = webdriver.ChromeOptions()
options.add_experimental_option("prefs", {
"download.default_directory": self.job_base,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
})
options.add_argument("disable-infobars")
options.add_argument("--disable-extensions")
browser = webdriver.Chrome(executable_path=os.path.join(settings.QUANTYLAB_BASE, 'drivers/chromedriver'), chrome_options=options)
try:
browser.get('https://opendart.fss.or.kr/disclosureinfo/fnltt/dwld/main.do')
elem = browser.find_element_by_css_selector('table.tb01')
for elem_a in elem.find_elements_by_link_text('다운로드'):
filename = re.findall(r'.*\(.*\'(.+)\'\).*', elem_a.get_attribute('onclick'))[0]
# 파일 다운로드
elem_a.click()
time.sleep(10)
# 압축파일 로드
filepath = os.path.join(base, filename)
z = zipfile.ZipFile(filepath)
for info in z.infolist():
# 한글 파일명 인코딩 수정
_filename = info.filename.encode('cp437').decode('euc-kr')
_filepath = os.path.join(base, _filename)
if os.path.exists(_filepath):
continue
info.filename = _filename
# 압축해제
z.extract(info, base)
z.close()
except Exception as e:
print(str(e))
finally:
browser.quit()

이렇게 압축해제까지 하면 2020_1분기보고서_04_현금흐름표_20210211.txt과 같은 파일들이 생성되어 있을 것입니다.

이 텍스트 파일은 다음과 같이 pandas로 쉽게 DataFrame으로 읽어올 수 있습니다. 다만 sep과 encoding을 다음과 같이 적절하게 정해주는 것이 중요합니다.

df = pd.read_csv(filepath, sep='\t', encoding='cp949')

여기서 당기, 당기 1분기, 당기 반기 등의 다양한 컬럼이 생기는데 저는 이들을 당기로 통합하여 정리했습니다.

다음은 포괄손익계산서의 일부 항목을 나타낸 것입니다.

항목명결산기준일보고서종류당기
영업수익2018-12-31사업보고서5,586,904,533,355
영업이익(손실)2018-12-31사업보고서942,532,561,543
당기순이익(손실)2018-12-31사업보고서627,901,873,332

그런데 재무제표를 볼 때 항목의 흐름을 보는 것이 중요합니다. 예를 들어 영업 이익이 꾸준히 증가하고 있는지를 보고 싶은 것입니다.

그래서 동일 항목에 대해서 결산기준일 순서대로 DataFrame을 재구성하고자 합니다. 이를 위해 DataFrame의 pivot 함수를 사용합니다.

df_pivot = df.pivot(values='value', index='항목명', columns='결산기준일')

동일 항목이지만 항목명이 다르게 적혀있는 경우가 있어서 이를 맞춰주는 처리를 했습니다.

그럼 다음과 같이 동일 항목의 값을 분기 순서대로 볼 수 있습니다.

항목명2015-12-312016-03-312016-06-302016-09-302016-12-312017-03-312017-06-302017-09-302017-12-312018-03-312018-06-302018-09-302018-12-312019-03-312019-06-302019-09-302019-12-312020-03-312020-06-302020-09-30
매출액3,251,157,100,000937,280,220,152987,281,364,2951,013,075,223,7704,022,629,619,9821,082,247,281,1961,129,631,366,8471,200,676,275,2204,678,468,928,0321,309,059,864,2481,363,615,953,3321,397,713,834,6745,586,904,533,3551,510,861,770,2821,630,275,004,3951,664,815,000,0786,593,400,065,2441,732,064,462,0971,902,467,957,4911,360,779,374,259
영업이익762,203,849,000256,827,536,675272,663,961,847282,298,051,5251,102,040,475,792290,797,289,304285,213,522,180312,084,238,4441,179,187,806,331256,980,563,870250,583,189,279221,718,402,565942,532,561,543206,244,504,954128,334,709,480202,086,039,366710,070,173,513221,495,078,328230,597,573,115291,726,798,004
당기순이익516,986,279,000165,036,146,535213,223,506,226198,000,157,034759,072,951,187210,876,050,870171,420,229,303215,798,264,368770,101,670,207153,751,654,038281,758,577,70168,386,763,908627,901,873,33287,585,903,29027,756,852,97585,268,053,088396,821,062,465134,874,581,86590,682,203,810235,342,965,699