[일상] PORT IT정보보호 ETF – 신규 금융상품 연구 보고서

20191114_신규 금융상품 연구 보고서_리서치1팀_임세준

제가 새내기 시절부터 몸담고 있는 동아리는 한국외대 증권투자연구회, POSTRADE입니다. 비교적 어린 나이에 동아리에 입회하여 1학년 2학기에 리서치1팀(퀀트팀)의 팀장이 되었습니다. 기존에 없던 새로운 정량분석(Quantitative analysis) 중심의 팀인 만큼, 무언가 새로운 연구를 직접 리드해야겠다는 생각이 문득 들었어요. 그래서 세 달전인 10월에 새로운 금융상품에 대한 선행연구를 진행하고 직접 모델 포트폴리오를 설정해보자는 목표를 설정하게 되었습니다.

그래서 한 달 간의 고민과 시행착오 끝에 ‘PORT IT정보보호 ETF’라는 금융상품을 개발하게 되었습니다. ETF(Exchange Traded Fund)는 상장지수펀드로, 지수를 추종하는 펀드를 주식처럼 증권시장에서 쉽게 매매할 수 있도록 설계된 상품입니다. 최근에 패시브 투자가 트렌드인 만큼, 증시에 다양한 ETF가 상장되어 있죠. 그렇지만, 저희 팀은 4차산업혁명의 후방산업이자 진정한 수혜자로 예상되는 IT정보보호 산업에 대한 ETF가 없다는 점에 주목하여 해당 연구를 진행하게 되었습니다. 아, 그리고 PORT라는 ETF 브랜드는 포스트레이드(Postrade)의 약자이자 ETF의 바다를 여행하는 투자자들의 항구와도 같은 역할을 하겠다는 비전을 바탕으로 지어냈습니다. 어짜피 선행연구인데, 제도권 자산운용사들을 카피해서 브랜드 이름이라도 멋있게 지어주기로 했죠.

저희 연구에 대해 관심이 있으신 분들은 자세한 내용은 위에 업로드한 보고서에서 확인해주시면 감사하겠습니다. 다만, PORT IT정보보호 ETF를 한줄로 요약하자면 ‘국내 IT정보보호 테마 내 상장주식을 시가총액 가중방식으로 편입하여 KOSDAQ 지수를 아웃퍼폼 하는 것을 목표로 하는 ETF’로 설명할 수 있을 것 같습니다. 

사실 이번 연구를 진행하면서 정말 힘들었습니다. 국내에는 없는 IT정보보호 테마에 대한 선행연구인지라 정보가 없을 뿐만 아니라, ETF를 개발하는 것은 저희 동아리 내에서 전례가 없었던 일인지라 모든 것을 처음부터 시작해야 했습니다. 그래서 미래에셋자산운용의 ETF 제작과정을 벤치마크하여 투자 유니버스(Investment Universe)와 모델 포트폴리오(PDF)를 순서대로 구성하는 방식을 사용했지만, 1학년 학부생으로써 진행과정에서 지적 한계를 마주해야만 했습니다. 특히 모델 포트폴리오 구성방식에 대해 CAPM, MPT, Efficient Frontier과 같은 다양한 포트폴리오 구성 방법론들을 적용해야할지 고민이 많았습니다. 그 뿐만 아니라, 설정한 모델의 백테스팅을 위한 시뮬레이터를 직접 파이썬으로 코딩하는 과정도 만만치 않았죠. 그리고 무엇보다, 팀장으로써 팀을 리드한다는 것 또한 많은 도전이 있는 일이었습니다. 아직 열아홉이었던 제게 너무나도 많은 과제들이 주어졌다는 생각이 들었죠.

