# Path: esg/generator_correlated_variables.py
# def generator_correlated_variables(corr_matrix, time_horizon, fixed_seed, method = 'cholesky'):
#     np.random.seed(fixed_seed)
#     ran = np.random.standard_normal((len(corr_matrix), time_horizon))
#         
#     if method == 'cholesky':
#         c = cholesky(corr_matrix, lower = True)
#     else:
#         evals, evecs = eigh(corr_matrix)
#         c = np.dot(evecs, np.diag(np.sqrt(evals)))
#     dw = np.dot(c, ran)
#     return dw
# 
# Path: esg/interest_rate/IR_model_util.py
# class IR_model_util(IR_model):
#     """
#     Document the role of this class TO DO
#     """
#     def __init__(self):
#         pass
# 
#     @staticmethod
#     def swap_rate_and_annuity(maturity, tenor, valuation_date, zcb_price):
#         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))
#     
#     @staticmethod
#     def DF(trajectory, begin, end):
#         v = [trajectory[t] for t in range(begin, end)]
#         return exp(-sum(v))
#         
#     @staticmethod
#     def discount_factor_custom(trajectory, begin, end):
#         v = [(trajectory[t] + trajectory[t+1])/2 for t in range(begin, end)]
#         return exp(-sum( v))
# 
#     def discount_factors(self, trajectory):
#         return [self.discount_factor_custom(trajectory, 0, t) for t in range(1, len(trajectory))]
# 
#     def test_martingale(self):
#         """
#         This method should be put somewhere else TO DO
#         """
#         for time_step in range(1, self.time_horizon):
#             discount_factor = []
#             epsilon = []
#             i = 1
#             while i <= self.N:
#                 self.get_IR_curve()
#                 discount_factor.append(self.discount_factor(begin = 0, end = time_step))
#                 i += 1
#             epsilon.append(np.absolute(np.mean(discount_factor) - self.zcb_price[0,time_step]))
#         return np.amax(epsilon)
# 
#     def get_IR_curve(self):
#         """This method returns IR term structures per time steps and trajectories"""
#         pass
# 
#     def get_deflator(self):
#         """This method returns deflators per time steps and trajectories"""
#         pass
# 
#     def calibrate(self, asset_data):
#         """Calibrate the IR model onto the market environment
#             This method returns the IR_curve and deflator"""
#         pass
# 
# Path: esg/interest_rate/IR_model_functions.py
# def diff(y, x):
#     n = len(y)
#     d = np.zeros(n-1,'d')
#     
#     for i in range(n-1):
#         d[i] = (y[i+1] - y[i])/(x[i+1] - x[i])
#     return d


from ..generator_correlated_variables import generator_correlated_variables
from .IR_model_util import IR_model_util
from .IR_model_functions import diff
from math import exp
import numpy as np
import pickle
        self.theta = derivf + np.dot(self.a, self.forward_rate[:-1]) + [self.sigma**2/(2*self.a)*(1 - exp(-2*self.a*t)) 
                                                                        for t in range(len(self.forward_rate)-1)]

    def get_IR_curve(self):
        """
            Method: get_IR_curve

            Function: return Interest Rate term structures, zero-bond curve per time step and its trajectories over time horizon

            Parameters: None
        """
        dw = generator_correlated_variables(corr_matrix = self.corr_matrix, time_horizon = self.time_horizon, fixed_seed = self.fixed_seed)
        # ===================================
        # Generate IR trajectory
        # ===================================
        self.trajectory = [self.forward_rate[0]]
        for time_step in range(1, self.time_horizon+1):
            dr = (self.theta[time_step-1] - self.a * self.trajectory[-1]) + self.sigma * dw[0, time_step-1]
            self.trajectory.append(self.trajectory[-1] + dr)
        # ====================================
        # Generate IR zero coupon curve
        # ====================================
        self.zcb_price = np.ones(shape = (self.time_horizon+30, len(self.spot_rate)))
        self.curves = np.ones(shape = (self.time_horizon+30, len(self.spot_rate)))
        zcb0 = [self.DF(trajectory = self.forward_rate, begin = 1, end = t) for t in range(2, len(self.spot_rate)+1)]
        zcb0.append(zcb0[-1]) # Linear extrapolation zero coupon price curve
        self.zcb_price[0,:] = zcb0
        
        self.curves[0,:] = [round(np.power(zcb0[t-1], -1/t) -1,5) for t in range(1, len(self.spot_rate)+1)]
        

