본문 바로가기

주식 데이터 분석

[Python] 국내 주식 종목 간 상관관계 분석해서 관련주 찾기

주식을 시작한지 1년 밖에 안됬지만 저는 겁도 없이 테마주, 급등주를 많이 했습니다.  (-50프로입니다 ㅎㅎ)

급등주와 테마주들을 투자하면서 느낀건 대장주가 오르면 연관된 주식들이 같이 오르는 경우가 많더군요.

(ex. 정치테마주, 바이오주, 건설주, 코로나 관련주 등등) 

그래서 각 종목마다 관련주를 상관계수와 상한VI를 카운팅해서 구해보려고 합니다. 

 

2022.05.02 - [데이터로 보는 주식] - [Python] 상한 VI를 카운팅해서 관련주 찾기

1.  국내 주식들 가격 가져오기 

우선 국내 주식들의 주가를 가져와야합니다. 

키움증권 api로 주가를 가져오는게 가장 깔끔하겠지만 개발환경이 맥북임으로 다른 방법을 사용하겠습니다. 

저는 사용하기 편한 pykrx api를 사용하여 주가를 가져오겠습니다.( https://github.com/sharebook-kr/pykrx

모든 종목의 2021년 05월 01부터 2022년 05 01일까지 1년 주가를 종가만 가져와 dataframe으로 만들겠습니다. 

from pykrx import stock
from tqdm import tqdm
import pandas as pd


def get_stock_price( start_day, end_day):
    print("start get_stock_price")
    #날짜를 2022-02-05 -> 20220205로 변경
    def str_day(d):
        return d.strftime('%Y%m%d')
    # 입력한 기간동안에 개장일을 구하기
    tmpDays = stock.get_market_ohlcv(start_day, end_day, "005930")
    days = list(map(str_day, tmpDays.index.to_list()))
    tmp = stock.get_market_ohlcv(days[0], market="ALL")
    # 모든 종목의 주가 가져와서 종가만 뽑아내기
    df_stock = tmp["종가"].to_frame(name=days[0]).T
    # vi
    for day in tqdm(days[1:]):
        ddf = stock.get_market_ohlcv(day, market="ALL")
        # stock
        tmp_stock = ddf["종가"].to_frame(name=str(day)).T
        df_stock = pd.concat([df_stock, tmp_stock])
        df_stock = df_stock.astype('float')

    def removeNull(df_stock):
        df_stock = df_stock.fillna(method='ffill')
        df_stock = df_stock.fillna(method='bfill')
        # df_stock = df_stock.dropna(axis=0)
        #지정한 기간 마지막날 거래가 된 종목들의 종목명만 가져옴 코스피, 코스닥만
        df = stock.get_market_ohlcv(days[-1], market="KOSPI")
        df2 = stock.get_market_ohlcv(days[-1], market="KOSDAQ")
        codeList = df.loc[df["저가"] != 0].index.tolist() + df2.loc[df2["저가"] != 0].index.tolist()
        df_stock = df_stock[codeList]
        #종목코드를 종목명으로 변환
        def codeChangeName(code):
            name = stock.get_market_ticker_name(code)
            return name

        df_code = df_stock.columns.tolist()
        df_stock.columns = list(map(codeChangeName, df_code))
        return df_stock

    st = removeNull(df_stock)
    st.to_csv("stockPrice.csv", mode='w')
get_stock_price("20220401","20220501")

출력

          AJ네트웍스    AK홀딩스     BGF    BGF리테일  ...      흥국  흥국에프엔비희림       힘스
20210503  5760.0  30250.0  6960.0  153000.0  ...  8700.0  4220.0  8200.0  12800.0
20210504  5820.0  30000.0  6900.0  158500.0  ...  8500.0  4205.0  8670.0  13050.0
20210506  5890.0  32300.0  7110.0  161000.0  ...  8630.0  4205.0  8640.0  13100.0
20210507  5910.0  32900.0  7060.0  166000.0  ...  8880.0  4295.0  9020.0  13250.0
20210510  6140.0  34700.0  7240.0  177000.0  ...  8860.0  4255.0  9360.0  13250.0
...          ...      ...     ...       ...  ...     ...     ...     ...      ...
20220425  6630.0  22100.0  5610.0  183000.0  ...  7060.0  4570.0  7770.0   8960.0
20220426  6660.0  22100.0  5630.0  181000.0  ...  7090.0  4605.0  8140.0   9090.0
20220427  6660.0  21300.0  5580.0  180000.0  ...  7040.0  4440.0  8500.0   9030.0
20220428  6640.0  21100.0  5650.0  181000.0  ...  7380.0  4445.0  8510.0   8920.0
20220429  6650.0  21500.0  5690.0  181000.0  ...  7810.0  4375.0  8640.0   9030.0

- 주의할점 

1. 거래정지, 상장폐지가 걸린 종목들의 가격은 0원으로 나와서 이를 제거

2. 각각의 종목을 일일이 api로 요청해서 주가를 가져오면 시간이 매우오래 걸림 

stock.get_market_ohlcv(day, market="ALL")로 하루씩 모든 종목의 주가를 가져와 concat하는게 빠르다.

1년치 주가는 4분정도 걸림 

 

 

2. 종목들간의 상관계수 구하기 

상관계수 구하는 방법 중 가장 일반적인 pearson 상관계수를 이용하여 구하였습니다.  

-1 ~ 1 사이 값으로 1에 가까울수록 양의 선형 상관관계를 갖고 -1에 가까울 수록 음의 선형 상관관계를 갖습니다. 

1에 가까울 수록 관련주입니다. 

 

def get_corrAll():
    print("start get_corrAll")
    stock = pd.read_csv("stockPrice.csv", dtype=str, index_col=0)
    def corr_stock(st):
        corr = st.astype('float') \
            .corr(method='pearson')\
            .fillna(method='ffill')\
            .fillna(method='bfill') \
            .fillna(0)
        corr= corr.round(4)
        return corr
    corr =corr_stock(stock)
    corr.to_csv("corr.csv", mode='w')
    return corr
          AJ네트웍스    AK홀딩스      BGF   BGF리테일  ...       흥국   흥국에프엔비       희림      힘스
AJ네트웍스       1.0   0.4276   0.4443   0.6049  ...    0.389    0.258   0.0178  0.2413
AK홀딩스     0.4276      1.0    0.944   0.6178  ...   0.7206   0.1037   0.0615  0.7216
BGF       0.4443    0.944      1.0   0.5235  ...   0.7507   0.1577  -0.0058  0.8034
BGF리테일    0.6049   0.6178   0.5235      1.0  ...   0.2878   0.1241   0.3278  0.2223
BNK금융지주  -0.4897  -0.3831  -0.4049  -0.4868  ...  -0.4256  -0.5147  -0.3212  -0.518
...          ...      ...      ...      ...  ...      ...      ...      ...     ...
흥구석유      0.2672    0.369    0.229    0.518  ...     0.15    0.295   0.5038   0.254
흥국         0.389   0.7206   0.7507   0.2878  ...      1.0   0.2424  -0.2794  0.6905
흥국에프엔비     0.258   0.1037   0.1577   0.1241  ...   0.2424      1.0   0.3991  0.4906
희림        0.0178   0.0615  -0.0058   0.3278  ...  -0.2794   0.3991      1.0  0.1805
힘스        0.2413   0.7216   0.8034   0.2223  ...   0.6905   0.4906   0.1805     1.0

위에 구한 corr 테이블에서 각 종목을 검색해서 정렬해보겠습니다. 

def search_corr(stockName):
    corr = pd.read_csv("corr.csv", dtype=str, index_col=0)
    sortCorr =corr[stockName].sort_values(ascending = False)
    print(sortCorr.head(10))
search_corr("한국선재")

대선 테마주가 핫할때 제게 -30프로를 맛보게 해준 한국선재로 검색 해보겠습니다. 

한국선재         1.0
홈센타홀딩스    0.9389
경남스틸      0.9172
보광산업      0.9046
삼일          0.89
DSR제강     0.8652
DSR       0.8486
동양피스톤     0.8485
휴맥스홀딩스    0.8287
미래에셋생명    0.8183

한국선재와 같이 저를 괴롭힌 경남스틸이 보입니다.. 

1위 부터 7위까지 전부 홍준표로 묶인 종목들입니다. 성능이 생각보다 괜찮네요..? 

 

우진            1.0
한전산업       0.7881
두산우        0.7826
일진파워       0.7693
삼성엔지니어링    0.7689
보성파워텍      0.7613
이지스밸류리츠    0.7382
두산2우B       0.732
우리기술       0.7263
금강공업       0.7246
Name: 우진, dtype: object

이번엔 원전 관련주 우진입니다. 

대충봐도 원전 관련주들이 대부분인거 같습니다. 

 

다음은 매우 배아픈 사료 관련주입니다 

한일사료              1.0
일동홀딩스          0.9303
신송홀딩스          0.9186
하인크코리아         0.9134
대신밸런스제11호스팩    0.8952
전방             0.8819
하이스틸           0.8557
신한서부티엔디리츠       0.846
현대사료           0.8398
코람코더원리츠        0.8336

이건 어림도없네요. 현대사료, 팜스토리 ,미래생명자원 등이 상위권이 아닙니다. 아무래도 최근 핫해진 종목이라 그런거 같습니다. 

기간을 1년에서 좀 줄이면 좀더 정확할꺼 같네요 

전체코드

from pykrx import stock
from tqdm import tqdm
import pandas as pd

def get_stock_price( start_day, end_day):
    print("start get_stock_price")
    #날짜를 2022-02-05 -> 20220205로 변경
    def str_day(d):
        return d.strftime('%Y%m%d')
    # 입력한 기간동안에 개장일을 구하기
    tmpDays = stock.get_market_ohlcv(start_day, end_day, "005930")
    days = list(map(str_day, tmpDays.index.to_list()))
    tmp = stock.get_market_ohlcv(days[0], market="ALL")
    # 모든 종목의 주가 가져와서 종가만 뽑아내기
    df_stock = tmp["종가"].to_frame(name=days[0]).T
    # vi
    for day in tqdm(days[1:]):
        ddf = stock.get_market_ohlcv(day, market="ALL")
        # stock
        tmp_stock = ddf["종가"].to_frame(name=str(day)).T
        df_stock = pd.concat([df_stock, tmp_stock])
        df_stock = df_stock.astype('float')

    def removeNull(df_stock):
        df_stock = df_stock.fillna(method='ffill')
        df_stock = df_stock.fillna(method='bfill')
        # df_stock = df_stock.dropna(axis=0)
        #지정한 기간 마지막날 거래가 된 종목들의 종목명만 가져옴 코스피, 코스닥만
        df = stock.get_market_ohlcv(days[-1], market="KOSPI")
        df2 = stock.get_market_ohlcv(days[-1], market="KOSDAQ")
        codeList = df.loc[df["저가"] != 0].index.tolist() + df2.loc[df2["저가"] != 0].index.tolist()
        df_stock = df_stock[codeList]
        #종목코드를 종목명으로 변환
        def codeChangeName(code):
            name = stock.get_market_ticker_name(code)
            return name

        df_code = df_stock.columns.tolist()
        df_stock.columns = list(map(codeChangeName, df_code))
        return df_stock

    st = removeNull(df_stock)
    st.to_csv("stockPrice.csv", mode='w')

def get_corrAll():
    print("start get_corrAll")
    stock = pd.read_csv("stockPrice.csv", dtype=str, index_col=0)
    def corr_stock(st):
        corr = st.astype('float') \
            .corr(method='pearson')\
            .fillna(method='ffill')\
            .fillna(method='bfill') \
            .fillna(0)
        corr= corr.round(4)
        return corr
    corr =corr_stock(stock)
    corr.to_csv("corr.csv", mode='w')
    return corr

def search_corr(stockName):
    corr = pd.read_csv("corr.csv", dtype=str, index_col=0)
    sortCorr =corr[stockName].sort_values(ascending = False)
    print(sortCorr.head(10))
    
get_stock_price("20210501", "20220501")
print(pd.read_csv("stockPrice.csv", dtype=str, index_col=0))
get_corrAll()
print(corr)
search_corr("한일사료")