├── __init__.py ├── alm ├── __init__.py └── Balance_Sheets.py ├── asset ├── __init__.py ├── bond │ └── __init__.py ├── call_BS_function.py ├── Asset_data.py └── test_Assets.py ├── esg ├── __init__.py ├── credit_risk │ ├── __init__.py │ ├── JLT.py │ └── credit_model_classes.py ├── model_equity │ ├── __init__.py │ ├── EQ_model_classes.py │ └── GBM_constant_volatility.py ├── interest_rate │ ├── __init__.py │ ├── IR_model_functions.py │ ├── IR_model_const.py │ ├── IR_model_util.py │ ├── IR_model_classes.py │ └── Hull_White_one_factor.py ├── test_generator_correlated_variables.py ├── generator_correlated_variables.py ├── SM_main.py ├── test_ESG_RN.py ├── Smith_Wilson.py ├── test_Smith_Wilson.py └── ESG_main.py ├── core_math ├── __init__.py ├── __pycache__ │ └── functions_credit.cpython-35.pyc ├── function.py ├── test_functions_credit.py ├── test_function_optim.py ├── excel_toolbox.py ├── functions_credit.py ├── credit_pickles.py └── function_optim.py ├── liability ├── __init__.py ├── liability_data │ ├── __init__.py │ ├── Liabilities_data_m.py │ └── Liabilities_data.py ├── liability_main.py ├── Technical_Provision.py ├── Model_Point.py └── Liabilities.py ├── market_environment ├── __init__.py └── Market_Environment.py ├── loss_function_calculator ├── __init__.py └── loss_function_main.py ├── internal_model ├── pca_test.xlsx ├── test_PCA.py ├── PCA_main.py └── PCA.py ├── __pycache__ ├── hello.cpython-35.pyc ├── init.cpython-35.pyc └── __init__.cpython-35.pyc ├── RunTestBench.py └── README.md /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /alm/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /asset/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /esg/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /asset/bond/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core_math/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /liability/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /esg/credit_risk/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /esg/model_equity/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /esg/interest_rate/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /market_environment/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /liability/liability_data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /loss_function_calculator/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /internal_model/pca_test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalm/ActuarialCashFlowModel/HEAD/internal_model/pca_test.xlsx -------------------------------------------------------------------------------- /__pycache__/hello.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalm/ActuarialCashFlowModel/HEAD/__pycache__/hello.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/init.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalm/ActuarialCashFlowModel/HEAD/__pycache__/init.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalm/ActuarialCashFlowModel/HEAD/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /core_math/__pycache__/functions_credit.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbalm/ActuarialCashFlowModel/HEAD/core_math/__pycache__/functions_credit.cpython-35.pyc -------------------------------------------------------------------------------- /core_math/function.py: -------------------------------------------------------------------------------- 1 | ## Python packages 2 | import numpy as np 3 | import math 4 | 5 | def f(x, *data): 6 | """ 7 | data = (PM_init, PM_in, PM_out, richesse_max) 8 | """ 9 | PM_init, PM_in, PM_out, richesse_max = data 10 | return PM_init*(1+x)+(PM_in - PM_out)*np.sqrt(1+x) - (PM_init + PM_in - PM_out) - richesse_max 11 | 12 | def solve_quadratic_equation(a, b, c): 13 | x = ((-1)* b - math.sqrt(b**2-4*a*c))/2*a 14 | y = ((-1)* b + math.sqrt(b**2-4*a*c))/2*a 15 | return x, y -------------------------------------------------------------------------------- /esg/interest_rate/IR_model_functions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def interpolate1(l): 4 | out = [] 5 | if l is not list: 6 | temp = list(l) 7 | if len(temp) == 1: 8 | pass 9 | else: 10 | for i in range(len(temp)-1): 11 | out.append(temp[i]) 12 | out.append((temp[i+1] + temp[i])/2) 13 | out.append(temp[-1]) 14 | return np.asarray(out) 15 | 16 | def diff(y, x): 17 | n = len(y) 18 | d = np.zeros(n-1,'d') 19 | 20 | for i in range(n-1): 21 | d[i] = (y[i+1] - y[i])/(x[i+1] - x[i]) 22 | return d 23 | -------------------------------------------------------------------------------- /liability/liability_main.py: -------------------------------------------------------------------------------- 1 | from .liability_data.Liabilities_data import Liabilities_data 2 | from .liability_data.Liabilities_data_m import Liabilities_data_m 3 | import pickle 4 | 5 | Working_URL = r'Feuille_de_calcul_ALM(Working).xlsm' 6 | 7 | def Liabilities_data_update(): 8 | liabilities_data = Liabilities_data_m() 9 | liabilities_data.update(Working_URL) 10 | with open(r'data\pickle\Liabilities_data1.pkl', 'wb') as output: 11 | pickle.dump(liabilities_data, output, pickle.HIGHEST_PROTOCOL) 12 | 13 | def Liabilities_data_test_update(): 14 | liabilities_data = Liabilities_data() 15 | liabilities_data.update(Working_URL) 16 | with open(r'data\pickle\Liabilities_data0.pkl', 'wb') as output: 17 | pickle.dump(liabilities_data, output, pickle.HIGHEST_PROTOCOL) 18 | -------------------------------------------------------------------------------- /RunTestBench.py: -------------------------------------------------------------------------------- 1 | #obj = TestStringMethods() 2 | #obj.test_full_module() 3 | from os import path, sys 4 | BASE_DIR = path.dirname(path.dirname(path.dirname(__file__))) # ands 5 | sys.path.append(BASE_DIR) 6 | 7 | if __name__ == "__main__": 8 | from .code.core_math.test.test_functions_credit import test_functions_credit 9 | print(BASE_DIR) 10 | test_functions_credit.test_full() 11 | 12 | 13 | 14 | 15 | #command = "python .core_math.test.test_functions_credit.py" 16 | #proc = subprocess.Popen(command,stdout=subprocess.PIPE,shell=True) 17 | #(out, err) = proc.communicate() 18 | #outwithoutreturn = out.rstrip('\n') 19 | #print(out) 20 | #execfile("python .core_math.test.test_functions_credit.py") 21 | #a = os.system("python .core_math.test.test_functions_credit.py") 22 | #print(a) 23 | #exec(open("core_math/test/test_functions_credit.py").read()) 24 | # 25 | #obj = TestStringMethods() 26 | # obj.test_fact() -------------------------------------------------------------------------------- /esg/model_equity/EQ_model_classes.py: -------------------------------------------------------------------------------- 1 | ## Python packages 2 | from abc import ABCMeta, abstractmethod, abstractproperty 3 | 4 | ######Original abstract class 5 | class EQ_model_base: 6 | """This class is the purely abstract class for all IR models we will implement in the future 7 | see https://pymotw.com/2/abc/""" 8 | 9 | __metaclass__=ABCMeta 10 | 11 | @abstractmethod 12 | def add_time_horizon(self, time_horizon): 13 | """This method implements the time horizon for EQ model""" 14 | return 15 | 16 | @abstractmethod 17 | def get_EQ_prices(self): 18 | """This method returns equity prices per time steps and trajectories""" 19 | return 20 | 21 | @abstractmethod 22 | def add_dividend_rate(self, dividend_rate): 23 | """This method implements the dividend rate per time steps and/or trajectories""" 24 | return 25 | 26 | @abstractmethod 27 | def calibrate(self, asset_data): 28 | """Calibrate the IR model onto the market environment""" 29 | return 30 | 31 | 32 | @abstractmethod 33 | def add_short_rate_trajectory(self, short_rate_trajectory): 34 | """This method implements the short rate trajectory""" 35 | return -------------------------------------------------------------------------------- /esg/interest_rate/IR_model_const.py: -------------------------------------------------------------------------------- 1 | ## Python packages 2 | import numpy as np 3 | from .IR_model_util import IR_model_util 4 | 5 | # ============================================== 6 | # Constant short rate model 7 | # ============================================== 8 | class IR_model_const(IR_model_util): 9 | 10 | def __init__(self): 11 | self.time_horizon = 0 # Time horizon for trajectory. 12 | #self.number_trajectories = 1 # By defaut, the number of trajectories is equal to one. 13 | 14 | def calibrate(self, asset_data): 15 | """Load base IR curve from the initial market_environment""" 16 | spot_rate = asset_data.get_list_market('EUR').spot_rate 17 | deflator = np.power(1./np.add(1,spot_rate), np.arange(1, len(spot_rate)+1)) 18 | self.curves = np.zeros(shape = (self.time_horizon+1, len(spot_rate))) 19 | self.deflators = np.zeros(shape = (self.time_horizon+1, len(spot_rate))) 20 | for time_step in range(self.time_horizon+1): 21 | self.curves[time_step,:] = spot_rate 22 | self.deflators[time_step,:] = deflator 23 | 24 | def get_IR_curve(self): 25 | self.trajectory = np.ones(self.time_horizon+1) 26 | return self.curves 27 | 28 | def get_deflator(self): 29 | return self.deflators 30 | -------------------------------------------------------------------------------- /internal_model/test_PCA.py: -------------------------------------------------------------------------------- 1 | # Python Programs 2 | from . import PCA 3 | ## Python packages 4 | from sklearn.decomposition import PCA as PCA_skl 5 | import numpy as np 6 | import unittest 7 | import xlwings as xw 8 | 9 | class TestPCAFunctions(unittest.TestCase): 10 | 11 | def test_pca_swap_rate(self, file='code\internal_model\pca_test.xlsx'): 12 | ## Input 13 | swap_rates, ignore = PCA.read_swap_rate(file) 14 | swap_rates = np.asarray(swap_rates) 15 | pca = PCA_skl(n_components=6) 16 | pca.fit(swap_rates) 17 | explained_variance1 = np.asarray(pca.explained_variance_ratio_) 18 | 19 | # Test 20 | eigval, eigvec, a1, a2, a3, alpha_min, alpha_max, beta_min, beta_max, gamma_min, gamma_max, ignore = PCA.pca_swap_rate(file_name = file) 21 | explained_variance2 = eigval/np.sum(eigval) 22 | identical = np.array_equal(np.round(explained_variance1,3), np.round(explained_variance2,3)) 23 | self.assertTrue(identical) 24 | 25 | def Test_PCAFunctions(): 26 | suite = unittest.TestLoader().loadTestsFromTestCase(TestPCAFunctions) 27 | a = unittest.TextTestRunner(verbosity=0).run(suite) 28 | xw.sheets['Testing'].range('K3').clear_contents() 29 | xw.sheets['Testing'].range('K3').value = str(a) 30 | 31 | -------------------------------------------------------------------------------- /alm/Balance_Sheets.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Dec 18 15:05:48 2015 4 | 5 | @author: Quang Dien DUONG 6 | """ 7 | 8 | class Balance_Sheets(object): 9 | """ 10 | DOCUMENTATION WILL BE COMPLETED LATER 11 | """ 12 | def __init__(self): 13 | self.assets = {} 14 | self.liabilities = {} 15 | self.cash_flows_in = {} 16 | self.cash_flows_out = {} 17 | 18 | def update(self, key_lv1, key_lv2, value): 19 | if key_lv1 == 'assets': 20 | self.assets[key_lv2] = value 21 | elif key_lv1 == 'liabilities': 22 | self.liabilities[key_lv2] = value 23 | elif key_lv1 == 'cash_flows_in': 24 | self.cash_flows_in[key_lv2] = value 25 | elif key_lv1 == 'cash_flows_out': 26 | self.cash_flows_out[key_lv2] = value 27 | else: 28 | raise ValueError("Could not find ", key_lv1) 29 | 30 | def get_value(self, key_lv1, key_lv2): 31 | if key_lv1 == 'assets': 32 | resu = self.assets[key_lv2] 33 | elif key_lv1 == 'liabilities': 34 | resu = self.liabilities[key_lv2] 35 | elif key_lv1 == 'cash_flows_in': 36 | resu = self.cash_flows_in[key_lv2] 37 | elif key_lv1 == 'cash_flows_out': 38 | resu = self.cash_flows_out[key_lv2] 39 | else: 40 | raise ValueError("Could not find ", key_lv1) 41 | return resu 42 | 43 | def get_cash_flow(self): 44 | return sum(self.cash_flows_in[key] for key in self.cash_flows_in.keys()) - sum(self.cash_flows_out[key] for key in self.cash_flows_out.keys()) 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ActuarialCashFlowModel 2 | Actuarial cash flow model 3 | 4 | ALM kernel instructions : 5 | 6 | 1. If this is the first time you pull the code from the repo, please double-click on the file CIFRE-hooks/RUN_ME.bat 7 | 8 | 2. The repo has two branches : master and dev. 9 | 2.1. master is the production branch which contains the latest working copy of the code. You must not work and commit on that branch. 10 | 2.2. dev is the development branch. You can perform your modification and commit to that branch.They will be later merged with the production branch by the administrator (see with Quang Dien or Jean-Baptiste) 11 | 12 | 3. Install Anaconda on your machine : 13 | 3.1. the executable can be found here https://3230d63b5fc54e62148e-c95ac804525aac4b6dba79b00b39d1d3.ssl.cf1.rackcdn.com/Anaconda3-2.4.0-Windows-x86_64.exe 14 | 3.2. once installed please update anaconda : open a command window and type "conda update conda" 15 | 16 | 4. Install Python 3.5: 17 | 4.1. the executable can be found here https://www.python.org/downloads/release/python-350/ 18 | 4.2. make sure that the dialog box "install python launcher" is ticked 19 | 4.3. make sure that the dialog box "modify python path" is left unticked 20 | 21 | 5. Install Git on your machine 22 | 5.1. the executable can be found here https://git-scm.com/download/win 23 | 24 | 6. If you don't know about Git, please take a Git tour and read the documents in the folder Git_guide 25 | 26 | 7. Before your start, set you name and email address into Git. To that end you can use the following commend within the Git bash : 27 | git config --global user.name "John Doe" 28 | git config --global user.email "John.Doe@fr.pwc.com" 29 | 30 | 8. Pour éditer le code python, utiliser le logiciel "Spyder" qui est livré avec anaconda 31 | 32 | -------------------------------------------------------------------------------- /asset/call_BS_function.py: -------------------------------------------------------------------------------- 1 | ## Python packages 2 | from math import log, sqrt, exp 3 | 4 | 5 | 6 | def f(x,y,a): 7 | return exp(-a*(x-y)) 8 | 9 | def integrand(x, a, theta): 10 | return sum( 0.5 * (f(x,y+1,a) * theta[y+1] + f(x,y,a) * theta[y]) for y in range(x)) 11 | 12 | def double_integration(time_horizon, a, theta): 13 | return sum( 0.5 * (integrand(x+1, a, theta) + integrand(x, a, theta)) for x in range(time_horizon + 1)) 14 | 15 | def upper_bounds(sigma_s, sigma_r, time_horizon, a, theta, S0, K, r0, div_yield = 0, rho = 0): 16 | T = time_horizon 17 | d = div_yield 18 | dblintegra = double_integration(time_horizon = T, a = a, theta = theta) 19 | sigma_11 = T * sigma_s ** 2 20 | sigma_12 = (rho * sigma_s * sigma_r/a) * (T - (1 - exp(-a * T))/a) 21 | sigma_22 = (sigma_r**2)/(a**2) * (T - 2 * (1 - exp(-a*T))/a + 0.5 * (1 - exp(-2*a*T))/a) 22 | D = sigma_11 + 2*sigma_12 + sigma_22 23 | C = - log((S0 * exp(-d*T))/K) + 0.5 * (sigma_s**2) * T - r0 * (1 - exp(-a*T))/a - dblintegra 24 | d1 = (sigma_11 + sigma_12 - C)/(sqrt(D)) 25 | d2 = d1 - sqrt(D) 26 | return d1, d2 27 | 28 | 29 | 30 | #if __name__ == '__main__': 31 | # # Initialize and calibrate IR model 32 | # path = 'Market_Environment.xls' 33 | # data = Asset_data() 34 | # data.update(path) 35 | # Interest_rate = IR_model.Hull_White_one_factor(market_name = 'EUR') 36 | # Interest_rate.calibrate(data) 37 | # # Parameters 38 | # a = Interest_rate.a 39 | # time_horizon = 10 40 | # theta = Interest_rate.theta 41 | # S0 = 1. 42 | # K = 1. 43 | # r0 = 0.01 44 | # sigma_s = 0.2 45 | # sigma_r = 0.01 46 | # # Test double_integration function 47 | # resu = double_integration(time_horizon, a, theta) 48 | # print(resu) 49 | # # Test upper_bounds function 50 | # d1, d2 = upper_bounds(sigma_s = sigma_s, sigma_r = sigma_r, time_horizon = time_horizon, a = a, theta = theta, S0 = S0, K = K, r0 = r0) 51 | # print("d1 = ",d1) 52 | # print("d2 = ",d2) -------------------------------------------------------------------------------- /esg/test_generator_correlated_variables.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.linalg import eigh, cholesky 3 | from pylab import plot, show, axis, subplot, xlabel, ylabel, grid 4 | from .generator_correlated_variables import generator_correlated_variables 5 | import matplotlib.pyplot as plt 6 | import xlwings as xw 7 | 8 | def test_generator_correlated_variables(): 9 | corr_matrix = np.array([ 10 | [ 1., 0.2, 0.9], 11 | [ 0.2, 1., 0.5], 12 | [ 0.9, 0.5, 1.] 13 | ]) 14 | time_horizon = 1000 15 | fixed_seed = 1000 16 | dw = generator_correlated_variables(corr_matrix = corr_matrix, time_horizon = time_horizon, fixed_seed = fixed_seed) 17 | 18 | fig = plt.figure() 19 | fig.set_size_inches(3,2.5) 20 | plt.plot(dw[0], dw[1], 'b.') 21 | plt.xlabel('dw[0]') 22 | plt.ylabel('dw[1]') 23 | plt.legend() 24 | xw.sheets['Testing'].range('G34').clear_contents() 25 | xw.sheets['Testing'].pictures.add(fig, name='graph221', 26 | left = xw.sheets['Testing'].range('G34').left, top = xw.sheets['Testing'].range('G34').top, update=True) 27 | 28 | fig = plt.figure() 29 | fig.set_size_inches(3,2.5) 30 | plt.plot(dw[0], dw[2], 'b.') 31 | plt.xlabel('dw[0]') 32 | plt.ylabel('dw[2]') 33 | plt.legend() 34 | xw.sheets['Testing'].range('K34').clear_contents() 35 | xw.sheets['Testing'].pictures.add(fig, name='graph223', 36 | left = xw.sheets['Testing'].range('K34').left, top = xw.sheets['Testing'].range('K34').top, update=True) 37 | 38 | fig = plt.figure() 39 | fig.set_size_inches(3,2.5) 40 | plt.plot(dw[1], dw[2], 'b.') 41 | plt.xlabel('dw[1]') 42 | plt.ylabel('dw[2]') 43 | plt.legend() 44 | xw.sheets['Testing'].range('L34').clear_contents() 45 | xw.sheets['Testing'].pictures.add(fig, name='graph224', 46 | left = xw.sheets['Testing'].range('L34').left, top = xw.sheets['Testing'].range('L34').top, update=True) -------------------------------------------------------------------------------- /esg/interest_rate/IR_model_util.py: -------------------------------------------------------------------------------- 1 | ## Program packages 2 | from .IR_model_classes import IR_model 3 | ## Python packages 4 | from math import exp 5 | import numpy as np 6 | 7 | 8 | class IR_model_util(IR_model): 9 | """ 10 | Document the role of this class TO DO 11 | """ 12 | def __init__(self): 13 | pass 14 | 15 | @staticmethod 16 | def swap_rate_and_annuity(maturity, tenor, valuation_date, zcb_price): 17 | return (zcb_price[valuation_date, maturity - 1] - zcb_price[valuation_date, tenor + maturity -1])/(sum(zcb_price[valuation_date, t] for t in range(maturity, maturity+tenor))), sum(zcb_price[valuation_date, t] for t in range(maturity, maturity+tenor)) 18 | 19 | @staticmethod 20 | def DF(trajectory, begin, end): 21 | v = [trajectory[t] for t in range(begin, end)] 22 | return exp(-sum(v)) 23 | 24 | @staticmethod 25 | def discount_factor_custom(trajectory, begin, end): 26 | v = [(trajectory[t] + trajectory[t+1])/2 for t in range(begin, end)] 27 | return exp(-sum( v)) 28 | 29 | def discount_factors(self, trajectory): 30 | return [self.discount_factor_custom(trajectory, 0, t) for t in range(1, len(trajectory))] 31 | 32 | def test_martingale(self): 33 | """ 34 | This method should be put somewhere else TO DO 35 | """ 36 | for time_step in range(1, self.time_horizon): 37 | discount_factor = [] 38 | epsilon = [] 39 | i = 1 40 | while i <= self.N: 41 | self.get_IR_curve() 42 | discount_factor.append(self.discount_factor(begin = 0, end = time_step)) 43 | i += 1 44 | epsilon.append(np.absolute(np.mean(discount_factor) - self.zcb_price[0,time_step])) 45 | return np.amax(epsilon) 46 | 47 | def get_IR_curve(self): 48 | """This method returns IR term structures per time steps and trajectories""" 49 | pass 50 | 51 | def get_deflator(self): 52 | """This method returns deflators per time steps and trajectories""" 53 | pass 54 | 55 | def calibrate(self, asset_data): 56 | """Calibrate the IR model onto the market environment 57 | This method returns the IR_curve and deflator""" 58 | pass 59 | -------------------------------------------------------------------------------- /esg/generator_correlated_variables.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.linalg import eigh, cholesky 3 | from pylab import plot, show, axis, subplot, xlabel, ylabel, grid 4 | 5 | #from pylab import plot, show, axis, subplot, xlabel, ylabel, grid 6 | 7 | def generator_correlated_variables(corr_matrix, time_horizon, fixed_seed, method = 'cholesky'): 8 | np.random.seed(fixed_seed) 9 | ran = np.random.standard_normal((len(corr_matrix), time_horizon)) 10 | 11 | if method == 'cholesky': 12 | c = cholesky(corr_matrix, lower = True) 13 | else: 14 | evals, evecs = eigh(corr_matrix) 15 | c = np.dot(evecs, np.diag(np.sqrt(evals))) 16 | dw = np.dot(c, ran) 17 | return dw 18 | 19 | # =========================== 20 | # Test function 21 | # =========================== 22 | #if __name__ == '__main__': 23 | # # ============================= 24 | # # Declare the correlated matrix 25 | # # ============================= 26 | # #corr_matrix = np.array([ 27 | # # [ 3.40, -2.75, -2.00], 28 | # # [ -2.75, 5.50, 1.50], 29 | # # [ -2.00, 1.50, 1.25] 30 | # # ]) 31 | # corr_matrix = np.array([ 32 | # [ 1., 0.2, 0.9], 33 | # [ 0.2, 1., 0.5], 34 | # [ 0.9, 0.5, 1.] 35 | # ]) 36 | # # ============================== 37 | # # Declare time_horizon 38 | # # ============================== 39 | # time_horizon = 1000 40 | # # ============================== 41 | # # Declare fixed_seed 42 | # # ============================== 43 | # fixed_seed = 1000 44 | # # ============================== 45 | # # Declare num_instrument 46 | # # ============================== 47 | # dw = generator_correlated_variables(corr_matrix = corr_matrix, time_horizon = time_horizon, fixed_seed = fixed_seed) 48 | # 49 | # # 50 | # # Plot les projections variées. 51 | # # 52 | # subplot(2,2,1) 53 | # plot(dw[0], dw[1], 'b.') 54 | # ylabel('dw[1]') 55 | # axis('equal') 56 | # grid(True) 57 | # 58 | # subplot(2,2,3) 59 | # plot(dw[0], dw[2], 'b.') 60 | # xlabel('dw[0]') 61 | # ylabel('dw[2]') 62 | # axis('equal') 63 | # grid(True) 64 | # 65 | # subplot(2,2,4) 66 | # plot(dw[1], dw[2], 'b.') 67 | # xlabel('dw[1]') 68 | # axis('equal') 69 | # grid(True) 70 | # 71 | # show() 72 | # 73 | # 74 | # 75 | -------------------------------------------------------------------------------- /esg/interest_rate/IR_model_classes.py: -------------------------------------------------------------------------------- 1 | ## Python packages 2 | from abc import ABCMeta, abstractmethod, abstractproperty 3 | 4 | class IR_model: 5 | """This class is the purely abstract class for all IR models we will implement in the future 6 | see https://pymotw.com/2/abc/""" 7 | 8 | __metaclass__=ABCMeta 9 | 10 | @abstractmethod 11 | def get_IR_curve(self): 12 | """This method returns IR term structures per time steps and trajectories""" 13 | return 14 | 15 | @abstractmethod 16 | def get_deflator(self): 17 | """This method returns deflators per time steps and trajectories""" 18 | return 19 | 20 | @abstractmethod 21 | def calibrate(self, asset_data): 22 | """Calibrate the IR model onto the market environment 23 | This method returns the IR_curve and deflator""" 24 | return 25 | 26 | 27 | 28 | 29 | 30 | #if __name__ == '__main__': 31 | # Working_URL = r'C:\Users\FR011526\Documents\ALM_credit(working)\Feuille_de_calcul_ALM(Working).xlsm' 32 | # 33 | # data = Asset_data() 34 | # data.update(Working_URL) 35 | # market = data.get_list_market('EUR') 36 | # # =========================== 37 | # # test Hull_White_one_factor 38 | # # =========================== 39 | # time_horizon = int(xw.sheets['ESG'].range('D3').value) 40 | # corr_matrix = market.corr_matrix 41 | # 42 | # Interest_rate = Hull_White_one_factor(market_name = market.name) 43 | # Interest_rate.time_horizon = time_horizon 44 | # Interest_rate.corr_matrix = corr_matrix 45 | # # ============================== 46 | # # Declare num_instrument 47 | # # ============================== 48 | # Interest_rate.calibrate(asset_data = data) 49 | # #Interest_rate.get_IR_curve() 50 | # #Interest_rate.get_deflator() 51 | # #plt.plot(range(Interest_rate.time_horizon), Interest_rate.curves[0][:Interest_rate.time_horizon]) 52 | # #plt.title("Hull & White zero coupon cuvre at t = 0") 53 | # num_traj = 1 54 | # fixed_seed = [2016 + i for i in range(num_traj)] 55 | # for traj_i in range(num_traj): 56 | # Interest_rate.fixed_seed = fixed_seed[traj_i] 57 | # Interest_rate.get_IR_curve() 58 | # Interest_rate.get_deflator() 59 | # plt.plot(range(Interest_rate.time_horizon), Interest_rate.spot_rate[:Interest_rate.time_horizon]) 60 | # 61 | # plt.xlabel('Maturity (in years)') 62 | # plt.ylabel('Rate') 63 | # plt.title('Evolution des taux courts at t = 0') 64 | # plt.show() -------------------------------------------------------------------------------- /esg/SM_main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 25 22:57:32 2016 4 | 5 | @author: Quang Dien DUONG 6 | """ 7 | 8 | ## Progam packages 9 | from .Smith_Wilson import Smith_Wilson 10 | ## Python packages 11 | import matplotlib.pyplot as plt 12 | import pickle 13 | import xlwings as xw 14 | 15 | Working_URL = r'Feuille_de_calcul_ALM(Working).xlsm' 16 | 17 | @xw.func 18 | def SM_extrapolation(save_data = True): 19 | sm = Smith_Wilson(file_name = Working_URL) 20 | # ========================================================= 21 | # get time horizon 22 | end_time = int(xw.sheets['Smith Wilson extrapolation'].range('C3').value) 23 | # ========================================================= 24 | # get time step 25 | s = int(xw.sheets['Smith Wilson extrapolation'].range('C9').value) 26 | sm.get_time_step(s) 27 | # ========================================================= 28 | # get UFR 29 | UFR = xw.sheets['Smith Wilson extrapolation'].range('C5').value/100 30 | sm.get_UFR(UFR) 31 | # ========================================================= 32 | # get alpha 33 | alpha = xw.sheets['Smith Wilson extrapolation'].range('C7').value 34 | sm.get_alpha(alpha) 35 | # ========================================================= 36 | sm.SM_extrapolation(end_time = end_time) 37 | # ============================================================================================================ 38 | # Plot ZCB_Price 39 | # ============================================================================================================ 40 | fig = plt.figure() 41 | plt.plot(range(1, len(sm.swap_rate_lists['Spot_Rates'])+1), sm.swap_rate_lists['Spot_Rates'], label = 'Extrapolated Zero Coupon Yield') 42 | plt.plot(range(1, len(sm.swap_rate_lists['Forward_Rates'])+1), sm.swap_rate_lists['Forward_Rates'], '--', label = 'Extrapolated Forward Rates') 43 | plt.legend() 44 | xw.sheets['Smith Wilson extrapolation'].range('J5').clear_contents() 45 | xw.sheets['Smith Wilson extrapolation'].pictures.add(fig, name='Extrapolated Zero Coupon Yield', 46 | left=xw.sheets['Swap Rates'].range('J5').left, top=xw.sheets['Swap Rates'].range('J5').top, 47 | update=True) 48 | # ============================================================================================================ 49 | # Affich PC1, PC2, PC3 50 | xw.sheets['Loss Function'].range('B5').value = sm.PC1 51 | xw.sheets['Loss Function'].range('D5').value = sm.PC2 52 | xw.sheets['Loss Function'].range('F5').value = sm.PC3 53 | if save_data: 54 | with open(r'data\pickle\SM_extra_data.pkl', 'wb') as output: 55 | pickle.dump(sm, output, pickle.HIGHEST_PROTOCOL) -------------------------------------------------------------------------------- /core_math/test_functions_credit.py: -------------------------------------------------------------------------------- 1 | # Python Programs 2 | from . import functions_credit 3 | #from ..core_math.excel_toolbox import excel_toolbox as et 4 | ## Python packages 5 | import numpy as np 6 | import unittest 7 | import pickle 8 | import xlwings as xw 9 | 10 | class TestFunctionCredit(unittest.TestCase): 11 | 12 | def test_fact(self): 13 | self.assertEqual(functions_credit.fact(1),1) 14 | self.assertEqual(functions_credit.fact(10),3628800) 15 | 16 | def test_diag_matrix(self): 17 | ### Input functions 18 | mat_A = np.matrix('2,-1,0; -1,0,2; -1,-2,4') 19 | # Execute function 20 | P,D,P_inv = functions_credit.diag_matrix(mat_A) 21 | res_A = np.dot(np.dot(P,D),P_inv) 22 | # Test 23 | identical_mat_A = np.array_equal(np.array(res_A.round(decimals=2)), np.array(mat_A)) 24 | self.assertTrue(identical_mat_A) 25 | 26 | def test_generator_matrix(self): 27 | # Input data 28 | with open('data\pickle\historical_transition_matrix.pkl', 'rb') as input: 29 | historical_transition_matrix = pickle.load(input) 30 | 31 | ### Execute function 32 | generator= functions_credit.generator_matrix(historical_transition_matrix) 33 | 34 | ### test output 35 | generator_expected = np.matrix('-6.21514196e-02, 6.20507783e-02, 4.15425424e-06, 5.86161767e-06, 5.00878176e-06, 2.76352434e-05, 1.09027149e-04, 5.21000537e-05; 2.25176281e-02, -1.12883379e-01, 8.13383698e-02, 1.89613619e-03, 3.20815086e-03, 2.37667333e-03, 2.51094053e-04, 1.29428837e-03; 1.69557137e-02, 3.75325542e-02, -1.13742915e-01, 4.94417097e-02, 3.72963067e-03, 4.22646616e-03, 0.00000000e+00, 1.85620504e-03; 1.62612024e-02, 1.59140165e-02, 5.77992721e-02, -1.30890913e-01, 2.37662606e-02, 5.96790526e-03, 7.53905945e-03, 3.64225885e-03; 0.00000000e+00, 5.62895560e-03, 1.18148693e-02, 7.76577797e-02, -1.99508524e-01, 8.19527762e-02, 5.64595570e-03, 1.68123276e-02; 0.00000000e+00, 0.00000000e+00, 1.37694066e-02, 1.49271265e-02, 7.58396271e-02, -2.23577332e-01, 7.43893517e-02, 4.45360632e-02; 0.00000000e+00, 4.00406374e-05, 1.02522921e-02, 1.58101388e-02, 2.97984279e-02, 1.12971607e-01,-3.19110235e-01, 1.50360743e-01;0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00') 36 | 37 | identical = np.array_equal(np.array(generator.round(decimals=5)), np.array(generator_expected.round(decimals=5))) 38 | self.assertTrue(identical) 39 | 40 | def Test_FunctionsCredit(): 41 | suite = unittest.TestLoader().loadTestsFromTestCase(TestFunctionCredit) 42 | a = unittest.TextTestRunner(verbosity=0).run(suite) 43 | xw.sheets['Testing'].range('E2').clear_contents() 44 | xw.sheets['Testing'].range('E2').value = str(a) 45 | 46 | -------------------------------------------------------------------------------- /liability/Technical_Provision.py: -------------------------------------------------------------------------------- 1 | class Technical_Provision(object): 2 | """ 3 | Objective: 4 | ========== 5 | This class provides a general framework in order to estimate the technical provision at each time steps. 6 | 7 | Attributes 8 | ========== 9 | 10 | 1. mathematical_provision: 11 | Type: array 12 | Function: corresponding list of mathematical provison values over time horizon 13 | 14 | 2. liquidity_risk_provision: 15 | Type: array 16 | Function: corresponding list of liquidity risk provision values over time horizon 17 | 18 | 3. profit_sharing_reserve: 19 | Type: array 20 | Function: corresponding list of profit sharing reserve values over time horizon 21 | 22 | 4. financial_risk_provision: 23 | Type: array 24 | Function: corresponding list of financial risk provision values over time horizon 25 | 26 | Methods 27 | ======= 28 | 29 | 1. update : 30 | update parameters if they are given before or if we want to customize their initial value. 31 | By default, all the attributes are empty. 32 | 33 | We just call the update() method once right after the object is initialized. 34 | """ 35 | def __init__(self): 36 | self.mathematical_provision = [] 37 | self.liquidity_risk_provision = [] 38 | self.profit_sharing_reserve = [] 39 | self.financial_risk_provision = [] 40 | def update(self, mathematical_provision = None, liquidity_risk_provision = None, profit_sharing_reserve = None, financial_risk_provision = None): 41 | """ 42 | Method: update 43 | 44 | Function: update their initial values if they are given. By default, all these parameters are None 45 | 46 | Parameters: 47 | 1. mathematical_provision: 48 | Type: float (positive only) 49 | Function: PM 50 | 2. liquidity_risk_provision: 51 | Type: float (positive only) 52 | Function: PRE 53 | 3. profit_sharing_reserve: 54 | Type: float (positive only) 55 | Function: PPE 56 | 4. financial_risk_provision: 57 | Type: float (positive only) 58 | Function: PAF 59 | """ 60 | if mathematical_provision is not None: 61 | self.mathematical_provision.append(mathematical_provision) 62 | if liquidity_risk_provision is not None: 63 | self.liquidity_risk_provision.append(liquidity_risk_provision) 64 | if profit_sharing_reserve is not None: 65 | self.profit_sharing_reserve.append(profit_sharing_reserve) 66 | if financial_risk_provision is not None: 67 | self.financial_risk_provision.append(financial_risk_provision) 68 | 69 | -------------------------------------------------------------------------------- /core_math/test_function_optim.py: -------------------------------------------------------------------------------- 1 | # Python Programs 2 | from . import functions_credit 3 | from . import function_optim 4 | ## Python packages 5 | import numpy as np 6 | import unittest 7 | import pickle 8 | from scipy.optimize import minimize 9 | import numpy.linalg as la 10 | import xlwings as xw 11 | import matplotlib.pyplot as plt 12 | 13 | 14 | 15 | def test_function_spread(): 16 | mu=5 17 | alpha=0.1 18 | sigma=0.75 19 | recovery_rate=0.35 20 | 21 | with open('data\pickle\historical_transition_matrix.pkl', 'rb') as input: 22 | historical_transition_matrix = pickle.load(input) 23 | 24 | historical_generator_matrix = functions_credit.generator_matrix(historical_transition_matrix) 25 | 26 | w, v = la.eig(historical_generator_matrix) 27 | eigenval_hist_gen = w.real 28 | eigenvect_hist_gen = (v.T).real 29 | for l in range(len(eigenvect_hist_gen)): 30 | eigenvect_hist_gen[l] = eigenvect_hist_gen[l]/la.norm(eigenvect_hist_gen[l]) 31 | 32 | eigenvect_hist_gen = eigenvect_hist_gen.T 33 | 34 | with open('data\pickle\spread.pkl', 'rb') as input: 35 | spread_list = pickle.load(input) 36 | col_index = pickle.load(input) 37 | row_index = pickle.load(input) 38 | 39 | AAA_AA=True 40 | 41 | def f_penal(v): 42 | return function_optim.function_optim(v[0], v[1], v[2], v[3], recovery_rate, eigenvect_hist_gen, eigenval_hist_gen, row_index, col_index, spread_list, AAA_AA ) 43 | + 1000 *(v[1] - 0.1)**2 + 1000 * (v[2] - 5 )**2 + 100* (v[3] -0.75)**2 44 | 45 | bdss = [(0.001,None), (0.01, 5), (1,20), (0.01,1)] 46 | res_penal = minimize(f_penal, x0=[3, 0.1, 5, 0.75], bounds = bdss) 47 | 48 | pi_0 = res_penal.x[0] 49 | alpha =res_penal.x[1] 50 | mu = res_penal.x[2] 51 | sigma = res_penal.x[3] 52 | 53 | spread = function_optim.function_spread(pi_0, alpha,mu, sigma, recovery_rate, eigenvect_hist_gen, eigenval_hist_gen) 54 | 55 | fig = plt.figure() 56 | fig.set_size_inches(6,5) 57 | plt.plot(range(20), np.asarray(spread)[:,0], label = 'spreads AAA') 58 | plt.plot(range(20), np.asarray(spread)[:,1], label = 'spreads AA') 59 | plt.plot(range(20), np.asarray(spread)[:,2], label = 'spreads A') 60 | plt.plot(range(20), np.asarray(spread)[:,3], label = 'spreads BBB') 61 | plt.plot(range(20), np.asarray(spread)[:,4], label = 'spreads BB') 62 | plt.plot(range(20), np.asarray(spread)[:,5], label = 'spreads B') 63 | plt.plot(range(20), np.asarray(spread)[:,6], label = 'spreads CCC') 64 | plt.xlabel('Time (in years)', fontsize=18) 65 | plt.ylabel('spreads', fontsize=16) 66 | plt.legend() 67 | xw.sheets['Testing'].range('E4').clear_contents() 68 | xw.sheets['Testing'].pictures.add(fig, name='Spread Calibré', 69 | left = xw.sheets['Testing'].range('E4').left, top = xw.sheets['Testing'].range('E4').top, update=True) 70 | def Test_FunctionsOptim(): 71 | test_function_spread() 72 | 73 | if __name__ == '__main__': 74 | unittest.main() 75 | -------------------------------------------------------------------------------- /internal_model/PCA_main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 25 22:57:32 2016 4 | 5 | @author: Quang Dien DUONG 6 | """ 7 | 8 | ### Progam packages 9 | from .PCA import pca_swap_rate 10 | from ..core_math import excel_toolbox as et 11 | ### Python packages 12 | import matplotlib.pyplot as plt 13 | import pickle 14 | import xlwings as xw 15 | 16 | 17 | 18 | Working_URL = r'Feuille_de_calcul_ALM(Working).xlsm' 19 | 20 | @xw.func 21 | def PCA_swap(): 22 | # ==================================================================================== 23 | # VL1, VL2, VL3 correspond respectively to the first, second and third vector loadings 24 | # ==================================================================================== 25 | eigenvalues, eigenvectors, VL1, VL2, VL3, alpha_min, alpha_max, beta_min, beta_max, gamma_min, gamma_max, maturities = pca_swap_rate(Working_URL) 26 | with open(r'data\pickle\pca_swap.pkl', 'wb') as output: 27 | pickle.dump(VL1, output, pickle.HIGHEST_PROTOCOL) 28 | pickle.dump(VL2, output, pickle.HIGHEST_PROTOCOL) 29 | pickle.dump(VL3, output, pickle.HIGHEST_PROTOCOL) 30 | pickle.dump(alpha_min, output, pickle.HIGHEST_PROTOCOL) 31 | pickle.dump(alpha_max, output, pickle.HIGHEST_PROTOCOL) 32 | pickle.dump(beta_min, output, pickle.HIGHEST_PROTOCOL) 33 | pickle.dump(beta_max, output, pickle.HIGHEST_PROTOCOL) 34 | pickle.dump(gamma_min, output, pickle.HIGHEST_PROTOCOL) 35 | pickle.dump(gamma_max, output, pickle.HIGHEST_PROTOCOL) 36 | et.dump_array(VL1, filename = r'data\pickle\First_vector_loading.csv') 37 | et.dump_array(VL2, filename = r'data\pickle\Second_vector_loading.csv') 38 | et.dump_array(VL3, filename = r'data\pickle\Third_vector_loading.csv') 39 | # ============================================================================================================ 40 | # Affich alpha_max, alpha_min, beta_max, beta_min, gamma_max, gamma_min 41 | xw.sheets['Swap Rates'].range('M54').value = alpha_max 42 | xw.sheets['Swap Rates'].range('M56').value = alpha_min 43 | xw.sheets['Swap Rates'].range('P54').value = beta_max 44 | xw.sheets['Swap Rates'].range('P56').value = beta_min 45 | xw.sheets['Swap Rates'].range('S54').value = gamma_max 46 | xw.sheets['Swap Rates'].range('S56').value = gamma_min 47 | # ============================================================================================================ 48 | # Plot VL1, VL2, VL3 49 | # ============================================================================================================ 50 | fig = plt.figure() 51 | plt.plot(maturities, VL1, label = 'Level') 52 | plt.plot(maturities, VL2, label = 'Slope') 53 | plt.plot(maturities, VL3, label = 'Curvature') 54 | plt.legend() 55 | xw.sheets['Swap Rates'].range('K8').clear_contents() 56 | xw.sheets['Swap Rates'].pictures.add(fig, name='Principal Component Analysis of the Swap Curve', 57 | left = xw.sheets['Swap Rates'].range('K8').left, top = xw.sheets['Swap Rates'].range('K8').top, update=True) -------------------------------------------------------------------------------- /core_math/excel_toolbox.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Dec 1 16:58:38 2015 4 | 5 | @author: FR007967 6 | 7 | 8 | This script is meant to encapsulate a number of functions which allow to easily load and dump data within excel spreadsheets 9 | """ 10 | 11 | ## Python packages 12 | import xlrd as xl 13 | import xlwt as xw 14 | import numpy as np 15 | 16 | def dump_array(array, filename = 'dump.csv'): 17 | """ 18 | Function : dump array within a spreadsheet 19 | 20 | Parameters : 21 | 1. array : 22 | Type : array 23 | Function : array to dump within an excel spreadsheet 24 | 2. filename : 25 | Type : string 26 | Function : name of the file used to dump the array 27 | """ 28 | np.savetxt(filename, array, delimiter=';', fmt='%1.5f') 29 | print('FILE {} SAVED'.format(filename)) 30 | 31 | def get_balance_sheet(file_name, list_data = None): 32 | pass 33 | 34 | def output_excel(file_name, dictionary): 35 | # ================================== 36 | # Create a Workbook 37 | # ================================== 38 | book = xw.Workbook() 39 | sheet = book.add_sheet("sheet_1") 40 | 41 | len_max = 0 42 | for key in dictionary.keys(): 43 | if len(key) > len_max: 44 | len_max = len(key) 45 | for subkey in dictionary[key].keys(): 46 | if len(subkey) > len_max: 47 | len_max = len(key) 48 | 49 | style1 = xw.easyxf('font: bold 1, color red;') 50 | style2 = xw.easyxf('font: bold 1, color blue;') 51 | sheet.col(0).width = 256 * (2 + len_max) 52 | 53 | n = 0 54 | for key in dictionary.keys(): 55 | sheet.write(n, 0, key, style1) 56 | n += 1 57 | for subkey in dictionary[key].keys(): 58 | sheet.write(n, 0, subkey, style2) 59 | for m in range(1, len(dictionary[key][subkey])+1): 60 | sheet.write(n, m, dictionary[key][subkey][m-1]) 61 | n += 1 62 | 63 | book.save(file_name) 64 | 65 | 66 | def WriteDicttoExcel(file_name, obj): 67 | book = xw.Workbook() 68 | sheet = book.add_sheet("Main") 69 | len_max = 0 70 | for key in obj.keys(): 71 | if len(key) > len_max: 72 | len_max = len(key) 73 | sheet.col(0).width = 256 * (2 + len_max) 74 | row = 0 75 | for key in obj.keys(): 76 | sheet.write(row, 0, key) 77 | for col in range(1, len(obj[key])+1): 78 | sheet.write(row, col, obj[key][col-1]) 79 | row += 1 80 | book.save(file_name) 81 | 82 | def Write2DListtoExcel(file_name, obj): 83 | book = xw.Workbook() 84 | sheet = book.add_sheet("Main") 85 | row = 0 86 | for l in obj: 87 | for col in range(len(l)): 88 | sheet.write(row, col, l[col]) 89 | row += 1 90 | book.save(file_name) 91 | 92 | # 93 | #if __name__ == '__main__': 94 | # #a = np.array([1,2,3]) 95 | # #dump_array(a) 96 | # filename1 = 'test_output_function.xls' 97 | # l = np.ones(52) 98 | # list_data = [] 99 | # list_data.append(l) 100 | # # Output 101 | # filename2 = 'test_output_excel_function.xls' 102 | # sub_dict_1 = collections.OrderedDict() 103 | # sub_dict_1['subkey 1_1'] = l 104 | # sub_dict_2 = collections.OrderedDict() 105 | # sub_dict_2['subkey 2_1'] = l 106 | # sub_dict_2['subkey 2_2'] = l 107 | # sub_dict_3 = collections.OrderedDict() 108 | # sub_dict_3['subkey 3_1'] = l 109 | # sub_dict_3['subkey 3_2'] = l 110 | # sub_dict_3['subkey 3_1'] = l 111 | # dictionary = collections.OrderedDict() 112 | # dictionary['key_lv_1'] = sub_dict_1 113 | # dictionary['key_lv_2'] = sub_dict_2 114 | # dictionary['key_lv_3'] = sub_dict_3 115 | # output_excel(file_name = filename2, dictionary = dictionary) -------------------------------------------------------------------------------- /liability/liability_data/Liabilities_data_m.py: -------------------------------------------------------------------------------- 1 | ## Progam packages 2 | from ..Model_Point import Model_Point 3 | ## Python packages 4 | import datetime as dt 5 | from xlrd import open_workbook 6 | import xlrd 7 | import numpy as np 8 | 9 | class Liabilities_data_m(object): 10 | def __init__(self): 11 | self.model_points = [] 12 | 13 | def get_TMG_dict(self, path, sheet_name = 'TMG_table'): 14 | # Initialize TMG dictionary 15 | self.TMG_dict ={} 16 | wb = xlrd.open_workbook(path) 17 | sheet = wb.sheet_by_name(sheet_name) 18 | number_of_rows = sheet.nrows 19 | number_of_columns = sheet.ncols 20 | for row in range(1, number_of_rows): 21 | TMG_name = str(sheet.cell(row, 0).value) 22 | TMG_list = [] 23 | for col in range(1, number_of_columns): 24 | TMG_list.append(sheet.cell(row,col).value) 25 | self.TMG_dict[TMG_name] = TMG_list 26 | 27 | def get_surrender_rate_dict(self, path, sheet_name = 'Lapse_table'): 28 | # Initialize surrender dictionary 29 | self.surrender_dict ={} 30 | wb = xlrd.open_workbook(path) 31 | sheet = wb.sheet_by_name(sheet_name) 32 | number_of_rows = sheet.nrows 33 | number_of_columns = sheet.ncols 34 | for row in range(1, number_of_rows): 35 | prod_name = str(sheet.cell(row, 0).value) 36 | surrender_list = [] 37 | for col in range(1, number_of_columns): 38 | surrender_list.append(sheet.cell(row,col).value) 39 | self.surrender_dict[prod_name] = np.concatenate((surrender_list, [surrender_list[-1] for t in range(100)]), axis = 0) 40 | 41 | def get_mortality_rate_dict(self, path, sheet_name = 'Mortality_table'): 42 | # Initialize mortality rate dictionary 43 | self.mortality_dict = {} 44 | wb = xlrd.open_workbook(path) 45 | sheet = wb.sheet_by_name(sheet_name) 46 | number_of_rows = sheet.nrows 47 | number_of_cols = sheet.ncols 48 | for col in range(1, number_of_cols): 49 | morta_list = [] 50 | sexe = str(sheet.cell(0,col).value) 51 | for row in range(1, number_of_rows): 52 | morta_list.append(sheet.cell(row,col).value) 53 | self.mortality_dict[sexe] = np.concatenate((morta_list,[morta_list[-1] for t in range(100)]), axis = 0) 54 | 55 | def update(self, path, sheet_name = 'Model_Points'): 56 | # Retrieve TMG dictionary and Surrender Rate dictionary 57 | self.get_TMG_dict(path) 58 | self.get_surrender_rate_dict(path) 59 | self.get_mortality_rate_dict(path) 60 | # Retrieve Model Points data 61 | wb = xlrd.open_workbook(path) 62 | sheet = wb.sheet_by_name(sheet_name) 63 | number_of_rows = sheet.nrows 64 | number_of_columns = sheet.ncols 65 | for row in range(1, number_of_rows): 66 | mp_data = [] 67 | for col in range(number_of_columns): 68 | cell = sheet.cell(row, col) 69 | if cell.ctype == 3: 70 | year, month, day, hour, minute, second = xlrd.xldate_as_tuple(cell.value,wb.datemode) 71 | value = dt.datetime(year,month,day) 72 | else: 73 | value = cell.value 74 | mp_data.append(value) 75 | mp = Model_Point() 76 | mp.update(*mp_data) 77 | mp.get_seniority() 78 | mp.mathematical_provision.append(mp.actual_math_provision) 79 | mp.TMG = self.TMG_dict[mp.TMG_type] 80 | mp.lapse_rate = self.surrender_dict[mp.lapse_type] 81 | mp.mortality_rate = self.mortality_dict[mp.sexe] 82 | self.model_points.append(mp) 83 | 84 | def affiche(self): 85 | for mdl_point in self.model_points: 86 | print(mdl_point) -------------------------------------------------------------------------------- /core_math/functions_credit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 13 14:30:31 2016 4 | 5 | @author: Jun JING 6 | """ 7 | ## Python packages 8 | import numpy as np 9 | 10 | 11 | def fact(n): 12 | """fact(n): compute the factorial of n (int >= 0)""" 13 | if n<2: 14 | return 1 15 | else: 16 | return n*fact(n-1) 17 | 18 | def exp_matrix(mat): 19 | """exp_matrix(): compute the exponential of a matrix""" 20 | prod=np.identity(len(mat)) 21 | som=np.identity(len(mat)) 22 | 23 | for k in range(50): 24 | prod=np.dot(prod,mat) 25 | som=som+prod/fact(k+1) 26 | 27 | return som 28 | 29 | def diag_matrix(mat): 30 | """ diag_matrix() : Matrix Diagonalization """ 31 | M=np.linalg.eig(mat)[1] 32 | D=np.diag(np.linalg.eig(mat)[0]) 33 | M_inv=np.linalg.inv(M) 34 | 35 | return M,D,M_inv 36 | 37 | 38 | def generator_matrix(mat): 39 | """generator_matrix() : compute the generator matrix from a transition matrix 40 | 41 | """ 42 | prod=np.identity(len(mat)) 43 | gen=np.identity(len(mat))-np.identity(len(mat)) 44 | 45 | for k in range(50): 46 | prod=np.dot(prod,(mat-np.identity(len(mat)))) 47 | 48 | gen=gen+prod/(k+1)*(-1)**(k+2) 49 | 50 | # vérifier les conditions pour être un bon generator matrix 51 | for k in range(len(gen)): 52 | for l in range(len(gen)): 53 | if l!=k and gen[k][l]<=0: 54 | gen[k][k]=gen[k][k]+gen[k][l] 55 | gen[k][l]=0 56 | 57 | 58 | return gen 59 | 60 | 61 | # 62 | #if __name__ == '__main__': 63 | # 64 | # Id=np.array([ 65 | # [1,0,0], 66 | # [0,1,0], 67 | # [0,0,1] 68 | # ]) 69 | # 70 | # gamma=np.array([ 71 | # [-0.1,0.1,0], 72 | # [0,0,0], 73 | # [0,0,0] 74 | # ]) 75 | # 76 | # gamma2=np.array([ 77 | # [0,0,0], 78 | # [1/11,-1/11,0], 79 | # [0,0,0] 80 | # ]) 81 | # 82 | # gamma3=np.array([ 83 | # [0,0,0], 84 | # [0,-0.1,0.1], 85 | # [0,0,0] 86 | # ]) 87 | # 88 | # gamma_hat=np.array([ 89 | # [-0.10084,0.10084,0], 90 | # [0.10909,-0.21818,0.10909], 91 | # [0,0,0] 92 | # ]) 93 | # 94 | # P_hat= np.dot(np.dot(Id+gamma,Id+gamma2),Id+gamma3) 95 | # # cas non homogène 96 | # P_hat_hom=exp_matrix(gamma_hat) 97 | # # cas homogène 98 | # 99 | # # Transition matrix 100 | # P=np.array([ 101 | # [0.9,0.08,0.017,0.003], 102 | # [0.05,0.85,0.09,0.01], 103 | # [0.01,0.09,0.8,0.1], 104 | # [0,0,0,1] 105 | # ]) 106 | # 107 | # # recovery rate 108 | # phi=0.35 109 | # 110 | # A,B,C=diag_matrix(P) 111 | # 112 | # 113 | # # zero coupon risky bond 114 | # v=np.array([0.5,0.75,0.6]) 115 | # # zero coupon risk free bond 116 | # B=np.array([0.8]*len(v)) 117 | # 118 | # q_tilde_to_Default=(B-v)/(B*(1-phi)) 119 | # 120 | # # q_tilde_to_Default=np.array([0.006,0.03,0.2]) 121 | # 122 | # p_to_Default=P[range(len(P)-1),3] 123 | # 124 | # # calcution of vector pi 125 | # pi=q_tilde_to_Default/p_to_Default 126 | # 127 | # 128 | # 129 | # # now, we fill the matrix of risk neutral probabilities 130 | # Q_tilde=np.eye(len(P)) 131 | # Q_tilde[range(len(P)-1),3]=q_tilde_to_Default 132 | # for i in range(len(P)): 133 | # for j in range(i+1,len(P)): 134 | # Q_tilde[i,j]=pi[i]*P[i,j] 135 | # Q_tilde[i,i]=1-np.sum(Q_tilde[i, range(i+1,len(P))]) 136 | # 137 | # 138 | # with open('historical_transition_matrix.pkl', 'rb') as input: 139 | # historical_transition_matrix = pickle.load(input) 140 | # 141 | # generator= generator_matrix(historical_transition_matrix) 142 | # print("generator matrix is", generator) 143 | # et.dump_array(generator, filename = 'generator.csv') -------------------------------------------------------------------------------- /esg/test_ESG_RN.py: -------------------------------------------------------------------------------- 1 | # Python Programs 2 | from ..asset.Asset_data import Asset_data 3 | from .model_equity.EQ_model_classes import EQ_model_base as EQ_model 4 | from .interest_rate.IR_model_classes import IR_model as IR_model 5 | from .credit_risk.credit_model_classes import credit_model_base as credit_model 6 | from .ESG_RN import ESG_RN 7 | from .ESG_main import * 8 | 9 | ## Python packages 10 | #import unittest 11 | import xlwings as xw 12 | import matplotlib.pyplot as plt 13 | from xlrd import open_workbook 14 | import pandas as pd 15 | 16 | def test_test_martingale_EQ(): 17 | temp_mk_name = xw.sheets['ESG'].range('D5').value 18 | temp_time_horizon = xw.sheets['ESG'].range('D3').value 19 | temp_number_trajectories = xw.sheets['ESG'].range('D1').value 20 | temp_volatilité_EQ = xw.sheets['Market_Environment'].range('C7').value 21 | 22 | xw.sheets['ESG'].range('D5').value = "EUR" 23 | xw.sheets['ESG'].range('D3').value = 50 24 | xw.sheets['ESG'].range('D1').value = 1 25 | xw.sheets['Market_Environment'].range('C7').value = 0 26 | 27 | ESG_calibrate() 28 | ESG_generate_scenarios(modif = False) 29 | ESG_test_martingale_EQ() 30 | 31 | df = pd.read_excel('data\Test_martingale_EQ.xls') 32 | df = df.as_matrix() 33 | 34 | fig = plt.figure() 35 | fig.set_size_inches(6,5) 36 | plt.plot(range(1, 50), df[0,], label = "Borne supérieure de l'IC à 95%") 37 | plt.plot(range(1, 50), df[2,], label = "Borne inférieure de l'IC à 95%") 38 | plt.plot(range(1, 50), df[4,], label = "Moyenne de MC actualisée") 39 | plt.xlabel('maturity') 40 | plt.ylabel('test de martingalité', fontsize=16) 41 | plt.legend() 42 | 43 | # Restore previous values 44 | xw.sheets['ESG'].range('D5').value = temp_mk_name 45 | xw.sheets['ESG'].range('D3').value = temp_time_horizon 46 | xw.sheets['ESG'].range('D1').value = temp_number_trajectories 47 | xw.sheets['Market_Environment'].range('C7').value = temp_volatilité_EQ 48 | 49 | # Plot the graph 50 | xw.sheets['Testing'].range('E60').clear_contents() 51 | xw.sheets['Testing'].pictures.add(fig, name='test martingale EQ', 52 | left = xw.sheets['Testing'].range('E60').left, top = xw.sheets['Testing'].range('E60').top, update=True) 53 | 54 | 55 | def test_test_martingale_IR(): 56 | temp_mk_name = xw.sheets['ESG'].range('D5').value 57 | temp_time_horizon = xw.sheets['ESG'].range('D3').value 58 | temp_number_trajectories = xw.sheets['ESG'].range('D1').value 59 | temp_volatilité_IR = xw.sheets['Market_Environment'].range('C4').value 60 | 61 | xw.sheets['ESG'].range('D5').value = "EUR" 62 | xw.sheets['ESG'].range('D3').value = 50 63 | xw.sheets['ESG'].range('D1').value = 1 64 | xw.sheets['Market_Environment'].range('C4').value = 0 65 | 66 | ESG_calibrate() 67 | ESG_generate_scenarios(modif = False) 68 | ESG_test_martingale_IR() 69 | 70 | df = pd.read_excel('data\Test_martingale_IR.xls') 71 | df = df.as_matrix() 72 | 73 | fig = plt.figure() 74 | fig.set_size_inches(6,5) 75 | plt.plot(range(1, 50), df[0,], label = "Borne supérieure de l'IC à 95%") 76 | plt.plot(range(1, 50), df[2,], label = "Borne inférieure de l'IC à 95%") 77 | plt.plot(range(1, 50), df[4,], label = "Prix de Monte Carlo") 78 | plt.xlabel('maturity') 79 | plt.ylabel('test de martingalité', fontsize=16) 80 | plt.legend() 81 | 82 | # Restore previous values 83 | xw.sheets['ESG'].range('D5').value = temp_mk_name 84 | xw.sheets['ESG'].range('D3').value = temp_time_horizon 85 | xw.sheets['ESG'].range('D1').value = temp_number_trajectories 86 | xw.sheets['Market_Environment'].range('C4').value = temp_volatilité_IR 87 | 88 | # Plot the graph 89 | xw.sheets['Testing'].range('E32').clear_contents() 90 | xw.sheets['Testing'].pictures.add(fig, name='test martingale IR', 91 | left = xw.sheets['Testing'].range('E32').left, top = xw.sheets['Testing'].range('E32').top, update=True) 92 | 93 | 94 | -------------------------------------------------------------------------------- /core_math/credit_pickles.py: -------------------------------------------------------------------------------- 1 | ## Python packages 2 | from __future__ import division 3 | from xlrd import open_workbook 4 | import numpy as np 5 | import pickle 6 | 7 | 8 | 9 | def get_historical_transition_matrix(file_name): 10 | """ 11 | Method : get_historical_transition_matrix 12 | Function : get the historical transition matrix from the excel file 13 | Parameter : 14 | 1. file_name 15 | Type : string 16 | Function : the file name 17 | """ 18 | wb = open_workbook(file_name) 19 | sheet = wb.sheet_by_name('Credit_Data') 20 | row_begin = 3 21 | row_end = 11 22 | col_begin = 1 23 | col_end = 9 24 | historical_transition_matrix = [] 25 | 26 | for row in range(row_begin, row_end): 27 | hist_trans_matrix=[] 28 | for col in range(col_begin, col_end): 29 | hist_trans_matrix.append(sheet.cell(row, col).value) 30 | historical_transition_matrix.append(hist_trans_matrix) 31 | 32 | return historical_transition_matrix 33 | 34 | def get_spread(file_name): 35 | """ 36 | Method : get_spread 37 | Function : get the market data of spread from the excel file 38 | Parameter : 39 | 1. file_name 40 | Type : string 41 | Function : the file name 42 | """ 43 | 44 | wb = open_workbook(file_name) 45 | sheet = wb.sheet_by_name('Credit_Data') 46 | 47 | row_begin = 3 48 | row_end = 8 49 | col_begin = 13 50 | col_end = 17 51 | spread_list = [] 52 | 53 | for row in range(row_begin, row_end): 54 | s_list=[] 55 | for col in range(col_begin, col_end): 56 | s_list.append(sheet.cell(row, col).value) 57 | spread_list.append(s_list) 58 | 59 | col_index =[sheet.cell(row_begin-1, col).value for col in range(col_begin, col_end)] 60 | row_index=[sheet.cell(row, col_begin-1).value for row in range(row_begin, row_end)] 61 | return spread_list, col_index, row_index 62 | 63 | 64 | def get_prices(file_name): 65 | wb = open_workbook(file_name) 66 | sheet = wb.sheet_by_name('Credit_Data') 67 | row_begin = 3 68 | col_begin = 22 69 | col_end = 26 70 | number_of_rows = sheet.nrows 71 | 72 | ps_list=[] 73 | for row in range(row_begin, number_of_rows): 74 | p_list=[] 75 | for col in range(col_begin, col_end): 76 | p_list.append(sheet.cell(row, col).value) 77 | ps_list.append(p_list) 78 | 79 | ps_vector = np.asarray(ps_list) 80 | 81 | prix_marche = np.zeros((7,30)) 82 | coupon_marche = np.zeros((7,30)) 83 | for row in range(len(ps_vector)): 84 | rating = int(ps_vector[row,0]) 85 | TtM = int(ps_vector[row,1]) 86 | prix_marche[rating, TtM-1] = ps_vector[row,2]/100 87 | coupon_marche[rating, TtM-1] = ps_vector[row,3]/100 88 | return prix_marche, coupon_marche 89 | 90 | 91 | def saving(): 92 | """ 93 | Method : saving 94 | 95 | Function : save objects like historical transition matrix and spreads (read from excel) to pickle files 96 | 97 | Parameter : None 98 | """ 99 | 100 | historical_transition_matrix = get_historical_transition_matrix() 101 | with open('data\pickle\historical_transition_matrix.pkl', 'wb') as output: 102 | pickle.dump(historical_transition_matrix, output, pickle.HIGHEST_PROTOCOL) 103 | 104 | 105 | spread_list, col_index, row_index = get_spread() 106 | with open('data\pickle\spread.pkl', 'wb') as output: 107 | pickle.dump(spread_list, output, pickle.HIGHEST_PROTOCOL) 108 | pickle.dump(col_index, output, pickle.HIGHEST_PROTOCOL) 109 | pickle.dump(row_index, output, pickle.HIGHEST_PROTOCOL) 110 | 111 | 112 | 113 | prix_marche, coupon_marche = get_prices() 114 | with open('data\pickle\bonds_prices.pkl','wb') as output: 115 | pickle.dump(prix_marche, output, pickle.HIGHEST_PROTOCOL) 116 | pickle.dump(coupon_marche,output, pickle.HIGHEST_PROTOCOL) 117 | 118 | -------------------------------------------------------------------------------- /liability/liability_data/Liabilities_data.py: -------------------------------------------------------------------------------- 1 | ## Progam packages 2 | from ..Model_Point import Model_Point 3 | ## Python packages 4 | import datetime as dt 5 | from xlrd import open_workbook 6 | import xlrd 7 | import numpy as np 8 | import xlwings as xw 9 | 10 | 11 | class Liabilities_data(object): 12 | """ 13 | Objective: 14 | ========== 15 | This class is meant to build up the policyholders database 16 | 17 | Attributes: 18 | =========== 19 | 20 | 1. model_points: 21 | Type: array 22 | Function: collection of the model points characterized by its id. 23 | 24 | Methods: 25 | ======== 26 | 27 | 1. update: 28 | """ 29 | def __init__(self): 30 | self.model_points = [] 31 | 32 | def update(self,path): 33 | """ 34 | Method: update 35 | 36 | Function: updates data from an excel file named "data\Liability_Data.xls". 37 | 38 | Parameter: 39 | 1. path: 40 | Type: string 41 | Function: a single directory or a file name (By default, path = 'data\Market_Environment.xls' and the excel file must be placed in the same folder as the main executed file) 42 | """ 43 | wb2 = open_workbook(path) 44 | sheet = wb2.sheet_by_name("MP_test") 45 | number_of_rows = sheet.nrows 46 | 47 | mdp = Model_Point() 48 | mdp.id = str(xw.sheets['MP_test'].range('B4').value) 49 | mdp.average_age = int(xw.sheets['MP_test'].range('B5').value) 50 | mdp.sexe = str(xw.sheets['MP_test'].range('B6').value) 51 | # ======================================================================================== 52 | # Souscription Date 53 | # ======================================================================================== 54 | assert sheet.cell(6,1).ctype == 3, 'Souscription Date must be datetime type' 55 | ms_date_number = sheet.cell(6,1).value 56 | year, month, day, hour, minute, second = xlrd.xldate_as_tuple(ms_date_number,wb2.datemode) 57 | mdp.subscription_date = dt.datetime(year, month, day) 58 | # ======================================================================================== 59 | # Valuation Date 60 | # ======================================================================================== 61 | assert sheet.cell(7,1).ctype == 3, 'Valuation Date must be datetime type' 62 | ms_date_number = sheet.cell(7,1).value 63 | year, month, day, hour, minute, second = xlrd.xldate_as_tuple(ms_date_number,wb2.datemode) 64 | mdp.valuation_date = dt.datetime(year, month, day) 65 | mdp.get_seniority() 66 | # ======================================================================================= 67 | mdp.premium = xw.sheets['MP_test'].range('B9').value 68 | mdp.actual_math_provision = xw.sheets['MP_test'].range('B10').value 69 | mdp.mathematical_provision.append(mdp.actual_math_provision) 70 | # =============================================================== 71 | # get TMG 72 | mdp.TMG_type = xw.sheets['MP_test'].range('B11').value 73 | mdp.TMG = mdp.TMG_type * np.ones(100) 74 | # =============================================================== 75 | mdp.rate_sensibility = xw.sheets['MP_test'].range('B12').value 76 | mdp.margin_rate = xw.sheets['MP_test'].range('B13').value 77 | mdp.number_contract = xw.sheets['MP_test'].range('B14').value 78 | # =============================================================== 79 | # get lapse rate 80 | mdp.lapse_type = xw.sheets['MP_test'].range('B15').value 81 | mdp.lapse_rate = mdp.lapse_type * np.ones(100) 82 | # =============================================================== 83 | 84 | mortality_rate = [] 85 | for row in range(3, number_of_rows): 86 | mort_rate = (sheet.cell(row, 4).value) 87 | mortality_rate.append(mort_rate) 88 | mdp.mortality_rate = np.concatenate((mortality_rate, [mortality_rate[-1] for t in range(100)]), axis = 0) 89 | 90 | self.model_points.append(mdp) 91 | 92 | def affiche(self): 93 | for mdl_point in self.model_points: 94 | print(mdl_point) 95 | -------------------------------------------------------------------------------- /liability/Model_Point.py: -------------------------------------------------------------------------------- 1 | class Model_Point(object): 2 | """ 3 | Definition: Model Point: 4 | ======================== 5 | The development of a portfolio of policies is modeled by using model points. 6 | Each model point corresponds to an individual policyholder account or 7 | to a pool of similar policyholder aaccounts which can be used to reduce the computational complexity, 8 | in particular in the case of very large insurance portfolios. 9 | 10 | Objective: 11 | ========== 12 | This class provides a general framework for simulating a model point. Each model point objet will be integrated into the portfolio of 13 | policies named "Liability_data0.model_points" (see class Liability_data0.py) 14 | 15 | Attributes: 16 | =========== 17 | 18 | id: string 19 | Model Point's identity. 20 | 21 | average_age: int 22 | Model Point's average age 23 | 24 | subscription_date: datetime (dd/mm/yyyy) 25 | Subscription Date. 26 | 27 | valuation_date: datetime (dd/mm/yyyy) 28 | Valuation Date. 29 | 30 | day_convention: int 31 | Day count convention assumes there are 30 days in a month and 360 days in a year. 32 | 33 | seniority: int 34 | Seniority or the number of years between the valuation date and the subscription date 35 | 36 | premium: float 37 | periodic premium 38 | 39 | mathematical_provision: array 40 | The model point's mathematical provision 41 | 42 | lapse_value: float (positive) 43 | contratual lapse value 44 | 45 | rate_sensibility: float (positive) 46 | 47 | mortality_rate: list/array-like 48 | corresponding list of mortality rate at differents age 49 | 50 | lapse_rate: list/array-like 51 | corresponding list of lapse rate at differents seniority 52 | 53 | TMG : float 54 | Taux minimum garanti 55 | 56 | desired_rate: float 57 | Expected revalorisation rate. 58 | 59 | Method 60 | ====== 61 | 62 | 1. __str__: 63 | 64 | """ 65 | def __init__(self): 66 | self.id = None 67 | self.average_age = None 68 | self.sexe = None 69 | self.subscription_date = None 70 | self.valuation_date = None 71 | self.day_convention = 365. 72 | self.premium = None 73 | self.actual_math_provision = None 74 | self.mathematical_provision = [] 75 | self.profit_sharing_rate = [0] 76 | self.TMG_type = None 77 | self.rate_sensibility = None 78 | self.margin_rate = None 79 | self.number_contract = None 80 | self.lapse_type = None 81 | self.TMG = [] 82 | self.mortality_rate = [] 83 | self.lapse_rate = [] 84 | # Output 85 | self.cash_flow_in = [] 86 | self.cash_flow_out = [] 87 | 88 | def update(self, id, average_age, sexe, subscription_date, valuation_date, premium, actual_math_provision, TMG_type, rate_sensibility, margin_rate, number_contract, lapse_type): 89 | self.id = id 90 | self.average_age = average_age 91 | self.sexe = sexe 92 | self.subscription_date = subscription_date 93 | self.valuation_date = valuation_date 94 | self.premium = premium 95 | self.actual_math_provision = actual_math_provision 96 | self.TMG_type = TMG_type 97 | self.rate_sensibility = rate_sensibility 98 | self.margin_rate = margin_rate 99 | self.number_contract = number_contract 100 | self.lapse_type = lapse_type 101 | 102 | def get_seniority(self): 103 | self.seniority = int((self.valuation_date - self.subscription_date).days/self.day_convention) 104 | 105 | def __str__(self): 106 | """ 107 | Method: __str__ 108 | 109 | Function: Print Model Point's characteristics 110 | 111 | Parameters: None 112 | """ 113 | return("Liability Data: \n" 114 | "\n" 115 | " Model Point ID: {0} \n" 116 | " ================ \n" 117 | " Average Age: {1} \n" 118 | " Sexe: {2} \n" 119 | " Subscription Date: {3} \n" 120 | " Valuation Date: {4} \n" 121 | " Premium: {5} \n" 122 | " Actual mathematical provision: {6} \n" 123 | " TMG_type: {7} \n" 124 | " Rate Sensibility: {8} \n" 125 | " Margin rate: {9} \n" 126 | " Number of Contract: {10} \n" 127 | " Lapse_type: {11} \n" 128 | .format(self.id, self.average_age, self.sexe, self.subscription_date, 129 | self.valuation_date,self.premium, self.actual_math_provision, 130 | self.TMG_type, self.rate_sensibility, self.margin_rate, self.number_contract, self.lapse_type)) 131 | 132 | 133 | -------------------------------------------------------------------------------- /market_environment/Market_Environment.py: -------------------------------------------------------------------------------- 1 | ## Python packages 2 | from xlrd import open_workbook 3 | import matplotlib 4 | import matplotlib.pyplot as plt 5 | 6 | class Market_Environment(object): 7 | """ 8 | Definition: Market Environment 9 | ============================== 10 | Market Environment refers to factors and forces that affect the ability to build and maintain a successful operation of an insurance company. 11 | 12 | Objective: 13 | ========== 14 | This class provides a general framework of a market environment relevant for asset valuation. 15 | 16 | Attributes: 17 | =========== 18 | 19 | 1. name: 20 | Type: string 21 | Function: market environment's name. 22 | 23 | 2. vol_rate: 24 | Type: float (positive only) 25 | Function: constant volativity of the interest rate 26 | 27 | 3. mean_rate: 28 | Type: float (positive only) 29 | Function: constant long term mean level of the interest rate (only for the Vasicek model) 30 | 31 | 4. speed_rate: 32 | Type: float (positive only) 33 | Function: constant speed of reversion. 34 | 35 | 5. init_stock: 36 | Type: float 37 | Function: normalised initial stock value (e.g. init_stock = 1) 38 | 39 | 6. vol_stock: 40 | Type: float (positive only) 41 | Function: constant volativity coefficient in the geometric brownian motion model which is commonly used for modeling in finance 42 | 43 | 7. dividend_rate: 44 | Type: float (positive only) 45 | Function: dividend return rate 46 | 47 | 8. coupon_rate: 48 | Type: float (positive only) 49 | Function: coupon interest rate 50 | 51 | 9. nominal: (This attribute will be deleted in the next version) 52 | Type: float (positive only) 53 | Function: bond's nominal value or face value 54 | 55 | 10. bond_maturity: (This attribute will be deleted in the next version) 56 | Type: int 57 | Function: bond's maturity. 58 | 59 | 11. curve: (This attribute will be deleted in the next version) 60 | Type: list/array-like 61 | Function: current zero-coupon curve if it is given 62 | 63 | 12. time_range: 64 | Type: list/array-like 65 | Function: time range of the current zero-coupon curve 66 | 67 | 13. maturity_range: 68 | Type: list/array-like 69 | Function: time range of the current forward yield curve 70 | 71 | 14. forward_rate: 72 | Type: list/array-like 73 | Function: current forward yield curve 74 | 75 | Method: 76 | ======= 77 | 78 | 1. __str__: 79 | 80 | """ 81 | 82 | def __init__(self): 83 | self.name = None 84 | self.vol_IR_rate = None 85 | self.speed_IR_rate = None 86 | 87 | self.init_stock = None 88 | self.vol_stock = None 89 | self.dividend_rate = None 90 | 91 | self.JLT_mu = None 92 | self.JLT_alpha = None 93 | self.JLT_sigma = None 94 | self.JLT_pi = None 95 | self.recovery_rate = None 96 | 97 | self.spread_list = None 98 | self.prix_marche = None 99 | self.coupon_marche = None 100 | self.spot_rates = [] 101 | self.corr_matrix = [] 102 | self.deflators = [] 103 | # ========================================== 104 | self.col_index = None 105 | self.row_index = None 106 | 107 | # Affiche les infos d'un marché financier 108 | def __str__(self): 109 | """ 110 | Method: __str__ 111 | 112 | Function: Print all the current market environment data 113 | 114 | Parameters: None 115 | """ 116 | return("Market Environment:\n" 117 | " \n" 118 | " Market Name = {0}\n" 119 | " \n" 120 | " Short Rate Parameters\n" 121 | " =====================\n" 122 | " Volativity = {1}\n" 123 | " Speed of Reversion = {2}\n" 124 | " \n" 125 | " Stock Parameters\n" 126 | " ================\n" 127 | " Initial Value = {3}\n" 128 | " Volativity = {4}\n" 129 | " Dividend Rate = {5}\n" 130 | " \n" 131 | " Credit Parameters\n" 132 | " ===============\n" 133 | " mu = {6}\n" 134 | " alpha = {7}\n" 135 | " sigma = {8}\n" 136 | " pi_0 = {9}\n" 137 | " Recovery Rate = {10}\n" 138 | .format(self.name, 139 | self.vol_IR_rate,self.speed_IR_rate, 140 | self.init_stock, self.vol_stock, self.dividend_rate, 141 | self.JLT_mu, self.JLT_alpha, self.JLT_sigma, self.JLT_pi, 142 | self.recovery_rate)) 143 | -------------------------------------------------------------------------------- /esg/Smith_Wilson.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jan 12 14:14:23 2016 4 | 5 | @author: FR011526 6 | """ 7 | #Program packages 8 | from ..core_math import excel_toolbox as et 9 | 10 | ## Python packages 11 | import numpy as np 12 | import collections 13 | from xlrd import open_workbook 14 | import xlrd 15 | import pickle 16 | 17 | 18 | class Smith_Wilson(object): 19 | def __init__(self, file_name, sheet_name = 'Smith Wilson extrapolation', exec=True): 20 | self.UFR_maturity = 59 21 | # ========================================= 22 | # Initialize Swap_Rate_List 23 | self.swap_rate_lists = collections.OrderedDict() 24 | # ========================================= 25 | 26 | wb = open_workbook(file_name) 27 | sheet = wb.sheet_by_name(sheet_name) 28 | 29 | maturities = [] 30 | coupon_yields = [] 31 | 32 | row = 15 33 | while sheet.cell_type(row,1) != xlrd.XL_CELL_EMPTY: 34 | maturities.append(int(sheet.cell( row, 1).value)) 35 | coupon_yields.append(sheet.cell( row, 2).value/100) 36 | row +=1 37 | 38 | self.swap_rate_lists['Maturities'] = maturities 39 | self.swap_rate_lists['Coupon_yields'] = coupon_yields 40 | 41 | if exec: 42 | with open(r'data\pickle\pca_swap.pkl', 'rb') as input: 43 | self.a1 = pickle.load(input) 44 | self.a2 = pickle.load(input) 45 | self.a3 = pickle.load(input) 46 | # =========================================================================================== 47 | # Get First Three Principal Components of the swap rates curve 48 | self.PC1 = np.dot(self.swap_rate_lists['Coupon_yields'], self.a1) 49 | self.PC2 = np.dot(self.swap_rate_lists['Coupon_yields'], self.a2) 50 | self.PC3 = np.dot(self.swap_rate_lists['Coupon_yields'], self.a3) 51 | # =========================================================================================== 52 | 53 | self.num_instrument = len(self.swap_rate_lists['Maturities']) 54 | self.llp = int(self.swap_rate_lists['Maturities'][-1]) 55 | self.m = np.ones(len(self.swap_rate_lists['Maturities'])) 56 | 57 | def get_time_step(self, s): 58 | self.s = s 59 | 60 | def get_UFR(self, UFR): 61 | self.UFR = UFR 62 | self.mu = [np.exp(-self.UFR * t) for t in range(1, self.llp+1)] 63 | 64 | def get_alpha(self, alpha): 65 | self.alpha = alpha 66 | 67 | def Wvector(self, time_step): 68 | W = [np.exp(-self.UFR * (time_step + u))*(self.alpha * min(time_step, u) - np.exp(-self.alpha * max(time_step, u)) * np.sinh(self.alpha * min(time_step, u))) for u in range(1,self.llp+1)] 69 | return W 70 | 71 | def Wmatrix(self): 72 | self.W = np.zeros(shape = (self.llp, self.llp)) 73 | for i in range(1,self.llp+1): 74 | for j in range(1,self.llp+1): 75 | self.W[i-1][j-1] = np.exp(-self.UFR*(i+j))*(self.alpha * min(i,j) - np.exp(-self.alpha * max(i,j)) * np.sinh(self.alpha * min(i,j))) 76 | return self.W 77 | 78 | def Cmatrix(self, coupon_rate_list): 79 | C = np.zeros(shape = (self.num_instrument, self.llp)) 80 | for i in range(1, self.num_instrument+1): 81 | for j in range(1, self.llp+1): 82 | if j < int(self.swap_rate_lists['Maturities'][i-1]): 83 | C[i-1][j-1] = coupon_rate_list[i-1] 84 | elif j == int(self.swap_rate_lists['Maturities'][i-1]): 85 | C[i-1][j-1] = 1 + coupon_rate_list[i-1] 86 | return C 87 | 88 | def get_eta_vector(self, coupon_rate_list): 89 | C = self.Cmatrix(coupon_rate_list) 90 | W = self.Wmatrix() 91 | CWC = np.dot(np.dot(C,W),np.transpose(C)) 92 | CWC_inv = np.linalg.inv(CWC) 93 | Cmu = np.dot(C,self.mu) 94 | mCmu = np.subtract(self.m, Cmu) 95 | eta = np.dot(CWC_inv, mCmu) 96 | return eta, C 97 | 98 | def SM_extrapolation(self, end_time, stress_test = False, output_excel = True): 99 | eta, C = self.get_eta_vector(coupon_rate_list = self.swap_rate_lists['Coupon_yields']) 100 | zcb_price = np.zeros(end_time) 101 | for time_step in range(end_time): 102 | W = self.Wvector(time_step + 1) 103 | zcb_price[time_step] = np.exp(-self.UFR * (time_step + 1)) + np.dot(eta, np.dot(C,W)) 104 | self.swap_rate_lists['Zero_Bond_Prices'] = zcb_price 105 | self.swap_rate_lists['Spot_Rates'] = - np.log(zcb_price) * np.array([1/t for t in range(1, len(zcb_price) + 1)]) 106 | # Build the forward rates curve from the extrapolated spot rates 107 | N = len(self.swap_rate_lists['Spot_Rates']) 108 | self.swap_rate_lists['Forward_Rates'] = np.zeros(N) 109 | for t in range(N): 110 | if t == 0: 111 | self.swap_rate_lists['Forward_Rates'][t] = np.log(1+self.swap_rate_lists['Spot_Rates'][t]) 112 | else: 113 | self.swap_rate_lists['Forward_Rates'][t] = np.log((1+self.swap_rate_lists['Spot_Rates'][t])**(t+1)/(1+self.swap_rate_lists['Spot_Rates'][t-1])**t) 114 | if output_excel: 115 | et.dump_array(self.swap_rate_lists['Zero_Bond_Prices'], filename = r'data\Zero_Bond_Prices.csv') 116 | et.dump_array(self.swap_rate_lists['Spot_Rates'], filename = r'data\Spot_Rates.csv') 117 | et.dump_array(self.swap_rate_lists['Forward_Rates'], filename = r'data\Forward_Rates.csv') 118 | 119 | 120 | 121 | 122 | 123 | #if __name__ == '__main__': 124 | # file_name = r'..\..\Feuille_de_calcul_ALM.xlsm' 125 | # sm = Smith_Wilson(file_name) 126 | # ========================================================= 127 | # get time horizon 128 | #end_time = int(xw.Range('Smith Wilson extrapolation', 'C3').value) 129 | # ========================================================= 130 | # get time step 131 | #s = int(xw.Range('Smith Wilson extrapolation', 'C9').value) 132 | #sm.get_time_step(s) 133 | # ========================================================= 134 | # get UFR 135 | #UFR = xw.Range('Smith Wilson extrapolation', 'C5').value/100 136 | #sm.get_UFR(UFR) 137 | # ========================================================= 138 | # get alpha 139 | #alpha = xw.Range('Smith Wilson extrapolation', 'C7').value 140 | #sm.get_alpha(alpha) 141 | #sm.SM_extrapolation(end_time = end_time) -------------------------------------------------------------------------------- /esg/model_equity/GBM_constant_volatility.py: -------------------------------------------------------------------------------- 1 | # For testing the model, we need to import a real spot rate trajectory 2 | from ..generator_correlated_variables import generator_correlated_variables 3 | from .EQ_model_classes import EQ_model_base 4 | 5 | # ========================================================= 6 | # Geometry Brownian Motion Constant Volatility Model 7 | # ========================================================= 8 | class GBM_constant_volatility(EQ_model_base): 9 | """ 10 | Objective: 11 | ========== 12 | This class is meant to generate simulated paths based on the Black-Scholes-Merton geometric Brownnian motion model with constant volatility 13 | 14 | Attributes 15 | ========== 16 | 17 | 1. corr_matrix: 18 | Type: 2 dimensional array 19 | Function: correlated geometric Brownnian motions matrix 20 | 21 | 2. time_horizon: 22 | Type: int 23 | Function: time horizon of the simulation for the overall system analysis 24 | 25 | 3. num_instrument: 26 | Type: int 27 | Function: number of investment instruments in the Portfolio (By default, this parameter is None) 28 | 29 | 4. fixed_seed: 30 | Type: int 31 | Function: to define the random state 32 | 33 | 5. market_name: 34 | Type: string 35 | Function: market environment's name 36 | 37 | Methods 38 | ======= 39 | 40 | 1. add_short_rate_trajectory: 41 | 42 | 2. calibrate: 43 | 44 | 3. get_EQ_prices: 45 | 46 | """ 47 | 48 | def __init__(self, corr_matrix = None, time_horizon = None, fixed_seed = None, market_name = None): 49 | if time_horizon is not None: 50 | self.time_horizon = time_horizon 51 | 52 | self.short_rate_trajectory = None 53 | 54 | if corr_matrix is not None: 55 | self.corr_matrix = corr_matrix 56 | 57 | self.number_EQ = 1 58 | 59 | if fixed_seed is not None: 60 | self.fixed_seed = fixed_seed 61 | 62 | if market_name is not None: 63 | self.market_name = market_name 64 | 65 | def add_short_rate_trajectory(self, short_rate_trajectory): 66 | """ 67 | Method: add_short_rate_trajectory 68 | 69 | Function: add a calibrated risk-free short rate trajectory into the model. 70 | 71 | Parameters: 72 | 1. short_rate_trajectory: 73 | Type: array 74 | Function: risk free short rate's trajectory 75 | """ 76 | self.short_rate_trajectory = short_rate_trajectory 77 | 78 | def calibrate(self, asset_data): 79 | """ 80 | Method: calibrate 81 | 82 | Function: Calibrate the EQ model onto the market environment. This method returns the calibrated model parameters 83 | 84 | Parameter: 85 | 1. asset_data: 86 | Type: instance of Asset_data class 87 | Function: see class Asset_data for more details. 88 | """ 89 | market = asset_data.get_list_market(self.market_name) 90 | self.volatility = market.vol_stock 91 | self.dividend_rate = market.dividend_rate 92 | self.EQ_init_value = market.init_stock 93 | 94 | 95 | def get_EQ_prices(self, initial_value, market_equity_shock = 0.): 96 | """ 97 | Method: get_EQ_prices 98 | 99 | Function: return 100 | 1. EQ_prices: (Type: array) normalised equity prices 101 | 2. EQ_book_value (Type: array) normalised book values of equity 102 | 3. EQ_income (Type: dictionary) normalised equity incomes include 103 | PMVL : (plus ou moins value latente) unrealised gains and losses 104 | PVL : (plus value latente) unrealised gains 105 | MVL : (moins value latente) unrealised losses 106 | Revenu : (revenus dégagés par les actifs) 107 | PVL_obligation_TF : unrealised gains of the fixed rate bonds 108 | PVL_hors_obligation : unrealised losses out of the fixed rate bonds 109 | MVL_obligation_TF : unrealised losses of the fixed rate bonds 110 | PMVR_hors_obligation : realised gains and losses out of fixed rate bonds 111 | PMVR_obligation_TF : realised gains and losses of the fixed rate bonds 112 | 113 | 4. performance rate and yield (return rate) 114 | """ 115 | self.EQ_total_return_index = [(1.0 + market_equity_shock) * initial_value] 116 | self.EQ_price_index = [(1.0 + market_equity_shock) * initial_value] 117 | self.perf_rate = [0] 118 | self.return_rate = [0] 119 | ########################################## 120 | 121 | ########################################## 122 | for time_step in range(1, self.time_horizon): 123 | dw = generator_correlated_variables(corr_matrix = self.corr_matrix, time_horizon = self.time_horizon, fixed_seed = self.fixed_seed) 124 | # ================================ 125 | sr = (self.short_rate_trajectory[time_step]+self.short_rate_trajectory[time_step-1])/2 126 | eq_total_return = self.EQ_total_return_index[-1] 127 | eq_price = self.EQ_price_index[-1] 128 | ds_total_return = sr * eq_total_return+ self.volatility * eq_total_return * dw[1,time_step-1] 129 | ds_price = (sr - self.dividend_rate) * eq_price + self.volatility * eq_price * dw[1,time_step-1] 130 | # ================================ 131 | self.EQ_total_return_index.append(self.EQ_total_return_index[-1] + ds_total_return) 132 | self.EQ_price_index.append(self.EQ_price_index[-1] + ds_price) 133 | # =================================================================== 134 | # Compute return rate/ taux de rendement cible 135 | # =================================================================== 136 | rate = (self.EQ_total_return_index[-1] - self.EQ_total_return_index[-2])/self.EQ_total_return_index[-2] 137 | self.perf_rate.append(rate) 138 | self.return_rate.append(self.perf_rate[-1]+self.dividend_rate) 139 | 140 | return self.EQ_price_index, self.EQ_total_return_index, self.perf_rate, self.return_rate 141 | 142 | -------------------------------------------------------------------------------- /esg/test_Smith_Wilson.py: -------------------------------------------------------------------------------- 1 | # Python Programs 2 | from .Smith_Wilson import Smith_Wilson 3 | #from ..core_math.excel_toolbox import excel_toolbox as et 4 | ## Python packages 5 | import numpy as np 6 | import unittest 7 | import xlwings as xw 8 | from xlrd import open_workbook 9 | from xlwt import Workbook 10 | 11 | class TestSmithWilson(unittest.TestCase): 12 | 13 | def test_Cmatrix(self): 14 | # Collect data specific to test 15 | file_name = r'Feuille_de_calcul_ALM(Working).xlsm' 16 | sheet_name = 'Smith Wilson extrapolation' 17 | 18 | wb = xw.books(file_name) 19 | # Backup existing values 20 | backup_maturities = xw.sheets[sheet_name].range('B16:B50').value 21 | backup_coupon_yields = xw.sheets[sheet_name].range('C16:C50').value 22 | # Values required for testing 23 | maturities = np.asarray([1,2,3,5]) 24 | coupon_yields = np.asarray([1.0,2.0,2.6,3.4]) 25 | ## Replace values for testing 26 | xw.sheets[sheet_name].range('B16:C50').value = None 27 | xw.sheets[sheet_name].range('B16:B50').value = maturities.reshape(4,1) 28 | xw.sheets[sheet_name].range('C16:C50').value = coupon_yields.reshape(4,1) 29 | 30 | wb.save() 31 | 32 | sm = Smith_Wilson(file_name = file_name, exec = False) 33 | 34 | sm.UFR = 0.042 35 | sm.alpha = 0.1 36 | sm.num_instrument = 4 37 | 38 | ## Execute function 39 | C_output = sm.Cmatrix(sm.swap_rate_lists['Coupon_yields']) 40 | C_output = C_output[:,[0,1,2,3,4]] 41 | 42 | ## Test 43 | C = np.matrix('1.01,0,0,0,0; 0.02,1.02,0,0,0; 0.026, 0.026, 1.026, 0, 0; 0.034, 0.034, 0.034, 0.034, 1.034') 44 | identical_mat = np.array_equal( C_output.round(decimals=2) , C.round(decimals=2) ) 45 | 46 | xw.sheets[sheet_name].range('B16:C50').value = None 47 | xw.sheets[sheet_name].range('B16:B50').value = np.asarray(backup_maturities).reshape(len(backup_maturities),1) 48 | xw.sheets[sheet_name].range('C16:C50').value = np.asarray(backup_coupon_yields).reshape(len(backup_coupon_yields),1) 49 | 50 | self.assertTrue(identical_mat) 51 | 52 | def test_Wmatrix(self): 53 | # Collect data specific to test 54 | file_name = r'Feuille_de_calcul_ALM(Working).xlsm' 55 | sheet_name = 'Smith Wilson extrapolation' 56 | 57 | # Backup existing values 58 | backup_maturities = xw.sheets[sheet_name].range('B16:B50').value 59 | backup_coupon_yields = xw.sheets[sheet_name].range('C16:C50').value 60 | # Values required for testing 61 | maturities = np.asarray([1,2,3,5]) 62 | coupon_yields = np.asarray([0.01,0.02,0.026,0.034]) 63 | 64 | ## Replace values for testing 65 | xw.sheets[sheet_name].range('B16:C50').value = None 66 | 67 | xw.sheets[sheet_name].range('B16:B50').value = maturities.reshape(4,1) 68 | xw.sheets[sheet_name].range('C16:C50').value = coupon_yields.reshape(4,1) 69 | 70 | sm = Smith_Wilson(file_name = file_name, exec = False) 71 | sm.UFR = 0.042 72 | sm.alpha = 0.1 73 | 74 | ## Execute function 75 | W_output = sm.Wmatrix() 76 | maturities_tab = np.asarray([1,2,3,4,5]) 77 | W_output = W_output[np.ix_(maturities_tab-1,maturities_tab-1)] 78 | 79 | xw.sheets[sheet_name].range('B16:C50').value = None 80 | xw.sheets[sheet_name].range('B16:B50').value = np.asarray(backup_maturities).reshape(len(backup_maturities),1) 81 | xw.sheets[sheet_name].range('C16:C50').value = np.asarray(backup_coupon_yields).reshape(len(backup_coupon_yields),1) 82 | 83 | ## Test 84 | W = np.matrix('0.009,0.016,0.022,0.027,0.031; 0.016, 0.030, 0.041, 0.051, 0.058; 0.022, 0.041, 0.058, 0.072, 0.083; 0.027, 0.051, 0.072, 0.090, 0.104; 0.031, 0.058, 0.083, 0.104, 0.122') 85 | W_rounded = W.round(decimals=2, out=None) 86 | W_output_rounded = W_output.round(decimals=2, out=None) 87 | 88 | identical_mat = np.array_equal( W_output_rounded , W_rounded ) 89 | self.assertTrue(identical_mat) 90 | 91 | 92 | def test_Eta_vector(self): 93 | # Collect data specific to test 94 | file_name = r'Feuille_de_calcul_ALM(Working).xlsm' 95 | sheet_name = 'Smith Wilson extrapolation' 96 | 97 | wb = xw.books(file_name) 98 | # Backup existing values 99 | backup_maturities = xw.sheets[sheet_name].range('B16:B50').value 100 | backup_coupon_yields = xw.sheets[sheet_name].range('C16:C50').value 101 | # Values required for testing 102 | maturities = np.asarray([1,2,3,5]) 103 | coupon_yields = np.asarray([1.0,2.0,2.6,3.4]) 104 | ## Replace values for testing 105 | xw.sheets[sheet_name].range('B16:C50').value = None 106 | xw.sheets[sheet_name].range('B16:B50').value = maturities.reshape(4,1) 107 | xw.sheets[sheet_name].range('C16:C50').value = coupon_yields.reshape(4,1) 108 | 109 | wb.save() 110 | 111 | sm = Smith_Wilson(file_name = file_name, exec = False) 112 | 113 | sm.UFR = 0.042 114 | sm.alpha = 0.1 115 | sm.num_instrument = 4 116 | 117 | ## Execute function 118 | # C matrix 119 | C_output = sm.Cmatrix(sm.swap_rate_lists['Coupon_yields']) 120 | C_output = C_output[:,[0,1,2,3,4]] 121 | # W matrix 122 | W_output = sm.Wmatrix() 123 | maturities_tab = np.asarray([1,2,3,4,5]) 124 | W_output = W_output[np.ix_(maturities_tab-1,maturities_tab-1)] 125 | # Get UFR => mu 126 | sm.get_UFR(0.042) 127 | # eta vector 128 | eta_vector_output , C = sm.get_eta_vector(sm.swap_rate_lists['Coupon_yields']) 129 | 130 | ## Test 131 | eta_vector = np.matrix('57.79, -33.5, 11.40, -5.47') 132 | eta_vector_output_rounded = eta_vector_output.round(decimals=0) 133 | eta_vector_rounded = eta_vector.round(decimals=0) 134 | eta_vector_rounded = np.asarray(eta_vector_rounded) 135 | 136 | identical_mat = np.allclose(eta_vector_output_rounded , eta_vector_rounded ) 137 | 138 | xw.sheets[sheet_name].range('B16:C50').value = None 139 | xw.sheets[sheet_name].range('B16:B50').value = np.asarray(backup_maturities).reshape(len(backup_maturities),1) 140 | xw.sheets[sheet_name].range('C16:C50').value = np.asarray(backup_coupon_yields).reshape(len(backup_coupon_yields),1) 141 | 142 | self.assertTrue(identical_mat) 143 | 144 | def Test_SmithWilson(): 145 | suite = unittest.TestLoader().loadTestsFromTestCase(TestSmithWilson) 146 | a = unittest.TextTestRunner(verbosity=0).run(suite) 147 | xw.sheets['Testing'].range('K49').clear_contents() 148 | xw.sheets['Testing'].range('K49').value = str(a) 149 | 150 | -------------------------------------------------------------------------------- /asset/Asset_data.py: -------------------------------------------------------------------------------- 1 | ## Progam packages 2 | from ..market_environment.Market_Environment import Market_Environment 3 | from ..core_math import credit_pickles 4 | ## Python packages 5 | from xlrd import open_workbook 6 | import xlrd 7 | import xlwings as xw 8 | import numpy as np 9 | 10 | class Asset_data(object): 11 | """ 12 | Objective: 13 | ========== 14 | This class is meant to build up the market environment database 15 | 16 | Attributes: 17 | =========== 18 | 19 | 1. market_environments: 20 | Type: list/array-like 21 | Function: collection of the market environments which is characterized by market's name. 22 | 23 | Methods: 24 | ======== 25 | 26 | 1. add_list_market: 27 | 28 | 2. get_list_market: 29 | 30 | 3. update: 31 | 32 | """ 33 | 34 | def __init__(self): 35 | self.market_environments = {} 36 | 37 | def add_list_market(self, key, list_object): 38 | """ 39 | Method: add_list_market 40 | 41 | Function: add a new market_environment object into the list market_environments 42 | 43 | Parameters: 44 | 1. key: 45 | Type: string 46 | Function: market's name 47 | 2. list_object: 48 | Type: instance of Market_Environment class 49 | Function: new market environment 50 | """ 51 | self.market_environments[key] = list_object 52 | 53 | def get_list_market(self, key): 54 | """ 55 | Method: get_list_market 56 | 57 | Function: return the market_environment object given the market's name 58 | 59 | Parameters: 60 | 1. key: 61 | Type: string 62 | Function: market's name 63 | """ 64 | return self.market_environments[key] 65 | 66 | def update(self, Working_URL=None): 67 | """ 68 | Method: update 69 | 70 | Function: Update data from an excel file named "Market_Environment.xls". 71 | 72 | Parameters: 73 | 1. path: 74 | Type: string 75 | Function: a single directory or a file name (By default, path = 'Market_Environment.xls' and the excel file must be placed in the same folder as the main executed file) 76 | 77 | """ 78 | markets = [] 79 | # ============================================================================================ 80 | # Euro market 81 | # ============================================================================================ 82 | market = Market_Environment() 83 | # ============================================================================================ 84 | # xlwings 0.10.1 version 85 | market.name = xw.sheets['Market_Environment'].range('C3').value 86 | market.vol_IR_rate = xw.sheets['Market_Environment'].range('C4').value 87 | market.speed_IR_rate = xw.sheets['Market_Environment'].range('C5').value 88 | market.init_stock = xw.sheets['Market_Environment'].range('C6').value 89 | market.vol_stock = xw.sheets['Market_Environment'].range('C7').value 90 | market.dividend_rate = xw.sheets['Market_Environment'].range('C8').value 91 | market.JLT_mu = xw.sheets['Market_Environment'].range('C9').value 92 | market.JLT_alpha = xw.sheets['Market_Environment'].range('C10').value 93 | market.JLT_sigma = xw.sheets['Market_Environment'].range('C11').value 94 | market.JLT_pi = xw.sheets['Market_Environment'].range('C12').value 95 | market.recovery_rate = xw.sheets['Market_Environment'].range('C13').value 96 | # ================================================================= 97 | sheet1 = open_workbook(r'Feuille_de_calcul_ALM(Working).xlsm').sheet_by_name("Market_Environment") 98 | sheet2 = open_workbook(r'Feuille_de_calcul_ALM(Working).xlsm').sheet_by_name("Correlation_Matrix") 99 | # ============================================================================================ 100 | # get spot rates 101 | spot_rates = [] 102 | for row in range(2, sheet1.nrows): 103 | if (sheet1.cell_type(row, 5) != xlrd.XL_CELL_EMPTY): 104 | spot_rates.append(sheet1.cell(row, 5).value) 105 | market.spot_rates = spot_rates 106 | # ============================================================================================ 107 | # get deflators 108 | deflators = [] 109 | for row in range(2, sheet1.nrows): 110 | if (sheet1.cell_type(row, 11) != xlrd.XL_CELL_EMPTY): 111 | deflators.append(sheet1.cell(row, 11).value) 112 | market.deflators = deflators 113 | # ============================================================================================ 114 | # get correlation matrix 115 | nb_rows = sheet2.nrows 116 | nb_cols = sheet2.ncols 117 | 118 | for row in range(1, nb_rows): 119 | arr = [] 120 | for col in range(1, nb_cols): 121 | value = (sheet2.cell(row, col).value) 122 | arr.append(value) 123 | market.corr_matrix.append(arr) 124 | market.corr_matrix = np.asarray(market.corr_matrix) 125 | # ======== Check if the correlation matrix is symmetric ===================================== 126 | assert np.allclose(market.corr_matrix, market.corr_matrix.T), "Correlation matrix must be symmetric" 127 | # ============================================================================================ 128 | markets.append(market) 129 | # ============================================================================================ 130 | for market in markets: 131 | key = market.name 132 | self.add_list_market(key, market) 133 | 134 | # ============================================================================================ 135 | # Get Credit data 136 | # ============================================================================================ 137 | market.historical_transition_matrix = credit_pickles.get_historical_transition_matrix(Working_URL) 138 | market.spread_list, market.col_index, market.row_index = credit_pickles.get_spread(Working_URL) 139 | market.prix_marche, market.coupon_marche = credit_pickles.get_prices(Working_URL) 140 | 141 | 142 | #if __name__ == '__main__': 143 | # Working_URL = r'..\..\Feuille_de_calcul_ALM(Working).xlsm' 144 | # 145 | # data = Asset_data() 146 | # data.update(Working_URL) 147 | # market = data.get_list_market('EUR') 148 | # print(market) 149 | # plt.plot(range(len(market.spot_rates)), market.spot_rates, label = 'Yield Curve') 150 | # plt.title('Test Asset_data.py') 151 | # plt.legend() 152 | # plt.show() 153 | # print("Historical Transition Matrix = ", np.round(np.asarray(market.historical_transition_matrix),4)) -------------------------------------------------------------------------------- /internal_model/PCA.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Dec 31 11:09:38 2015 4 | 5 | @author: Quang Dien DUONG 6 | """ 7 | 8 | ## Python packages 9 | from numpy import linalg as la 10 | from xlrd import open_workbook 11 | import numpy as np 12 | import pickle 13 | 14 | 15 | # =================================================================== 16 | # PCA on the interest swap rate 17 | # =================================================================== 18 | 19 | def read_swap_rate(file_name): 20 | book = open_workbook(file_name) 21 | sheet = book.sheet_by_name('Swap Rates') 22 | number_of_rows = sheet.nrows 23 | #number_of_columns = sheet.ncols 24 | swap_rates = [] 25 | maturity_range = [int(sheet.cell(2, col).value) for col in range(2,8)] 26 | for row in range(3, number_of_rows): 27 | rate_list = [] 28 | for col in range(2, 8): 29 | rate_list.append(sheet.cell(row,col).value/100) 30 | swap_rates.append(rate_list) 31 | return swap_rates, maturity_range 32 | 33 | def pca_swap_rate(file_name): 34 | swap_matrix, maturity_range = read_swap_rate(file_name) 35 | cov_mat = np.cov(np.array(swap_matrix).T) 36 | w, v = la.eig(cov_mat) 37 | eigenvalues = w.real 38 | eigenvectors = (v.T).real 39 | norm_a1 = np.linalg.norm(eigenvectors[0]) 40 | norm_a2 = np.linalg.norm(eigenvectors[1]) 41 | norm_a3 = np.linalg.norm(eigenvectors[2]) 42 | a1 = np.divide(eigenvectors[0], norm_a1) 43 | a2 = np.divide(-eigenvectors[1], norm_a2) 44 | a3 = np.divide(eigenvectors[2], norm_a3) 45 | alpha_range = [] 46 | beta_range = [] 47 | gamma_range = [] 48 | for l in swap_matrix: 49 | alpha_range.append(np.dot(l, a1)) 50 | beta_range.append(np.dot(l, a2)) 51 | gamma_range.append(np.dot(l, a3)) 52 | 53 | alpha_min = min(alpha_range) 54 | alpha_max = max(alpha_range) 55 | beta_min = min(beta_range) 56 | beta_max = max(beta_range) 57 | gamma_min = min(gamma_range) 58 | gamma_max = max(gamma_range) 59 | return eigenvalues, eigenvectors, a1, a2, a3, alpha_min, alpha_max, beta_min, beta_max, gamma_min, gamma_max, maturity_range 60 | 61 | # ================================================================== 62 | # PCA on the interest spot rate 63 | # ================================================================== 64 | 65 | def read_spot_rate(file_name): 66 | book = open_workbook(file_name) 67 | sheet = book.sheet_by_name('Main') 68 | number_of_rows = sheet.nrows 69 | number_of_columns = sheet.ncols 70 | spot_rates = [] 71 | for row in range(number_of_rows): 72 | rate_list = [] 73 | for col in range(1, number_of_columns): 74 | rate_list.append(sheet.cell(row, col).value) 75 | spot_rates.append(rate_list) 76 | time_horizon = len(spot_rates[0]) 77 | return range(1, time_horizon+1), spot_rates 78 | 79 | def get_increment_forward_curve(file_name): 80 | maturities, a = read_spot_rate(file_name) 81 | dr = [] 82 | for i in range(len(a)-1): 83 | dr.append(np.subtract(a[i], a[i+1])) 84 | mu = np.mean(dr, axis = 0) 85 | x = np.subtract(dr, mu) 86 | N = len(x) 87 | cov_mat = np.multiply(1/N, np.dot(x.T, x)) 88 | eigenvalues, eigenvectors = la.eig(cov_mat) 89 | w = eigenvalues.real 90 | v = (eigenvectors.T).real 91 | return w, v, a 92 | 93 | def pca_analysis(file_name): 94 | w, v, a = get_increment_forward_curve(file_name) 95 | norm_v0 = np.linalg.norm(v[0]) 96 | norm_v1 = np.linalg.norm(v[1]) 97 | norm_v2 = np.linalg.norm(v[2]) 98 | # ============================ 99 | a1 = np.absolute(np.divide(v[0], norm_v0)) 100 | #PC1 = np.dot(a[0],a1) 101 | # ============================ 102 | a2 = np.divide(v[1], norm_v1) 103 | #PC2 = np.dot(a[0],a2) 104 | # ============================ 105 | a3 = np.divide(v[2], norm_v2) 106 | #PC3 = np.dot(a[0],a3) 107 | return a1, a2, a3 108 | 109 | def saving(): 110 | eigval, eigvec, a1, a2, a3, alpha_min, alpha_max, beta_min, beta_max, gamma_min, gamma_max, ignore = pca_swap_rate(file_name = 'data\Tool_extrapolation_risk_free_rate.xlsx') 111 | with open('data\pickle\pca_swap.pkl', 'wb') as output: 112 | pickle.dump(a1, output, pickle.HIGHEST_PROTOCOL) 113 | pickle.dump(a2, output, pickle.HIGHEST_PROTOCOL) 114 | pickle.dump(a3, output, pickle.HIGHEST_PROTOCOL) 115 | pickle.dump(alpha_min, output, pickle.HIGHEST_PROTOCOL) 116 | pickle.dump(alpha_max, output, pickle.HIGHEST_PROTOCOL) 117 | pickle.dump(beta_min, output, pickle.HIGHEST_PROTOCOL) 118 | pickle.dump(beta_max, output, pickle.HIGHEST_PROTOCOL) 119 | pickle.dump(gamma_min, output, pickle.HIGHEST_PROTOCOL) 120 | pickle.dump(gamma_max, output, pickle.HIGHEST_PROTOCOL) 121 | 122 | 123 | if __name__=='__main__': 124 | #file_name = 'Continuously_Compounded_Spot_Rate.xls' 125 | # Test read_spot_rate method 126 | #maturity, spot_rate = read_spot_rate(file_name) 127 | #plt.plot(maturity, spot_rate[0]) 128 | # Test pca_analysis method 129 | #a1, a2, a3 = pca_analysis(file_name) 130 | #with open('pca.pkl', 'wb') as output: 131 | # pickle.dump(a1, output, pickle.HIGHEST_PROTOCOL) 132 | # pickle.dump(a2, output, pickle.HIGHEST_PROTOCOL) 133 | # pickle.dump(a3, output, pickle.HIGHEST_PROTOCOL) 134 | #plt.plot(maturity, a1) 135 | #plt.plot(maturity, a2) 136 | #plt.plot(maturity, a3) 137 | #plt.show() 138 | #et.dump_array(a1, filename = 'a1.csv') 139 | #et.dump_array(a2, filename = 'a2.csv') 140 | #et.dump_array(a3, filename = 'a3.csv') 141 | 142 | 143 | # ================================================== 144 | # test read_swap_rate method 145 | # ================================================== 146 | sr, ignore = read_swap_rate(r'C:\Users\FR011526\Documents\ALM_credit(working)_modified\code\internal_model\pca_test.xlsx') 147 | #print(sr[0]) 148 | #print(sr[-1]) 149 | # ================================================= 150 | # test pca_swap_rate method 151 | # ================================================= 152 | filename = r'C:\Users\FR011526\Documents\ALM_credit(working)_modified\code\internal_model\pca_test.xlsx' 153 | eigval, eigvec, a1, a2, a3, alpha_min, alpha_max, beta_min, beta_max, gamma_min, gamma_max, ignore = pca_swap_rate(file_name = filename) 154 | #print("Explained variance of the first principal component = ", eigval[0]/(sum(eigval))) 155 | #print("Explained variance of the second principal component = ", eigval[1]/(sum(eigval))) 156 | #print("Explained variance of the third principal component = ", eigval[2]/(sum(eigval))) 157 | #print("alpha_min = ", alpha_min) 158 | #print("alpha_max = ", alpha_max) 159 | #print("beta_min = ", beta_min) 160 | #print("beta_max = ", beta_max) 161 | #print("gamma_min = ", gamma_min) 162 | #print("gamma_max = ", gamma_max) 163 | #et.dump_array(a1, filename = 'First_vector_loading.csv') 164 | #et.dump_array(a2, filename = 'Second_vector_loading.csv') 165 | #et.dump_array(a3, filename = 'Third_vector_loading.csv') 166 | #saving() 167 | # ======================================================= 168 | # Test pca.pkl 169 | # ======================================================= 170 | #with open('pca_swap.pkl', 'rb') as input: 171 | # a1 = pickle.load(input) 172 | # a2 = pickle.load(input) 173 | # a3 = pickle.load(input) 174 | # alpha_min = pickle.load(input) 175 | # alpha_max = pickle.load(input) 176 | # beta_min = pickle.load(input) 177 | # beta_max = pickle.load(input) 178 | # gamma_min = pickle.load(input) 179 | # gamma_max = pickle.load(input) 180 | 181 | #print("alpha_min = ", alpha_min) 182 | #print("alpha_max = ", alpha_max) 183 | #print("beta_min = ", beta_min) 184 | #print("beta_max = ", beta_max) 185 | #print("gamma_min = ", gamma_min) 186 | #print("gamma_max = ", gamma_max) -------------------------------------------------------------------------------- /esg/interest_rate/Hull_White_one_factor.py: -------------------------------------------------------------------------------- 1 | ## Program Packages 2 | from ..generator_correlated_variables import generator_correlated_variables 3 | ## Python Packages 4 | from .IR_model_util import IR_model_util 5 | from .IR_model_functions import diff 6 | from math import exp 7 | import numpy as np 8 | import pickle 9 | # ======================================================================================================================================================================== 10 | # One factor Hull & White short rate model 11 | # The Hull-White Short Rate Model is defined as: 12 | # dr(t) = (theta(t) - a * r(t)) * dt + sigma * dW(t), where a and sigma are constants, and theta(t) is chosen in order to fit the input term structure of interest rates. 13 | # ======================================================================================================================================================================== 14 | 15 | class Hull_White_one_factor(IR_model_util): 16 | """ 17 | Definition: 18 | =========== 19 | The Hull-White Short Rate Model is defined as: 20 | dr(t) = (theta(t) - a * r(t)) * dt + sigma * dW(t), where a and sigma are constants, and theta(t) is chosen in order to fit the input term structure of interest rates. 21 | 22 | Objective: 23 | ========== 24 | This class is meant to generate simulated paths based on the Hull and White interest rate model. 25 | 26 | Attributes: 27 | =========== 28 | Input 29 | 30 | 1. time_horizon: 31 | Type: int 32 | Function: time horizon of the simulation for the overall system analysis 33 | 2. fixed_seed: 34 | Type: int 35 | Function: to define the random state 36 | 3. num_instrument 37 | Type: int 38 | Function: number of instruments 39 | 4. corr_matrix: 40 | Type: 2 dimensional array 41 | Function: correlated geometric Brownnian motions matrix 42 | 5. sigma: 43 | Type: float (positive only) 44 | Function: constant volatility parameter 45 | 6. a: 46 | Type: float (positive only) 47 | Function: mean reversion factor 48 | 7. r0: 49 | Type: float 50 | Function: initial interest rate value. 51 | """ 52 | 53 | def __init__(self, a = None, sigma = None, market_name = None, r0 = None, alpha = None, beta = None, gamma = None): 54 | self.time_horizon = 0 55 | self.fixed_seed = None 56 | self.a = a 57 | self.sigma = sigma 58 | self.market_name = market_name 59 | if r0 is not None: 60 | self.r0 = r0 61 | else: 62 | self.r0 = 0 63 | self.corr_matrix = None 64 | # ============================================================================ 65 | # Afin de faire des calculs avec la méthode de MC, on prend N = 100.000 66 | # ============================================================================ 67 | #self.N = 50 68 | # ================================================== 69 | # Shock lebels 70 | # ================================================== 71 | self.alpha = alpha 72 | self.beta = beta 73 | self.gamma = gamma 74 | 75 | def calibrate(self, asset_data): 76 | """ 77 | Method: calibrate 78 | 79 | Function: Calibrate the IR model onto the market environment. This method returns the calibrated model parameters 80 | 81 | Parameter: 82 | 1. asset_data: 83 | Type: instance of Asset_data class 84 | Function: see class Asset_data for more details. 85 | """ 86 | market = asset_data.get_list_market(self.market_name) 87 | if self.a is None: 88 | self.a = market.speed_IR_rate 89 | 90 | if self.sigma is None: 91 | self.sigma = market.vol_IR_rate 92 | 93 | self.spot_rate = market.spot_rates 94 | # ===================================== 95 | if self.alpha is not None: 96 | with open(r'data\pickle\adjusted_alpha_zcb_curves.pkl', 'rb') as input: 97 | zcb_curves = pickle.load(input) 98 | self.spot_rate = zcb_curves[self.alpha] 99 | 100 | if self.beta is not None: 101 | with open(r'data\pickle\adjusted_beta_zcb_curves.pkl', 'rb') as input: 102 | zcb_curves = pickle.load(input) 103 | self.spot_rate = zcb_curves[self.beta] 104 | 105 | if self.gamma is not None: 106 | with open(r'data\pickle\adjusted_gamma_zcb_curves.pkl', 'rb') as input: 107 | zcb_curves = pickle.load(input) 108 | self.spot_rate = zcb_curves[self.gamma] 109 | 110 | # ===================================== 111 | self.forward_rate = np.zeros(len(self.spot_rate)) 112 | # =============================================== 113 | # By defaut, self.forward_rate[0] = 0 114 | # =============================================== 115 | for t in range(1, len(self.forward_rate)): 116 | if t == 1: 117 | self.forward_rate[t] = np.log(1+self.spot_rate[t-1]) 118 | else: 119 | self.forward_rate[t] = np.log((1+self.spot_rate[t-1])**t/(1+self.spot_rate[t-2])**(t-1)) 120 | derivf = diff(self.forward_rate, range(len(self.forward_rate))) 121 | self.theta = derivf + np.dot(self.a, self.forward_rate[:-1]) + [self.sigma**2/(2*self.a)*(1 - exp(-2*self.a*t)) 122 | for t in range(len(self.forward_rate)-1)] 123 | 124 | def get_IR_curve(self): 125 | """ 126 | Method: get_IR_curve 127 | 128 | Function: return Interest Rate term structures, zero-bond curve per time step and its trajectories over time horizon 129 | 130 | Parameters: None 131 | """ 132 | dw = generator_correlated_variables(corr_matrix = self.corr_matrix, time_horizon = self.time_horizon, fixed_seed = self.fixed_seed) 133 | # =================================== 134 | # Generate IR trajectory 135 | # =================================== 136 | self.trajectory = [self.forward_rate[0]] 137 | for time_step in range(1, self.time_horizon+1): 138 | dr = (self.theta[time_step-1] - self.a * self.trajectory[-1]) + self.sigma * dw[0, time_step-1] 139 | self.trajectory.append(self.trajectory[-1] + dr) 140 | # ==================================== 141 | # Generate IR zero coupon curve 142 | # ==================================== 143 | self.zcb_price = np.ones(shape = (self.time_horizon+30, len(self.spot_rate))) 144 | self.curves = np.ones(shape = (self.time_horizon+30, len(self.spot_rate))) 145 | zcb0 = [self.DF(trajectory = self.forward_rate, begin = 1, end = t) for t in range(2, len(self.spot_rate)+1)] 146 | zcb0.append(zcb0[-1]) # Linear extrapolation zero coupon price curve 147 | self.zcb_price[0,:] = zcb0 148 | 149 | self.curves[0,:] = [round(np.power(zcb0[t-1], -1/t) -1,5) for t in range(1, len(self.spot_rate)+1)] 150 | 151 | forward_rate = - diff(np.log(zcb0), range(1,len(self.spot_rate)+1)) 152 | 153 | for time_step in range(1,self.time_horizon+1): 154 | B = np.array([(1 - exp(-self.a * t))/self.a for t in range(1, len(self.forward_rate) + 1 - time_step)]) 155 | k1 = B* forward_rate[time_step]*zcb0[time_step] 156 | k2 = self.sigma**2/(4*self.a**3)*(exp(2 * self.a * time_step)-1) 157 | k3 = np.array([np.power(exp(-self.a*t) - exp(-self.a*time_step) ,2) for t in range(time_step+1, len(self.forward_rate)+1)]) 158 | BB = np.exp( k1 - k2 * k3 ) 159 | PP = np.array(self.zcb_price[0, time_step : len(self.forward_rate)+1])/self.zcb_price[0,time_step] 160 | A = PP*BB 161 | #Buid curve 162 | zcb_price = A*np.exp(-B*self.trajectory[time_step]) 163 | curve = - np.log(zcb_price) * np.array([1/t for t in range(1, len(zcb_price) + 1)]) 164 | #Constant extrapolation of curve 165 | curve_full = np.concatenate( (curve, [curve[-1] for n in range(len(self.forward_rate)-len(zcb_price))]), axis=0) 166 | zcb_price_full = np.power(1./np.add(1, curve_full), np.arange(1,len(curve_full)+1)) 167 | self.zcb_price[time_step,] = zcb_price_full 168 | self.curves[time_step,] = curve_full 169 | 170 | return self.curves, self.trajectory 171 | 172 | def get_deflator(self): 173 | """ 174 | Method: get_deflator 175 | 176 | Function: return deflators per time step 177 | 178 | Parameter: None 179 | """ 180 | self.deflators = np.power(1./np.add(1,self.curves), np.arange(1, len(self.forward_rate)+1)) 181 | return self.deflators 182 | -------------------------------------------------------------------------------- /esg/credit_risk/JLT.py: -------------------------------------------------------------------------------- 1 | ## Progam packages 2 | from .credit_model_classes import credit_model_base 3 | from ...asset.Asset_data import Asset_data 4 | from ..generator_correlated_variables import generator_correlated_variables 5 | from ...core_math.function_optim import function_optim 6 | from ...core_math.functions_credit import generator_matrix, exp_matrix 7 | 8 | ## Python packages 9 | from scipy.linalg import inv, norm 10 | from numpy import linalg as la 11 | from scipy.optimize import minimize 12 | import numpy as np 13 | 14 | 15 | class JLT(credit_model_base): 16 | """ 17 | The JLT model is inplemented here 18 | 19 | Attributes: 20 | ========== 21 | 22 | Input: 23 | _______ 24 | 1. pi_0 : initial value of the risk premium 25 | Type : float 26 | 27 | 2. mu : long term average parameter 28 | Type : float 29 | 30 | 3. alpha : speed of adjustment parameter 31 | Type : float 32 | 33 | 4. recovery_rate : recorevry rate when default 34 | Type : float 35 | 36 | 5. sigma : volatility parameter 37 | Type : float 38 | 39 | 6. market_name : market name 40 | Type : string 41 | 42 | Output: 43 | _______ 44 | 1. RN_migration_matrix : risk-neutral migration matrix 45 | Type : matrix 7x7 46 | 47 | 2. spreads : credit spreads 48 | Type : vector of length 7 49 | 50 | Methods: 51 | _______ 52 | 1. add_time_horizon 53 | 54 | 2. get_spread 55 | 56 | 3. get_hist_transition_matrix 57 | 58 | 4. calibrate_spread 59 | 60 | 5. calibrate_price 61 | 62 | 6. generate_spreads_and_matrix 63 | 64 | 7. test_diffusion_pi 65 | 66 | """ 67 | 68 | def __init__(self, pi_0= None, mu= None, alpha= None, sigma= None, recovery_rate= None, market_name= None): 69 | self.time_horizon=0 70 | self.recovery_rate = recovery_rate 71 | 72 | # initiate 73 | self.market_spread = None 74 | self.eigenval_hist_gen= None 75 | self.eigenvect_hist_gen= None 76 | 77 | self.historical_transition_matrix = None 78 | self.RN_migration_matrix=[] 79 | 80 | self.spreads=[] 81 | 82 | self.mu=mu 83 | self.alpha=alpha 84 | self.sigma=sigma 85 | 86 | self.corr_matrix= None 87 | self.fixed_seed = None 88 | self.num_instrument = 0 89 | 90 | self.pi_0=pi_0 91 | 92 | self.market_name=market_name 93 | # prend comme entrée IR_model, ou pas... On a défini également une méthode qui permet d'aller récupérer les taux zéro coupons d'un modèle IR 94 | # ici, on a peut-etre seulement besoin de tout initialiser à vide 95 | # l'intérêt de la définition est qu'il est prêt d'être utilisé, plus simple 96 | # or une méthode permet de modifier/ d'accéder à des attributes depuis extérieur. 97 | 98 | def getMatrixJLT(self,t,T): 99 | out = None 100 | d = self.eigenval_hist_gen 101 | if self.sigma !=0: 102 | v = np.sqrt(self.alpha**2 - 2*d*self.sigma**2) 103 | denominator = (v+self.alpha)*(np.exp(v*(T-t))-1)+2*v 104 | A = (2*self.alpha*self.mu)/(self.sigma**2)*np.log((2*v*np.exp(0.5*(self.alpha+v)*(T-t)))/denominator) 105 | B = - (2*d*(np.exp(v*(T-t))-1))/denominator 106 | value = np.exp(A - B*self.risk_premium[t]) 107 | out = np.diag(value) 108 | else: 109 | temp = (self.risk_premium[t]+np.exp(-self.alpha*t))*(T-t) + 1/(self.alpha)*(np.exp(-self.alpha*T)-np.exp(-self.alpha*t)) 110 | value = np.exp(d*temp) 111 | out = np.diag(value) 112 | return out 113 | 114 | def add_time_horizon(self,time_horizon): 115 | """ 116 | Method : add_time_horizon 117 | 118 | Function : add the time horizon 119 | 120 | Parameter : 121 | 1. time_horizon 122 | Type : int 123 | Function : correspond to the time horizon 124 | """ 125 | self.time_horizon = time_horizon 126 | 127 | 128 | 129 | def get_spread(self,asset_data): 130 | """ 131 | Method : get_spread 132 | 133 | Function : retrieve the spread from the pickle file 134 | 135 | Parameter : None 136 | """ 137 | # read the market spread data ''of time 0'' 138 | market = asset_data.get_list_market(self.market_name) 139 | spread_list = market.spread_list 140 | col_index = market.col_index 141 | row_index = market.row_index 142 | self.market_spread = spread_list, col_index, row_index 143 | 144 | 145 | def get_hist_transition_matrix(self, asset_data): 146 | """ 147 | Method : get_hist_transition_matrix 148 | 149 | Function : retrieve the historical transition matrix from the pickle file and then deduce the generator matrix, its eigenvectors and its eigenvalues. 150 | 151 | Parameter : None 152 | """ 153 | 154 | market = asset_data.get_list_market(self.market_name) 155 | historical_transition_matrix = market.historical_transition_matrix 156 | 157 | self.historical_transition_matrix = historical_transition_matrix 158 | 159 | self.historical_generator_matrix = generator_matrix(self.historical_transition_matrix) 160 | 161 | w, v = la.eig(self.historical_generator_matrix) 162 | eigenval_hist_gen = w.real 163 | eigenvect_hist_gen = (v.T).real 164 | for l in range(len(eigenvect_hist_gen)): 165 | eigenvect_hist_gen[l] = eigenvect_hist_gen[l]/norm(eigenvect_hist_gen[l]) 166 | 167 | eigenvect_hist_gen = eigenvect_hist_gen.T 168 | 169 | self.eigenval_hist_gen= eigenval_hist_gen 170 | self.eigenvect_hist_gen= eigenvect_hist_gen 171 | 172 | def calibrate_spread(self, asset_data, AAA_AA): 173 | """ 174 | Method : calibrate_spread 175 | 176 | Function : calibrate the model on the market data of spread 177 | 178 | Parameter : 179 | 1. asset_data 180 | Type : instance of Asset_data class 181 | Function : see class Asset_data for more details. 182 | 183 | 2. AAA_AA 184 | Type : boolean 185 | Function : if it is true, then only spreads of AAA and AA ratings are used for the calibration 186 | """ 187 | market = asset_data.get_list_market(self.market_name) 188 | if self.mu is None: 189 | self.mu = market.JLT_mu 190 | if self.sigma is None: 191 | self.sigma = market.JLT_sigma 192 | if self.alpha is None: 193 | self.alpha = market.JLT_alpha 194 | if self.pi_0 is None: 195 | self.pi_0 = market.JLT_pi 196 | if self.recovery_rate is None: 197 | self.recovery_rate = market.recovery_rate 198 | 199 | spread_list, col_index, row_index = self.market_spread 200 | 201 | def f(pi_0): 202 | return function_optim(pi_0, self.alpha, self.mu, self.sigma, self.recovery_rate, 203 | self.eigenvect_hist_gen, self.eigenval_hist_gen, 204 | row_index, col_index, spread_list,AAA_AA) 205 | 206 | bds = [(0.001,None)] 207 | res = minimize(f,x0=2, bounds=bds ) 208 | self.pi_0 = res.x[0] 209 | return self.pi_0 210 | 211 | 212 | 213 | def calibrate_price(self, asset_data): 214 | """ 215 | Method : calibrate_price 216 | 217 | Function : calibrate the model on the market data of bonds' price 218 | 219 | Parameter : 220 | 1. asset_data 221 | Type : instance of Asset_data class 222 | Function : see class Asset_data for more details. 223 | 224 | """ 225 | market = asset_data.get_list_market(self.market_name) 226 | if self.mu is None: 227 | self.mu = market.JLT_mu 228 | if self.sigma is None: 229 | self.sigma = market.JLT_sigma 230 | if self.alpha is None: 231 | self.alpha = market.JLT_alpha 232 | if self.pi_0 is None: 233 | self.pi_0 = market.JLT_pi 234 | if self.recovery_rate is None: 235 | self.recovery_rate = market.recovery_rate 236 | 237 | spread_list, col_index, row_index = self.market_spread 238 | 239 | def f(pi_0): 240 | return function_optim(pi_0, self.alpha, self.mu, self.sigma, 241 | self.recovery_rate, self.eigenvect_hist_gen, self.eigenval_hist_gen, 242 | row_index, col_index, spread_list) 243 | 244 | res = minimize(f,x0=2) 245 | self.pi_0 = res.x[0] 246 | return self.pi_0 247 | 248 | 249 | 250 | def generate_spreads_and_matrix(self): 251 | """ 252 | Method : generate_spreads_and_matrix 253 | 254 | Function : generate the spreads and risk-neutral transition matrix with parameters in the model 255 | 256 | Parameter : None 257 | 258 | """ 259 | self.spreads=[] 260 | self.RN_migration_matrix=[] 261 | 262 | dw = generator_correlated_variables(corr_matrix = self.corr_matrix, time_horizon = self.time_horizon, fixed_seed = self.fixed_seed) 263 | # =================================== 264 | # Generate CIR process 265 | # =================================== 266 | self.risk_premium=[self.pi_0] 267 | for time_step in range(1,self.time_horizon+1): 268 | dpi = self.alpha*(self.mu-self.risk_premium[-1]) + self.sigma*np.sqrt(self.risk_premium[-1])*dw[2,time_step-1] 269 | self.risk_premium.append(max(0,self.risk_premium[-1] + dpi)) 270 | 271 | 272 | for t in range(self.time_horizon+1): 273 | #une boucle de bas de temps 274 | RN_generator_matrix_t = np.dot(np.dot(self.eigenvect_hist_gen, np.diag(self.risk_premium[t]*self.eigenval_hist_gen)), inv(self.eigenvect_hist_gen)) 275 | RN_migration_matrix_t = exp_matrix(RN_generator_matrix_t).astype('Float64') 276 | self.RN_migration_matrix.append(RN_migration_matrix_t) 277 | 278 | for t in range(self.time_horizon+1): 279 | spread_T = [] 280 | for T in range(t+1,t+21): 281 | spread_t_T = [] 282 | JLTmatrix = self.getMatrixJLT(t,T) 283 | I = np.identity(len(self.eigenval_hist_gen)) 284 | RN_migration_matrix_t_T = I + np.dot(np.dot(self.eigenvect_hist_gen,(JLTmatrix-I)),inv(self.eigenvect_hist_gen)) 285 | if all(1-(1-self.recovery_rate)*RN_migration_matrix_t_T.T[-1][:-1] > 0): 286 | spread_t_T = -1/(T-t)* np.log(1-(1-self.recovery_rate)*RN_migration_matrix_t_T.T[-1][:-1]) 287 | else: 288 | raise ValueError('value in log not defined!') 289 | spread_T.append(spread_t_T) 290 | self.spreads.append(spread_T) 291 | 292 | # self.spreads est une liste qui possède trois dimensions : 1. bas de temps; 2. maturité; 3. notation 293 | return self.RN_migration_matrix, self.spreads 294 | 295 | -------------------------------------------------------------------------------- /loss_function_calculator/loss_function_main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 25 22:57:32 2016 4 | 5 | @author: Quang Dien DUONG 6 | """ 7 | # Program Python 8 | from ..esg.ESG_main import ESG_calibrate,ESG_generate_scenarios 9 | from ..alm.alm_main import Compute_BEL 10 | from ..core_math import excel_toolbox as et 11 | # Packages Python 12 | import matplotlib.pyplot as plt 13 | import pickle 14 | import numpy as np 15 | import xlwings as xw 16 | 17 | 18 | Working_URL = r'Feuille_de_calcul_ALM(Working).xlsm' 19 | 20 | 21 | @xw.func 22 | def Loss_Function_generate_stressed_scenarios(): 23 | # =============================================================== 24 | # Get alpha_max/min; beta_max/min; gamma_max/min 25 | alpha_max = xw.sheets['Loss Function'].range('B2').value 26 | alpha_min = xw.sheets['Loss Function'].range('B3').value 27 | nb_test_alpha = int(xw.sheets['Loss Function'].range('B7').value) 28 | beta_max = xw.sheets['Loss Function'].range('D2').value 29 | beta_min = xw.sheets['Loss Function'].range('D3').value 30 | nb_test_beta = xw.sheets['Loss Function'].range('D7').value 31 | gamma_max = xw.sheets['Loss Function'].range('F2').value 32 | gamma_min = xw.sheets['Loss Function'].range('F3').value 33 | nb_test_gamma = xw.sheets['Loss Function'].range('F7').value 34 | 35 | PC1 = xw.sheets['Loss Function'].range('B5').value 36 | PC2 = xw.sheets['Loss Function'].range('D5').value 37 | PC3 = xw.sheets['Loss Function'].range('F5').value 38 | # ================================================================ 39 | alpha_arr = np.linspace(alpha_min, alpha_max, num = nb_test_alpha) 40 | beta_arr = np.linspace(beta_min, beta_max, num = nb_test_beta) 41 | gamma_arr = np.linspace(gamma_min, gamma_max, num = nb_test_gamma) 42 | 43 | alpha_arr = list(alpha_arr) 44 | beta_arr = list(beta_arr) 45 | gamma_arr = list(gamma_arr) 46 | 47 | alpha_arr.append(PC1) 48 | beta_arr.append(PC2) 49 | gamma_arr.append(PC3) 50 | 51 | alpha_arr.sort() 52 | beta_arr.sort() 53 | gamma_arr.sort() 54 | 55 | # ================================================================================================= 56 | # Point out the positions of PC1, PC2, PC3 within respectively alpha_arr, beta_arr 57 | indexPC1 = np.where(np.asarray(alpha_arr) == PC1)[0][0] 58 | indexPC2 = np.where(np.asarray(beta_arr) == PC2)[0][0] 59 | indexPC3 = np.where(np.asarray(gamma_arr) == PC3)[0][0] 60 | xw.sheets['Loss Function'].range('B6').value = indexPC1 61 | xw.sheets['Loss Function'].range('D6').value = indexPC2 62 | xw.sheets['Loss Function'].range('F6').value = indexPC3 63 | # ================================================================================================= 64 | 65 | # ================================================================================================= 66 | # get time horizon 67 | end_time = int(xw.sheets['Smith Wilson extrapolation'].range('C3').value) 68 | # ================================================================================================= 69 | #SM = None 70 | #with open(r'C:\Users\FR011526\Documents\ALM_QDD_Interface_Excel\SM_extra_data.pkl', 'rb') as input: 71 | # SM = pickle.load(input) 72 | zcb_curves_alpha = [] 73 | zcb_curves_beta = [] 74 | zcb_curves_gamma = [] 75 | for alpha in alpha_arr: 76 | with open(r'data\pickle\SM_extra_data.pkl', 'rb') as input: 77 | SM_alpha = pickle.load(input) 78 | SM_alpha.swap_rate_lists['Coupon_yields'] = SM_alpha.swap_rate_lists['Coupon_yields'] + (alpha - SM_alpha.PC1) * np.array(SM_alpha.a1) 79 | SM_alpha.SM_extrapolation(end_time = end_time, output_excel = False) 80 | zcb_curves_alpha.append(SM_alpha.swap_rate_lists['Spot_Rates']) 81 | for beta in beta_arr: 82 | with open(r'data\pickle\SM_extra_data.pkl', 'rb') as input: 83 | SM_beta = pickle.load(input) 84 | SM_beta.swap_rate_lists['Coupon_yields'] = SM_beta.swap_rate_lists['Coupon_yields'] + (beta - SM_beta.PC2) * np.array(SM_beta.a2) 85 | SM_beta.SM_extrapolation(end_time = end_time, output_excel = False) 86 | zcb_curves_beta.append(SM_beta.swap_rate_lists['Spot_Rates']) 87 | for gamma in gamma_arr: 88 | with open(r'data\pickle\SM_extra_data.pkl', 'rb') as input: 89 | SM_gamma = pickle.load(input) 90 | SM_gamma.swap_rate_lists['Coupon_yields'] = SM_gamma.swap_rate_lists['Coupon_yields'] + (gamma - SM_gamma.PC3) * np.array(SM_gamma.a3) 91 | SM_gamma.SM_extrapolation(end_time = end_time, output_excel = False) 92 | zcb_curves_gamma.append(SM_gamma.swap_rate_lists['Spot_Rates']) 93 | with open(r'data\pickle\adjusted_alpha_zcb_curves.pkl', 'wb') as output: 94 | pickle.dump(zcb_curves_alpha, output, pickle.HIGHEST_PROTOCOL) 95 | with open(r'data\pickle\adjusted_beta_zcb_curves.pkl', 'wb') as output: 96 | pickle.dump(zcb_curves_beta, output, pickle.HIGHEST_PROTOCOL) 97 | with open(r'data\pickle\adjusted_gamma_zcb_curves.pkl', 'wb') as output: 98 | pickle.dump(zcb_curves_gamma, output, pickle.HIGHEST_PROTOCOL) 99 | 100 | 101 | # ========================================================================= 102 | # Export zcb_curves_alpha.xlsx, zcb_curves_beta.xlsx, zcb_curves_gamma.xlsx 103 | # ========================================================================= 104 | et.Write2DListtoExcel(file_name = r'data\pickle\zcb_curves_alpha.csv', obj = zcb_curves_alpha) 105 | et.Write2DListtoExcel(file_name = r'data\pickle\zcb_curves_beta.csv', obj = zcb_curves_beta) 106 | et.Write2DListtoExcel(file_name = r'data\pickle\zcb_curves_gamma.csv', obj = zcb_curves_gamma) 107 | # =========================================================================================================================================== 108 | # get_adjusted_zero_coupon_curves 109 | # =========================================================================================================================================== 110 | fig = plt.figure(figsize = (10,8)) 111 | adj = plt.subplots_adjust(hspace = 0.4, wspace = 0.4) 112 | 113 | sp = plt.subplot(2,2,1) 114 | for zcb_curve in zcb_curves_alpha: 115 | x = range(1, len(zcb_curve)+1) 116 | y = zcb_curve 117 | l1 = plt.plot(x,y, linewidth = 1) 118 | lx = plt.xlabel("Maturity") 119 | ly = plt.ylabel("Zero-coupon bond curve") 120 | tl = plt.title("Choc on the first principal component") 121 | 122 | sp = plt.subplot(2,2,2) 123 | for zcb_curve in zcb_curves_beta: 124 | x = range(1, len(zcb_curve)+1) 125 | y = zcb_curve 126 | l1 = plt.plot(x,y, linewidth = 1) 127 | lx = plt.xlabel("Maturity") 128 | ly = plt.ylabel("Zero-coupon bond curve") 129 | tl = plt.title("Choc on the second principal component") 130 | 131 | sp = plt.subplot(2,2,3) 132 | for zcb_curve in zcb_curves_gamma: 133 | x = range(1, len(zcb_curve)+1) 134 | y = zcb_curve 135 | l1 = plt.plot(x,y, linewidth = 1) 136 | lx = plt.xlabel("Maturity") 137 | ly = plt.ylabel("Zero-coupon bond curve") 138 | tl = plt.title("Choc on the third principal component") 139 | xw.sheets['Loss Function'].range('I2').clear_contents() 140 | xw.sheets['Loss Function'].pictures.add(fig,name='Adjusted Zero Coupon Bond Curves', 141 | left=xw.sheets['Loss Function'].range('I2').left, top=xw.sheets['Loss Function'].range('I2').top, 142 | update=True) 143 | 144 | @xw.func 145 | def Compute_BEL_sensitivities_alpha(): 146 | nb_test_alpha = int(xw.sheets['Loss Function'].range('B7').value) 147 | BEL = [] 148 | FondsPropres = [] 149 | for alpha in range(nb_test_alpha+1): 150 | ESG_calibrate(alpha = alpha) 151 | ESG_generate_scenarios(modif = True) 152 | BE,FP = Compute_BEL(balance_sheet = False, modif = True) 153 | BEL.append(BE) 154 | FondsPropres.append(FP) 155 | BEL0 = xw.sheets['ALM'].range('L2').value 156 | FP0 = xw.sheets['ALM'].range('T13').value 157 | BEL = np.asarray(BEL) 158 | deltaBEL = (BEL - BEL0)/BEL0 159 | FondsPropres = np.asarray(FondsPropres) 160 | deltaFP = (FondsPropres - FP0)/FP0 161 | with open(r'data\pickle\Compute_BEL_sensitivities_alpha.pkl', 'wb') as output: 162 | pickle.dump(deltaBEL, output, pickle.HIGHEST_PROTOCOL) 163 | pickle.dump(deltaFP, output, pickle.HIGHEST_PROTOCOL) 164 | return deltaBEL, deltaFP 165 | 166 | @xw.func 167 | def Display_BEL_sensitivities_alpha(): 168 | deltaBEL = None 169 | with open(r'data\pickle\Compute_BEL_sensitivities_alpha.pkl', 'rb') as input: 170 | deltaBEL = pickle.load(input) 171 | ignored = pickle.load(input) 172 | # ============================================= 173 | # Configure X axes 174 | indexPC1 = xw.sheets['Loss Function'].range('B6').value 175 | nb_test_alpha = int(xw.sheets['Loss Function'].range('B7').value) 176 | x_axis = np.arange(nb_test_alpha+1) - indexPC1 177 | 178 | xw.sheets['Loss Function'].range('C79').value = x_axis 179 | xw.sheets['Loss Function'].range('C80').value = deltaBEL 180 | 181 | @xw.func 182 | def Display_FP_sensitivities_alpha(): 183 | deltaFP = None 184 | with open(r'data\pickle\Compute_BEL_sensitivities_alpha.pkl', 'rb') as input: 185 | ignored = pickle.load(input) 186 | deltaFP = pickle.load(input) 187 | 188 | xw.sheets['Loss Function'].range('C81').value = deltaFP 189 | 190 | 191 | def Compute_BEL_sensitivities_equity_index(): 192 | level_max = xw.sheets['Loss Function'].range('C15').value 193 | level_min = xw.sheets['Loss Function'].range('F15').value 194 | 195 | nb_tests_max = int(xw.sheets['Loss Function'].range('C17').value) 196 | nb_tests_min = int(xw.sheets['Loss Function'].range('F17').value) 197 | 198 | assert level_max >= 0, "Level max must be positive" 199 | assert level_min <= 0, "Level min must be negative" 200 | 201 | 202 | # Merge two arrays and remove duplicates 203 | l1 = np.linspace(0, level_max, nb_tests_max+1) 204 | l2 = np.linspace(level_min, 0, nb_tests_min+1) 205 | 206 | test_list = list(l2) 207 | test_list.extend(x for x in l1 if x not in test_list) 208 | test_list = np.asarray(test_list) 209 | test_list = np.around(test_list, decimals = 2) 210 | 211 | BEL = [] 212 | FondsPropres = [] 213 | for s in test_list: 214 | ESG_calibrate(market_equity_shock = s) 215 | ESG_generate_scenarios(modif = True) 216 | BE, FP = Compute_BEL(balance_sheet = False, modif = True) 217 | BEL.append(BE) 218 | FondsPropres.append(FP) 219 | BEL0 = xw.sheets['ALM'].range('L2').value 220 | FP0 = xw.sheets['ALM'].range('T13').value 221 | BEL = np.asarray(BEL) 222 | deltaBEL = (BEL - BEL0)/BEL0 223 | FondsPropres = np.asarray(FondsPropres) 224 | deltaFP = (FondsPropres - FP0)/FP0 225 | with open(r'data\pickle\Compute_BEL_sensitivities_equity_index.pkl', 'wb') as output: 226 | pickle.dump(deltaBEL, output, pickle.HIGHEST_PROTOCOL) 227 | pickle.dump(deltaFP, output, pickle.HIGHEST_PROTOCOL) 228 | pickle.dump(test_list, output, pickle.HIGHEST_PROTOCOL) 229 | return deltaBEL, deltaFP, test_list 230 | 231 | def Display_BEL_sensitivities_equity_index(): 232 | test_list = None 233 | deltaBEL = None 234 | with open(r'data\pickle\Compute_BEL_sensitivities_equity_index.pkl', 'rb') as input: 235 | deltaBEL = pickle.load(input) 236 | ignored = pickle.load(input) 237 | test_list = pickle.load(input) 238 | xw.sheets['Loss Function'].range('C119').value = test_list 239 | xw.sheets['Loss Function'].range('C120').value = deltaBEL 240 | 241 | def Display_FP_sensitivities_equity_index(): 242 | deltaFP = None 243 | with open(r'data\pickle\Compute_BEL_sensitivities_equity_index.pkl', 'rb') as input: 244 | ignored1 = pickle.load(input) 245 | deltaFP = pickle.load(input) 246 | ignored2 = pickle.load(input) 247 | xw.sheets['Loss Function'].range('C121').value = deltaFP -------------------------------------------------------------------------------- /esg/ESG_main.py: -------------------------------------------------------------------------------- 1 | ## Program packages 2 | from ..asset.Asset_data import Asset_data 3 | from .ESG_RN import ESG_RN 4 | #from .interest_rate import IR_model_classes as IR_model 5 | from .model_equity.GBM_constant_volatility import GBM_constant_volatility 6 | #from .credit_risk import credit_model_classes as credit_model 7 | from .credit_risk.JLT import JLT 8 | from .interest_rate.Hull_White_one_factor import Hull_White_one_factor 9 | from .interest_rate.IR_model_util import IR_model_util 10 | from ..core_math import excel_toolbox as et 11 | ## Python packages 12 | import matplotlib.pyplot as plt 13 | import pickle 14 | import numpy as np 15 | import xlwings as xw 16 | import xlwt 17 | 18 | 19 | Working_URL = r'Feuille_de_calcul_ALM(Working).xlsm' 20 | 21 | def ESG_calibrate(**kwords): 22 | data = Asset_data() 23 | data.update(Working_URL) 24 | mk_name = str(xw.sheets['ESG'].range('D5').value) 25 | market = data.get_list_market(mk_name) 26 | # ============================================== 27 | # Define Equity model 28 | Equity = GBM_constant_volatility() 29 | # ============================================== 30 | # Define Interest Rate model 31 | Interest_rate = Hull_White_one_factor() 32 | # ============================================== 33 | # credit_model 34 | # ============================================== 35 | Credit = JLT() 36 | # Initialise ESG 37 | ESG = ESG_RN(data, Interest_rate, Equity, Credit) 38 | ESG.add_corr_matrix(corr_matrix = market.corr_matrix) 39 | ESG.add_market_name(market_name = 'EUR') 40 | if bool(kwords): 41 | for kw in kwords: 42 | setattr(ESG, kw, kwords[kw]) 43 | ESG.calibrate_models() 44 | with open(r'data\pickle\modif_ESG.pkl', 'wb') as output: 45 | pickle.dump(ESG, output, pickle.HIGHEST_PROTOCOL) 46 | else: 47 | ESG.calibrate_models() 48 | with open(r'data\pickle\ESG.pkl', 'wb') as output: 49 | pickle.dump(ESG, output, pickle.HIGHEST_PROTOCOL) 50 | 51 | def ESG_generate_scenarios(modif = False): 52 | ESG = None 53 | if modif: 54 | with open(r'data\pickle\modif_ESG.pkl', 'rb') as input: 55 | ESG = pickle.load(input) 56 | else: 57 | with open(r'data\pickle\ESG.pkl', 'rb') as input: 58 | ESG = pickle.load(input) 59 | # ============================================== 60 | # update time horizon 61 | time_horizon = xw.sheets['ESG'].range('D3').value 62 | time_horizon = int(time_horizon) 63 | 64 | # ============================================== 65 | # get number of scenarios 66 | ESG.number_trajectories = int(xw.sheets['ESG'].range('D1').value) 67 | # ============================================== 68 | ESG.get_seed() 69 | # Update the variable time_horizon of the objects deriving from the following classes IR_model, EQ_model, credit_model 70 | ESG.update_time_horizon(time_horizon) 71 | 72 | for traj in range(ESG.number_trajectories): 73 | ESG.get_scenario(traj_i = traj) 74 | # ============================================== 75 | # Save ESG 76 | # ============================================== 77 | if modif: 78 | with open(r'data\pickle\modif_ESG_updated.pkl', 'wb') as output: 79 | pickle.dump(ESG, output, pickle.HIGHEST_PROTOCOL) 80 | else: 81 | with open(r'data\pickle\ESG_updated.pkl', 'wb') as output: 82 | pickle.dump(ESG, output, pickle.HIGHEST_PROTOCOL) 83 | 84 | @xw.func 85 | def ESG_generate_EQ_prices(): 86 | ESG = None 87 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 88 | ESG = pickle.load(input) 89 | fig = plt.figure() 90 | resu_price_index = [] 91 | resu_total_return = [] 92 | for traj in range(ESG.number_trajectories): 93 | plt.plot(range(ESG.time_horizon), ESG.scenarios[traj]['EQ_prices']) 94 | resu_price_index.append(ESG.scenarios[traj]['EQ_prices']) 95 | resu_total_return.append(ESG.scenarios[traj]['EQ_total_returns']) 96 | plt.legend() 97 | 98 | xw.sheets['ESG'].range('F12').clear_contents() 99 | xw.sheets['ESG'].pictures.add(fig, name='Black Schole', 100 | left=xw.sheets['ESG'].range('F12').left, top=xw.sheets['ESG'].range('F12').top, 101 | width=450, height=325, update=True) 102 | et.Write2DListtoExcel(file_name = r'data\EQ_Price_Index.xls', obj = resu_price_index) 103 | et.Write2DListtoExcel(file_name = r'data\EQ_Total_Return.xls', obj = resu_total_return) 104 | 105 | @xw.func 106 | def ESG_generate_short_rates(): 107 | ESG = None 108 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 109 | ESG = pickle.load(input) 110 | fig = plt.figure() 111 | short_rates = [] 112 | for traj in range(ESG.number_trajectories): 113 | plt.plot(range(ESG.time_horizon+1), ESG.scenarios[traj]['Short_rates']) 114 | short_rates.append(ESG.scenarios[traj]['Short_rates']) 115 | plt.plot() 116 | 117 | xw.sheets['ESG'].range('S12').clear_contents() 118 | xw.sheets['ESG'].pictures.add(fig, name='Short rates', 119 | left=xw.sheets['ESG'].range('S12').left, top=xw.sheets['ESG'].range('S12').top, 120 | width= 450, height=325, 121 | update=True) 122 | 123 | et.Write2DListtoExcel(file_name = r'data\Short_rates.xls', obj = short_rates) 124 | 125 | 126 | def ESG_generate_spread_curve(): 127 | ESG = None 128 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 129 | ESG = pickle.load(input) 130 | fig = plt.figure() 131 | Tmax = int(xw.sheets['ESG'].range('AG38').value) 132 | spreads = np.asarray(ESG.scenarios[0]['spreads_spot'][0][:Tmax]) 133 | plt.plot(range(Tmax),spreads.T[0], label='AAA') 134 | plt.plot(range(Tmax),spreads.T[1], label='AA') 135 | plt.plot(range(Tmax),spreads.T[2], label='A') 136 | plt.plot(range(Tmax),spreads.T[3], label='BBB') 137 | plt.plot(range(Tmax),spreads.T[4], label='BB') 138 | plt.plot(range(Tmax),spreads.T[5], label='B') 139 | plt.plot(range(Tmax),spreads.T[6], label='CCC') 140 | plt.legend(loc = 'best') 141 | plt.plot() 142 | 143 | xw.sheets['ESG'].range('AF12').clear_contents() 144 | xw.sheets['ESG'].pictures.add(fig, name='Spread Curves', 145 | left=xw.sheets['ESG'].range('AF12').left, top=xw.sheets['ESG'].range('AF12').top, 146 | width= 450, height=325, 147 | update=True) 148 | 149 | @xw.func 150 | def ESG_test_martingale_IR(): 151 | ESG = None 152 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 153 | ESG = pickle.load(input) 154 | epsilon_IR, var, mean = ESG.test_martingale_IR() 155 | 156 | xw.sheets['ESG'].range('T47').clear_contents() 157 | xw.sheets['ESG'].range('T47').value = epsilon_IR 158 | mean = np.asarray(mean) 159 | var = np.asarray(var) 160 | N = ESG.number_trajectories 161 | 162 | sup = ESG.IR_model.zcb_price[0, :ESG.time_horizon-1] + 1.96 / np.sqrt(N) * var 163 | inf = ESG.IR_model.zcb_price[0, :ESG.time_horizon-1] - 1.96 / np.sqrt(N) * var 164 | fig = plt.figure() 165 | plt.plot(range(1, ESG.time_horizon), sup, label = "Borne supérieure de l'IC à 95%") 166 | plt.plot(range(1, ESG.time_horizon), inf, label = "Borne inférieure de l'IC à 95%") 167 | plt.plot(range(1, ESG.time_horizon), mean, label = "Prix Monte Carlo") 168 | plt.legend() 169 | xw.sheets['ESG'].range('F56').clear_contents() 170 | xw.sheets['ESG'].pictures.add(fig, name='Test martingale pour le modèle de taux', 171 | left = xw.sheets['ESG'].range('F56').left, top = xw.sheets['ESG'].range('F56').top, 172 | update=True) 173 | # ============================================================== 174 | # Create and save Excel Workbook 175 | # ============================================================== 176 | book = xlwt.Workbook() 177 | sheet = book.add_sheet("Main") 178 | style1 = xlwt.easyxf('font: bold 1, color red;') 179 | sheet.write(0,0, "BORNE_SUP", style1) 180 | sheet.write(2,0, "MEAN", style1) 181 | sheet.write(4,0, "BONRE_INF", style1) 182 | for col in range(len(mean)): 183 | sheet.write(1, col, sup[col]) 184 | sheet.write(3, col, mean[col]) 185 | sheet.write(5, col, inf[col]) 186 | book.save(r'data\Test_martingale_IR.xls') 187 | 188 | def ESG_test_martingale_EQ(): 189 | ESG = None 190 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 191 | ESG = pickle.load(input) 192 | epsilon_EQ, var, mean = ESG.test_martingale_EQ() 193 | xw.sheets['ESG'].range('T50').clear_contents() 194 | xw.sheets['ESG'].range('T50').value = epsilon_EQ 195 | mean = np.asarray(mean) 196 | var = np.asarray(var) 197 | N = ESG.number_trajectories 198 | sup = np.ones(ESG.time_horizon-1) + 1.96 / np.sqrt(N) * var 199 | inf = np.ones(ESG.time_horizon-1) - 1.96 / np.sqrt(N) * var 200 | fig = plt.figure() 201 | plt.plot(range(1, ESG.time_horizon), sup, label = "Borne supérieure de l'IC à 95%") 202 | plt.plot(range(1, ESG.time_horizon), inf, label = "Borne inférieure de l'IC à 95%") 203 | plt.plot(range(1, ESG.time_horizon), mean, label = "Moyenne de MC actualisée") 204 | plt.legend() 205 | xw.sheets['ESG'].range('W56').clear_contents() 206 | xw.sheets['ESG'].pictures.add(fig, name='Test martingale pour le modèle des actions', 207 | left = xw.sheets['ESG'].range('W56').left, top = xw.sheets['ESG'].range('W56').top, 208 | update=True) 209 | #xw.Plot(fig).show('Test martingale pour le modèle des actions', left = xw.sheets['ESG'].range('W56').left, top = xw.sheets['ESG'].range('W56').top) 210 | # ============================================================== 211 | # Create and save Excel Workbook 212 | # ============================================================== 213 | book = xlwt.Workbook() 214 | sheet = book.add_sheet("Main") 215 | style1 = xlwt.easyxf('font: bold 1, color red;') 216 | sheet.write(0,0, "BORNE_SUP", style1) 217 | sheet.write(2,0, "MEAN", style1) 218 | sheet.write(4,0, "BONRE_INF", style1) 219 | for col in range(len(mean)): 220 | sheet.write(1, col, sup[col]) 221 | sheet.write(3, col, mean[col]) 222 | sheet.write(5, col, inf[col]) 223 | book.save(r'data\Test_martingale_EQ.xls') 224 | 225 | def ESG_generate_deflators(): 226 | ESG = None 227 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 228 | ESG = pickle.load(input) 229 | Tmax = int(xw.sheets['ESG'].range('Y38').value) 230 | 231 | # ====================================== 232 | # Create Excel Workbook 233 | # ====================================== 234 | book = xlwt.Workbook() 235 | sheet = book.add_sheet("Main") 236 | begin = 0 237 | for traj in range(ESG.number_trajectories): 238 | for col in range(ESG.time_horizon+1): 239 | for row in range(Tmax): 240 | sheet.write(row + begin, col, ESG.scenarios[traj]['Deflators'][col, row]) 241 | begin += Tmax + 1 242 | book.save(r'data\Deflators_to_Tmax.xls') 243 | 244 | 245 | def ESG_generate_1y_ZCY(): 246 | ESG = None 247 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 248 | ESG = pickle.load(input) 249 | 250 | DFdict = [] 251 | for traj in range(ESG.number_trajectories): 252 | DFlist = [np.exp(-ESG.scenarios[traj]['Short_rates'][t]) for t in range(1, ESG.time_horizon + 1)] 253 | DFdict.append(DFlist) 254 | et.Write2DListtoExcel(file_name = r'data\One_year_Zero_coupon_yields.xls', obj = DFdict) 255 | 256 | 257 | #if __name__ == "__main__": 258 | # Liabilities_data_test_update() 259 | # Liabilities_data = None 260 | # with open(r'C:\Users\FR011526\Documents\ALM_credit(working)\Liabilities_data0.pkl', 'rb') as input: 261 | # Liabilities_data = pickle.load(input) 262 | # 263 | # ESG = None 264 | # with open(r'C:\Users\FR011526\Documents\ALM_credit(working)\ESG_updated.pkl', 'rb') as input: 265 | # ESG = pickle.load(input) 266 | # #deltaBEL, deltaFP = Compute_BEL_sensitivities_alpha() 267 | # #deltaBEL, deltaFP = Compute_BEL_sensitivities_equity_index() 268 | # BEL, FP = Compute_BEL() 269 | # 270 | -------------------------------------------------------------------------------- /esg/credit_risk/credit_model_classes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 1 15:36:21 2016 4 | 5 | @author: Jun JING 6 | """ 7 | 8 | ## Progam packages 9 | #from ...asset.Asset_data import Asset_data 10 | from ..generator_correlated_variables import generator_correlated_variables 11 | from ...core_math.function_optim import function_optim 12 | from ...core_math.functions_credit import generator_matrix, exp_matrix 13 | 14 | ## Python packages 15 | from abc import ABCMeta, abstractmethod 16 | from scipy.linalg import inv, norm 17 | from numpy import linalg as la 18 | from scipy.optimize import minimize 19 | import numpy as np 20 | 21 | class credit_model_base: 22 | """ 23 | This is purely abstract class for all credit models we will implement in the future 24 | """ 25 | 26 | __metaclass__=ABCMeta 27 | 28 | @abstractmethod 29 | def add_time_horizon(self, time_horizon): 30 | """This method add time horizon """ 31 | return 32 | 33 | @abstractmethod 34 | def get_spread(self): 35 | """This method get the market spread""" 36 | return 37 | 38 | @abstractmethod 39 | def get_RN_transition_matrix(self): 40 | """This method get the historical transition matrix""" 41 | return 42 | 43 | 44 | @abstractmethod 45 | def calibrate_spread(self): 46 | """Calibrate the credit model onto the market environment""" 47 | return 48 | 49 | 50 | @abstractmethod 51 | def generate_spreads_and_matrix(self): 52 | """generate the spreads""" 53 | return 54 | 55 | 56 | 57 | 58 | 59 | class JLT(credit_model_base): 60 | """ 61 | The JLT model is inplemented here 62 | 63 | Attributes: 64 | ========== 65 | 66 | Input: 67 | _______ 68 | 1. pi_0 : initial value of the risk premium 69 | Type : float 70 | 71 | 2. mu : long term average parameter 72 | Type : float 73 | 74 | 3. alpha : speed of adjustment parameter 75 | Type : float 76 | 77 | 4. recovery_rate : recorevry rate when default 78 | Type : float 79 | 80 | 5. sigma : volatility parameter 81 | Type : float 82 | 83 | 6. market_name : market name 84 | Type : string 85 | 86 | Output: 87 | _______ 88 | 1. RN_migration_matrix : risk-neutral migration matrix 89 | Type : matrix 7x7 90 | 91 | 2. spreads : credit spreads 92 | Type : vector of length 7 93 | 94 | Methods: 95 | _______ 96 | 1. add_time_horizon 97 | 98 | 2. get_spread 99 | 100 | 3. get_hist_transition_matrix 101 | 102 | 4. calibrate_spread 103 | 104 | 5. calibrate_price 105 | 106 | 6. generate_spreads_and_matrix 107 | 108 | 7. test_diffusion_pi 109 | 110 | """ 111 | 112 | def __init__(self, pi_0= None, mu= None, alpha= None, sigma= None, recovery_rate= None, market_name= None): 113 | self.time_horizon=0 114 | self.recovery_rate = recovery_rate 115 | 116 | # initiate 117 | self.market_spread = None 118 | self.eigenval_hist_gen= None 119 | self.eigenvect_hist_gen= None 120 | 121 | self.historical_transition_matrix = None 122 | self.RN_migration_matrix=[] 123 | 124 | self.spreads=[] 125 | 126 | self.mu=mu 127 | self.alpha=alpha 128 | self.sigma=sigma 129 | 130 | self.corr_matrix= None 131 | self.fixed_seed = None 132 | self.num_instrument = 0 133 | 134 | self.pi_0=pi_0 135 | 136 | self.market_name=market_name 137 | # prend comme entrée IR_model, ou pas... On a défini également une méthode qui permet d'aller récupérer les taux zéro coupons d'un modèle IR 138 | # ici, on a peut-etre seulement besoin de tout initialiser à vide 139 | # l'intérêt de la définition est qu'il est prêt d'être utilisé, plus simple 140 | # or une méthode permet de modifier/ d'accéder à des attributes depuis extérieur. 141 | 142 | def getMatrixJLT(self,t,T): 143 | out = None 144 | d = self.eigenval_hist_gen 145 | if self.sigma !=0: 146 | v = np.sqrt(self.alpha**2 - 2*d*self.sigma**2) 147 | denominator = (v+self.alpha)*(np.exp(v*(T-t))-1)+2*v 148 | A = (2*self.alpha*self.mu)/(self.sigma**2)*np.log((2*v*np.exp(0.5*(self.alpha+v)*(T-t)))/denominator) 149 | B = - (2*d*(np.exp(v*(T-t))-1))/denominator 150 | value = np.exp(A - B*self.risk_premium[t]) 151 | out = np.diag(value) 152 | else: 153 | temp = (self.risk_premium[t]+np.exp(-self.alpha*t))*(T-t) + 1/(self.alpha)*(np.exp(-self.alpha*T)-np.exp(-self.alpha*t)) 154 | value = np.exp(d*temp) 155 | out = np.diag(value) 156 | return out 157 | 158 | def add_time_horizon(self,time_horizon): 159 | """ 160 | Method : add_time_horizon 161 | 162 | Function : add the time horizon 163 | 164 | Parameter : 165 | 1. time_horizon 166 | Type : int 167 | Function : correspond to the time horizon 168 | """ 169 | self.time_horizon = time_horizon 170 | 171 | 172 | 173 | def get_spread(self,asset_data): 174 | """ 175 | Method : get_spread 176 | 177 | Function : retrieve the spread from the pickle file 178 | 179 | Parameter : None 180 | """ 181 | # read the market spread data ''of time 0'' 182 | market = asset_data.get_list_market(self.market_name) 183 | spread_list = market.spread_list 184 | col_index = market.col_index 185 | row_index = market.row_index 186 | self.market_spread = spread_list, col_index, row_index 187 | 188 | 189 | def get_hist_transition_matrix(self, asset_data): 190 | """ 191 | Method : get_hist_transition_matrix 192 | 193 | Function : retrieve the historical transition matrix from the pickle file and then deduce the generator matrix, its eigenvectors and its eigenvalues. 194 | 195 | Parameter : None 196 | """ 197 | 198 | market = asset_data.get_list_market(self.market_name) 199 | historical_transition_matrix = market.historical_transition_matrix 200 | 201 | self.historical_transition_matrix = historical_transition_matrix 202 | 203 | self.historical_generator_matrix = generator_matrix(self.historical_transition_matrix) 204 | 205 | w, v = la.eig(self.historical_generator_matrix) 206 | eigenval_hist_gen = w.real 207 | eigenvect_hist_gen = (v.T).real 208 | for l in range(len(eigenvect_hist_gen)): 209 | eigenvect_hist_gen[l] = eigenvect_hist_gen[l]/norm(eigenvect_hist_gen[l]) 210 | 211 | eigenvect_hist_gen = eigenvect_hist_gen.T 212 | 213 | self.eigenval_hist_gen= eigenval_hist_gen 214 | self.eigenvect_hist_gen= eigenvect_hist_gen 215 | 216 | def calibrate_spread(self, asset_data, AAA_AA): 217 | """ 218 | Method : calibrate_spread 219 | 220 | Function : calibrate the model on the market data of spread 221 | 222 | Parameter : 223 | 1. asset_data 224 | Type : instance of Asset_data class 225 | Function : see class Asset_data for more details. 226 | 227 | 2. AAA_AA 228 | Type : boolean 229 | Function : if it is true, then only spreads of AAA and AA ratings are used for the calibration 230 | """ 231 | market = asset_data.get_list_market(self.market_name) 232 | if self.mu is None: 233 | self.mu = market.JLT_mu 234 | if self.sigma is None: 235 | self.sigma = market.JLT_sigma 236 | if self.alpha is None: 237 | self.alpha = market.JLT_alpha 238 | if self.pi_0 is None: 239 | self.pi_0 = market.JLT_pi 240 | if self.recovery_rate is None: 241 | self.recovery_rate = market.recovery_rate 242 | 243 | spread_list, col_index, row_index = self.market_spread 244 | 245 | def f(pi_0): 246 | return function_optim(pi_0, self.alpha, self.mu, self.sigma, self.recovery_rate, 247 | self.eigenvect_hist_gen, self.eigenval_hist_gen, 248 | row_index, col_index, spread_list,AAA_AA) 249 | 250 | bds = [(0.001,None)] 251 | res = minimize(f,x0=2, bounds=bds ) 252 | self.pi_0 = res.x[0] 253 | return self.pi_0 254 | 255 | 256 | 257 | def calibrate_price(self, asset_data): 258 | """ 259 | Method : calibrate_price 260 | 261 | Function : calibrate the model on the market data of bonds' price 262 | 263 | Parameter : 264 | 1. asset_data 265 | Type : instance of Asset_data class 266 | Function : see class Asset_data for more details. 267 | 268 | """ 269 | market = asset_data.get_list_market(self.market_name) 270 | if self.mu is None: 271 | self.mu = market.JLT_mu 272 | if self.sigma is None: 273 | self.sigma = market.JLT_sigma 274 | if self.alpha is None: 275 | self.alpha = market.JLT_alpha 276 | if self.pi_0 is None: 277 | self.pi_0 = market.JLT_pi 278 | if self.recovery_rate is None: 279 | self.recovery_rate = market.recovery_rate 280 | 281 | spread_list, col_index, row_index = self.market_spread 282 | 283 | def f(pi_0): 284 | return function_optim(pi_0, self.alpha, self.mu, self.sigma, 285 | self.recovery_rate, self.eigenvect_hist_gen, self.eigenval_hist_gen, 286 | row_index, col_index, spread_list) 287 | 288 | res = minimize(f,x0=2) 289 | self.pi_0 = res.x[0] 290 | return self.pi_0 291 | 292 | 293 | 294 | def generate_spreads_and_matrix(self): 295 | """ 296 | Method : generate_spreads_and_matrix 297 | 298 | Function : generate the spreads and risk-neutral transition matrix with parameters in the model 299 | 300 | Parameter : None 301 | 302 | """ 303 | self.spreads=[] 304 | self.RN_migration_matrix=[] 305 | 306 | dw = generator_correlated_variables(corr_matrix = self.corr_matrix, time_horizon = self.time_horizon, fixed_seed = self.fixed_seed) 307 | # =================================== 308 | # Generate CIR process 309 | # =================================== 310 | self.risk_premium=[self.pi_0] 311 | for time_step in range(1,self.time_horizon+1): 312 | dpi = self.alpha*(self.mu-self.risk_premium[-1]) + self.sigma*np.sqrt(self.risk_premium[-1])*dw[2,time_step-1] 313 | self.risk_premium.append(max(0,self.risk_premium[-1] + dpi)) 314 | 315 | 316 | for t in range(self.time_horizon+1): 317 | #une boucle de bas de temps 318 | RN_generator_matrix_t = np.dot(np.dot(self.eigenvect_hist_gen, np.diag(self.risk_premium[t]*self.eigenval_hist_gen)), inv(self.eigenvect_hist_gen)) 319 | RN_migration_matrix_t = exp_matrix(RN_generator_matrix_t).astype('Float64') 320 | self.RN_migration_matrix.append(RN_migration_matrix_t) 321 | 322 | for t in range(self.time_horizon+1): 323 | spread_T = [] 324 | for T in range(t+1,t+21): 325 | spread_t_T = [] 326 | JLTmatrix = self.getMatrixJLT(t,T) 327 | I = np.identity(len(self.eigenval_hist_gen)) 328 | RN_migration_matrix_t_T = I + np.dot(np.dot(self.eigenvect_hist_gen,(JLTmatrix-I)),inv(self.eigenvect_hist_gen)) 329 | if all(1-(1-self.recovery_rate)*RN_migration_matrix_t_T.T[-1][:-1] > 0): 330 | spread_t_T = -1/(T-t)* np.log(1-(1-self.recovery_rate)*RN_migration_matrix_t_T.T[-1][:-1]) 331 | else: 332 | raise ValueError('value in log not defined!') 333 | spread_T.append(spread_t_T) 334 | self.spreads.append(spread_T) 335 | 336 | # self.spreads est une liste qui possède trois dimensions : 1. bas de temps; 2. maturité; 3. notation 337 | return self.RN_migration_matrix, self.spreads 338 | 339 | -------------------------------------------------------------------------------- /liability/Liabilities.py: -------------------------------------------------------------------------------- 1 | ## Progam packages 2 | from .Technical_Provision import Technical_Provision 3 | from .liability_data.Liabilities_data_m import Liabilities_data_m 4 | from .liability_data.Liabilities_data import Liabilities_data 5 | from ..core_math import excel_toolbox as et 6 | ## Python packages 7 | import numpy as np 8 | import pickle 9 | 10 | class Liabilities(object): 11 | """ 12 | objective: 13 | ========== 14 | This class is meant to implement the liabilities cash flows at each time steps 15 | 16 | Attributes: 17 | ========== 18 | 19 | 1. capitalization_reserve: 20 | Type: array 21 | Function: corresponding list of capitalization reserve values over time horizon 22 | 23 | 2. liabilities_data: 24 | Type: instance of Liabilities_data class 25 | Function: see Liabilities_data class 26 | 27 | 3. technical_provision: 28 | Type: instance of Technical_Provision class 29 | Function: see Technical_Provision class 30 | 31 | 4. own_fund: 32 | Type: array 33 | Function: corresponding list of own fund values over time horizon 34 | 35 | 5. cash_flow_in: 36 | Type: array 37 | Function: corresponding list of cash flow in values over time horizon 38 | 39 | 6. cash_flow_out: 40 | Type: array 41 | Function: corresponding list of cash flow out values over time horizon 42 | 43 | 7. contractual_margin: 44 | Type: array 45 | Function: corresponding list of contractual margin values over time horizon 46 | 47 | Methods: 48 | ======== 49 | 50 | 1. update: 51 | updates all current information given the liabilities_data. 52 | 53 | 2. update_cash_flow_in_out: 54 | 55 | 3. update_mathematical_provision: 56 | 57 | 4. update_capitalization_reserve: 58 | 59 | 5. update_own_fund: 60 | """ 61 | 62 | def __init__(self): 63 | # Stock 64 | self.capitalization_reserve = [] 65 | self.technical_provision = None 66 | self.own_fund = [] 67 | # ==================================================================================================== 68 | # In this version, we take into account the presence of the provision for risk on the equity portfolio 69 | # We name it PRE = Provision pour Risque d'Exigibilité 70 | # ==================================================================================================== 71 | self.PRE = [] 72 | self.liabilities_data = None 73 | # Flux 74 | self.cash_flow_in = [] 75 | self.cash_flow_out = [] 76 | self.contractual_margin = [0] 77 | # ============================== 78 | 79 | # Attention!!! Just update once at the beginning 80 | def update(self, capitalization_reserve, technical_provision, own_fund, PRE, liabilities_data): 81 | """ 82 | Method: update 83 | 84 | Function: initialise all Liabilities attributes here given the liabilities_data 85 | 86 | Parameters: 87 | 1. capitalization_reserve: 88 | Type: float (positive only) 89 | Function: capitalization reserve's initial value. 90 | 91 | 2. technical_provision: 92 | Type: instance of Technical_Provision class 93 | Function: initialise all technical provision attributes given liabilities_data. 94 | 95 | 3. own_fund: 96 | Type: float (positive only) 97 | Function: own fund's initial value. 98 | 99 | 4. liabilities_data: 100 | Type: instance of Liabilities_data class 101 | Function: see Liabilities_data for more details 102 | """ 103 | # Stock 104 | self.capitalization_reserve.append(capitalization_reserve) 105 | self.technical_provision = technical_provision 106 | self.own_fund.append(own_fund) 107 | self.PRE.append(PRE) 108 | # Inputs 109 | self.liabilities_data = liabilities_data 110 | self.technical_provision.mathematical_provision.append(sum(mdlp.mathematical_provision[0] for mdlp in self.liabilities_data.model_points)) 111 | if len(self.technical_provision.profit_sharing_reserve) == 0: 112 | self.technical_provision.profit_sharing_reserve.append(0) 113 | 114 | 115 | # asset_income is an attribut (a list) of class Asset which contains the realised gain and loss values 116 | def update_cash_flow_in_out(self, valuation_date): 117 | """ 118 | Method: update_cash_flow_in_out 119 | 120 | Function: update periodic premium and surrender value of each model points given the liabilities_data at each time steps as well as liabilities cash flows. 121 | 122 | Parameter: None 123 | """ 124 | for mdlp in self.liabilities_data.model_points: 125 | mr = np.array(mdlp.mortality_rate) 126 | sr = 1 - mr 127 | sr_mdlp = np.prod(sr[int(mdlp.average_age)-valuation_date:int(mdlp.average_age)]) 128 | mdlp.cash_flow_in.append(mdlp.premium * mdlp.number_contract * sr_mdlp) 129 | mdlp.cash_flow_out.append(mdlp.dynamical_lapse_rate * mdlp.mathematical_provision[-1] * sr_mdlp) 130 | 131 | self.cash_flow_in.append(sum(mdlp.cash_flow_in[-1] for mdlp in self.liabilities_data.model_points)) 132 | self.cash_flow_out.append(sum(mdlp.cash_flow_out[-1] for mdlp in self.liabilities_data.model_points)) 133 | 134 | def update_mathematical_provision(self): 135 | """ 136 | Method: update_mathematical_provision 137 | 138 | Function: update the mathematical provision value of each model points given the profit sharing rate and its total value after revaluation. 139 | 140 | Parameter: None 141 | """ 142 | for mdlp in self.liabilities_data.model_points: 143 | resu = mdlp.mathematical_provision[-1]*(1 + mdlp.profit_sharing_rate[-1]) + (mdlp.cash_flow_in[-1] - mdlp.cash_flow_out[-1])*np.sqrt(1+mdlp.profit_sharing_rate[-1]) 144 | mdlp.mathematical_provision.append(resu) 145 | self.technical_provision.mathematical_provision.append(sum(mdlp.mathematical_provision[-1] for mdlp in self.liabilities_data.model_points)) 146 | #for mdlp in self.liabilities_data.model_points: 147 | # mdlp.allocation = (mdlp.mathematical_provision[-1])/self.technical_provision.mathematical_provision[-1] 148 | 149 | def update_mathematical_provision_TMG(self): 150 | """ 151 | Method: update_mathematical_provision 152 | 153 | Function: update the mathematical provision value of each model points given the profit sharing rate and its total value after revaluation. 154 | 155 | Parameter: None 156 | """ 157 | for mdlp in self.liabilities_data.model_points: 158 | resu = mdlp.mathematical_provision[-1]*(1 + mdlp.TMG[mdlp.seniority]) + (mdlp.cash_flow_in[-1] - mdlp.cash_flow_out[-1])*np.sqrt(1+mdlp.TMG[mdlp.seniority]) 159 | mdlp.mathematical_provision.append(resu) 160 | self.technical_provision.mathematical_provision.append(sum(mdlp.mathematical_provision[-1] for mdlp in self.liabilities_data.model_points)) 161 | 162 | def update_own_fund(self, valuation_date, additional_fund = 0): 163 | """ 164 | Method: update_own_fund 165 | 166 | Function: update the contractual margin value and the own fund value at each time steps after revaluation. 167 | 168 | Parameter: 169 | 1. additional_fund: 170 | Type: float 171 | Function: a quantity of money taken from the own fund when we do not have enough richness to fund distributed wealth. 172 | """ 173 | # Compute contractual margin 174 | somme = 0 175 | for mdlp in self.liabilities_data.model_points: 176 | somme += mdlp.mathematical_provision[-2]*(1 + mdlp.profit_sharing_rate[-1] + mdlp.margin_rate) + (mdlp.cash_flow_in[-1] - mdlp.cash_flow_out[-1]) * np.sqrt(1 + mdlp.profit_sharing_rate[-1] + mdlp.margin_rate) 177 | self.contractual_margin.append(somme - self.technical_provision.mathematical_provision[-1]) 178 | # compute own_fund 179 | self.own_fund[valuation_date] = self.own_fund[valuation_date] + self.contractual_margin[-1] - additional_fund 180 | 181 | 182 | def treatment_end_scenario(self): 183 | """ 184 | Method : treatment_end_scenario 185 | 186 | Function : release technical provision as a single cash-flow at the end of projections. 187 | """ 188 | #Reset technical provisions within mdlp 189 | for mdlp in self.liabilities_data.model_points: 190 | mdlp.cash_flow_out[-1] += mdlp.mathematical_provision[-1] 191 | mdlp.mathematical_provision[-1] = 0 192 | 193 | #Increase the amount of last cash-flow out by the amount of technical provisions 194 | self.cash_flow_out[-1] += self.technical_provision.mathematical_provision[-1] + self.technical_provision.profit_sharing_reserve[-1] 195 | #self.cash_flow_out[-1] += self.technical_provision.mathematical_provision[-1] 196 | self.technical_provision.mathematical_provision[-1] = 0 197 | self.technical_provision.profit_sharing_reserve[-1] = 0 198 | 199 | def treatment_end_scenario_det(self): 200 | """ 201 | Method : treatment_end_scenario 202 | 203 | Function : release technical provision as a single cash-flow at the end of projections. 204 | """ 205 | #Reset technical provisions within mdlp 206 | for mdlp in self.liabilities_data.model_points: 207 | mdlp.cash_flow_out[-1] += mdlp.mathematical_provision[-1] 208 | mdlp.mathematical_provision[-1] = 0 209 | 210 | #Increase the amount of last cash-flow out by the amount of technical provisions 211 | self.cash_flow_out[-1] += self.technical_provision.mathematical_provision[-1] 212 | self.technical_provision.mathematical_provision[-1] = 0 213 | 214 | 215 | 216 | 217 | # 218 | #if __name__ == '__main__': 219 | # # ====================== 220 | # # Initial data 221 | # # ====================== 222 | # PPE = 5000 223 | # Reserve_de_Kpi = 5000 224 | # Fond_propre = 10000 225 | # additional_fund = 0 226 | # PRE = 0 227 | # # ======================== 228 | # # Update Liabilities_data 229 | # # ======================== 230 | # liabilities_data_path = 'Liability_Data_test.pkl' 231 | # #liabilities_data_path = 'Liabilities_data.pkl' 232 | # # ==================================================== 233 | # # Initialize liabilities_data used for the calculation 234 | # # ==================================================== 235 | # with open(liabilities_data_path, 'rb') as input: 236 | # liabilities_data = pickle.load(input) 237 | # 238 | # PT = Technical_Provision() 239 | # PT.update(profit_sharing_reserve = PPE) 240 | # # ======================== 241 | # # Initialize Liabilities() 242 | # # ======================== 243 | # Passif = Liabilities() 244 | # # ===================== 245 | # # Update initial data 246 | # # ===================== 247 | # Passif.update(capitalization_reserve = Reserve_de_Kpi, technical_provision = PT, own_fund = Fond_propre, PRE = PRE, liabilities_data = liabilities_data) 248 | # # ================== 249 | # # Test object Passif 250 | # # ================== 251 | # print('Test class Liabilities') 252 | # print('Financial Statement \n' 253 | # '=================== \n' 254 | # '\n' 255 | # 'I. Technical Provision \n' 256 | # ' =================== \n' 257 | # ' 1. Mathematical Provision: {0:.2f} \n' 258 | # ' 2. Profit Sharing Reserve: {1:.2f} \n' 259 | # 'II. Capitalization Reserve: {2:.2f} \n' 260 | # 'III PRE: {3: .2f} \n' 261 | # 'IV. Own Fund: {4:.2f} \n' 262 | # .format(Passif.technical_provision.mathematical_provision[-1] 263 | # ,Passif.technical_provision.profit_sharing_reserve[-1] 264 | # ,Passif.capitalization_reserve[-1] 265 | # ,Passif.PRE[-1] 266 | # ,Passif.own_fund[-1])) 267 | # #Passif.liabilities_data.affiche() 268 | # # =================================== 269 | # # Test update_cash_flow_in_out method 270 | # # =================================== 271 | # #Passif.update_cash_flow_in_out(valuation_date = 0) 272 | # #print('Test update_cash_flow_in_out() method') 273 | # #print('cash_flow_in = ', Passif.cash_flow_in[-1]) 274 | # #print('cash_flow_out = ', Passif.cash_flow_out[-1]) 275 | # # ======================================== 276 | # # Test update_mathematical_provision 277 | # # ======================================== 278 | # #for mdlp in Passif.liabilities_data.model_points: 279 | # # mdlp.profit_sharing_rate[0] = 0.005 280 | # #Passif.update_mathematical_provision() 281 | # # ========================================= 282 | # # Test update_capitalization_reserve method 283 | # # ========================================= 284 | # #Passif.update_capitalization_reserve(asset_income, 0) 285 | # # =========================== 286 | # # Test update_own_fund method 287 | # # =========================== 288 | # #Passif.update_own_fund(additional_fund) 289 | # # ========================================= 290 | # # Test update_profit_sharing_reserve method 291 | # # ========================================= 292 | # #distributed_wealth = 100000 293 | # #Passif.update_profit_sharing_reserve(distributed_wealth = distributed_wealth) 294 | # #print('Financial Statement \n' 295 | # # '=================== \n' 296 | # # '\n' 297 | # # 'I. Technical Provision \n' 298 | # # ' =================== \n' 299 | # # ' 1. Mathematical Provision: {0:.2f} \n' 300 | # # ' 2. Profit Sharing Reserve: {1:.2f} \n' 301 | # # 'II. Capitalization Reserve: {2:.2f} \n' 302 | # # 'III. Own Fund: {3:.2f} \n' 303 | # # .format(Passif.technical_provision.mathematical_provision[-1] 304 | # # ,Passif.technical_provision.profit_sharing_reserve[-1] 305 | # # ,Passif.capitalization_reserve[-1] 306 | # # ,Passif.own_fund[-1])) 307 | -------------------------------------------------------------------------------- /core_math/function_optim.py: -------------------------------------------------------------------------------- 1 | ## Python packages 2 | from scipy.linalg import inv 3 | import numpy as np 4 | 5 | def function_optim(pi_0, alpha,mu, sigma, recovery_rate, vect, val, maturity_list,rating_list, spread_list, AAA_AA): 6 | 7 | """ 8 | Method : function_optim 9 | 10 | Function : compute the square of the difference between the market spread and the theoretical spread of the model 11 | 12 | Parameter : 13 | 1. pi_0 14 | Type : float 15 | Function : initial value of the risk premium 16 | 17 | 2. alpha 18 | Type : float 19 | Function : the rate of adjustment parameter 20 | 21 | 3. mu 22 | Type : float 23 | Function : the long-term average parameter 24 | 25 | 4. sigma 26 | Type : float 27 | Function : the volatility parameter 28 | 29 | 5. recovery_rate 30 | Type : float 31 | Function : the recovery rate parameter 32 | 33 | 6. vect 34 | Type : matrix 35 | Function : the eigenvectors of historical generator matrix 36 | 37 | 7. val 38 | Type : vector 39 | Function : the eingenvalues of historical generator matrix 40 | 41 | 8. maturity_list 42 | Type : list 43 | Function : the maturity list of the market data of credit spread 44 | 45 | 9. rating_list 46 | Type : list 47 | Function : the rating list of the market data of credit spread 48 | 49 | 10. spread_list 50 | Type : list 51 | Function : the market data of credit spread depending on rating and time to maturity 52 | 53 | 54 | 11. AAA_AA 55 | Type : boolean 56 | Function : if true, then the model is only calibrated on the rating AAA and AA 57 | 58 | """ 59 | 60 | vect_t_jK = inv(vect).transpose()[-1] 61 | 62 | theoretical_spread_list_pi=[] 63 | for t in maturity_list: 64 | if sigma != 0: 65 | v = np.sqrt(alpha**2-2*val[:-1]*sigma**2) 66 | A = 2*alpha*mu/sigma**2 * np.log(2* v *np.exp(1/2*(alpha+v)* t) / ((v+alpha)*(np.exp(v* t)-1)+2*v ) ) 67 | B = (-2* val[:-1]* (np.exp(v * t)-1)) / ((v+alpha)*(np.exp(v * t)-1)+2*v) 68 | exp_Q = np.exp(A - B*pi_0) 69 | else: 70 | exp_Q = np.exp(val[:-1]*mu*t) 71 | 72 | proba_to_default= np.dot(np.dot(vect[:-1,:-1],np.diag(exp_Q-1)),vect_t_jK[:-1]) 73 | 74 | # c'est un vecteur de dimension K-1, et il depend de time to maturity 75 | 76 | theoretical_spread = -1/t * np.log(1-(1-recovery_rate) * proba_to_default) 77 | theoretical_spread_list_pi.append(theoretical_spread) 78 | # theoretical_spread_list de taille len(maturity)*(K-1) 79 | 80 | shortened_theo_spread_list_pi =[] 81 | for k in range(len(rating_list)): 82 | # uniquement quand les notations sont dans l'ordre 83 | shortened_theo_spread_list_pi.append(np.asarray(theoretical_spread_list_pi)[:,k]) 84 | 85 | 86 | if AAA_AA is True: 87 | diff_matrix_2=(np.asarray(shortened_theo_spread_list_pi).T[:,:2] - np.asarray(spread_list)[:,:2] )**2 88 | norme2_diff_spread_pi=np.sum(diff_matrix_2) 89 | else: 90 | diff_matrix_2=(np.asarray(shortened_theo_spread_list_pi).T - np.asarray(spread_list) )**2 91 | norme2_diff_spread_pi=np.sum(diff_matrix_2) 92 | 93 | 94 | return norme2_diff_spread_pi 95 | 96 | 97 | 98 | 99 | def function_optim_v_price(pi_0, alpha,mu, sigma, recovery_rate, vect, val, price_matrix,coupon_matrix ,IR0): 100 | 101 | """ 102 | Method : function_optim_v_price 103 | 104 | Function : compute the theorectical price of bond whose price is observable on the market 105 | 106 | Parameter : 107 | 1. pi_0 108 | Type : float 109 | Function : initial value of the risk premium 110 | 111 | 2. alpha 112 | Type : float 113 | Function : the rate of adjustment parameter 114 | 115 | 3. mu 116 | Type : float 117 | Function : the long-term average parameter 118 | 119 | 4. sigma 120 | Type : float 121 | Function : the volatility parameter 122 | 123 | 5. recovery_rate 124 | Type : float 125 | Function : the recovery rate parameter 126 | 127 | 6. vect 128 | Type : matrix 129 | Function : the eigenvectors of historical generator matrix 130 | 131 | 7. val 132 | Type : vector 133 | Function : the eingenvalues of historical generator matrix 134 | 135 | 8. price_matrix 136 | Type : matrix 137 | Function : the market price of bonds 138 | 139 | 9. coupon_matrix 140 | Type : matrix 141 | Function : the coupon rate of bonds 142 | 143 | 10. IR_0 144 | Type : vector 145 | Function : IR curve at time t=0 146 | 147 | 148 | """ 149 | 150 | 151 | #allocation_init = allocation_init/sum(sum(allocation_init)) 152 | 153 | vect_t_jK = inv(vect).transpose()[-1] 154 | proba_to_survive =[] 155 | for TtM in range(1, len(price_matrix[0,:])+1): 156 | v = np.sqrt(alpha**2-2*val[:-1]*sigma**2) 157 | A = 2*alpha*mu/sigma**2 * np.log(2* v *np.exp(1/2*(alpha+v)* TtM) / ((v+alpha)*(np.exp(v* TtM)-1)+2*v ) ) 158 | B = (-2* val[:-1]* (np.exp(v * TtM)-1)) / ((v+alpha)*(np.exp(v * TtM)-1)+2*v) 159 | exp_Q = np.exp(A - B*pi_0) 160 | 161 | 162 | proba_to_default_TtM= np.dot(np.dot(vect[:-1, :-1],np.diag(exp_Q-1)),vect_t_jK[:-1]) 163 | # c'est un vecteur de dimension K-1, et il depend de time to maturity 164 | proba_to_survive_TtM = np.ones(len(proba_to_default_TtM))- proba_to_default_TtM 165 | proba_to_survive.append(proba_to_survive_TtM) 166 | 167 | proba_to_survive = np.asarray(proba_to_survive) 168 | 169 | p_theorique =0 170 | for TtM in range(1, len(price_matrix[0,:])+1): 171 | for k in range(len(price_matrix)): 172 | p_theorique += sum(np.multiply(np.divide(coupon_matrix[k,TtM-1], np.power(1+IR0[0:TtM],np.arange(1,TtM+1))), proba_to_survive[:TtM,k])) + np.multiply(np.divide(1, np.power(1+IR0[TtM-1],TtM)), proba_to_survive[TtM-1,k]) 173 | 174 | # normalement, on doit calibrer les prix des obligations qui paient le même taux de coupon que celles sur le marché 175 | 176 | return p_theorique 177 | 178 | 179 | def function_spread(pi_0, alpha,mu, sigma, recovery_rate, vect, val): 180 | """ 181 | Method : function_spread 182 | 183 | Function : compute the theorectical spread 184 | 185 | Parameter : 186 | 1. pi_0 187 | Type : float 188 | Function : initial value of the risk premium 189 | 190 | 2. alpha 191 | Type : float 192 | Function : the rate of adjustment parameter 193 | 194 | 3. mu 195 | Type : float 196 | Function : the long-term average parameter 197 | 198 | 4. sigma 199 | Type : float 200 | Function : the volatility parameter 201 | 202 | 5. recovery_rate 203 | Type : float 204 | Function : the recovery rate parameter 205 | 206 | 6. vect 207 | Type : matrix 208 | Function : the eigenvectors of historical generator matrix 209 | 210 | 7. val 211 | Type : vector 212 | Function : the eingenvalues of historical generator matrix 213 | 214 | """ 215 | vect_t_jK = inv(vect).transpose()[-1] 216 | 217 | theoretical_spread_list_pi=[] 218 | for t in range(1,21): 219 | v = np.sqrt(alpha**2-2*val[:-1]*sigma**2) 220 | A = 2*alpha*mu/sigma**2 * np.log(2* v *np.exp(1/2*(alpha+v)* t) / ((v+alpha)*(np.exp(v* t)-1)+2*v ) ) 221 | B = (-2* val[:-1]* (np.exp(v * t)-1)) / ((v+alpha)*(np.exp(v * t)-1)+2*v) 222 | exp_Q = np.exp(A - B*pi_0) 223 | 224 | 225 | proba_to_default= np.dot(np.dot(vect[:-1,:-1],np.diag(exp_Q-1)),vect_t_jK[:-1]) 226 | 227 | # c'est un vecteur de dimension K-1, et il depend de time to maturity 228 | 229 | theoretical_spread = -1/t * np.log(1-(1-recovery_rate) * proba_to_default) 230 | theoretical_spread_list_pi.append(theoretical_spread) 231 | 232 | 233 | return theoretical_spread_list_pi 234 | 235 | 236 | #if __name__ == '__main__': 237 | # 238 | # 239 | # 240 | # mu=5 241 | # alpha=0.1 242 | # sigma=0.75 243 | # recovery_rate=0.35 244 | # 245 | # 246 | # with open('historical_transition_matrix.pkl', 'rb') as input: 247 | # historical_transition_matrix = pickle.load(input) 248 | # 249 | # 250 | # 251 | # historical_generator_matrix = generator_matrix(historical_transition_matrix) 252 | # 253 | # w, v = la.eig(historical_generator_matrix) 254 | # eigenval_hist_gen = w.real 255 | # eigenvect_hist_gen = (v.T).real 256 | # for l in range(len(eigenvect_hist_gen)): 257 | # eigenvect_hist_gen[l] = eigenvect_hist_gen[l]/norm(eigenvect_hist_gen[l]) 258 | # 259 | # eigenvect_hist_gen = eigenvect_hist_gen.T 260 | # 261 | # eigenval_hist_gen= eigenval_hist_gen 262 | # eigenvect_hist_gen= eigenvect_hist_gen 263 | # 264 | # 265 | # with open('spread.pkl', 'rb') as input: 266 | # spread_list = pickle.load(input) 267 | # col_index = pickle.load(input) 268 | # row_index = pickle.load(input) 269 | # 270 | # 271 | # AAA_AA=True 272 | # def f(pi_0): 273 | # return function_optim(pi_0, alpha, mu, sigma, recovery_rate, eigenvect_hist_gen, eigenval_hist_gen, row_index, col_index, spread_list, AAA_AA) 274 | 275 | 276 | 277 | 278 | # bds = [(0.001,None)] 279 | # res = minimize(f,x0=0.001, bounds=bds ) 280 | # print('le pi 0 calibré est ', res.x) 281 | # print('Erreur:', f(pi_0=res.x[0])) 282 | # 283 | # 284 | # def f_penal(v): 285 | # return function_optim(v[0], v[1], v[2], v[3], recovery_rate, eigenvect_hist_gen, eigenval_hist_gen, row_index, col_index, spread_list, AAA_AA ) 286 | # + 1000 *(v[1] - 0.1)**2 + 1000 * (v[2] - 5 )**2 + 100* (v[3] -0.75)**2 287 | # bdss = [(0.001,None), (0.01, 5), (1,20), (0.01,1)] 288 | # res_penal = minimize(f_penal, x0=[3, 0.1, 5, 0.75], bounds = bdss) 289 | # print('parametres...', res_penal.x) 290 | # 291 | # 292 | # 293 | # 294 | # with open('bonds_prices.pkl','rb') as input: 295 | # prix_marche = pickle.load(input) 296 | # coupon_marche = pickle.load(input) 297 | # 298 | # 299 | # IR0 = np.asarray([ 0.0385451 , 0.03759469, 0.03740671, 0.03754671, 0.03778315, 300 | # 0.03804784, 0.03833469, 0.03864606, 0.03898394, 0.03935032, 301 | # 0.03974095, 0.04013042, 0.04049706, 0.04082651, 0.04110895, 302 | # 0.04133755, 0.04150739, 0.04161484, 0.04165719, 0.04163233, 303 | # 0.04154272, 0.04140577, 0.04123881, 0.04105542, 0.04086619, 304 | # 0.0406794 , 0.04050149, 0.04033747, 0.04019122, 0.0400658 , 305 | # 0.03996266, 0.03987917, 0.03981225, 0.03975929, 0.03971813, 306 | # 0.03968691, 0.03966409, 0.03964836, 0.0396386 , 0.03963387, 307 | # 0.03963337, 0.0396364 , 0.03964239, 0.03965084, 0.03966133, 308 | # 0.0396735 , 0.03968703, 0.03970166, 0.03971717, 0.03973336, 309 | # 0.03975007, 0.03976717, 0.03978453, 0.03980205, 0.03981965, 310 | # 0.03983726, 0.03985481, 0.03987226, 0.03988957, 0.03990669, 311 | # 0.0399236 , 0.03994029, 0.03995672, 0.03997289, 0.03998878, 312 | # 0.04000439, 0.0400197 , 0.04003473, 0.04004946, 0.04006389, 313 | # 0.04007802, 0.04009186, 0.04010542, 0.04011868, 0.04013166, 314 | # 0.04014436, 0.04015679, 0.04016895, 0.04018084, 0.04019248, 315 | # 0.04020387, 0.04021501, 0.04022591, 0.04023658, 0.04024702, 316 | # 0.04025723, 0.04026723, 0.04027702, 0.0402866 , 0.04029599, 317 | # 0.04030518, 0.04031418, 0.04032299, 0.04033163, 0.04034009, 318 | # 0.04034838, 0.04035651, 0.04036447, 0.04037228, 0.04037994, 319 | # 0.04038745, 0.04039482, 0.04040205, 0.04040914, 0.04041609, 320 | # 0.04042292, 0.04042963, 0.04043621, 0.04044267, 0.04044901, 321 | # 0.04045525, 0.04046137, 0.04046738, 0.04047329, 0.0404791 , 322 | # 0.04048481, 0.04049042, 0.04049594, 0.04050136, 0.0405067 , 323 | # 0.04051195, 0.04051711, 0.04052219, 0.04052718, 0.0405321 , 324 | # 0.04053694, 0.0405417 , 0.04054639, 0.04055101, 0.04055555, 325 | # 0.04056003, 0.04056444, 0.04056878, 0.04057306, 0.04057727, 326 | # 0.04058142, 0.04058552, 0.04058955, 0.04059352, 0.04059744, 327 | # 0.0406013 , 0.04060511, 0.04060887, 0.04061257, 0.04061622, 328 | # 0.04061982, 0.04062337, 0.04062688, 0.04063034, 0.04063375]) 329 | # 330 | # #pi_0 = 2 331 | # #a = function_optim_v_price(pi_0, alpha,mu, sigma, recovery_rate, eigenvect_hist_gen, eigenval_hist_gen, prix_marche,coupon_marche ,IR0) 332 | # 333 | # #print(a) 334 | # #print(sum(sum(prix_marche))) 335 | # 336 | # def g(pi_0): 337 | # return np.absolute(function_optim_v_price(pi_0, alpha,mu, sigma, recovery_rate, eigenvect_hist_gen, eigenval_hist_gen, prix_marche,coupon_marche ,IR0) - sum(sum(prix_marche))) 338 | # 339 | 340 | 341 | 342 | 343 | 344 | 345 | # pi_solution = minimize(g,x0=0.1) 346 | # #pi_solution = fsolve(g, 5) 347 | # print('le pi 0 calibré par la méthode 2 : ', pi_solution.x) 348 | # print('Erreur lié: ', g(pi_0=pi_solution.x[0]) ) 349 | # 350 | # pi_0 = res_penal.x[0] 351 | # alpha =res_penal.x[1] 352 | # mu = res_penal.x[2] 353 | # sigma = res_penal.x[3] 354 | # 355 | # #pi_0 = 5 356 | # spread = function_spread(pi_0, alpha,mu, sigma, recovery_rate, eigenvect_hist_gen, eigenval_hist_gen) 357 | # 358 | -------------------------------------------------------------------------------- /asset/test_Assets.py: -------------------------------------------------------------------------------- 1 | # Python Programs 2 | from ..esg.ESG_main import * 3 | from .Assets import Assets 4 | from .bond.Bonds_Portfolio import Bonds_Portfolio 5 | ## Python packages 6 | import unittest 7 | import pickle as pk 8 | import numpy as np 9 | 10 | 11 | 12 | 13 | class TestAssets(unittest.TestCase): 14 | def test_add_delete_EQ(self): 15 | ## Input 16 | temp_mk_name = xw.sheets['ESG'].range('D5').value 17 | temp_time_horizon = xw.sheets['ESG'].range('D3').value 18 | temp_number_trajectories = xw.sheets['ESG'].range('D1').value 19 | temp_volatilité_IR = xw.sheets['Market_Environment'].range('C4').value 20 | 21 | time_horizon = 50 22 | 23 | xw.sheets['ESG'].range('D5').value = "EUR" 24 | xw.sheets['ESG'].range('D3').value = time_horizon 25 | xw.sheets['ESG'].range('D1').value = 1 26 | xw.sheets['Market_Environment'].range('C4').value = 0 27 | 28 | ESG_calibrate() 29 | ESG_generate_scenarios(modif = False) 30 | ESG = None 31 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 32 | ESG = pickle.load(input) 33 | 34 | spot_rates = ESG.asset_data.get_list_market("EUR").spot_rates 35 | test_bonds_target_allocation = np.asarray([ 36 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,7,8,9,10], 37 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7,8,9], 38 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7,8], 39 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7], 40 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 41 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 42 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 43 | ]) 44 | 45 | bonds_portfolio = Bonds_Portfolio(time_horizon = time_horizon, 46 | ESG_RN_scenarios_traj_i = ESG.scenarios[0], 47 | target_allocation= test_bonds_target_allocation, 48 | init_IR_curve= spot_rates) 49 | bonds_portfolio.initialize_allocation(amount=1000) 50 | bonds_portfolio.initialize_unit_bonds_book_value() 51 | bonds_portfolio.initialize_unit_bonds_market_value() 52 | 53 | Portfolio = Assets(ESG, bonds_portfolio) 54 | Portfolio.initialize_EQ_value(value = 100) 55 | 56 | amount = 10 57 | valuation_date = 10 58 | initial_number_EQ = Portfolio.number_EQ 59 | 60 | for time_step in range(1,time_horizon): 61 | Portfolio.get_asset_income_and_EQ_value(traj_i = 0, valuation_date = time_step) 62 | Portfolio.reset_asset_income_and_EQ_value(valuation_date = time_step) 63 | PMVL = Portfolio.EQ_market_value[time_step] - Portfolio.EQ_book_value[time_step] 64 | PVL = max(0, PMVL) 65 | MVL = max(0, -PMVL) 66 | PVL_hors_obligation = PVL 67 | Revenu = Portfolio.ESG_RN.EQ_model.dividend_rate * Portfolio.EQ_market_value[time_step - 1] 68 | income_dict = {'PMVL': PMVL, 'PVL': PVL, 'MVL': MVL, 'Revenu': Revenu, 'PVL_obligation_TF': 0, 'MVL_obligation_TF': 0, 'PMVR_hors_obligation': 0, 'PMVR_obligation_TF': 0, 'PVL_hors_obligation': PVL_hors_obligation} 69 | Portfolio.asset_income.append(income_dict) 70 | ## Execute 71 | if time_step == valuation_date: 72 | Portfolio.add_EQ(amount = amount, traj_i=0, valuation_date= valuation_date) 73 | Portfolio.delete_EQ(amount= amount, valuation_date= valuation_date) 74 | 75 | # Test Output 76 | self.assertEqual(round(Portfolio.number_EQ,3),initial_number_EQ) 77 | 78 | # Restore previous values 79 | xw.sheets['ESG'].range('D5').value = temp_mk_name 80 | xw.sheets['ESG'].range('D3').value = temp_time_horizon 81 | xw.sheets['ESG'].range('D1').value = temp_number_trajectories 82 | xw.sheets['Market_Environment'].range('C4').value = temp_volatilité_IR 83 | 84 | def test_add_delete_bonds(self): 85 | ## input 86 | temp_mk_name = xw.sheets['ESG'].range('D5').value 87 | temp_time_horizon = xw.sheets['ESG'].range('D3').value 88 | temp_number_trajectories = xw.sheets['ESG'].range('D1').value 89 | temp_volatilité_IR = xw.sheets['Market_Environment'].range('C4').value 90 | 91 | time_horizon = 50 92 | 93 | xw.sheets['ESG'].range('D5').value = "EUR" 94 | xw.sheets['ESG'].range('D3').value = time_horizon 95 | xw.sheets['ESG'].range('D1').value = 1 96 | xw.sheets['Market_Environment'].range('C4').value = 0 97 | 98 | ESG_calibrate() 99 | ESG_generate_scenarios(modif = False) 100 | ESG = None 101 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 102 | ESG = pickle.load(input) 103 | 104 | spot_rates = ESG.asset_data.get_list_market("EUR").spot_rates 105 | test_bonds_target_allocation = np.asarray([ 106 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,7,8,9,10], 107 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7,8,9], 108 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7,8], 109 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7], 110 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 111 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 112 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 113 | ]) 114 | 115 | bonds_portfolio = Bonds_Portfolio(time_horizon = time_horizon, 116 | ESG_RN_scenarios_traj_i = ESG.scenarios[0], 117 | target_allocation= test_bonds_target_allocation, 118 | init_IR_curve= spot_rates) 119 | bonds_portfolio.initialize_allocation(amount=1000) 120 | bonds_portfolio.initialize_unit_bonds_book_value() 121 | bonds_portfolio.initialize_unit_bonds_market_value() 122 | 123 | valuation_date1 = 10 124 | valuation_date2 = 15 125 | amount = 100. 126 | 127 | for time_step in range(1, time_horizon): 128 | if len(bonds_portfolio.allocation_matrix) == time_step: 129 | bonds_portfolio.allocation_matrix.append(bonds_portfolio.allocation_matrix[-1]) 130 | 131 | new_num=[] 132 | for k in range(7): 133 | num_k = [] 134 | for t in range(time_step): 135 | num_k.append(bonds_portfolio.num_of_bonds[k][t,:]) 136 | num_k.append(np.zeros(20)) 137 | new_num.append(np.asarray(num_k)) 138 | bonds_portfolio.num_of_bonds = new_num 139 | 140 | num_of_bonds_temps =[] 141 | allocation_mat_temps =[] 142 | for k in range(7): 143 | num_of_bonds_k_temps = [] 144 | allocation_mat_k_temps =[] 145 | for k2 in range(8): 146 | num_of_bonds_k_temps.append(bonds_portfolio.num_of_bonds[k]*ESG.scenarios[0]['RN_migration_matrix'][time_step][k,k2]) 147 | # num_of_bonds_k_temps de dimension 8*une matrice(temps d'achat et maturité) 148 | allocation_mat_k_temps.append(bonds_portfolio.allocation_matrix[time_step][k,:]*ESG.scenarios[0]['RN_migration_matrix'][time_step][k,k2]) 149 | num_of_bonds_temps.append(num_of_bonds_k_temps) 150 | # num_of_bonds_temps de dimension 7*8*XXX 151 | allocation_mat_temps.append(allocation_mat_k_temps) 152 | 153 | # une somme sur k, selon k2, cela donne les nouveaux num_of_bonds et allocation_mat après migration 154 | num_of_bonds = sum(np.asarray(num_of_bonds_temps)) 155 | allocation_mat = sum(np.asarray(allocation_mat_temps)) 156 | # num_of_bonds de dim 8*XXX 157 | 158 | bonds_portfolio.num_of_bonds = num_of_bonds[:7] 159 | bonds_portfolio.allocation_matrix[time_step] = allocation_mat[:7] 160 | 161 | if time_step == valuation_date1: 162 | bef_MV = np.sum(np.asarray(bonds_portfolio.get_market_value_list(valuation_date1))) 163 | # Test 164 | bonds_portfolio.add_bonds(amount=amount, valuation_date=valuation_date1) 165 | bonds_portfolio.delete_bonds(amount=amount, valuation_date=valuation_date1, book_value=False) 166 | aft_MV = np.sum(np.asarray(bonds_portfolio.get_market_value_list(valuation_date1))) 167 | # Output 168 | self.assertEqual(bef_MV,aft_MV) 169 | elif time_step == valuation_date2: 170 | bef_BV = np.sum(np.asarray(bonds_portfolio.get_book_value_list(valuation_date2))) 171 | # Test 172 | bonds_portfolio.add_bonds(amount=amount, valuation_date=valuation_date2) 173 | bonds_portfolio.delete_bonds(amount=amount, valuation_date=valuation_date2, book_value=True) 174 | aft_BV = np.sum(np.asarray(bonds_portfolio.get_book_value_list(valuation_date2))) 175 | # Output 176 | self.assertEqual(bef_BV,aft_BV) 177 | 178 | # Restore previous values 179 | xw.sheets['ESG'].range('D5').value = temp_mk_name 180 | xw.sheets['ESG'].range('D3').value = temp_time_horizon 181 | xw.sheets['ESG'].range('D1').value = temp_number_trajectories 182 | xw.sheets['Market_Environment'].range('C4').value = temp_volatilité_IR 183 | 184 | def test_execute_unrealised_bonds(self): 185 | ## Input 186 | temp_mk_name = xw.sheets['ESG'].range('D5').value 187 | temp_time_horizon = xw.sheets['ESG'].range('D3').value 188 | temp_number_trajectories = xw.sheets['ESG'].range('D1').value 189 | temp_volatilité_IR = xw.sheets['Market_Environment'].range('C4').value 190 | 191 | time_horizon = 50 192 | 193 | xw.sheets['ESG'].range('D5').value = "EUR" 194 | xw.sheets['ESG'].range('D3').value = time_horizon 195 | xw.sheets['ESG'].range('D1').value = 1 196 | xw.sheets['Market_Environment'].range('C4').value = 0 197 | 198 | ESG_calibrate() 199 | ESG_generate_scenarios(modif = False) 200 | ESG = None 201 | with open(r'data\pickle\ESG_updated.pkl', 'rb') as input: 202 | ESG = pickle.load(input) 203 | 204 | spot_rates = ESG.asset_data.get_list_market("EUR").spot_rates 205 | test_bonds_target_allocation = np.asarray([ 206 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,7,8,9,10], 207 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7,8,9], 208 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7,8], 209 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7], 210 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 211 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 212 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 213 | ]) 214 | 215 | bonds_portfolio = Bonds_Portfolio(time_horizon = time_horizon, 216 | ESG_RN_scenarios_traj_i = ESG.scenarios[0], 217 | target_allocation= test_bonds_target_allocation, 218 | init_IR_curve= spot_rates) 219 | bonds_portfolio.initialize_allocation(amount=1000) 220 | bonds_portfolio.initialize_unit_bonds_book_value() 221 | bonds_portfolio.initialize_unit_bonds_market_value() 222 | 223 | valuation_date = 10 224 | 225 | for time_step in range(1, time_horizon): 226 | if len(bonds_portfolio.allocation_matrix) == time_step: 227 | bonds_portfolio.allocation_matrix.append(bonds_portfolio.allocation_matrix[-1]) 228 | 229 | new_num=[] 230 | for k in range(7): 231 | num_k = [] 232 | for t in range(time_step): 233 | num_k.append(bonds_portfolio.num_of_bonds[k][t,:]) 234 | num_k.append(np.zeros(20)) 235 | new_num.append(np.asarray(num_k)) 236 | bonds_portfolio.num_of_bonds = new_num 237 | 238 | num_of_bonds_temps =[] 239 | allocation_mat_temps =[] 240 | for k in range(7): 241 | num_of_bonds_k_temps = [] 242 | allocation_mat_k_temps =[] 243 | for k2 in range(8): 244 | num_of_bonds_k_temps.append(bonds_portfolio.num_of_bonds[k]*ESG.scenarios[0]['RN_migration_matrix'][time_step][k,k2]) 245 | # num_of_bonds_k_temps de dimension 8*une matrice(temps d'achat et maturité) 246 | allocation_mat_k_temps.append(bonds_portfolio.allocation_matrix[time_step][k,:]*ESG.scenarios[0]['RN_migration_matrix'][time_step][k,k2]) 247 | num_of_bonds_temps.append(num_of_bonds_k_temps) 248 | # num_of_bonds_temps de dimension 7*8*XXX 249 | allocation_mat_temps.append(allocation_mat_k_temps) 250 | 251 | # une somme sur k, selon k2, cela donne les nouveaux num_of_bonds et allocation_mat après migration 252 | num_of_bonds = sum(np.asarray(num_of_bonds_temps)) 253 | allocation_mat = sum(np.asarray(allocation_mat_temps)) 254 | # num_of_bonds de dim 8*XXX 255 | 256 | bonds_portfolio.num_of_bonds = num_of_bonds[:7] 257 | bonds_portfolio.allocation_matrix[time_step] = allocation_mat[:7] 258 | 259 | if time_step == valuation_date: 260 | bef_BV = np.sum(np.asarray(bonds_portfolio.get_book_value_list(valuation_date))) 261 | bef_MV = np.sum(np.asarray(bonds_portfolio.get_market_value_list(valuation_date))) 262 | 263 | PMVL = bef_MV - bef_BV 264 | if PMVL > 0.: 265 | amount = PMVL 266 | # Test 267 | bonds_portfolio.execute_unrealised_bonds_gain(amount= amount, valuation_date= valuation_date) 268 | aft_BV = np.sum(np.asarray(bonds_portfolio.get_book_value_list(valuation_date))) 269 | aft_MV = np.sum(np.asarray(bonds_portfolio.get_market_value_list(valuation_date))) 270 | # Output 271 | self.assertEqual(bef_BV,aft_BV) 272 | self.assertEqual(aft_MV,aft_BV) 273 | else: 274 | amount = np.absolute(PMVL) 275 | # Test 276 | bonds_portfolio.execute_unrealised_bonds_loss(amount= amount, valuation_date= valuation_date) 277 | aft_BV = np.sum(np.asarray(bonds_portfolio.get_book_value_list(valuation_date))) 278 | aft_MV = np.sum(np.asarray(bonds_portfolio.get_market_value_list(valuation_date))) 279 | # Output 280 | self.assertEqual(bef_BV,aft_BV) 281 | self.assertEqual(aft_MV,aft_BV) 282 | 283 | 284 | # Restore previous values 285 | xw.sheets['ESG'].range('D5').value = temp_mk_name 286 | xw.sheets['ESG'].range('D3').value = temp_time_horizon 287 | xw.sheets['ESG'].range('D1').value = temp_number_trajectories 288 | xw.sheets['Market_Environment'].range('C4').value = temp_volatilité_IR 289 | 290 | def Test_Assets(): 291 | suite = unittest.TestLoader().loadTestsFromTestCase(TestAssets) 292 | a = unittest.TextTestRunner(verbosity=0).run(suite) 293 | xw.sheets['Testing'].range('K2').clear_contents() 294 | xw.sheets['Testing'].range('K2').value = str(a) 295 | --------------------------------------------------------------------------------