본문 바로가기
Programming/Quant

[퀀트투자를 위한 툴 만들기 1] 대신증권 API로 주식시장 전종목에 대한 지표 추출하기

by 지표덕후 2021. 3. 1.

파이썬과 엑셀 xlwings 패키지를 활용해 퀀트투자를 위한 종목선정 툴을 만들었습니다.
mokeya.tistory.com/67?category=914399

 

[퀀트투자를 위한 툴 만들기 0] 파이썬과 엑셀을 활용한 종목 선정 프로그램 기획

계량적인 방식으로 주식투자를 해보려고 합니다. 철저하게 숫자만 가지고 살지 말지를 판단하는 겁니다. 소위 말하는 퀀트투자인 건데, 비계량적인 투자방식을 완전히 포기할 순 없어, (... 라고

mokeya.tistory.com

 

 

 

이 작업은 크게 네 가지 과업으로 이루어지는데,
이 포스팅은 그 첫 번째 단계에 대한 내용을 담고 있습니다.
(아래 목차 클릭 시 해당 포스팅으로)

1. 대신증권API로 주식시장 전종목에 대한 정보 추출(현재 글)
2. Company Guide(Fn Guide) 웹을 크롤링해 전종목에 대한 재무정보 추출
3. 1과 2의 결과물을 종목코드를 기준으로 합친 후 투자지표 필드 추가
4. 3의 결과물을 가지고 xlwings 패키지 활용하여 엑셀툴 제작

코딩의 결과


결과물은 다음과 같은 정보를 가지고 있습니다.
아래 정보는 MarketEye를 통해 가져올 수 있는 정보 중 일부에 해당하는 것이므로,
Cybos의 도움말 페이지에서 API명세서를 살펴보시고 필요한 정보를 더 추가해보셔도 좋을 것 같습니다.

 

또한 일부 컬럼은 API로 받아온 값을 그대로 사용하지 않고 제가 가공한 것입니다.
가령 종목코드 앞에 붙어 있는 'A-'를 제거한다든가,
시가총액을 종목 현재가 기준으로 계산한다든가,
백분율 지표의 형태를 소수점 둘째 자리로 통일시킨 것 등입니다.  

컬럼명 설명 혹은 값 예시
종목구분 1 (1은 주권을 의미. 투자회사, 리츠, ETF 등을 의미하는 0~17까지의 구분 코드가 존재)
종목코드 A005930
현재가 84,800
종목명 삼성전자
상장주식수 5,969,782,000
외국인보유비율(%) 54.73%
52주최고가 96,800
52주최저가 45,350
배당수익률(%) 3.53%
부채비율(%) 37.07%
자기자본이익률(%) (ROE, Return On Equity) 9.99%
매출증가율(%) 2.78%
순이익증가율(%) 21.48%
경상이익 36,345,117,000,000
분기매출액증가율(%) 2.78%
분기영업이익액증가율(%) 29.62%
분기순이익증가율(%) 21.48%
분기년월 202012
시가총액 (현재 주가를 기준으로 계산된 시총) 506,237,513,600,000
정제코드 (기존 종목코드 앞에 붙은 "A"를 제거한 코드) 005930

작업환경


1. 파이썬3.7(32bit) - 대신증권API는 64bit로 사용 불가
2. 파이썬IDE는 JetBrain의 Pycharm - 관리자 권한으로 실행
3. 대신증권 계좌를 보유하고 있어야 하고, CYOBS Plus(대신증권 API 서비스) ID 등록 필요
4. 코드를 정상 실행(RUN)하기 위해서는 CYBOS Plus 클라이언트가 (관리자 권한으로) 실행 중이어야 함

CYBOS Plus 로그인 화면(API를 사용하기 위해서는 플러스 탭에서 로그인해야 하며, 당연히 공인인증서 필요)


코드


기본적으로 API와 여러 클래스에 대한 명세서는 CybosPlus 도움말 페이지에서 확인할 수 있습니다.
이 중 퀀트 종목 선정 프로그램에서 주로 활용하게 되는 클래스는
'MarketEye' 클래스이고, 이 클래스는 여러 종목에 대한 각종 정보를 구하는 데 사용합니다.

https://money2.daishin.com/e5/mboard/ptype_basic/HTS_Plus_Helper/DW_Basic_List_Page.aspx?boardseq=284&m=9508&p=8839&v=8642

 

사이보스플러스 도움말 - 대신증권

 

money2.daishin.com


COM은 Component object model의 약어로, 
특정 프로그래밍 언어로 개발된 유용한 객체를 그 외 다른 언어로 사용할 수 있게 해줍니다.
이번 파이썬 코드에서도, 다른 프로그래밍 언어로 작성된 COM 객체를 생성하기 위해
win32com.client 모듈을 사용합니다.

# COM(Component Object Model)을 비롯, 필요한 모듈 불러오기
import win32com.client
import win32com
import time

 

 

 

필요한 정보의 종류(m_InfoList)와
정보 추출 대상 종목(m_CodeList)을 받아 값을 반환하는 함수(subMarketEye)를 작성합니다.
각 정보가 하나의 열(column)을, 종목 하나가 하나의 행(row)를 차지하는 테이블을 그려내는 함수입니다. 

def subMarketEye(m_InfoList, m_CodeList):
    ## 대신 api 세팅
    instMarketEye = win32com.client.Dispatch("cpsysdib.MarketEye")
    instMarketEye.SetInputValue(0, m_InfoList)  # SetInputValue(type, value) - type=0은 필드(재무정보) 혹은 필드 배열을 의미
    instMarketEye.SetInputValue(1,
                                m_CodeList)  # SetInputValue(type, value) - type=1은 종목코드 혹은 종목코드 배열을 의미. 최대 200개 종목까지 가능
    instMarketEye.BlockRequest()

    numField = instMarketEye.GetHeaderValue(0)  # GetHeaderValue(type) - type=0은 필드 개수; 1은 필드명의 배열; 2는 종목 개수
    numData = instMarketEye.GetHeaderValue(2)
    
    data = []
    for ixRow in range(numData):
        tempdata = [instCpCodeMgr.GetStockSectionKind(m_CodeList[ixRow])]
        for ixCol in range(numField):
            # GetDataVale(type, index) - type은 요청한 필드의 인덱스, index는 요청한 종목의 인덱스
            # 주당 가격이 큰 종목의 경우 천 단위로 표현이 되기 때문에 1,000을 곱해줌
            if instCpCodeMgr.IsBigListingStock(instMarketEye.GetDataValue(0, ixRow)) and ixCol == 3:
                tempdata.append(instMarketEye.GetDataValue(ixCol, ixRow) * 1000)  
            else :
                tempdata.append(instMarketEye.GetDataValue(ixCol, ixRow))
        data.append(tempdata)
    return data

 

 

 

최종 결과물에 담을 정보의 종류(컬럼)를 지정하고,
코스피와 코스닥에 상장된 전 종목의 코드를 지정하여
최종 결과물을 .csv로 출력합니다.
이 때 주의할 점은 CybosPlus에서는 한 번에 요청할 수 있는 정보의 양을 한정해두었기 때문에,
for loop 구문을 통해 한 번에 200개 종목씩 끊어서 요청해야 한다는 것입니다.
그리고 몇몇 컬럼들은 형변환을 통하여 소수점 둘째자리의 백분율로 표시가 되도록 코드를 추가했습니다.

from pandas import DataFrame

if __name__ == "__main__":
    m_InfoList = [0, 4, 17, 20, 21, 63, 64, 74, 75, 77
                , 78, 80, 87, 97, 98, 99, 100, 111]  # 요청 필드 - 이 부분을 수정해야 함
    codeList = instCpCodeMgr.GetStockListByMarket(1)  # 시장구분(CPE_MARKET_KIND)에 따른 주식종목배열을 반환
    codeList2 = instCpCodeMgr.GetStockListByMarket(2)  # CPE_MARKET_KIND=0은 구분없음, 1은 코스피, 2는 코스닥, 3은 프리보드, 4는 KRX
    m_CodeList = codeList + codeList2

    data = []
    rqCodeList = []
    # 한 번에 요청 가능한 양 내에서 끊어서 반복 요청
    for i, code in enumerate(m_CodeList):
        rqCodeList.append(code)
        if len(rqCodeList) == 200:
            data = data + subMarketEye(m_InfoList, rqCodeList)
            rqCodeList = []
            continue

    if len(rqCodeList) > 0:
        data = data + subMarketEye(m_InfoList, rqCodeList)

    dfMarketEye = DataFrame(data, columns=['종목구분', '종목코드', '현재가', '종목명', '상장주식수', '외국인보유비율(%)', '52주최고가', '52주최저가', '배당수익률(%)', '부채비율(%)', '자기자본이익률(%)'
                                , '매출증가율(%)', '순이익증가율(%)', '경상이익', '분기매출액증가율(%)', '분기영업이익액증가율(%)', '분기경상이익증가율(%)', '분기순이익증가율(%)', '분기년월'])

    # 시가총액 계산하여 컬럼 추가하기
    dfMarketEye['시가총액'] = dfMarketEye['현재가'] * dfMarketEye['상장주식수']

    # 종목코드에서 'A' 빼기
    dfMarketEye['정제코드'] = dfMarketEye['종목코드'].str.extract('(\d+)')

    # 백분율 지표 소수점 둘째 자리의 퍼센티지로 표현하기
    dfMarketEye['외국인보유비율(%)'] = dfMarketEye['외국인보유비율(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['배당수익률(%)'] = dfMarketEye['배당수익률(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['부채비율(%)'] = dfMarketEye['부채비율(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['자기자본이익률(%)'] = dfMarketEye['자기자본이익률(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['매출증가율(%)'] = dfMarketEye['매출증가율(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['순이익증가율(%)'] = dfMarketEye['순이익증가율(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['분기매출액증가율(%)'] = dfMarketEye['분기매출액증가율(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['분기영업이익액증가율(%)'] = dfMarketEye['분기영업이익액증가율(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['분기경상이익증가율(%)'] = dfMarketEye['분기경상이익증가율(%)'].apply('{0:.2f}%'.format)
    dfMarketEye['분기순이익증가율(%)'] = dfMarketEye['분기순이익증가율(%)'].apply('{0:.2f}%'.format)

    # 스팩(SPAC) 종목 제외
    # dfMarketEye = dfMarketEye[~dfMarketEye['종목명'].str.contains('스팩')]

    # '분기년월' 정보가 없는 종목 제외
    # dfMarketEye = dfMarketEye[dfMarketEye['분기년월']!= ""]

    # 결과물 출력
    print(dfMarketEye)
    dfMarketEye.to_csv('subMarketEye.csv', encoding="cp949")

 

 

 

다음 포스팅에서는 대신증권API로는 커버하지 못 하는 투자지표를 보강하기 위하여
FnGuide의 지표를 가져오는 코드를 작성해보겠습니다.

댓글