자본시장은 효율적인가? 금융공학

'주가는 예측 가능한 것일까?'라는 질문은 일찍부터 경제학자들의 큰 관심거리였다. 이 질문에 대해 체계적인 답변이 가능하게 된 것은 컴퓨터가 경제학에 도입되어 시계열 데이터분석이 가능하게 된 1950년대였다.

주가를 예측하는 것이 가능할까?

그런데 당시의 경제학자들은 경기변동에도 주기가 있듯이 주가에도 일종의 주기가 있을 것이라는 막연한 기대를 갖고 있었다. 모리스 켄들도 그런 사람중의 한 명이었다. 그런데 켄들은 1953년에 발표한 '경제 시계열분석: 가격'이란 논문에서, 주가의 움직임이 당초 그가 예상했던 것과 달리 '랜덤 워크'를 보인다는 사실을 보고했다.

랜덤 워크란..."금융경제학 사용설명서, 이찬근 저" 중에서...

몇 주전 강남 예스24 중고서점에서 산 책을 이제 읽어 보는 중이다. 최근에는 기술서 위주에서 에세이 형식의 글을 자주 보는데 맘이 편하다.

그나저나 랜덤워크는 과거의 시계열 데이터를 이용해 주가의 미래 움직임을 전혀 예측할 수 없다는 의미인데, 예측할 수 있는 게 하나 있기는 하다.

아무리 술취한 사람이 랜덤워크를 해도 오른 발 다음에는 왼발이 나갈 것이다. 그리고 다시 오른 발이 나갈 것이고...ㅎㅎㅎ

R에서 NA를 이해하는 신박한 방법 GNU R

다음은  blog.revolutionanalytics.com에 실린 'The trick to understanding NAs (missing values) in R'이라는 글을 옮긴 것입니다.

R에서 NA값을 어떻게 다루는 지를 보여주는 간단한 퍼즐이 하나 있다.

R에서  NA^0은 얼마일까?


다음과 같이 입력하여 쉽게 답을 구할 수 있다.

> NA^0
[1] 1

왜 1이지? 하는 호기심이 생긴다. 대부분의 사람들은 NA가 될 거라고 생각할 지도 모르겠다. 왜냐면 NA가 들어간 코드가 그랬으니까. 이것을 이해하기 위한 팁을 소개하고자 한다. NA를 숫자가 아니라, 숫자가 있어야 할 자리(placeholder)로 생각하자(숫자가 무엇인지는 중요하지 않다)

모든 숫자가 NA^0에서 NA를 대신할 수 있다. 양수라면 0승은 1이다. 음수도 마찬가지이다. 심지어는 0조차 수학적으로 1이다. NA^0에서 NA자리를 차지할 숫자가 무엇이든 답은 1이다. 그래서 R은 1이라는 결과를 돌려주는 것이다. NA가 아닌 결과를 돌려주는 몇 가지 다른 예를 보자.

> NA || TRUE
[1] TRUE

여기서 NA는 논리값의 자리를 차지하고 있으며 TRUE 또는 FALSE 값을 대신하고 있다. 참이든 거짓이든 답은 같을 것이다.

> TRUE || TRUE
[1] TRUE
> FALSE || TRUE
[1] TRUE

x가 최소 한 개 이상의 TRUE를 가지고 있다면 논리벡터에 NA가 포함되어 있어도 TRUE를 돌려줄 수 있다.  마찬가지로 NA && FALSE 는 항상 FALSE이다. 물론 여러 다른 예들이 있다. 그러나 R 에서 NA가 포함된 연산으로 혼란스럽다면 이것을 항상 기억해두길 바란다. R이 NA를 어떻게 다루는 지 더 자세히 알고 싶다면 R Language Definition 을 찾아 보길 바란다.

 

그림으로 보는 참조 전달과 값 전달의 차이

참조 전달과 값 전달의 차이

CAPM vs. Fama French Three Factor Model 증권

십 몇년 전에 파마-프렌치 3팩터모델을 구현해 놓은 적이 있는데, 얼마 전에 이 모델과 만들어 놓은 프로그램을 설명해달라는 부탁이 있어 급히 모델에 관한 자료를 만들어 보았습니다. 프로그램은 백업해 놓은 걸 찾지 못했지만, 요즘에 R과 Jupyter Notebook으로 훌륭하게 만든 게 많이 있어 그나마 위안이 됩니다.

오늘은 간단히 파마-프렌치 모델에 대한 PT를 올려 봅니다.

