Skip to content

krx

2 posts with the tag “krx”

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

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

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

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

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

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

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

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

먼저 개별종목 공매도 거래 데이터를 받아오겠습니다. 크롬이나 웨일 브라우저에서 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

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

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

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

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

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

Section titled “공매도 거래와 공매도 잔고 데이터 합치기”

이제 두 데이터를 합쳐서 확인해 보겠습니다. 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

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

파이썬으로 KRX에서 전종목 기본 정보 얻어오기

이번 포스트에서는 코스피, 코스닥, 코넥스의 상장 종목들을의 기본 정보를 파이썬으로 획득하는 방법을 다룹니다. 한국거래소(KRX)에서 운영하는 기업공시채널 KIND라는 서비스가 있는데 여기서 정보를 얻어와 보겠습니다.

네이버 웨일이나 구글 크롬으로 접속하시길 추천드립니다.

먼저 KIND에 접속합니다.

1

위 그림의 하단에 빨간 박스 표시한 상장법인상세정보 메뉴를 클릭합니다.

그럼 다음과 같은 화면이 나오는데 여기서 우측에 있는 상장법인목록 메뉴를 클릭합니다.

2

그럼 상장법인목록을 확인할 수 있는 페이지가 뜨는데 여기서 EXCEL 버튼을 누르면 전체 상장 법인 목록을 다운받을 수 있습니다.

3

상장법인 목록 파이썬으로 가져오기

Section titled “상장법인 목록 파이썬으로 가져오기”

이 파일을 파이썬으로 바로 다운 받을 수도 있습니다. 다양한 방법이 있겠지만 여기서는 pandasread_html 함수를 사용하겠습니다.

이제 개발자 도구를 열어봅니다. 웨일의 경우 다음과 같은 방법으로 개발자 도구를 열 수 있습니다.

  • 우측 상단의 버튼을 클릭하고 기타 / 고급기능 / 개발자 도구를 클릭
  • Ctrl + Shift + I
  • F12

그럼 다음과 같이 개발자 도구가 나타납니다. (브라우저 하단에 뜰 수도 있습니다.)

4

여기서 Network 탭으로 이동합니다. 이동한 다음 상장법인목록 페이지의 EXCEL 버튼을 클릭합니다.

그럼 개발자 도구의 Network 탭에 다음과 같이 요청 정보가 표시됩니다.

5

여기서 빨간색 박스로 표시한 Request URL을 확인하고 아래로 쭉 내려서 Form Data를 확인합니다.

6

이제 이 정보를 가지고 요청 코드를 작성합니다.

def get_stocks(market=None):
market_type = ''
if market == 'kospi':
market_type = '&marketType=stockMkt'
elif market == 'kosdaq':
market_type = '&marketType=kosdaqMkt'
elif market == 'konex':
market_type = '&marketType=konexMkt'
url = 'http://kind.krx.co.kr/corpgeneral/corpList.do?currentPageSize=5000&pageIndex=1&method=download&searchType=13{market_type}'.format(market_type=market_type)
list_df_stocks = pd.read_html(url, header=0, converters={'종목코드': lambda x: str(x)})
df_stocks = list_df_stocks[0]
return df_stocks

read_html 함수를 호출할 때 첫 번째 행을 헤더로 인식하도록 headers=0을 넣어줬고 종목코드를 숫자로 인식하지 않도록 converters 인자를 주었습니다.

이렇게 코스피, 코스닥, 코넥스 전체 또는 시장 별로 종목 목록을 받아올 수 있습니다.

받아온 데이터에는 다음과 같이 회사명, 종목코드, 업종, 주요제품, 상장일, 결산월, 대표자명, 홈페이지, 지역 정보가 포함됩니다.

회사명종목코드업종주요제품상장일결산월대표자명홈페이지지역
DSR1556601차 비철금속 제조업합섬섬유로프2013-05-1512월홍석빈http://www.dsr.com부산광역시
GS글로벌001250상품 종합 도매업수출입업(시멘트,철강금속,전기전자,섬유,기계화학),상품중개,광업,채석업/하수처리 서비스/부동산 임대1976-06-2612월김태형http://www.gsgcorp.com서울특별시
HDC현대산업개발294870건물 건설업외주주택, 자체공사, 일반건축, 토목 등2018-06-1212월권순호, 정경구http://www.hdc-dvp.com서울특별시
KG케미칼001390기초 화학물질 제조업콘크리트혼화제, 비료, 친환경농자재, 수처리제1989-08-2512월곽정현, 김재익http://www.kgchem.co.kr울산광역시
LG이노텍011070전자부품 제조업기타 전자부품 제조업2008-07-2412월정철동http://www.lginnotek.co.kr서울특별시