파이썬이 제공하는 여러 편한 툴들을 활용해 시뮬레이션을 할 수 있을 때가 많다. 대표적으로 scipy를 활용해 linear optimization을 했었던 저번 포스트가 있었는데, 이번에는 그런 모듈을 활용하는 것은 아니고 간단한 사고실험 수준의 시뮬레이션을 해볼까 한다.
일반적으로, 투자를 할 때 자산군을 나눠 투자를 하는 것이 전체 포트폴리오 가치의 분산을 낮추고, 서로 다른 성격의 자산덕분에 여러 기회를 잃지 않는다는 점에서 권장된다. 여기서 시도해볼 것은 가격의 변동이 있는 주식과 현금을 나눠 투자하고, 가치가 변할 때마다 리밸런싱을 하는 상황을 가정한다.
순서는 다음과 같다.
1. 최초에 10,000만큼 자산을 보유하고 있고, 주식:현금 을 설정(여기서는 5:5)한다.
2. 주식 가격의 변동이 발생하고, 그 가격변동에 따른 자산의 변동이 발생한다.
3. 변동된 자산에 기존 설정한 비율에 맞춰 주식을 매도/매수하여 현금의 증감이 발생한다.
4. 시간 기준이 바뀌면, 2-3과정을 반복한다.
위 알고리즘에서 가장 핵심적인 2-3에 대해 살펴보자면, 다음과 같다.
먼저, 주식 가격의 변동부터 만들어 주자. 가격 변동 폭을 만들어주기 위해서 여러 방법에 대해 고민했는데, 지난 20년간 매일 수익의 변화가 정규분포에 가까운 모양임을 감안해 수익률의 변동을 np.random.normal을 이용해 만들어주기로 결정했다. (아마도 그이상의 시계열로 놓고 보더라도 비슷한 결과가 나오리라 생각된다.)
최초(0년차)부터 30년 후인 (30년차)까지 값을 조정할 수 있게끔 시계열(time_series)과 주식비중(stock_ratio), 그리고 최초 주식가격을 50으로 설정하는 것은 덤이다.
import numpy as np
import pandas as pd
stock_ratio = 0.5 # 주식비율 설정
time_series = 30 # 시계열 설정
# 수익률의 분포는 정규분포(normal distribution)로 가정
# %를 적용하기 편하게 -1~1 값으로 만들어 주기위해 10으로 나눔
random_return = np.random.normal(size=time_series+1)/10
price = [50] # 최초 주식가격 50으로 설정
for i in range(time_series):
new_price = price[i]*(1+random_return[i])
price.append(new_price)
price_dict = {'Price': price, '(n+1)Return(%)':random_return*100}
df= pd.DataFrame(data = price_dict)
df.head()
위 코드의 결과는 대략적으로 아래 사진처럼 나올 것이고, 실행때마다 전혀 다른 값이 나오는 점은 유의해야 한다.
(수익률의 분포) 참고:
blog.naver.com/seanpark1121/221809077962
20년간 코스피 관련 몇 가지 숫자, 기록들
1 도입주식투자에 관심이 있는 경우 한국에서 태어난 이상 한국기업에 투자를 피하긴 어렵습니다. 물론 ...
blog.naver.com
초기 자산인주가의 변동이 발생하고 다음이 조금 어려울 수 있는데, 이는 하나씩 설명하기보다 유기적으로 연결되는 부분이라 한꺼번에 기술한다. 각 데이터들에 대한 설명부터 하자.
assets = [ 내 자산의 가치(asset) ] = 현금 + 주식계좌
stocks = [ 계좌에 있는 주식수 ]
stock_accnt = [ 내 주식 계좌의 가치 ] = stocks * price
cash = [ 현금 보유량 ] = asset - stocks * price
현재(당기) [i+1]
전기 [i]
각각 필요한 설명은 코드 밑에 주석을 첨부하였다. 그리고 결과는 아래와 같이 잘 나오고 있음을 확인할 수 있다. 다행스럽게도 전반적인 자산은 + 를 기록했음을 확인할 수 있다.
# 초기값 조건
initial_assets = 10000
initial_stocks = math.floor(initial_assets*stock_ratio/df['Price'].iloc[0])
initial_stock_accnt = initial_stocks * df['Price'].iloc[0]
initial_cash = initial_assets - initial_stock_accnt
# 각 관심있는 정보를 담을 Lists
assets = [initial_assets]
stocks = [initial_stocks]
stock_accnt = [initial_stock_accnt]
cash = [initial_cash]
for i in range(time_series):
delta_price = df['Price'].iloc[i+1]-df['Price'].iloc[i]
delta_asset = delta_price * stocks[i]
present_asset = assets[i] + delta_asset
assets.append(present_asset)
# 주식가격의 변동(delta_price)는 현재[i+1]와 전기[i]의 차이인데,
# 현재 내 자산의 가치는 전기에 가지고 있던 가격변동과 주식의 숫자를 곱한만큼(delta_asset) 변한다.
# 따라서, 당기 자산의 크기는 전기 자산의 크기와 자산의 변동만큼 더해주면 된다.
delta_cash = delta_asset * (1-stock_ratio)
delta_stocks = round(-delta_cash/df['Price'].iloc[i+1],0)
present_stocks = stocks[i] + delta_stocks
stocks.append(present_stocks)
# 위에서 결정된 delta_asset에 현금비중(1-주식비중)을 곱해 현금의 변화량을 측정
# 현금의 변화량에 현재 가격을 나눠 사야/팔아야하는 주식 수(주식의 증감)를 산정
# 주의해야할 것이 현금과 주식의 방향은 반대(즉, 현금이 빠져나간다는 것은 주식에 유입된다는 뜻)
# 이렇게 산정된 주식의 증감(delta_stocks)을 전기에 갖고 있던 주식수에 더해 현재 주식 수 계산
present_stock_accnt = present_stocks * df['Price'].iloc[i+1]
stock_accnt.append(present_stock_accnt)
present_cash = assets[i+1] - present_stock_accnt
cash.append(present_cash)
# 주식계좌가치(stock_accnt)는 현재 주식수와 현재 주식 가격의 곱
# 그리고 남는 현금은 현재 전체 자산에서 현재 주식계좌를 뺀 값
dictionary = {'Stocks': stocks,
'Stock Account' : stock_accnt,
'Cash' : cash,
'Assets': assets}
df_accnt = pd.DataFrame(data = dictionary)
summary = pd.concat([df, df_accnt], axis =1)
summary.tail()
그리고 이 결과를 matplotlib.pyplot으로 시각화하면 다음과 같다.
import matplotlib.pyplot as plt
plt.plot(summary.index, summary['Assets'])
plt.xlabel('Time')
plt.ylabel('Assets')
plt.show()
이를 여러번 시도할 수 있게끔, 전체 코드를 구성하였다. 여기에, 각 시도마다 나오는 계좌전체 수익률을 계산하기 위해 CAGR 계산하는 것만 더 추가했습니다.
# 종합
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
CAGRs = []
time_series = 30
stock_ratio = 0.5
for trial in range(100):
random_return = np.random.normal(size=time_series+1)/10
price = [50]
for i in range(time_series):
new_price = price[i]*(1+random_return[i])
price.append(new_price)
price_dict = {'Price': price, '(n+1)Retrun(%)': random_return*100}
df= pd.DataFrame(data = price_dict)
initial_assets = 10000
initial_stocks = round(initial_assets*stock_ratio/df['Price'].iloc[0],0)
initial_stock_accnt = initial_stocks * df['Price'].iloc[0]
initial_cash = initial_assets - initial_stock_accnt
assets = [initial_assets]
stocks = [initial_stocks]
stock_accnt = [initial_stock_accnt]
cash = [initial_cash]
for i in range(time_series):
delta_price = df['Price'].iloc[i+1]-df['Price'].iloc[i]
delta_asset = delta_price * stocks[i]
present_asset = assets[i] + delta_asset
assets.append(present_asset)
delta_cash = delta_asset * (1-stock_ratio)
delta_stocks = round(-delta_cash/df['Price'].iloc[i+1],0)
present_stocks = stocks[i] + delta_stocks
stocks.append(present_stocks)
present_stock_accnt = present_stocks * df['Price'].iloc[i+1]
stock_accnt.append(present_stock_accnt)
present_cash = assets[i+1] - present_stock_accnt
cash.append(present_cash)
dictionary = {'Stocks': stocks,
'Stock Account' : stock_accnt,
'Cash' : cash,
'Assets': assets}
df_accnt = pd.DataFrame(data = dictionary)
summary = pd.concat([df, df_accnt], axis =1)
plt.plot(summary.index, summary['Assets'])
CAGR = round(((summary['Assets'].iloc[time_series]/summary['Assets'].iloc[0])**(1/time_series)-1)*100, 1)
CAGRs.append(CAGR)
print(CAGRs)
plt.xlabel('Time')
plt.ylabel('Assets')
plt.show()
CAGRs = [-0.7, 0.7, 0.8, 0.4, 0.6, -1.1, -2.1, 0.5, 0.1, -0.2, 0.0, 1.1, 0.5, 0.2, 0.7, 2.3, -0.0, 2.0, 1.3, -0.8, 0.1, -1.6, -0.9, -1.5, -0.9, 1.5, -0.5, -0.4, -0.4, -0.9, -1.1, -0.5, -0.3, -0.8, -2.1, 0.1, -0.3, -1.0, 0.2, -0.5, 0.2, -1.2, -0.4, -1.6, -1.2, 1.1, -1.2, 1.4, -0.3, -0.0, -1.3, -0.0, 0.5, 0.3, -0.6, 0.1, -2.0, 0.8, 1.8, 0.5, -0.0, 1.4, -1.7, -0.7, -0.4, 0.2, 1.3, -0.4, 1.6, -1.4, 0.2, -0.1, 1.8, -0.3, -1.4, -0.5, -0.4, -0.9, -0.8, -1.4, 2.0, 1.3, -1.1, -0.1, -0.9, 1.0, -0.3, 0.9, 0.3, 0.7, -1.9, -0.8, -0.1, 1.4, -2.0, 0.1, 1.2, -0.2, -1.1, -0.3]
이는 거의 횡보(왜냐하면, 0을 중심으로 하는 정규분포를 기반으로 수익률을 계산했기 때문에)인 상황과 배당이 없음을 감안할 필요가 있을 듯하다. 나중에 기회가 되면, 이 결과 중 더 나은 결과가 나오게 된 원인에 대한 분석 포스트도 가능하면 해볼 예정이다.
'Python > Visualization' 카테고리의 다른 글
Matplotlib으로 산점도 그리기 (Scatter plot) (0) | 2024.08.23 |
---|---|
Matplotlib으로 선 그래프(line plot) 그리기 | 보조축, 그래프 여러개 그리는 방법 (0) | 2024.08.23 |
Basemap 위에 국내 발전사업허가 현황 버블차트 그리기 | Matplotlib (0) | 2021.12.20 |
mpl-toolkits.basemap을 활용한 세계 발전소 데이터를 활용한 발전원별, 용량별 현황시각화 | Matplotlib (0) | 2021.11.13 |
파이썬을 활용한 지역별 월전력판매량 시각화하기 | Folium (0) | 2021.09.04 |