# Path: esg/generator_correlated_variables.py
# def generator_correlated_variables(corr_matrix, time_horizon, fixed_seed, method = 'cholesky'):
#     np.random.seed(fixed_seed)
#     ran = np.random.standard_normal((len(corr_matrix), time_horizon))
#         
#     if method == 'cholesky':
#         c = cholesky(corr_matrix, lower = True)
#     else:
#         evals, evecs = eigh(corr_matrix)
#         c = np.dot(evecs, np.diag(np.sqrt(evals)))
#     dw = np.dot(c, ran)
#     return dw
# 
# Path: esg/interest_rate/IR_model_util.py
# class IR_model_util(IR_model):
#     """
#     Document the role of this class TO DO
#     """
#     def __init__(self):
#         pass
# 
#     @staticmethod
#     def swap_rate_and_annuity(maturity, tenor, valuation_date, zcb_price):
#         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))
#     
#     @staticmethod
#     def DF(trajectory, begin, end):
#         v = [trajectory[t] for t in range(begin, end)]
#         return exp(-sum(v))
#         
#     @staticmethod
#     def discount_factor_custom(trajectory, begin, end):
#         v = [(trajectory[t] + trajectory[t+1])/2 for t in range(begin, end)]
#         return exp(-sum( v))
# 
#     def discount_factors(self, trajectory):
#         return [self.discount_factor_custom(trajectory, 0, t) for t in range(1, len(trajectory))]
# 
#     def test_martingale(self):
#         """
#         This method should be put somewhere else TO DO
#         """
#         for time_step in range(1, self.time_horizon):
#             discount_factor = []
#             epsilon = []
#             i = 1
#             while i <= self.N:
#                 self.get_IR_curve()
#                 discount_factor.append(self.discount_factor(begin = 0, end = time_step))
#                 i += 1
#             epsilon.append(np.absolute(np.mean(discount_factor) - self.zcb_price[0,time_step]))
#         return np.amax(epsilon)
# 
#     def get_IR_curve(self):
#         """This method returns IR term structures per time steps and trajectories"""
#         pass
# 
#     def get_deflator(self):
#         """This method returns deflators per time steps and trajectories"""
#         pass
# 
#     def calibrate(self, asset_data):
#         """Calibrate the IR model onto the market environment
#             This method returns the IR_curve and deflator"""
#         pass
# 
# Path: esg/interest_rate/IR_model_functions.py
# def diff(y, x):
#     n = len(y)
#     d = np.zeros(n-1,'d')
#     
#     for i in range(n-1):
#         d[i] = (y[i+1] - y[i])/(x[i+1] - x[i])
#     return d


# Path: esg/interest_rate/Hull_White_one_factor.py
from ..generator_correlated_variables import generator_correlated_variables
from .IR_model_util import IR_model_util
from .IR_model_functions import diff
from math import exp
import numpy as np
import pickle

        self.theta = derivf + np.dot(self.a, self.forward_rate[:-1]) + [self.sigma**2/(2*self.a)*(1 - exp(-2*self.a*t)) 
                                                                        for t in range(len(self.forward_rate)-1)]

    def get_IR_curve(self):
        """
            Method: get_IR_curve

            Function: return Interest Rate term structures, zero-bond curve per time step and its trajectories over time horizon

            Parameters: None
        """
        dw = generator_correlated_variables(corr_matrix = self.corr_matrix, time_horizon = self.time_horizon, fixed_seed = self.fixed_seed)
        # ===================================
        # Generate IR trajectory
        # ===================================
        self.trajectory = [self.forward_rate[0]]
        for time_step in range(1, self.time_horizon+1):
            dr = (self.theta[time_step-1] - self.a * self.trajectory[-1]) + self.sigma * dw[0, time_step-1]
            self.trajectory.append(self.trajectory[-1] + dr)
        # ====================================
        # Generate IR zero coupon curve
        # ====================================
        self.zcb_price = np.ones(shape = (self.time_horizon+30, len(self.spot_rate)))
        self.curves = np.ones(shape = (self.time_horizon+30, len(self.spot_rate)))
        zcb0 = [self.DF(trajectory = self.forward_rate, begin = 1, end = t) for t in range(2, len(self.spot_rate)+1)]
        zcb0.append(zcb0[-1]) # Linear extrapolation zero coupon price curve
        self.zcb_price[0,:] = zcb0
        
        self.curves[0,:] = [round(np.power(zcb0[t-1], -1/t) -1,5) for t in range(1, len(self.spot_rate)+1)]
        

        forward_rate = - diff(np.log(zcb0), range(1,len(self.spot_rate)+1))