"""
    Copyright 2017 Victoria Xie

    This file is part of Macro_Nowcast.

    Risk_Budgeting is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Risk_Budgeting is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
"""

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import minimize

"""
This file constructs a strategic ERP portfolio and compare with an equal weighted portfolio.
"""


# risk budgeting approach optimisation object function
def obj_fun(x, p_cov, rb):
	return np.sum((x*np.dot(p_cov, x)/np.dot(x.transpose(), np.dot(p_cov, x))-rb)**2)


# constraint on sum of weights equal to one
def cons_sum_weight(x):
	return np.sum(x)-1.0


# constraint on weight larger than zero
def cons_long_only_weight(x):
	return x


# calculate risk budgeting portfolio weight give risk budget
def rb_p_weights(asset_rets, rb):
	# number of ARP series
	num_arp = asset_rets.shape[1]
	# covariance matrix of asset returns
	p_cov = asset_rets.cov()
	# initial weights
	w0 = 1.0 * np.ones((num_arp, 1)) / num_arp
	# constraints
	cons = ({'type': 'eq', 'fun': cons_sum_weight}, {'type': 'ineq', 'fun': cons_long_only_weight})
	# portfolio optimisation
	return minimize(obj_fun, w0, args=(p_cov, rb), method='SLSQP', constraints=cons)


if __name__ == "__main__":
	# 1. Load ARP data
	rf_data = pd.read_excel("data/data_rp.xlsx", "RF")  # load daily risk free rate data
	arp_data = pd.read_excel("data/data_rp.xlsx", "RP")  # load daily risk premia data
	rf_data = rf_data[1:]
	arp_data = arp_data[1:]
	rf_data = rf_data.apply(pd.to_numeric)
	arp_data = arp_data.apply(pd.to_numeric)

	# 2. Calculate ARP excess returns
	arp_rets = (np.log(arp_data) - np.log(arp_data.shift(1)))[1:]
	arp_rets = arp_rets.sub(rf_data.squeeze()/252, axis='index')

	# 3. Construct risk budgeting portfolio
	# portfolio dates
	p_dates = arp_rets.index[arp_rets.index >= '2005-01-03']
	# previous month
	pre_mth = 12

	# initialise portfolio weights matrix
	w = pd.DataFrame(index=p_dates, columns=arp_rets.columns)
	# initialise portfolio return matrix
	p_rets = pd.DataFrame(index=p_dates, columns=['Risk Parity'])

	for t in p_dates:

		# construct risk budgeting portfolio and re-balance on monthly basis
		if t.month==pre_mth:
			# keep the same portfolio weights within the month
			w.ix[t] = w.iloc[w.index.get_loc(t)-1]
		else:
			# update the value of the previous month record
			pre_mth = t.month
			# re-balance the portfolio at the start of the month
			w.ix[t] = rb_p_weights(arp_rets[arp_rets.index < t], 1.0/num_arp).x

		# calculate risk budgeting portfolio returns
		p_rets.ix[t] = np.sum(w.ix[t] * arp_rets.ix[t])

	# 4. Construct equal weighted portfolio
	ew_rets = pd.DataFrame(np.sum(1.0*arp_rets[arp_rets.index>=p_dates[0]]/num_arp, axis=1), columns=['Equal Weighted'])

	# 5. Plot the portfolio cumulative returns
	p_cumrets = (p_rets + 1).cumprod()
	ew_cumrets = (ew_rets + 1).cumprod()

	pd.concat([p_cumrets, ew_cumrets], axis=1).plot()
	plt.show()