그래도 무사히 보고서를 완성하고 나니, 좋은 기회들이 제게 찾아왔습니다. 시립대 법학관에서 시립대 증권연구회 UoStock과의 연합 세션에서 시립대 학생들과 정보를 교류하고(사실 우리 윤화 누나의 ‘한국기업평가’ 리서치 보고서와 제 ‘PORT IT정보보호 ETF’로 압살하고 왔다고 생각하고 있습니다), 저희 포스트레이드의 홈커밍데이에서 현직 증권맨 선배님들 앞에서 직접 제 연구에 대해 발표할 기회도 가지게 되었습니다. 그리고, 하나금융투자에 재직중이신 한 유튜버 선배님께서 제 연구를 유튜브 채널에 소개까지 해주셨습니다.

아무튼, 이번 선행연구는 제게 금융상품의 제작 뿐만 아니라 IT정보보호 산업에 대한 이해까지 넓혀준, 지적성장을 이끌어준 프로젝트였습니다. 그 뿐만 아니라, 제 커리어에도 많은 도움을 줄 수 있는 결과물까지 만들 수 있었죠. 제가 노력한 만큼의 결과가 따라와줘서 다행입니다. 다음에는 더욱 액티브한 운용이 가능한 ‘스마트 베타 ETF’와 함께, IT정보보호 산업에 대한 이해도를 더욱 높히는 과정을 걷고자 합니다.

[기술] Binance 암호화폐 자산 현황을 파이 차트로 만들기

최근에 제 자산이 어떻게, 얼마나, 어느 비중만큼 있는지 다시 확인하는 작업을 하고 있었습니다. 사실 지난 고등학교 3년동안 투자는 해놓고선, 정작 자산이 어떻게 변동되는지 살펴보지 않았던게 사실입니다. 체크카드로 긁을 때마다 날라오는 통장 잔고만 보고선, 충분하다 생각하면 넘어가는 경우가 대부분이였죠.

이제서야 정신차리고 확인해보니, 암호화폐와 미국 증시는 대폭락장을 겪고 있었습니다. 세상은 변하는데 내 포트폴리오는 과거에 머물고 있었던 거죠. 당연히 리스크 헷지 같은 것이 될리가 없었고, 그대로 손실을 껴안고 있었습니다. 더 이상 이럴순 없다는 생각에, 주기적으로 자산 현황을 추적하는 일을 해야겠다는 생각이 들었습니다.

특히, 가장 시급한건 변동성이 큰 암호화폐였습니다. 대충 코딩을 해서라도, 내가 어떤 코인을 얼마나 가지고 있는지 시각화 해서 결과를 알아보고 싶어서 작업을 바로 시작하게 되었습니다. 올해 봄 즈음, 나름 알고리즘 트레이딩에 관심이 많아 Binance API를 만지작거린 경험이 있었습니다. 이동평균선 크로스와 볼린저 밴드를 이용한, 아주 단순한 알고리즘이었고, 당연히 수익은 내지 못했습니다. 그래도 잘 보관해놔서, 이번에 빠르게 꺼내다 쓸 수 있었습니다.

패키지 불러오기 및 설정
Binance API 데이터를 가져오기 위해 python-binance 패키지를 사용했습니다. 패키지 설명서는 여기서 확인할 수 있습니다. 사실 RESTful API를 통해서 직접 POST해서 가져올 수도 있지만, 이미 좋은 패키지가 있는데 그럴 이유가 전혀 없었습니다. 아무튼, 필요한 패키지들을 불러오고선 기본적인 설정들을 해줬습니다.

from binance.client import Client
import warnings
warnings.filterwarnings("ignore")
from matplotlib import pyplot as plt


그다음에는 가장 기본적으로 Client()에 API Key와 Secret 키를 입력해줘서 접근 권한을 얻도록 해줬습니다. 너무 빨리 타임아웃이 발생하지 않도록 넉넉히 100ms으로 잡아뒀습니다.

client = Client('Binance API Key', 'Binance Secret Key', {"verify": False, "timeout": 100})

 

비트코인 최근 가격 불러오기
이제부터 다양한 정보들을 직접 불러오는데, 가장 먼저 비트코인의 최신 가격을 불러왔습니다. 방법은 간단하게, 1분 단위 OHLCV Kline 과거 데이터 중에서 가장 최근 Close 가격을 가져오도록 해두었습니다. 즉, 가장 최근의 가격 정보를 쿼리하는 것입니다.