주식 등 위험자산의 기대수익률을 산출하는 CAPM은 '기대수익률=무위험수익률+(시장의 기대수익률-무위험수익률)×베타값'으로 설명된다. 여기서 베타값은 시장변동성에 대한 민감도로, 이 값이 클수록 위험은 높아진다. 그러나 베타는 천차만별인 개별 종목들의 주가를 모두 설명하기에는 역부족이었다.

파마 교수는 케네스 프렌치(Kenneth French) 다트머스대 교수와 함께 1963년부터 1990년까지 27년에 걸친 9500개 종목의 주가 추이를 분석한 결과를 토대로 1992년 '파마-프렌치 3요인(3 Factors) 모델'을 제시하는 논문을 내놨다. 여기서 3요인은 시가총액, 장부가치/시가총액(PBR이 아니다), 시장 등 3가지 변수를 뜻한다. 파마 교수는 이를 통해 시가총액이 작고 장부가치/시가총액이 낮은 종목일수록 초과수익을 올리기에 유리하다는 것을 증명해냈다.




[팩트폭력]엑셀은 인터넷보다 오래되었다 EXCEL/VBA

엑셀이 인터넷보다 먼저 나온 것이 맞는 데, '아 그렇치~맞네 맞어' 하며 얼마 전 주변에 제가 말한 게 생각난다. '데스크탑 애플리케이션에서 살아 남는 것은 MS Office밖에 없을 것 같다. 나중엔 모든 개발이 웹 애플리케이션이 될 것이다' 이미 미래시제가 아닌 현재진행형으로 일어나고 있는 현상일지도 모른다. 사실 개발자 구인소식만 봐도 데스크탑 개발자를 찾는 사례는 그리 많지 않다.

Felix Zumstein의 글(Fetching market data into Excel using Python)을 빌려 엑셀에 대한 흉을 보자면:
However, to get data from these providers into Excel, there is an issue: Excel has been around long before the Internet became widespread and VBA has not been updated for years. For example, VBA does not offer an easy way to deal with the JSON format, the de facto standard to exchange data over the Internet nowadays.
To gather data in Excel, we usually have to use a separate add-in for each data provider. Moreover, these generally do not support a BDH-style formula.
엑셀도 문제이긴 하지만, 가장 열 받는 점은 MS넘들이 VBA 개발에 너무 소홀한 점이다. 베이직언어만 사용할 거면 비주얼베이직닷넷 수준으로 하던 가, 아니면 파이썬이나 c언어를 엑셀에 네이티브하게 붙도록 하던가.... VSTO같은 편법은 이제 그만 했으면 한다.

어제도 파이썬으로 quandl 데이터를 가져와서 R로 만든 전략3개를 옮기느라 Felix Zumstein의 포스팅이 눈에 더 잘 들어온다. 테스트하는 과정에서 자주 quandl 데이터를 가져오다 짤릴 수 있으니 다운받은 데이터는 피클로 절임하고 , 또 불러오는 경우 다운받은 걸 읽어 오도록 하였다.
import quandl
import pandas as pd
import numpy as np

date0 = '2007-01-03'
date1 = '2017-03-31'

try:
vix = pd.read_pickle("df_vix(" + date0 + date1 + ")")
spx = pd.read_pickle("df_spx(" + date0 + date1 + ")")
skw = pd.read_pickle("df_skw(" + date0 + date1 + ")")

except Exception as e:
vix = quandl.get("YAHOO/INDEX_VIX", start_date=date0, end_date=date1)
vix.to_pickle("df_vix(" + date0 + date1 + ")")
spx = quandl.get("YAHOO/INDEX_GSPC", start_date=date0, end_date=date1)
spx.to_pickle("df_spx(" + date0 + date1 + ")")
skw = quandl.get("CBOE/SKEW", start_date=date0, end_date=date1)
skw.to_pickle("df_skw(" + date0 + date1 + ")")

spx['vix'] = vix['Close']
spx['close.ratio'] = (spx['High'] - spx['Close']) / (spx['High'] - spx['Low'])
spx['exposure'] = 10 * (spx['close.ratio'] - 0.5) / 3
spx['exposure'][((spx['vix'] > 20) & (spx['close.ratio'] > 0.8))] = 1
spx['exposure'][((spx['vix'] > 20) & (spx['close.ratio'] <= 0.2))] = -1
spx['exposure'][(spx['vix'] <= 20)] = 0

vix['sma'] = vix['Close'].rolling(22).mean()
skw['sma'] = skw['SKEW'].rolling(22).mean()
vix_rank = vix['sma'][-499:]
skw_rank = skw['sma'][-499:]

vix_rank = vix_rank.rank(ascending=True)[-1]/500
skw_rank = skw_rank.rank(ascending=True)[-1]/500
....

1 2 3 4 5 6 7 8 9 10 다음