164 lines
5.7 KiB
Python
164 lines
5.7 KiB
Python
|
|
import os
|
||
|
|
import math
|
||
|
|
import numpy as np
|
||
|
|
import pandas as pd
|
||
|
|
from scipy.stats import norm
|
||
|
|
import matplotlib.pyplot as plt
|
||
|
|
from backend.config import currencies
|
||
|
|
|
||
|
|
file = 'history/AUD_JPY_D.csv'
|
||
|
|
folder = 'history/'
|
||
|
|
|
||
|
|
def monteCarloSimulation(file, n_days, n_iters):
|
||
|
|
"""
|
||
|
|
file: csv file containing at least a 'Close' column with asset prices inside
|
||
|
|
n_days: the number of days to simulate retruns for
|
||
|
|
n_iters: the number of different simulations to run
|
||
|
|
|
||
|
|
returns the price_matrix of the simulation
|
||
|
|
"""
|
||
|
|
# import data
|
||
|
|
df = pd.read_csv(file)
|
||
|
|
prices = df['Close']
|
||
|
|
# set formula's variables
|
||
|
|
log_returns = np.log(1 + prices.pct_change())
|
||
|
|
u = log_returns.mean()
|
||
|
|
var = log_returns.var()
|
||
|
|
drift = u - (0.5 * var)
|
||
|
|
std = log_returns.std()
|
||
|
|
drift = pd.Series(drift)
|
||
|
|
std = pd.Series(std)
|
||
|
|
z = norm.ppf(np.random.rand(n_days, n_iters))
|
||
|
|
# generate future daily returns
|
||
|
|
daily_returns = np.exp(drift.values + std.values * z)
|
||
|
|
p0 = prices.iloc[-1] # last price in our original data is the first price in our simulation
|
||
|
|
price_matrix = np.zeros_like(daily_returns)
|
||
|
|
price_matrix[0] = p0
|
||
|
|
# start the simulation
|
||
|
|
for t in range(1, n_days):
|
||
|
|
price_matrix[t] = price_matrix[t-1] * daily_returns[t]
|
||
|
|
plt.plot(price_matrix)
|
||
|
|
plt.show()
|
||
|
|
|
||
|
|
return price_matrix
|
||
|
|
|
||
|
|
# Brownian motion
|
||
|
|
def Brownian(seed, N):
|
||
|
|
np.random.seed(seed)
|
||
|
|
dt = 1/N # timestep
|
||
|
|
b = np.random.normal(0., 1., int(N)) * np.sqrt(dt) # Brownian normal increments
|
||
|
|
W = np.cumsum(b) #Brownian path
|
||
|
|
return W, b
|
||
|
|
|
||
|
|
# Geometric Brownian Motion
|
||
|
|
def GBM(p0, mu, sigma, W, T, N):
|
||
|
|
t = np.linspace(0., 1., N+1)
|
||
|
|
P = list()
|
||
|
|
P.append(p0)
|
||
|
|
for i in range(1, int(N + 1)):
|
||
|
|
drift = (mu - 0.5 * sigma ** 2) * t[i]
|
||
|
|
diffusion = sigma * W[i-1]
|
||
|
|
p_temp = p0 * np.exp(drift + diffusion)
|
||
|
|
P.append(p_temp)
|
||
|
|
return P, t
|
||
|
|
|
||
|
|
# Euler Maruyama Approximation
|
||
|
|
def eulerApprox(p0, mu, sigma, b, T, N, M):
|
||
|
|
dt = M * (1/N) # step size
|
||
|
|
L = N / M
|
||
|
|
wi = [p0]
|
||
|
|
for i in range(0, int(L)):
|
||
|
|
Winc = np.sum(b[(M * (i - 1) + M) : (M * i + M)])
|
||
|
|
w_i_new = wi[i] + mu * wi[i] * dt + sigma * wi[i] * Winc
|
||
|
|
wi.append(w_i_new)
|
||
|
|
return wi, dt
|
||
|
|
|
||
|
|
# plot these stochastic compared to a given asset
|
||
|
|
def plot_stochastics(file, seed1, seed2, seed3):
|
||
|
|
df = pd.read_csv(file)
|
||
|
|
n = len(df) - 1
|
||
|
|
returns = df['Close'].pct_change()
|
||
|
|
p0 = df['Close'][n]
|
||
|
|
N = 2.0 ** 6
|
||
|
|
W1 = Brownian(seed1, N)[0]
|
||
|
|
W2 = Brownian(seed2, N)[0]
|
||
|
|
W3 = Brownian(seed3, N)[0]
|
||
|
|
T = 1.0
|
||
|
|
mu = np.mean(returns) * 252.0
|
||
|
|
sigma = np.std(returns) * np.sqrt(252.0)
|
||
|
|
|
||
|
|
gbm1 = GBM(p0, mu, sigma, W1, T, N)[0]
|
||
|
|
gbm2 = GBM(p0, mu, sigma, W2, T, N)[0]
|
||
|
|
gbm3 = GBM(p0, mu, sigma, W3, T, N)[0]
|
||
|
|
t = GBM(p0, mu, sigma, W1, T, N)[1]
|
||
|
|
|
||
|
|
plt.plot(t, gbm1, label='GMB1', ls='--')
|
||
|
|
plt.plot(t, gbm2, label='GBM2', ls='--')
|
||
|
|
plt.plot(t, gbm3, label='GBM3', ls='--')
|
||
|
|
plt.plot(t, df['Close'][-65:], label='Actual')
|
||
|
|
plt.ylabel('Asset Price, $')
|
||
|
|
plt.title('Geometric Brownian Motion')
|
||
|
|
plt.legend(loc='upper left')
|
||
|
|
plt.show()
|
||
|
|
|
||
|
|
# Markowitz Portfolio optimization, efficient frontier, MPT, etc.
|
||
|
|
def optimalPortfolio(asset_list, n_portfolios=2500):
|
||
|
|
"""
|
||
|
|
asset_list: list of asset symbols to extract
|
||
|
|
type: CSV or GET; CSV from local file or GET from yahoo
|
||
|
|
"""
|
||
|
|
data = pd.DataFrame()
|
||
|
|
# get asset pricing from local data
|
||
|
|
if type == 'csv':
|
||
|
|
for asset in asset_list:
|
||
|
|
folder = 'history/'
|
||
|
|
name_window = '%s_D' % asset
|
||
|
|
for files in os.walk(folder):
|
||
|
|
for f in files[2]:
|
||
|
|
if name_window in f:
|
||
|
|
f = os.path.join(folder, f)
|
||
|
|
df = pd.read_csv(f)
|
||
|
|
data[asset] = df['Close']
|
||
|
|
elif type == 'GET':
|
||
|
|
for asset in asset_list:
|
||
|
|
s = stock(asset).chart_table(range='1y')
|
||
|
|
data[asset] = s['close']
|
||
|
|
|
||
|
|
# convert prices to returns and calculate daily mean returns and covariance
|
||
|
|
returns = data.pct_change()
|
||
|
|
n = returns.shape[1]
|
||
|
|
mean_daily_returns = returns.mean()
|
||
|
|
cov_matrix = returns.cov()
|
||
|
|
# set array to hold results
|
||
|
|
results = np.zeros((3+n, n_portfolios))
|
||
|
|
# calculate the n portfolios
|
||
|
|
for i in range(n_portfolios):
|
||
|
|
# set random weights; rebalance weights to sum 1
|
||
|
|
weights = np.random.random(n)
|
||
|
|
weights /= np.sum(weights)
|
||
|
|
# calculate portfolio return and volatility
|
||
|
|
port_return = np.sum(mean_daily_returns * weights) * 252
|
||
|
|
port_std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights))) * np.sqrt(252)
|
||
|
|
# store results and calculate/store Sharpe ratio
|
||
|
|
results[0,i] = port_return
|
||
|
|
results[1,i] = port_std
|
||
|
|
results[2,i] = results[0,i] / results[1,i]
|
||
|
|
# iterate through weight vector and add weights to the results
|
||
|
|
for j in range(len(weights)):
|
||
|
|
results[j+3,i] = weights[j]
|
||
|
|
# plot results
|
||
|
|
cols = ['Returns', 'Std', 'Sharpe'] + asset_list
|
||
|
|
results_frame = pd.DataFrame(results.T, columns=cols)
|
||
|
|
max_sharpe_port = results_frame.iloc[results_frame['Sharpe'].idxmax()]
|
||
|
|
min_vol_port = results_frame.iloc[results_frame['Std'].idxmin()]
|
||
|
|
plt.scatter(results_frame.Std, results_frame.Returns, c=results_frame.Sharpe, cmap='RdYlBu')
|
||
|
|
plt.colorbar()
|
||
|
|
plt.ylabel('Returns')
|
||
|
|
plt.xlabel('Volatility')
|
||
|
|
plt.title('Portfolio optimization test for %d portfolios with %d assets' % (n_portfolios, n))
|
||
|
|
plt.scatter(max_sharpe_port[1], max_sharpe_port[0], marker=(5,1,0), color='r', s=1000)
|
||
|
|
plt.scatter(min_vol_port[1], min_vol_port[0], marker=(5,1,0), color='g', s=1000)
|
||
|
|
plt.show()
|
||
|
|
print("Portfolio weights with the best returns: ", max_sharpe_port)
|
||
|
|
print("Portfolio weights with the lowest volatility: ", min_vol_port)
|