BTCPrice = float(client.get_klines(symbol='BTCUSDT',
                            interval=client.KLINE_INTERVAL_1MINUTE,
                            limit=1,
                            requests_params={'timeout':100})[0][4])


참고로 OHLCV 정보가 list in list 형태로 제공되기 때문에, 적절히 리스트에서 골라 써야 합니다. [0]을 통해서 첫번째이자 유일한 값에 접근하고, [4]로 1분 봉 마감가격인 Close에 접근했습니다.

# get_klines 출력값
[[
1499040000000, # Open time
"0.01634790", # Open
"0.80000000", # High
"0.01575800", # Low
"0.01577100", # Close -> List[0][4]
"148976.11427815", # Volume
1499644799999, # Close time
"2434.19055334", # Quote asset volume
308, # Number of trades
"1756.87402397", # Taker buy base asset volume
"28.46694368", # Taker buy quote asset volume
"17928899.62484339" # Can be ignored
]]

 

Binance 자산 현황을 불러 데이터 전처리하기
그 다음에는 내 Binance 거래소 지갑에 있는 자산 현황을 불러온다. 간단하게 get_account()를 통해 내가 가지고 있는 코인들과 잔고 수량을 확인할 수 있습니다. 자산 현황 정보는 dict in list in dict 형태로 주기 때문에, 적절히 내가 보유한 코인들과 잔고 수량을 볼 수 있게 접근해줘야 합니다.

