def __init__(self, mc, G_S, G_d, d_inits=None): self.mc = mc self.n = mc.n self.P = mc.P self.mf_S = MultFunctionalFiniteMarkov(self.mc, G_S) self.mf_d = MultFunctionalFiniteMarkov(self.mc, G_d, M_inits=d_inits) self.P_hat = self.P * self.mf_S.M_matrix self.P_tilde = self.P_hat * self.mf_d.M_matrix if not self._check_spectral_radius(): msg = 'P_tilde has an eigenvalue not smaller than one' # Ignored by warnings.filterwarnings('ignore') # somewhere in qunatecon # warnings.warn(msg, UserWarning) print('Warning:', msg) # Solve the linear equation v = P_tilde v + P_hat 1 A = np.identity(self.n) - self.P_tilde b = self.P_hat.dot(np.ones(self.n)) self.v = np.linalg.solve(A, b)
class AssetPricingMultFiniteMarkov(object): """ Class representing the asset pricing model with stochastic discount factor and dividend processes governed by a multiplicative functional. Parameters ---------- mc : MarkovChain MarkovChain instance with n states representing the underlying `X` process. G_S : array_like(float, ndim=2) Discount rate matrix. Must be of shape n x n. G_d : array_like(float, ndim=2) Growth rate matrix for the dividend. Must be of shape n x n. d_inits : array_like(float, ndim=1), optional(default=None) Array containing the initial values of the dividend, one for each state. Must be of length n. If not specified, default to the vector of all ones. If it is a scalar, then it is converted to the constant vector of that scalar. Attributes ---------- mc : MarkovChain See Parameters. G_S, G_d : ndarray(float, ndim=2) See Parameters. d_inits: ndarray(float, ndim=1) See Parameters. n : scalar(int) Number of the state. P : ndarray(float, ndim=2) Transition probability matrix of `mc`. mf_S : MultFunctionalFiniteMarkov MultFunctionalFiniteMarkov instance for the stochastic discount factor process. mf_d : MultFunctionalFiniteMarkov MultFunctionalFiniteMarkov instance for the dividend process. v : ndarray(float, ndim=1) Dividend-price ratios. """ def __init__(self, mc, G_S, G_d, d_inits=None): self.mc = mc self.n = mc.n self.P = mc.P self.mf_S = MultFunctionalFiniteMarkov(self.mc, G_S) self.mf_d = MultFunctionalFiniteMarkov(self.mc, G_d, M_inits=d_inits) self.P_hat = self.P * self.mf_S.M_matrix self.P_tilde = self.P_hat * self.mf_d.M_matrix if not self._check_spectral_radius(): msg = 'P_tilde has an eigenvalue not smaller than one' # Ignored by warnings.filterwarnings('ignore') # somewhere in qunatecon # warnings.warn(msg, UserWarning) print('Warning:', msg) # Solve the linear equation v = P_tilde v + P_hat 1 A = np.identity(self.n) - self.P_tilde b = self.P_hat.dot(np.ones(self.n)) self.v = np.linalg.solve(A, b) def _check_spectral_radius(self): """ Check that the eigenvalues of P_tilde are smaller than one. Under the premise that P_tilde is nonnegative, this implies that I - P_tilde is inverse positive. """ eig_vals, _ = np.linalg.eig(self.P_tilde) return (eig_vals < 1).all() def simulate(self, ts_length, X_init=None, num_reps=None, random_state=None): """ Simulate the discount factor and dividend processes. Parameters ---------- ts_length : scalar(int) Length of each simulation. X_init : scalar(int), optional(default=None) Initial state of the `X` process. If None, the initial state is randomly drawn. num_reps : scalar(int), optional(default=None) Number of repetitions of simulation. random_state : scalar(int) or np.random.RandomState, optional(default=None) Random seed (integer) or np.random.RandomState instance to set the initial state of the random number generator for reproducibility. If None, a randomly initialized RandomState is used. Returns ------- res: APSMFMSimulateResult Simulation result represetned as a `APSMFMSimulateResult`. See `APSMFMSimulateResult` for details. The array for each attribute is of shape `(ts_length,)` if `num_reps=None`, or of shape `(num_reps, ts_length)` otherwise. """ X = self.mc.simulate(ts_length, init=X_init, num_reps=num_reps, random_state=random_state) return self.generate_paths(X) def generate_paths(self, X): """ Given a simulation of the `X` process, generate sample paths of the discount factor and dividend processes. Parameters ---------- X : array_like(int) Array containing the sample path(s) of the `X` process. Returns ------- res: APSMFMSimulateResult Simulation result represetned as a `APSMFMSimulateResult`. See `APSMFMSimulateResult` for details. The array for each attribute is of the same shape as `X`. """ res_S = self.mf_S.generate_paths(X) res_d = self.mf_d.generate_paths(X) S, S_tilde = res_S.M, res_S.M_tilde d, d_tilde = res_d.M, res_d.M_tilde p = d * self.v[X] res = APSMFMSimulateResult(X=X, S=S, S_tilde=S_tilde, d=d, d_tilde=d_tilde, p=p) return res