공매도 데이터 분석하기

2022-01-19 • quant금융데이터분석, 공매도 • 4 min read

이번 포스트에서는 파이썬으로 공매도 데이터를 분석해 봅니다. 공매도 데이터 수집에 대해서는 이 포스트를 확인해 주세요.

공매도 거래, 공매도 잔고, 종목 정보 데이터를 가져옵니다. 이 부분은 개인적으로 사용하는 데이터베이스를 활용하므로 퀀티랩 카페에서 CSV 파일을 다운받아 사용하시기 바랍니다.

import pandas as pd
from quantylab.common.db.mongodb import MongoDBClient
coll = MongoDBClient.get_coll('short')
cursor = coll.find({'date': {'$gte': '20210601', '$lte': '20220114'}}, {'_id': 0, 'dt_update': 0, 'date_update': 0}, sort=[('date', -1)])
df_short = pd.DataFrame(list(cursor))
coll = MongoDBClient.get_coll('shortbalance')
cursor = coll.find({'date': {'$gte': '20210601', '$lte': '20220114'}}, {'_id': 0, 'dt_update': 0, 'date_update': 0}, sort=[('date', -1)])
df_shortbalance = pd.DataFrame(list(cursor))
coll = MongoDBClient.get_coll('stockcodes')
cursor = coll.find({}, {'_id': 0, 'code': 1, 'name': 1})
df_stockcodes = pd.DataFrame(list(cursor))

위에서 준비한 세 DataFrame을 합칩니다. 이 합친 데이터를 CSV 파일로 저장해 둡니다. 이 CSV 데이터를 카페에 올려 놓았습니다. 카페 가입하셔서 다운받으시고 질문도 해주시면 감사하겠습니다.

df = pd.merge(df_short, df_shortbalance[['code', 'date', 'bal_rto', 'bal_amt', 'bal_qty']], on=['code', 'date'], how='left')
df = pd.merge(df, df_stockcodes, on='code', how='left')
df = df[['name'] + df.columns.tolisZt()[:-1]]
df.to_csv('20220113_analysis_short.csv', index=False)

CSV 파일을 다운받으셨다면 다음 부분부터 직접 해보실 수 있습니다. 다음 코드는 CSV 파일을 불러오는 부분입니다.

import pandas as pd
str_columns = ['code', 'date']
df = pd.read_csv('20220113_analysis_short.csv', converters={k: lambda x: str(x) for k in str_columns})

code, date를 숫자로 읽는 것을 방지하기 위해서 converters 인자를 넣어줍니다. 그리고 다음 코드에서처럼 date로 정렬하고 % 단위를 떼고 NaN 값이 포함된 행을 버립니다.

df = df.sort_values(by='date')
# % 단위 떼기
df['short_ratio'] /= 100
df['bal_rto'] /= 100
df = df.dropna().copy()
name code date avg_price avg_price_ratio close diff diffratio short_amount short_ratio short_volume volume bal_rto bal_amt bal_qty
LX홀딩스 383800 20210601 11078 -78 11000 50 0.46 26214 0.012591000000000001 23662 1879288 0.0 34639000.0 3149.0
DB금융투자 016610 20210601 7215 5 7220 50 0.7 412 0.003169 571 180173 0.0 0.0 0.0
CJ제일제당 097950 20210601 484327 1173 485500 -3000 -0.61 122535 0.0437 2530 57895 0.0029 21517360000.0 44320.0
CJ씨푸드 011150 20210601 4819 1 4820 105 2.23 22 3.8e-05 45 1188132 0.0083 1435482760.0 297818.0
BGF리테일 282330 20210601 183995 5 184000 2500 1.38 64950 0.089838 3530 39293 0.0007000000000000001 2272032000.0 12348.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
엠씨넥스 097520 20220114 56312 -12 56300 3800 7.24 141 1.9e-05 25 1321720 0.0029 2909752900.0 51683.0
더블유게임즈 192080 20220114 53966 234 54200 -400 -0.73 47851 0.10421200000000001 8867 85086 0.0139 13883763600.0 256158.0
PI첨단소재 178920 20220114 52734 366 53100 1000 1.92 34556 0.044195000000000005 6553 148275 0.0086 13446300600.0 253226.0
포스코케미칼 003670 20220114 129380 620 130000 -2000 -1.52 94033 0.043436 7268 167325 0.0235 236509260000.0 1819302.0
LG이노텍 011070 20220114 381022 3478 384500 32000 9.08 3552349 0.05383 93232 1731987 0.005600000000000001 50517917000.0 131386.0

이 데이터에서 종목별로 상환 규모를 확인하고 공매도 거래량, 공매도 잔고, 상환 규모의 5일 이동평균을 구합니다. 이 과정은 몇 분 정도 걸릴 수 있습니다. 그래서 tqdm으로 진행율을 확인합니다.