AssetBalance = client.get_account()
#get_account 불러오기 샘플
{'makerCommission': 10,
'takerCommission': 10,
'buyerCommission': 0,
'sellerCommission': 0,
'canTrade': True,
'canWithdraw': True,
'canDeposit': True,
'updateTime': 1539110138808,
'balances': [{'asset': 'NEO', 'free': '0.00000000', 'locked': '0.00000000'}, {'asset': 'LTC', 'free': '0.00000000', 'locked': '0.00000000'}...]


간단하게는 Dict[‘balances’]로 먼저 접근하고선, for문으로 개별 딕셔너리를 조회하는 방법이 있습니다. 여기서 if절로 잔고 수량이 0 이상인 코인들만 거른 다음에 AvailableAsset이라는 리스트에 저장시켜줬습니다.

AvailableAsset = []
for Asset in AssetBalance['balances']:
    if float(Asset['free']) > 0:
        AvailableAsset.append(Asset)

 

그 다음에는 for문으로 AltPricePair라는 딕셔너리에 {코인명 : 코인 잔고 수량} 페어를 만들어 저장시켜줬습니다. 이를 위해, PairName에 000BTC 형식으로 환율 거래쌍 이름을 만들어 검색했습니다. 여기서 문제는 BTCBTC나 USDTBTC와 같은 거래쌍은 없는지라, 그대로 돌리면 BTC 잔고를 검색하려다 ‘그런 거래쌍은 없다’라는 에러가 뿜어져 나옵니다. 이를 해결하기 위해서, try-except문을 이용해서 에러가 나타나면 바로 패스하도록 만들었습니다.

이렇게 거래쌍을 통해 OHLCV 값을 불러오면, 해당 코인의 비트코인 기준 가격이 나옵니다. 이걸 해당 코인 수량만큼 곱해주면, 잔고 금액이 비트코인 기준으로 얼마나 있는지 알 수 있습니다. 하지만 우리는 비트코인보다 미국 달러가 익숙하기 때문에, 이전에 BTCPrice에 불러온 비트코인 가격을 여기다 곱해주면 미국 달러 기준 잔고량으로 변환 가능합니다. 이 값을 AltPricePair딕셔너리에 키-벨류 페어로 저장시켜 주도록 했습니다.

AltPricePair = {}
for Asset in AvailableAsset:
    PairName = Asset['asset'] + 'BTC'
    try:
        MinuteKlineRaw = client.get_klines(symbol=PairName,
                                           interval=client.KLINE_INTERVAL_1MINUTE,
                                           limit=1,
                                           requests_params={'timeout': 100})
        CurrentPrice = MinuteKlineRaw[0][4]
        AltPricePair[Asset['asset']] = float(CurrentPrice) * float(Asset['free']) * BTCPrice
    except:
        continue

이전에 BTCBTC나 USDTBTC와 같은 환율 거래쌍은 존재하지 않아 불러올 수 없다고 했습니다. 그렇다고 비트코인이나 미국 달러 자산 값을 빼고 알트코인들의 가치만 더할 수는 없습니다. 다행히도, get_asset_balance()라고 개별 코인에 대한 잔고 수량을 불러오는 함수가 있습니다. 비트코인은 잔고 수량을 불러서, 비트코인의 미국 달러 가격에 곱해줘서 잔고 금액을 MyBTCBalance에 저장합니다. 미국 달러화는 당연히 잔고 수량이 잔고 금액과 동일하니, 그대로 사용해 MyUSDTBalance에 저장합니다. 그리고 가장 중요한 알트코인의 가격, AltPricePair에 수많은 키-벨류 페어에서 values()를 통해 벨류 값들의 리스트를 구합니다. 그리고선 바로 sum()으로 그 합들을 MyAltBalance에 저장합니다.

마지막으로 MyBTCBalance, MyUSDTBalance, MyAltBalace 이 셋을 모두 더해주면, 내 Binance 자산의 총 합계를 미국 달러화 기준으로 볼 수 있습니다.

MyBTCBalance = BTCPrice * float(client.get_asset_balance('BTC')['free'])
MyUSDTBalance = float(client.get_asset_balance('USDT')['free'])
MyAltBalance = sum(AltPricePair.values())


먼저 AltPricePair에 있는 {코인명: 코인의 미국 달러 기준 잔고 금액} 딕셔너리 페어처럼, 비트코인과 미국 달러화도 똑같이 업데이트하여 저장해줍니다. 그러고선 혼동이 없도록, 이렇게 업데이트된 딕셔너리를 PricePair이라고 다시 이름 지어줍니다.

AltPricePair.update({'BTC' : MyBTCBalance})
AltPricePair.update({'USDT' : MyUSDTBalance})
PricePair = AltPricePair


Matplotlib을 통한 파이 차트 만들기
이제 데이터 전처리가 끝났으니, 자산의 코인별 비중을 시각화 시켜야합니다.
PricePair 딕셔너리에 코인명을 담고 있는 키 부분을 keys()를 통해, PriceKey에 리스트 형태로 저장한다. 또한, 코인의 미국 달러 기준 잔고 금액이 담겨있는 벨류 부분은 PriceValue에 values()로 리스트 형태로 저장합니다. 그러고선, 다시 PriceValue는 for문으로 소수점 2자리 이하 값으로 반올림을 시켜 예쁘게 만들어줍니다. 그럼 각각의 PriceKey와 PriceValue가 순서에 맞게 정갈한 리스트를 내놓습니다.

PriceKey = PricePair.keys()
PriceValue = PricePair.values()
PriceValue = [round(elem, 2) for elem in PriceValue]
#PriceKey와 PriceValue 리스트
dict_keys(['BNB', 'GAS', 'BAT', 'XRP', 'XLM', 'BTC', 'USDT'])
[0.04, 0.0, 212.14, 219.99, 129.67, 165.62, 0.0]


마지막으로 Matplotlib의 pie()를 통해 플롯해주는데, 그냥 PriceValue만 넣으면 아무 것도 없는 밍밍한 파이 차트가 나옵니다. 여기에 퍼센테이지를 값과 실제 잔고 금액을 표시하고 싶었습니다. 퍼센테이지는 autopct에 적당히 설정해주면 되지만, 실제 잔고 금액은 어떻게 넣나 고민이 많았습니다. 결국 Stackoverflow에 한 함수를 끌어와서, 잘 모르지만 일단 사용하게 됬습니다.

def make_autopct(values):
    def my_autopct(pct):
        total = sum(values)
        val = int(round(pct*total/100.0))
        return '{p:.2f}%  ({v:d})'.format(p=pct,v=val)
    return my_autopct


이걸 autopct에 넣어주고선, 레전드를 설정해줬더니 투박하지만 제 역할은 하는 파이 차트를 결국 띄울 수 있었습니다.


전체 코드

from binance.client import Client
import warnings
warnings.filterwarnings("ignore")
from matplotlib import pyplot as plt

client = Client('A94G89HZGqMF8niazexzsN7Vo8ygzVmvxAFbxtYH6IossrzIAtuPTgoj1k4iAXyq', 'wG18VxuQjGb8F33opQKJQX6EmWMuz0cwZPEh4aYTjxYmOF6NBNasUys5G6sEvOGa', {"verify": False, "timeout": 100})

BTCPrice = float(client.get_klines(symbol='BTCUSDT',
                            interval=client.KLINE_INTERVAL_1MINUTE,
                            limit=1,
                            requests_params={'timeout': 100})[0][4])

AssetBalance = client.get_account()
print(AssetBalance)

AvailableAsset = []
for Asset in AssetBalance['balances']:
    if float(Asset['free']) > 0:
        AvailableAsset.append(Asset)

AltPricePair = {}
for Asset in AvailableAsset:
    PairName = Asset['asset'] + 'BTC'
    try:
        MinuteKlineRaw = client.get_klines(symbol=PairName,
                                           interval=client.KLINE_INTERVAL_1MINUTE,
                                           limit=1,
                                           requests_params={'timeout': 100})
        CurrentPrice = MinuteKlineRaw[0][4]
        AltPricePair[Asset['asset']] = float(CurrentPrice) * float(Asset['free']) * BTCPrice
    except:
        continue



MyBTCBalance = BTCPrice * float(client.get_asset_balance('BTC')['free'])
MyUSDTBalance = float(client.get_asset_balance('USDT')['free'])
MyAltBalance = sum(AltPricePair.values())

AltPricePair.update({'BTC' : MyBTCBalance})
AltPricePair.update({'USDT' : MyUSDTBalance})

PricePair = AltPricePair

PriceKey = PricePair.keys()
PriceValue = PricePair.values()
PriceValue = [round(elem, 2) for elem in PriceValue]

print(PriceKey)
print(PriceValue)

def make_autopct(values):
    def my_autopct(pct):
        total = sum(values)
        val = int(round(pct*total/100.0))
        return '{p:.2f}%  ({v:d})'.format(p=pct,v=val)
    return my_autopct

plt.pie(PriceValue, labels=PriceKey, autopct=make_autopct(PriceValue))
plt.legend(PriceKey)
plt.show()

print(MyAltBalance + MyUSDTBalance + MyBTCBalance)

 

배운점
먼저, 코딩도 당분간 두통 때문에 그만둬야겠습니다. 정말 모니터 앞에 오래 앉아있으면 심한 번아웃이 옵니다. 블루라이트 때문인가도 싶습니다. 파이썬의 문법 면에서 배운점은, 딕셔너리는 리스트처럼 + 만으로 합쳐지지 않는다는 것입니다. 기존의 딕셔너리에 update()로 새 딕셔너리를 업데이트하여 더해줘야 합니다.

마지막으로, 사실 나는 초보자로 데이터 과학을 접하면서 허탈하다는 생각이 종종 듭니다. 그렇게 데이터 수집과 전처리 과정을 거쳐 내놓은 결과물이 고작 차트와 같은 시각화 자료입니다. 물론 데이터 과학에서 시각화는 일부분이고, 무엇보다 시각화된 자료에서 유의미한 상관관계를 찾아 사용자의 의사결정에 도움이 된다면 그 역할을 다한 셈이죠. 하지만, 개인적으로 짧은 프로젝트를 하면서 내가 데이터 과학이 정말 하고싶은지에 대해 다시 고민하게 된 것 같습니다.