Abstract

주식시장에는 ‘개미는 기관, 외인(외국인)을 이길 수 없다‘는 말이 있다. 정보의 접근성이나, 투자 자금의 단위, 공매도 등 보다 자유로운 투자가 가능하다는 점 등에서 기관이 개인보다 유리한 위치에 있기 때문이며 나또한 어느정도 동의한다. 그래서 많은 이들이 매매를 할 때 기관, 외인들의 매매 정보를 참고한다. 하지만 실제로 이들의 매매를 관찰해보면 대량 매수한 시점이후로 주가가 떨어지거나, 반대로 대량 매도이후 주가가 상승하는 등의 현상을 적잖이 볼 수 있다.
그래서 파이썬을 이용해 기관, 외인의 순매매량 정보를 이용해 어떤식으로 투자에 활용할 수 있을지에 대해 분석해보기로 한다.
데이터 출처 : KRX
데이터 수집 방법 : 이전 포스트 참조

데이터 전처리

Import libraries

import pandas as pd
import numpy as np
import time
import pickle
from datetime import datetime
from tqdm import tqdm

import pymysql
from sqlalchemy import create_engine
from pandas.io import sql

import cufflinks as cf
cf.go_offline()
cf.set_config_file(offline=False, world_readable=True)
PASSWORD=your_mysql_password

Mysql에서 데이터 불러오기

#import data
conStock = pymysql.connect(
    user='root', 
    passwd=PASSWORD, 
    host='127.0.0.1', 
    db='stock', #database(기관, 외인) name
    charset='utf8'
)

conPrice = pymysql.connect(
    user='root', 
    passwd=PASSWORD, 
    host='127.0.0.1', 
    db='stock_price', #database(가격) name
    charset='utf8'
)

def allCodes():
    '''
    데이터베이스에 저장된 코드(799개) return
    '''
    cur = conStock.cursor()
    cur.execute('show tables')
    codes = [c[0] for c in cur]
    return codes
		
def extractDf(cur, code):
    '''
    코드를 입력받아 해당하는 dataframe 반환
    '''
    sql = f"select * from {code}"
    cur.execute(sql)
    result = cur.fetchall();

    df = pd.DataFrame(result)
    columnNames = [x[0] for x in cur.description]
    df.columns = columnNames
    return df

데이터 전처리

class Preprocessor:
    '''
    데이터의 scaling과 관련된 메소드
    '''
    def normalize(arr):
        #     sign = np.sign(arr)
        #     arr = np.log(np.abs(arr)+1)*sign
        return (arr-np.mean(arr))/np.std(arr)
    
    def minmax(arr):
        return((arr-np.min(arr))/(np.max(arr)-np.min(arr)))

class TradeStradegy:
    '''
    mergePrice : 기관, 외인 데이터베이스로부터 추출된 데이터프레임과
    주가 데이터베이스로부터 추출된 데이터프레임 결합
    
    stradegy1 : 기관, 외인 순매매 데이터를 이용한 매매 전략(다음 포스트에서 다룰 예정)
    '''
    def __init__(self, code):
        self.code = code
        self.curC = conStock.cursor()
        self.curP = conPrice.cursor()
        self.df = self.mergePrice()
        
    def mergePrice(self):
        dfWho = extractDf(self.curC, self.code)
        dfPrice = extractDf(self.curP, self.code)
        
        dfWho['Date'] = pd.to_datetime(dfWho['Date'])
        dfWho = dfWho.drop(columns = ['Close','Change','Volume'])
        df = dfWho.merge(dfPrice,how='left',on='Date')
        return(df)
    
    def stradegy1(self, criteria, dayLater, who="foreign_sum"): #or inst_sum
        return -1

간단한 EDA

codes = allCodes()
tss = TradeStradegy('c003490') #대한항공의 주식코드(003490)
df = tss.mergePrice()
dfPlot = df[['Date','Close','inst_sum','foreign_sum']]
dfPlot.iplot(x='Date', y=['inst_sum','foreign_sum'], title = "기관, 외국인의 일일 순매매량")

inst_sum : 기관 순매매량

, foreign_sum : 외인 순매매량이다.
일일 순매매량의 경우 중간중간 솟은(이상치)부분을 제외하고는 plot을 통해 정보를 얻어내기가 쉽지않았다.
순매매량을 누적합한 값(cumsum)을 이용해 종가(Close)와 같이 plotting해 보았다. 이때 종가와 순매매량은 scale의 차이가 크기때문에
minmax scaling을 이용해 변수의 범위를 조정한 후 plotting을 했다.

dfPlot1 = dfPlot[::-1]
dfPlot1.iloc[:,2:] = dfPlot1.iloc[:,2:].cumsum()
#scaling for plotting
for i in range(1,dfPlot1.shape[1]):
        dfPlot1.iloc[:,i] = Preprocessor.minmax(dfPlot1.iloc[:,i])
				
dfPlot1.iplot(x='Date', y=['inst_sum','foreign_sum','Close'], title = "기관, 외국인 누적 순매매량(Scaled) + 종가")

대한항공의 주가를 선택한 이유는 내가 보유하고있는 종목이기 때문이다.
가장 눈에띄는 점은 기관 누적 순매매량주가(종가)는 굉장히 유사한 흐름을 보였다는 것이다. 하지만 이러한 정보는 매매에 활용하기 힘들다(주가에 선행하는 정보가 아니기 때문). 예를들어 ‘외인이 2주간에 걸쳐 매도세를 보이면 그다음 2주후 주가가 하락한다.’ 와 같은 정보가 유의미한 정보일 것이다(물론 이러한 현상이 유의미한지에 대해서는 여러 case들에 대해 통계적 검정을 거쳐야 할 것이다).
추가적으로 삼성전자의 주가 plot을 겸해서 패턴을 찾아보자. 두개의 plot에서 공통적으로 기관, 외인들의 누적 순매매량에 따른 주가의 횡보의 패턴이 보이는가?
나는 단순히 plot을 이용해서 이러한 유의미한 패턴을 찾기는 힘들었다. 그래서 다음 포스트에서는 좀더 많은 데이터(799개 종목 전체)를 이용해
statistical methods를 이용해 데이터 분석을 해보자.
*분석 관련 문의사항이나 아이디어 등은 아래 메일로 연락부탁드립니다.