Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConsRiskySolver #1108

Merged
merged 61 commits into from
Jul 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
16a4f98
create ConsRiskySolver
alanlujan91 Jan 25, 2022
d94c2d9
Update ConsRiskyAssetModel.py
alanlujan91 Jan 31, 2022
e71a6bc
Update ConsRiskyAssetModel.py
alanlujan91 Jan 31, 2022
665bac1
Update ConsRiskyAssetModel.py
alanlujan91 Jan 31, 2022
21f6e20
Update ConsRiskyAssetModel.py
alanlujan91 Jan 31, 2022
aa74667
Update ConsIndShockModel.py
alanlujan91 Jan 31, 2022
394613a
Update ConsRiskyAssetModel.py
alanlujan91 Jan 31, 2022
a156122
Update ConsRiskyAssetModel.py
alanlujan91 Jan 31, 2022
135a563
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Jan 31, 2022
fa29229
add IndepDstnBool
alanlujan91 Jan 31, 2022
2463e8e
Update ConsRiskyAssetModel.py
alanlujan91 Jan 31, 2022
05b0216
Update ConsIndShockModel.py
alanlujan91 Feb 2, 2022
6fffd00
Update ConsRiskyAssetModel.py
alanlujan91 Feb 2, 2022
bb10b78
Update ConsRiskyAssetModel.py
alanlujan91 Feb 2, 2022
b2194df
Update ConsRiskyAssetModel.py
alanlujan91 Feb 2, 2022
989bb54
Create example_ConsRiskyAssetModel.py
alanlujan91 Feb 2, 2022
074c810
Update ConsRiskyAssetModel.py
alanlujan91 Feb 2, 2022
5b40457
move example
alanlujan91 Feb 2, 2022
d40f327
Update example notebook
alanlujan91 Feb 2, 2022
43645ba
Optimize share by linear interpolation
alanlujan91 Feb 2, 2022
3c0c903
Update ShareGrid
alanlujan91 Feb 11, 2022
d83b0c8
Remove limits
alanlujan91 Feb 11, 2022
3247549
Update borrowing constraint
alanlujan91 Feb 11, 2022
719dad3
Calculate preIncShkvPfunc
alanlujan91 Feb 11, 2022
aac7edf
Use preInc and preRisky solvers
alanlujan91 Feb 11, 2022
8e6720a
Update vFunc
alanlujan91 Feb 11, 2022
8c0a44c
Update ConsBasicPortfolioSolver
alanlujan91 Feb 11, 2022
83b73fe
Add RiskyReturnGivenFixedPortfolioShareType
alanlujan91 Feb 11, 2022
bdc2ca6
Add Fixed Share solver
alanlujan91 Feb 11, 2022
23b9feb
Update example_ConsRiskyAssetModel.ipynb
alanlujan91 Feb 11, 2022
bbf9dc3
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Feb 11, 2022
e639c4e
Update ConsPortfolio
alanlujan91 Feb 12, 2022
e47f383
Update ConsRiskyAssetModel.py
alanlujan91 Feb 12, 2022
a655627
Update RiskyAsset Examples
alanlujan91 Feb 12, 2022
48f50b3
Solve some tests
alanlujan91 Feb 12, 2022
cad4ca3
Update ConsRiskyAssetModel.py
alanlujan91 Feb 15, 2022
ffe00a9
Changes with CDC
alanlujan91 Feb 23, 2022
c04144d
Rfree not time varying yet
alanlujan91 Feb 24, 2022
7db9a04
ShareGrid from 0.0
alanlujan91 Feb 25, 2022
b339653
fix exponent bug
alanlujan91 Feb 25, 2022
79f56ba
fix notebook
alanlujan91 Feb 25, 2022
d66344e
remove limit mechanism until it's figured out
alanlujan91 Feb 25, 2022
e8f3057
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Mar 2, 2022
be5f870
Initial distribution engines
alanlujan91 Mar 2, 2022
7a4d0b7
Make Rfree time varying
alanlujan91 Mar 8, 2022
771c4e7
fix Rfree bug
alanlujan91 Mar 8, 2022
235c07d
update tests
alanlujan91 Mar 22, 2022
ec95618
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Mar 22, 2022
aa69b35
update notebooks
alanlujan91 Mar 22, 2022
488c740
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Apr 12, 2022
e87c60a
cFunc Limit
alanlujan91 Apr 12, 2022
c5a48de
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Apr 20, 2022
af0800e
finalize risky asset consumer type
alanlujan91 Apr 20, 2022
ec556b7
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Apr 22, 2022
0fdf887
add doc to PortfolioBool
alanlujan91 Apr 26, 2022
1c87d99
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Jun 28, 2022
f103063
fix calc_expectations bug
alanlujan91 Jun 29, 2022
0f87675
update changelog
alanlujan91 Jun 29, 2022
10bf946
add PortfolioBisect option to be implemented later
alanlujan91 Jun 30, 2022
23b703d
fix bugs
alanlujan91 Jun 30, 2022
94159d6
Merge remote-tracking branch 'upstream/master' into risky_cism
alanlujan91 Jul 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Release Date: TBD
* General control transitions based on decision rules in `FrameAgentType`. [#1117](https://github.com/econ-ark/HARK/pull/1117)
* Adds `distr_of_function` tool to calculate the distribution of a function of a discrete random variable. [#1144](https://github.com/econ-ark/HARK/pull/1144)
* Changes the `DiscreteDistribution` class to allow for arbitrary array-valued random variables. [#1146](https://github.com/econ-ark/HARK/pull/1146)
* Adds `IndShockRiskyAssetConsumerType` as agent which can invest savings all in safe asset, all in risky asset, a fixed share in risky asset, or optimize its portfolio. [#1107](https://github.com/econ-ark/HARK/issues/1107)
* Updates all HARK models to allow for age-varying interest rates. [#1150](https://github.com/econ-ark/HARK/pull/1150)


Expand Down
68 changes: 42 additions & 26 deletions HARK/ConsumptionSaving/ConsIndShockModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ def def_utility_funcs(self):
None
"""
self.u = lambda c: utility(c, gam=self.CRRA) # utility function
self.uP = lambda c: utilityP(c, gam=self.CRRA) # marginal utility function
# marginal utility function
self.uP = lambda c: utilityP(c, gam=self.CRRA)
self.uPP = lambda c: utilityPP(
c, gam=self.CRRA
) # marginal marginal utility function
Expand Down Expand Up @@ -384,7 +385,8 @@ def make_cFunc_PF(self):

# Add two attributes to enable calculation of steady state market resources.
self.Ex_IncNext = 1.0 # Perfect foresight income of 1
self.mNrmMinNow = mNrmNow[0] # Relabeling for compatibility with add_mNrmStE
# Relabeling for compatibility with add_mNrmStE
self.mNrmMinNow = mNrmNow[0]

def add_mNrmTrg(self, solution):
"""
Expand Down Expand Up @@ -843,7 +845,8 @@ def m_nrm_next(self, shocks, a_nrm):
Parameters
----------
shocks: [float]
Permanent and transitory income shock levels. a_nrm: float
Permanent and transitory income shock levels.
a_nrm: float
Normalized market assets this period

Returns
Expand Down Expand Up @@ -1035,9 +1038,11 @@ def add_stable_points(self, solution):
self.GICNrm = GICNrm

if GICRaw:
solution = self.add_mNrmStE(solution) # find steady state m, if it exists
# find steady state m, if it exists
solution = self.add_mNrmStE(solution)
if GICNrm:
solution = self.add_mNrmTrg(solution) # find target m, if it exists
# find target m, if it exists
solution = self.add_mNrmTrg(solution)

return solution

Expand Down Expand Up @@ -1075,10 +1080,9 @@ def solve(self):
solution : ConsumerSolution
The solution to the one period problem.
"""
self.aNrmNow = np.asarray(self.aXtraGrid) + self.BoroCnstNat
aNrm = self.aNrmNow
aNrmNow = self.prepare_to_calc_EndOfPrdvP()
EndOfPrdvP = self.calc_EndOfPrdvP()
solution = self.make_basic_solution(EndOfPrdvP, aNrm, self.make_linear_cFunc)
solution = self.make_basic_solution(EndOfPrdvP, aNrmNow, self.make_linear_cFunc)
solution = self.add_MPC_and_human_wealth(solution)
solution = self.add_stable_points(solution)

Expand Down Expand Up @@ -1616,7 +1620,8 @@ def pre_solve(self):
self.update_solution_terminal() # Solve the terminal period problem

# Fill in BoroCnstArt and MaxKinks if they're not specified or are irrelevant.
if not hasattr(self, "BoroCnstArt"): # If no borrowing constraint specified...
# If no borrowing constraint specified...
if not hasattr(self, "BoroCnstArt"):
self.BoroCnstArt = None # ...assume the user wanted none

if not hasattr(self, "MaxKinks"):
Expand Down Expand Up @@ -1740,7 +1745,8 @@ def sim_birth(self, which_agents):
self.state_now["pLvl"][which_agents] = Lognormal(
pLvlInitMeanNow, self.pLvlInitStd, seed=self.RNG.randint(0, 2**31 - 1)
).draw(N)
self.t_age[which_agents] = 0 # How many periods since each agent was born
# How many periods since each agent was born
self.t_age[which_agents] = 0

if not hasattr(
self, "PerfMITShk"
Expand Down Expand Up @@ -1838,13 +1844,15 @@ def transition(self):
RfreeNow = self.get_Rfree()

# Calculate new states: normalized market resources and permanent income level
pLvlNow = pLvlPrev * self.shocks["PermShk"] # Updated permanent income level
# Updated permanent income level
pLvlNow = pLvlPrev * self.shocks["PermShk"]
# Updated aggregate permanent productivity level
PlvlAggNow = self.state_prev["PlvlAgg"] * self.PermShkAggNow
# "Effective" interest factor on normalized assets
ReffNow = RfreeNow / self.shocks["PermShk"]
bNrmNow = ReffNow * aNrmPrev # Bank balances before labor income
mNrmNow = bNrmNow + self.shocks["TranShk"] # Market resources after income
# Market resources after income
mNrmNow = bNrmNow + self.shocks["TranShk"]

return pLvlNow, PlvlAggNow, bNrmNow, mNrmNow, None

Expand Down Expand Up @@ -2065,25 +2073,28 @@ def check_conditions(self, verbose=None):
# Make a dictionary to specify an idiosyncratic income shocks consumer
init_idiosyncratic_shocks = dict(
init_perfect_foresight,
**{
# assets above grid parameters
**{ # assets above grid parameters
"aXtraMin": 0.001, # Minimum end-of-period "assets above minimum" value
"aXtraMax": 20, # Maximum end-of-period "assets above minimum" value
"aXtraNestFac": 3, # Exponential nesting factor when constructing "assets above minimum" grid
# Exponential nesting factor when constructing "assets above minimum" grid
"aXtraNestFac": 3,
"aXtraCount": 48, # Number of points in the grid of "assets above minimum"
"aXtraExtra": [
None
], # Some other value of "assets above minimum" to add to the grid, not used
# Income process variables
"PermShkStd": [0.1], # Standard deviation of log permanent income shocks
# Standard deviation of log permanent income shocks
"PermShkStd": [0.1],
"PermShkCount": 7, # Number of points in discrete approximation to permanent income shocks
"TranShkStd": [0.1], # Standard deviation of log transitory income shocks
# Standard deviation of log transitory income shocks
"TranShkStd": [0.1],
"TranShkCount": 7, # Number of points in discrete approximation to transitory income shocks
"UnempPrb": 0.05, # Probability of unemployment while working
"UnempPrbRet": 0.005, # Probability of "unemployment" while retired
"IncUnemp": 0.3, # Unemployment benefits replacement rate
"IncUnempRet": 0.0, # "Unemployment" benefits when retired
"BoroCnstArt": 0.0, # Artificial borrowing constraint; imposed minimum level of end-of period assets
# Artificial borrowing constraint; imposed minimum level of end-of period assets
"BoroCnstArt": 0.0,
"tax_rate": 0.0, # Flat income tax rate
"T_retire": 0, # Period of retirement (0 --> no retirement)
"vFuncBool": False, # Whether to calculate the value function during solution
Expand Down Expand Up @@ -2242,8 +2253,10 @@ def get_shocks(self):

N = np.sum(these)
if N > 0:
IncShkDstnNow = self.IncShkDstn[t] # set current income distribution
PermGroFacNow = self.PermGroFac[t] # and permanent growth factor
# set current income distribution
IncShkDstnNow = self.IncShkDstn[t]
# and permanent growth factor
PermGroFacNow = self.PermGroFac[t]
# Get random draws of income shocks from the discrete distribution
IncShks = IncShkDstnNow.draw(N)

Expand All @@ -2257,7 +2270,8 @@ def get_shocks(self):
N = np.sum(newborn)
if N > 0:
these = newborn
IncShkDstnNow = self.IncShkDstn[0] # set current income distribution
# set current income distribution
IncShkDstnNow = self.IncShkDstn[0]
PermGroFacNow = self.PermGroFac[0] # and permanent growth factor

# Get random draws of income shocks from the discrete distribution
Expand Down Expand Up @@ -2468,8 +2482,7 @@ def test(agent):
False: "\nThe given parameter values violate the Mortality Adjusted Aggregate Growth Imatience Condition; the GPFAggLivPrb is: {0.GPFAggLivPrb}",
}

verbose_messages = {
# (see {0.url}/#WRIC for more).',
verbose_messages = { # (see {0.url}/#WRIC for more).',
True: " Therefore, a target level of the ratio of aggregate market resources to aggregate permanent income exists.\n",
# (see {0.url}/#WRIC for more).'
False: " Therefore, a target ratio of aggregate resources to aggregate permanent income may not exist.\n",
Expand Down Expand Up @@ -2958,9 +2971,12 @@ def __init__(
**{
"Rboro": 1.20, # Interest factor on assets when borrowing, a < 0
"Rsave": 1.02, # Interest factor on assets when saving, a > 0
"BoroCnstArt": None, # kinked R is a bit silly if borrowing not allowed
"CubicBool": True, # kinked R is now compatible with linear cFunc and cubic cFunc
"aXtraCount": 48, # ...so need lots of extra gridpoints to make up for it
"BoroCnstArt": None,
# kinked R is a bit silly if borrowing not allowed
"CubicBool": True,
# kinked R is now compatible with linear cFunc and cubic cFunc
"aXtraCount": 48,
# ...so need lots of extra gridpoints to make up for it
}
)
del init_kinked_R["Rfree"] # get rid of constant interest factor
Expand Down
88 changes: 8 additions & 80 deletions HARK/ConsumptionSaving/ConsPortfolioModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from copy import deepcopy

import numpy as np
from scipy.optimize import minimize_scalar

from HARK import (
MetricObject,
Expand Down Expand Up @@ -173,6 +172,8 @@ def __init__(self, verbose=False, quiet=False, **kwds):
params.update(kwds)
kwds = params

self.PortfolioBool = True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the documentation for this variable? It could go in the docstring.

Why is this variable named in CamelCase rather than snake_case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to name it in a similar pattern to CubicBool and vFuncBool


# Initialize a basic consumer type
RiskyAssetConsumerType.__init__(self, verbose=verbose, quiet=quiet, **kwds)

Expand Down Expand Up @@ -244,82 +245,6 @@ def update_solution_terminal(self):
dvdsFuncFxd=dvdsFuncFxd_terminal,
)

def update_ShareGrid(self):
"""
Creates the attribute ShareGrid as an evenly spaced grid on [0.,1.], using
the primitive parameter ShareCount.

Parameters
----------
None

Returns
-------
None
"""
self.ShareGrid = np.linspace(0.0, 1.0, self.ShareCount)
self.add_to_time_inv("ShareGrid")

def update_ShareLimit(self):
"""
Creates the attribute ShareLimit, representing the limiting lower bound of
risky portfolio share as mNrm goes to infinity.

Parameters
----------
None

Returns
-------
None
"""
if "RiskyDstn" in self.time_vary:
self.ShareLimit = []
for t in range(self.T_cycle):
RiskyDstn = self.RiskyDstn[t]
temp_f = lambda s: -((1.0 - self.CRRA) ** -1) * np.dot(
(self.Rfree + s * (RiskyDstn.X - self.Rfree)) ** (1.0 - self.CRRA),
RiskyDstn.pmf,
)
SharePF = minimize_scalar(temp_f, bounds=(0.0, 1.0), method="bounded").x
self.ShareLimit.append(SharePF)
self.add_to_time_vary("ShareLimit")

else:
RiskyDstn = self.RiskyDstn
temp_f = lambda s: -((1.0 - self.CRRA) ** -1) * np.dot(
(self.Rfree + s * (RiskyDstn.X - self.Rfree)) ** (1.0 - self.CRRA),
RiskyDstn.pmf,
)
SharePF = minimize_scalar(temp_f, bounds=(0.0, 1.0), method="bounded").x
self.ShareLimit = SharePF
self.add_to_time_inv("ShareLimit")

def get_Rfree(self):
"""
Calculates realized return factor for each agent, using the attributes Rfree,
RiskyNow, and ShareNow. This method is a bit of a misnomer, as the return
factor is not riskless, but would more accurately be labeled as Rport. However,
this method makes the portfolio model compatible with its parent class.

Parameters
----------
None

Returns
-------
Rport : np.array
Array of size AgentCount with each simulated agent's realized portfolio
return factor. Will be used by get_states() to calculate mNrmNow, where it
will be mislabeled as "Rfree".
"""
Rport = (
self.controls["Share"] * self.shocks["Risky"]
+ (1.0 - self.controls["Share"]) * self.Rfree
)
self.Rport = Rport
return Rport

def initialize_sim(self):
"""
Initialize the state of simulation attributes. Simply calls the same method
Expand Down Expand Up @@ -1339,7 +1264,10 @@ def solve(self):
# Adjust some of the existing parameters in the dictionary
init_portfolio["aXtraMax"] = 100 # Make the grid of assets go much higher...
init_portfolio["aXtraCount"] = 200 # ...and include many more gridpoints...
init_portfolio["aXtraNestFac"] = 1 # ...which aren't so clustered at the bottom
init_portfolio["BoroCnstArt"] = 0.0 # Artificial borrowing constraint must be turned on
init_portfolio["CRRA"] = 5.0 # Results are more interesting with higher risk aversion
# ...which aren't so clustered at the bottom
init_portfolio["aXtraNestFac"] = 1
# Artificial borrowing constraint must be turned on
init_portfolio["BoroCnstArt"] = 0.0
# Results are more interesting with higher risk aversion
init_portfolio["CRRA"] = 5.0
init_portfolio["DiscFac"] = 0.90 # And also lower patience
Loading