from tqdm import tqdm
for k, g in tqdm(df.groupby('code')):
    # 상환 규모 확인
    # 당일 short_volume - (당일 bal_qty - 전일 bal_qty)
    df.loc[df['code'] == k, 'bal_qty_diff'] = g['bal_qty'].diff()
    df.loc[df['code'] == k, 'repay_volume'] = (df.loc[df['code'] == k, 'short_volume'] - df.loc[df['code'] == k, 'bal_qty_diff']).clip(0)
    df.loc[df['code'] == k, 'repay_ratio'] = df.loc[df['code'] == k, 'repay_volume'] / df.loc[df['code'] == k, 'volume']
    # 이동평균
    df.loc[df['code'] == k, 'short_ratio_ma5'] = g['short_ratio'].fillna(0).rolling(5).mean()
    df.loc[df['code'] == k, 'bal_rto_ma5'] = g['bal_rto'].fillna(0).rolling(5).mean()
    df.loc[df['code'] == k, 'repay_ratio_ma5'] = df.loc[df['code'] == k, 'repay_ratio'] .fillna(0).rolling(5).mean()

이렇게 구한 repay_ratio_ma5로 내림차순 정렬을 해보면 다음과 같은 결과를 얻을 수 있습니다.

_df = df[df['date'] == '20220114'].sort_values(by='repay_ratio_ma5', ascending=False)
_df
name code date avg_price avg_price_ratio close diff diffratio short_amount short_ratio short_volume volume bal_rto bal_amt bal_qty bal_qty_diff repay_volume repay_ratio short_ratio_ma5 bal_rto_ma5 repay_ratio_ma5
BGF리테일 282330 20220114 145501 -1501 144000 -2000 -1.37 188743 0.31445 12972 41253 0.0013 3342384000.0 23211.0 4353.0 8619.0 0.20893025961748238 0.24250639999999998 0.0013000000000000002 0.2374309360178176
세방전지 004490 20220114 69346 -146 69200 -1800 -2.54 22322 0.094635 3219 34015 0.0191 18506225200.0 267431.0 -6377.0 9596.0 0.2821108334558283 0.1048104 0.02026 0.22422523200232863
호텔신라 008770 20220114 77304 96 77400 -1100 -1.4 594543 0.222531 76910 345615 0.0717 217719311400.0 2812911.0 83286.0 0.0 0.0 0.271113 0.06978000000000001 0.2142213088295697
금호석유화학 011780 20220114 174115 -115 174000 2500 1.46 293854 0.075005 16877 225011 0.05 263459838000.0 1514137.0 -44184.0 61061.0 0.2713689552955189 0.0773396 0.0519 0.20765174156183894
효성첨단소재 298050 20220114 532588 412 533000 -8000 -1.48 314067 0.159077 5897 37070 0.0147 35143888000.0 65936.0 15093.0 0.0 0.0 0.1540592 0.01316 0.18448044554531187
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
콤텍시스템 031820 20220114 957 0 957 -16 -1.64 1 9e-06 8 937184 0.0011 123620475.0 129175.0 0.0 8.0 8.536210605388056e-06 4.259999999999999e-05 0.0010999999999999998 4.258825517099485e-05
TCC스틸 002710 20220114 12950 0 12950 800 6.58 3 0.0 2 5084832 0.0 0.0 0.0 0.0 2.0 3.933266625131371e-07 3.48e-05 0.0 3.473958840739652e-05
미래산업 025560 20220114 13650 100 13750 -350 -2.48 1 2.5e-05 1 39706 0.0 0.0 0.0 0.0 1.0 2.518511056263537e-05 2.46e-05 0.0 2.4705969543888606e-05
이스타코 015020 20220114 3105 0 3105 -110 -3.42 1 5.999999999999999e-06 4 721686 0.0 0.0 0.0 0.0 4.0 5.542576688476706e-06 2.3000000000000024e-05 0.0 2.2816870909533658e-05
오리엔트바이오 002630 20220114 1315 5 1320 20 1.54 0 0.0 1 2479455 0.0013 198262680.0 150199.0 0.0 1.0 4.033144380519106e-07 5.200000000000005e-06 0.0013 5.125393445035716e-06

이 종목들 중에서 공매도 상환율이 높은 상위 5개 종목을 살펴보겠습니다.

BGF리테일

1

최근 공매도 거래가 높은데 공매도 잔고는 크게 늘지 않고 유지되고 있습니다. 즉, 공매도 상환율도 높은 상황입니다. 공매도자들의 판단이 엇갈리고 있다고 보이는 지점입니다.

세방전지

2

공매도 거래는 꾸준히 있는데 공매도 잔고가 떨어지고 있습니다.

호텔신라

3

11월에 어닝쇼크로 공매도 잔고가 급격하게 늘었습니다. 이후 공매도 거래는 많은데 공매도 잔고는 조금씩 오르는 모습입니다.

금호석유화학

4

공매도 잔고가 꾸준히 늘다가 최근 하락 전환하는 모습입니다.

효성첨단소재

5

최근 공매도 거래가 상당한 상황입니다.