financialkit.asset_pricing#
Williams’s dividend discount model and Gordon growth (through
financialkit.time_value.perpetuity_present_value()),
Modigliani-Miller and the weighted average cost of capital, and
Shiller’s variance bound; Sharpe’s CAPM with the market as the tangency
portfolio (financialkit.risk.safety_first_portfolio()), Jensen’s
alpha, Black’s zero-beta CAPM and the Gibbons-Ross-Shanken test;
time-series factor regressions (through
financialkit.time_series.ols()), Fama-MacBeth premia, Ross’s APT
and its arbitrage portfolios, and the characteristic sorts behind the
Fama-French and Carhart factors; Lucas’s tree, Breeden’s consumption
CAPM and the Mehra-Prescott economy; Harrison-Kreps state prices with
scipy.optimize.linprog and the Hansen-Jagannathan bound.
financialkit.asset_pricing: valuation, the CAPM, factor models, consumption-based pricing and the SDF.
Williams’s dividend discount model and Gordon growth (through
financialkit.time_value.perpetuity_present_value()),
Modigliani-Miller and the cost of capital, Shiller’s variance bound;
Sharpe’s CAPM with the market as the tangency portfolio, Jensen’s alpha,
Black’s zero-beta CAPM and the Gibbons-Ross-Shanken test; time-series
factor regressions (through financialkit.time_series.ols()),
Fama-MacBeth premia, Ross’s APT and arbitrage portfolios, and the
characteristic sorts behind Fama-French and Carhart factors; Lucas’s
tree, Breeden’s consumption CAPM and Mehra-Prescott’s equity premium
puzzle; Harrison-Kreps state prices and the Hansen-Jagannathan bound.
Return panels have shape (n_periods, n_assets), and the
consumption-based models use gross returns.
- class financialkit.asset_pricing.CAPMEquilibrium(market_weights, betas, expected_returns, risk_free, market_return, market_vol)[source]#
Bases:
objectThe CAPM equilibrium of a mean-variance market with a riskless asset.
- Parameters:
Expected market return minus the riskless rate.
- Type:
- class financialkit.asset_pricing.ConsumptionEconomy(beta, gamma)[source]#
Bases:
ABCAn endowment economy with a CRRA representative agent who eats the dividend of a Lucas tree.
- Parameters:
- class financialkit.asset_pricing.ConsumptionMoments(price_dividend, risk_free, equity_return, equity_vol, sdf_mean, sdf_vol)[source]#
Bases:
objectUnconditional moments of a consumption-based economy; all returns are gross.
- Parameters:
Expected equity return minus the riskless return.
- Type:
- class financialkit.asset_pricing.EconomyPath(growth, sdf, equity_return, risk_free)[source]#
Bases:
objectA simulated path of a consumption economy; returns are gross and dated at the period’s end.
- class financialkit.asset_pricing.ExcessVolatilityResult(price, rational_price, std_price, std_rational)[source]#
Bases:
objectShiller’s variance-bound comparison of actual and ex-post rational prices.
- class financialkit.asset_pricing.FactorPanel(excess_returns, factors, betas, alphas)[source]#
Bases:
objectExcess returns generated by \(r^e_t = \alpha + B f_t + \varepsilon_t\).
- class financialkit.asset_pricing.FactorRegressionResult(alphas, betas, alpha_se, r_squared, residuals)[source]#
Bases:
objectTime-series regressions \(r^e_{it} = \alpha_i + \beta_i' f_t + \varepsilon_{it}\), one per asset.
- Parameters:
- class financialkit.asset_pricing.FamaMacBethResult(gammas, premia, std_errors, betas)[source]#
Bases:
objectFama-MacBeth cross-sectional regressions \(r^e_{it} = \gamma_{0t} + \gamma_t' \beta_i + u_{it}\).
- premia: ndarray#
Time averages of the gammas: the zero-beta excess return, then the factor premia.
- Type:
ndarray, shape (k + 1,)
- class financialkit.asset_pricing.FiniteStateMarket(payoffs, prices, probabilities=None)[source]#
Bases:
objectA one-period market with finitely many states, traded payoffs and their prices.
- Parameters:
Examples
A bond paying 1 and a stock paying 2 or 0.5, priced 0.95 and 1:
>>> mkt = FiniteStateMarket([[1.0, 2.0], [1.0, 0.5]], [0.95, 1.0]) >>> mkt.state_prices().round(4).tolist(), mkt.is_complete, mkt.is_arbitrage_free() ([0.35, 0.6], True, True)
- arbitrage_portfolio()[source]#
Holdings that cost nothing or less and pay something and never lose, if any exist.
Solves a linear program over holdings
thetawith payoffsX theta >= 0and costp @ theta <= 0, maximising the total payoff minus the cost within a unit box. Returns zeros when the market is arbitrage free.- Return type:
- Returns:
ndarray, shape (N,)
- is_arbitrage_free(tol=1e-10)[source]#
Whether a strictly positive state-price vector exists (Harrison-Kreps).
- price_bounds(claim)[source]#
Lowest and highest arbitrage-free price of a new payoff.
The extremes of \(q' x\) over nonnegative state prices that price the traded assets. The bounds coincide in a complete market.
- state_prices()[source]#
A strictly positive state-price vector, unique when the market is complete.
- Return type:
- Returns:
ndarray, shape (S,)
- Raises:
ValueError – If the market admits an arbitrage.
- class financialkit.asset_pricing.GRSResult(statistic, p_value, df1, df2, alphas)[source]#
Bases:
objectGibbons, Ross and Shanken’s joint F-test that every alpha is zero.
- class financialkit.asset_pricing.HJBound(sdf_means, sdf_stds)[source]#
Bases:
objectHansen and Jagannathan’s lower bound on the volatility of a stochastic discount factor.
- class financialkit.asset_pricing.LucasTree(beta, gamma, mu=0.018, sigma=0.036)[source]#
Bases:
ConsumptionEconomyLucas’s tree with i.i.d. lognormal growth: every price in closed form.
Log consumption growth is \(g \sim N(\mu, \sigma^2)\), and the tree’s dividend is consumption. The price-dividend ratio is constant, \(P/D = \kappa / (1 - \kappa)\) with \(\kappa = \beta e^{(1-\gamma)\mu + (1-\gamma)^2\sigma^2/2}\), the riskless rate is \(\log R_f = -\log\beta + \gamma\mu - \gamma^2\sigma^2/2\), and the log equity premium is \(\log E[R_e] - \log R_f = \gamma\sigma^2\).
- Parameters:
Examples
>>> m = LucasTree(beta=0.99, gamma=2.0, mu=0.02, sigma=0.036).moments() >>> round(m.risk_free - 1, 4), round(m.equity_premium, 4) (0.0486, 0.0027)
- property kappa: float#
\(\beta E[G^{1-\gamma}]\), the discount applied to next period’s dividend.
- Type:
- class financialkit.asset_pricing.MehraPrescott(beta, gamma, mu=0.018, delta=0.036, phi=0.43)[source]#
Bases:
ConsumptionEconomyMehra and Prescott’s two-state Markov economy.
Gross consumption growth is \(\lambda_1 = 1 + \mu + \delta\) or \(\lambda_2 = 1 + \mu - \delta\), and the chain stays in its state with probability \(\phi\). The price-dividend ratios \(w_i\) solve \(w_i = \beta \sum_j \phi_{ij} \lambda_j^{1-\gamma}(w_j + 1)\), and the riskless return in state \(i\) is \(1 / (\beta \sum_j \phi_{ij} \lambda_j^{-\gamma})\). The defaults are their 1889-1978 calibration: mean growth 1.8%, standard deviation 3.6% and first-order autocorrelation -0.14.
- Parameters:
Examples
>>> m = MehraPrescott(beta=0.99, gamma=10.0).moments() >>> round(m.risk_free - 1, 3), round(m.equity_premium, 4) (0.131, 0.0269)
- property growth_autocorr: float#
First-order autocorrelation of consumption growth,
2 phi - 1.- Type:
- moments()[source]#
Unconditional moments of the riskless rate, the equity return and the SDF.
- Return type:
- price_dividend#
Price-dividend ratio in each state.
- Type:
ndarray, shape (2,)
- risk_free#
Gross riskless return from each state.
- Type:
ndarray, shape (2,)
- class financialkit.asset_pricing.ZeroBetaResult(market_weights, zero_beta_weights, zero_beta_rate, betas, expected_returns, market_return)[source]#
Bases:
objectBlack’s zero-beta CAPM for an efficient market portfolio, without a riskless asset.
- Parameters:
- financialkit.asset_pricing.apt_arbitrage_portfolio(expected_returns, betas)[source]#
A zero-cost, zero-beta portfolio that earns any deviation from linear pricing.
The weights are the residuals of the cross-sectional regression of expected returns on a constant and the betas. Being orthogonal to the constant, they sum to zero (no cost); being orthogonal to every beta, they carry no factor risk; and their expected return is the sum of squared residuals, positive unless the APT holds exactly. With many assets the idiosyncratic risk of this portfolio diversifies away, which is Ross’s argument that the residuals must be small.
- Parameters:
- Return type:
- Returns:
ndarray, shape (n,) – Weights; their expected return is
weights @ expected_returns.
Examples
>>> import numpy as np >>> w = apt_arbitrage_portfolio([0.05, 0.08, 0.13], [0.0, 1.0, 2.0]) >>> bool(np.isclose(w.sum(), 0.0, atol=1e-9)), bool(np.isclose(w @ [0.0, 1.0, 2.0], 0.0, atol=1e-9)) (True, True) >>> bool(np.isclose(w @ [0.05, 0.08, 0.13], 6.6667e-05, rtol=1e-4)) True
- financialkit.asset_pricing.apt_expected_returns(betas, premia, risk_free=0.0)[source]#
Ross’s linear pricing relation \(E[r_i] = r_f + \beta_i' \lambda\).
- Parameters:
- Return type:
- Returns:
float or ndarray, shape (n,)
Examples
>>> round(apt_expected_returns([1.2, 0.5], [0.05, 0.02], 0.03), 12) 0.1
- financialkit.asset_pricing.ar1_dividend_price(dividends, rate, rho, mean)[source]#
The rational price when dividends follow an AR(1) around a constant mean.
For \(D_{t+1} - \bar D = \rho (D_t - \bar D) + \varepsilon_{t+1}\), the expected present value of future dividends is \(\bar D / r + \rho (D_t - \bar D) / (1 + r - \rho)\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> round(ar1_dividend_price(1.0, 0.05, 0.9, 1.0), 12) 20.0
- financialkit.asset_pricing.capm_equilibrium(expected_returns, cov, risk_free)[source]#
The market portfolio and betas of a mean-variance economy with a riskless asset.
- Parameters:
- Return type:
- Returns:
CAPMEquilibrium – Every asset lies on the security market line:
sml_errorsis zero.
- financialkit.asset_pricing.capm_expected_return(beta, risk_free, market_return)[source]#
The security market line, \(r_f + \beta (E[r_m] - r_f)\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> round(capm_expected_return(1.5, 0.03, 0.08), 12) 0.105
- financialkit.asset_pricing.ccapm_expected_excess_return(consumption_beta, consumption_vol, gamma)[source]#
Breeden’s consumption CAPM, \(E[R_i] - R_f \approx \gamma\, \beta_{i,c}\, \sigma_c^2\).
The first-order form of \(E[R_i] - R_f = -R_f \mathrm{Cov}(m, R_i)\) with \(m \approx \beta(1 - \gamma \Delta c)\): the premium is risk aversion times the covariance of the return with consumption growth. It is exact for log returns in
LucasTree.- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> round(ccapm_expected_excess_return(2.0, 0.02, 5.0), 12) 0.004
- financialkit.asset_pricing.dividend_discount_value(dividends, rate, terminal_growth=None)[source]#
Williams’s investment value: the present value of a share’s dividends.
- Parameters:
dividends (
ArrayLike) – Dividends forecast for periods1 .. n.rate (
float) – Required return per period.terminal_growth (
float|None) – If given, dividends after periodngrow at this rate forever, and their Gordon value atnis discounted back and added. If omitted, the share is worth only thendividends shown.
- Return type:
- Returns:
float
Examples
Three dividends of 1, 1.1 and 1.2, then 3% growth, at a 10% required return:
>>> round(dividend_discount_value([1.0, 1.1, 1.2], 0.10, terminal_growth=0.03), 4) 15.9858
- financialkit.asset_pricing.estimate_beta(returns, market_returns)[source]#
Sample beta \(\mathrm{Cov}(r_i, r_m) / \mathrm{Var}(r_m)\).
- Parameters:
- Return type:
- Returns:
float or ndarray, shape (n,)
Examples
>>> estimate_beta([0.02, -0.04, 0.06], [0.01, -0.02, 0.03]) 2.0
- financialkit.asset_pricing.ex_post_rational_price(dividends, rate, terminal_price)[source]#
Shiller’s \(p^*_t\): the value of the dividends that were actually paid.
\(p^*_t = (p^*_{t+1} + D_{t+1}) / (1 + r)\), run backwards from \(p^*_T\) =
terminal_price.- Parameters:
- Return type:
- Returns:
ndarray, shape (T,)
Examples
>>> ex_post_rational_price([1.0, 1.0, 1.0], 0.0, 5.0).tolist() [7.0, 6.0, 5.0]
- financialkit.asset_pricing.fama_macbeth(excess_returns, factors=None, betas=None)[source]#
Fama and MacBeth’s two-pass estimate of factor risk premia.
First pass: each asset’s betas, from full-sample time-series regressions on
factors(or supplied directly asbetas). Second pass: each period, a cross-sectional regression of the excess returns on a constant and the betas. The premia are the time averages of the slopes, with standard errors from their time-series variation, which absorbs cross-sectional correlation of the errors.- Parameters:
- Return type:
- Returns:
FamaMacBethResult –
premia[0]is the average zero-beta excess return (zero if the model holds),premia[1:]the factor premia.
- financialkit.asset_pricing.gordon_growth_price(next_dividend, rate, growth)[source]#
Gordon’s price of a share whose dividends grow at a constant rate, \(D_1 / (r - g)\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> gordon_growth_price(2.0, 0.09, 0.04) 40.0
- financialkit.asset_pricing.grs_test(excess_returns, factors)[source]#
Gibbons, Ross and Shanken’s F-test that all time-series alphas are zero.
\[F = \frac{T - N - K}{N}\, \frac{\hat\alpha' \hat\Sigma^{-1} \hat\alpha}{1 + \bar f' \hat\Omega^{-1} \bar f} \sim F_{N,\, T - N - K}\]with \(\hat\Sigma\) and \(\hat\Omega\) the maximum-likelihood residual and factor covariances. The statistic measures how much the squared Sharpe ratio of the tangency portfolio improves when the test assets are added to the factors.
- financialkit.asset_pricing.hansen_jagannathan_bound(mean_returns, cov, sdf_means)[source]#
The smallest volatility of a discount factor that prices a set of gross returns.
For \(E[m R] = 1\) with \(E[m] = v\), \(\sigma^2(m) \ge (\mathbf 1 - v \mu)' \Sigma^{-1} (\mathbf 1 - v \mu)\), attained by \(m^* = v + (\mathbf 1 - v\mu)'\Sigma^{-1}(R - \mu)\).
- Parameters:
- Return type:
- Returns:
HJBound
Examples
One asset: the bound is \(|1 - v\mu| / \sigma\).
>>> hansen_jagannathan_bound([1.07], [[0.0289]], [0.99]).sdf_stds.round(4).tolist() [0.3488]
- financialkit.asset_pricing.implied_cost_of_equity(price, next_dividend, growth)[source]#
The required return that the Gordon model reads off a price, \(D_1 / P + g\).
- Parameters:
- Return type:
- Returns:
float or ndarray – Dividend yield plus growth.
Examples
>>> round(implied_cost_of_equity(40.0, 2.0, 0.04), 12) 0.09
- financialkit.asset_pricing.implied_risk_aversion(equity_premium, equity_vol, consumption_vol, correlation=1.0)[source]#
The risk aversion that the consumption CAPM needs to explain a premium.
\(\gamma = \dfrac{E[R_e] - R_f}{\rho\, \sigma_e\, \sigma_c}\): the Sharpe ratio divided by the volatility of consumption growth, scaled up by the imperfect correlation between the two.
- Parameters:
- Return type:
- Returns:
float
Examples
Mehra and Prescott’s sample (premium 6.18%, equity volatility 16.54%, consumption volatility 3.57%), with perfect correlation:
>>> round(implied_risk_aversion(0.0618, 0.1654, 0.0357), 1) 10.5
- financialkit.asset_pricing.jensen_alpha(excess_returns, market_excess_returns)[source]#
Jensen’s alpha: the intercept of a regression of excess returns on the market’s.
- Parameters:
- Return type:
- Returns:
FactorRegressionResult –
alphasper period and their t-statisticsalpha_t.
- financialkit.asset_pricing.levered_cost_of_equity(asset_return, debt_return, debt_to_equity, tax_rate=0.0)[source]#
Modigliani-Miller Proposition II: the expected return on levered equity.
\(r_E = r_A + (r_A - r_D)(1 - t) D / E\).
- Parameters:
asset_return (
ArrayLike) – Required return on the firm’s assets (the unlevered cost of capital).debt_return (
ArrayLike) – Required return on its debt.debt_to_equity (
ArrayLike) – Market-value leverage \(D / E\).tax_rate (
float) – Corporate tax rate;0gives the 1958 proposition, a positive value the 1963 correction with perpetual debt.
- Return type:
- Returns:
float or ndarray
Examples
>>> round(levered_cost_of_equity(0.10, 0.05, 1.0), 12) 0.15
- financialkit.asset_pricing.levered_firm_value(unlevered_value, debt, tax_rate=0.0)[source]#
Modigliani-Miller Proposition I: \(V_L = V_U + t D\).
Without taxes the value of the firm does not depend on its debt; with a corporate tax and permanent debt the tax shield adds \(t D\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> levered_firm_value(100.0, 40.0), levered_firm_value(100.0, 40.0, tax_rate=0.25) (100.0, 110.0)
- financialkit.asset_pricing.long_short_factor(signal, returns, quantile=0.3)[source]#
Return of the top
quantileof assets bysignalminus the bottomquantile.- Parameters:
- Return type:
- Returns:
ndarray, shape (T,)
Examples
>>> round(float(long_short_factor([1.0, 2.0, 3.0, 4.0], [[0.01, 0.02, 0.03, 0.05]], 0.5)[0]), 12) 0.025
- financialkit.asset_pricing.max_sharpe_ratio(expected_excess_returns, cov)[source]#
The largest Sharpe ratio of any portfolio, \(\sqrt{\mu' \Sigma^{-1} \mu}\).
Hansen and Jagannathan’s bound in its simplest form: \(\sigma(m)/E(m)\) is at least this number.
Examples
>>> round(max_sharpe_ratio([0.06], [[0.04]]), 12) 0.3
- financialkit.asset_pricing.momentum_signal(returns, lookback=12, skip=1)[source]#
Jegadeesh-Titman momentum: the compound return over months
t - lookbacktot - skip - 1.- Parameters:
- Return type:
- Returns:
ndarray, shape (T, n) – Signal available at the start of each period; the first
lookbackrows arenan.
Examples
>>> import numpy as np >>> momentum_signal(np.full((4, 1), 0.1), lookback=3, skip=1)[3].round(12).tolist() [0.21]
- financialkit.asset_pricing.simulate_factor_model(n_periods, betas, factor_means, factor_cov, idio_vol=0.05, alphas=None, rng=None)[source]#
Normal factor returns and independent normal residuals.
- Parameters:
- Return type:
- Returns:
FactorPanel
Examples
>>> panel = simulate_factor_model(120, [0.5, 1.0, 1.5], [0.006], [[0.002]], rng=0) >>> panel.excess_returns.shape, panel.factors.shape ((120, 3), (120, 1))
- financialkit.asset_pricing.sort_portfolios(signal, returns, n_groups=10)[source]#
Equal-weighted returns of portfolios formed on
signal, lowest group first.- Parameters:
- Return type:
- Returns:
ndarray, shape (T, n_groups) –
nanin periods with fewer valid assets than groups.
Examples
>>> sort_portfolios([3.0, 1.0, 2.0, 4.0], [[0.3, 0.1, 0.2, 0.4]], n_groups=2).tolist() [[0.15000000000000002, 0.35]]
- financialkit.asset_pricing.sustainable_growth(return_on_equity, payout_ratio)[source]#
Gordon’s growth rate of a firm that reinvests its retained earnings, \(g = b \cdot ROE\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
>>> round(sustainable_growth(0.15, 0.6), 12) 0.06
- financialkit.asset_pricing.time_series_regression(excess_returns, factors)[source]#
Regress each asset’s excess return on the factors, with an intercept.
This is the test of a model whose factors are traded excess returns: the CAPM with the market, Fama and French’s three factors, or Carhart’s four. A single factor reproduces Jensen’s alpha.
- Parameters:
- Return type:
- Returns:
FactorRegressionResult
Examples
>>> import numpy as np >>> f = np.array([0.01, -0.02, 0.03, 0.00, 0.02]) >>> res = time_series_regression(0.001 + 1.5 * f, f) >>> res.alphas.round(12).tolist(), res.betas.round(12).tolist() ([0.001], [[1.5]])
- financialkit.asset_pricing.variance_bound(price, dividends, rate, terminal_price=None)[source]#
Compare the volatility of a price with that of its ex-post rational price.
If \(p_t = E_t[p^*_t]\), then \(p^*_t = p_t + u_t\) with a forecast error \(u_t\) uncorrelated with \(p_t\), so \(\sigma(p) \le \sigma(p^*)\).
- Parameters:
- Return type:
- Returns:
ExcessVolatilityResult
- financialkit.asset_pricing.wacc(equity_return, debt_return, debt_to_value, tax_rate=0.0)[source]#
Weighted average cost of capital, \((1 - L) r_E + L r_D (1 - t)\) with \(L = D / V\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
With the equity return from Proposition II, the WACC is the asset return:
>>> round(wacc(0.15, 0.05, 0.5), 12) 0.1
- financialkit.asset_pricing.zero_beta_capm(expected_returns, cov, zero_beta_rate)[source]#
Black’s CAPM without a riskless asset.
Any efficient portfolio \(m\) prices every asset linearly, \(E[r_i] = E[r_z] + \beta_{im} (E[r_m] - E[r_z])\), where \(z\) is the minimum-variance portfolio uncorrelated with \(m\). Here the market is the efficient portfolio whose zero-beta rate is
zero_beta_rate: the frontier point where a tangent from that rate touches it.- Parameters:
- Return type:
- Returns:
ZeroBetaResult