-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPython_MinVol_Solver.py
More file actions
119 lines (101 loc) · 4.04 KB
/
Python_MinVol_Solver.py
File metadata and controls
119 lines (101 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
from __future__ import division
import math
import os
import sys
import xlwings as xw
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from numpy.linalg import inv, pinv
from scipy.optimize import minimize
from colorama import Fore, Style, init
init(autoreset=True)
# Find Workbook
# When bundled as a PyInstaller EXE, __file__ doesn't exist — use sys.executable instead
if getattr(sys, 'frozen', False):
current_directory = os.path.dirname(sys.executable)
else:
current_directory = os.path.dirname(os.path.abspath(__file__))
workbook_filename = "PORTFOLIO ALLOCATION MODEL.xlsm"
workbook_path = os.path.join(current_directory, workbook_filename)
wb = xw.Book(workbook_path)
print('\n' "Current Directory:" '\n', current_directory)
print("Workbook Path:" '\n', workbook_path, '\n')
# Define n
ws = wb.sheets["Security Entry"]
n = ws.range("B37").value
n = int(n) # Convert to integer if needed
# Print to verify the value of n
print(Fore.LIGHTYELLOW_EX + "NUMBER OF ASSETS (n):" '\n', n, '\n')
# Define OMEGA
ws_omega = wb.sheets["Matrixes"]
omega_range = ws_omega.range("VarCovarMatrix") # OMEGA matrix data
omega = np.matrix(omega_range.value)
if n < 30:
omega = omega[:n, :n]
display_omega = pd.DataFrame(omega)
display_omega.index = display_omega.index + 1
display_omega.columns = display_omega.columns + 1
print(Fore.LIGHTYELLOW_EX + "OMEGA MATRIX:\n", display_omega)
# Define Empty W Variable
W = [1/n] * n
print(Fore.LIGHTCYAN_EX + "\nInitialised Ws (1/n):")
print(W)
print(Fore.BLUE + "Sum of Initial Ws: ", np.sum(W))
# risk budgeting optimization
def calculate_portfolio_var(W, omega):
# function that calculates portfolio risk
W = np.matrix(W)
return math.sqrt((W*omega*W.T)[0,0])
sigma = calculate_portfolio_var(W, omega)
print(Fore.LIGHTCYAN_EX + "\nInitial Portfolio Volatility: ", sigma)
def calculate_risk_contribution(W, omega):
# function that calculates asset contribution to total risk
W = np.matrix(W)
sigma = np.sqrt(calculate_portfolio_var(W,omega))
if sigma == 0:
return np.zeros_like(W)
# Marginal Risk Contribution
MRC = (omega*W.T)/sigma
# Risk Contribution
RC = np.multiply(MRC,(W.T/sigma))
return RC/sigma**2
rc_values = calculate_risk_contribution(W, omega)
print(Fore.LIGHTCYAN_EX + "\nInitial Risk Contributions:")
for i, rc in enumerate(rc_values):
print(f"RC {i+1}: {rc}")
print(Fore.BLUE + "Sum of Initial RCs: ", np.sum(rc_values))
print()
def min_volatility_objective(x,pars):
# calculate portfolio risk
omega = pars[0] # covariance table
sig_p = np.sqrt(calculate_portfolio_var(x,omega)) # portfolio sigma
asset_RC = calculate_risk_contribution(x,omega)
J = sum(np.square(asset_RC))[0,0] * 1000 # sum of squared error
return sig_p
def min_weight_constraint(x,min_weight):
return x-min_weight
min_weight = 1 / (2 * n)
def total_weight_constraint(x):
return np.sum(x)-1.0
def long_only_constraint(x):
return x
x_t = 0 # your risk budget percent of total portfolio risk (equal risk)
cons = ({'type': 'eq', 'fun': total_weight_constraint}, # Sum of Ws = 1 Constraint
{'type': 'ineq', 'fun': lambda x: min_weight_constraint(x, min_weight)}) # Minimum W Constraint
res = minimize(min_volatility_objective, W, args=[omega,], method='SLSQP',constraints=cons, options={'disp': True, 'ftol': 1e-12})
W_rb = np.asmatrix(res.x)
print(Fore.LIGHTGREEN_EX + "\nOptimised MIN_VOL Ws:")
print(W_rb)
print(Fore.GREEN + "Sum of OPTIMISED MIN_VOL Ws: ", np.sum(W_rb))
erc_sigma = calculate_portfolio_var(W_rb, omega)
print(Fore.LIGHTGREEN_EX + "\nOPTIMISED MIN_VOL Portfolio Volatility: ", erc_sigma)
optimised_rc_values = calculate_risk_contribution(W_rb, omega)
print(Fore.LIGHTGREEN_EX + "\nOPTIMISED MIN_VOL Risk Contributions:")
for i, rc in enumerate(optimised_rc_values):
print(f"RC {i+1}: {rc}")
print(Fore.GREEN + "Sum of Optimised RCs: ", np.sum(optimised_rc_values))
# Plug Optimized MIN_VOL Ws into Excel Doc
W_rb = np.array(W_rb)
ws_portfolio = wb.sheets["Portfolio"]
ws_portfolio.range("MinVol_Ws").options(transpose=True).value = W_rb