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

Correct human wealth with risky returns #1403

Merged
merged 8 commits into from
Apr 12, 2024
Merged

Conversation

mnwhite
Copy link
Contributor

@mnwhite mnwhite commented Mar 28, 2024

Through a combination of math and experimentation, I've found the correct computation of "human wealth" in the presence of risky returns. I've implemented it for RiskyAssetModel and FixedShareModel. On the next commit I'll get it working for the PortfolioChoice models, and then turn to ConsMarkovModel.

The good news is that I have the right answer. The bad news is that (other than partly getting to it via whiteboard math and following some clues from experimentation) I don't have a proof of why it's correct. I'll write it up in a short file and circulate.

This PR will also fix a very small mistake in the computation of hNrm by calc_limiting_values; that code uses an alternate definition of hNrm that will be off by exactly 1.

  • Tests for new functionality/models or Tests to reproduce the bug-fix in code.
  • Updated documentation of features that add new functionality.
  • Update CHANGELOG.md with major/minor changes.

Through a combination of math and experimentation, I've found the correct computation of "human wealth" in the presence of risky returns. I've implemented it for RiskyAssetModel and FixedShareModel. On the next commit I'll get it working for the PortfolioChoice models, and then turn to ConsMarkovModel.
@llorracc
Copy link
Collaborator

llorracc commented Mar 28, 2024 via email

@alanlujan91 alanlujan91 self-requested a review March 28, 2024 20:11
@mnwhite
Copy link
Contributor Author

mnwhite commented Mar 28, 2024

No, the infimum and supremum of distribution support hasn't been put in yet. There's no special impact of the extreme values in the human wealth formula, but I suspect tail realizations have a disproportionate impact on "human wealth", especially with unbounded support. I'll play with it.

@mnwhite
Copy link
Contributor Author

mnwhite commented Mar 28, 2024

I think I'm clicking the wrong thing @alanlujan91 in trying to make you a reviewer. Don't merge this yet, obviously.

Seems to mostly work, *except* that MPCmin is now off by 0.00001 or so. This only becomes evident if you have an enormous top gridpoint, like m=10000. I have a guess why.
@mnwhite
Copy link
Contributor Author

mnwhite commented Mar 29, 2024

More mixed news: The updated hNrm formula seems to be correct when there's portfolio choice... but now MPCmin is off by 0.0001 or so. I'm not sure if this is because ShareLimit is slightly off, or because there's a mismatch between solving a large (but finite) number of periods vs a true infinite horizon.

The proper human wealth and lower bound of MPC calculations are now in the "advanced" portfolio solver in ConsPortfolioModel, but not actually used in the consumption function. Calculation of the limiting share has been tweaked to be more accurate.

The "basic" solver in ConsRiskyAssetModel now goes through a loop to refine its search for optimal portfolio share at each aNrm gridpoint by "zooming in". We will probably want to add a parameter to specify how many times to do this loop, but it's currently set to 3.
I broke everything
Incorporate proper discounting for human wealth into ConsMarkovModel so that extrapolation works properly.
@mnwhite
Copy link
Contributor Author

mnwhite commented Apr 9, 2024

This is mostly done and ready to be merged. The one thing that hasn't been done is implementing the "refined share search" in the bells-and-whistles ConsPortfolioModel. So hNrm and MPCmin are calculated correctly (and stored in the solution), but not actually used for the consumption function, because they can break due to tiny inaccuracies in the ShareFunc.

There are three options here:

  1. Leave as is and merge, returning to do the "refined search" for that solver in another PR because it takes substantial work. Also leave hNrm and MPCmin unused by the cFunc for now.

  2. Use hNrm and MPCmin to construct the limiting linear cFunc, with the knowledge that it will break if aXtraMax is too high. Maybe leave a note in the documentation about this.

  3. Do all of the remaining work on ConsPortfolioModel in this PR.

@llorracc
Copy link
Collaborator

llorracc commented Apr 9, 2024 via email

Copy link
Member

@alanlujan91 alanlujan91 left a comment

Choose a reason for hiding this comment

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

In favor of option 1 but please review my previous comments first.

Comment on lines +446 to +456
def calc_hNrm(S):
Risky = S["Risky"]
PermShk = S["PermShk"]
TranShk = S["TranShk"]
G = PermGroFac * PermShk
Rport = ShareLimit * Risky + (1.0 - ShareLimit) * Rfree
hNrm = (G / Rport**CRRA) * (TranShk + solution_next.hNrm)
return hNrm

# This correctly accounts for risky returns and risk aversion
hNrmNow = expected(calc_hNrm, ShockDstn) / R_adj
Copy link
Member

Choose a reason for hiding this comment

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

can we assume E[R_adj] is independent of E[h_nrm]? I know Risky and IncShk are independent in this case, but not sure you can just pull out R_adj when hNrm has the term `Risky / Rport ** (1-CRRA)

aka should calc_hNrm be

def calc_hNrm(S):
    Rport = ShareLimit * R + (1.0 - ShareLimit) * Rfree
    Radj =  Rport ** (1.0 - CRRA)
    
    Risky = S["Risky"]
    PermShk = S["PermShk"]
    TranShk = S["TranShk"]
    G = PermGroFac * PermShk
    Rport = ShareLimit * Risky + (1.0 - ShareLimit) * Rfree
    hNrm = (G / Rport**CRRA) * (TranShk + solution_next.hNrm) / Radj
    
    return hNrm

Copy link
Member

Choose a reason for hiding this comment

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

Actually this simplifies to

def calc_hNrm(S):    
    Risky = S["Risky"]
    PermShk = S["PermShk"]
    TranShk = S["TranShk"]
    G = PermGroFac * PermShk
    Rport = ShareLimit * Risky + (1.0 - ShareLimit) * Rfree
    hNrm = (G / Rport) * (TranShk + solution_next.hNrm) 
    
    return hNrm

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's very specifically not that, and that's the point of this PR. To get the limiting linear cFunc correct, the code I included for hNrm is what needs to be done. It very specifically computes R_adj outside of that statement and applies it outside it as well.

When RiskyShare is chosen optimally, the first order condition of the optimization problem in calc_limiting_share means that we could just discount by Rfree rather than use the more complicated calculation.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, I don't know how to derive this equation, but it just made intuitive sense to me that if both the nominator and denominator (h_nrm, R_adj) include the rv. risky, then we should not be evaluating the expectations separately.

From reading your emails, it seemed you don't quite have a proof for this? Is it possible to at least get a visual proof? How did you figure out it's this expression and not something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's correct, I don't have a proof. I came up with it by a combination of trial and error and intuition from what I was seeing as I tried different parameter values:

  1. The "hNrm error" was directly related to return risk: bigger RiskyStd made the difference between calculated and "correct" hNrm bigger.

  2. The sign of the error depended on whether CRRA was greater or lesser than 1. When CRRA=1, the extent of return risk had no effect and there was no error. The further from 1 that rho was, the larger the error (fixing riskiness of returns).

So I knew there had to be something that canceled out for all values of rho when there's no return risk, and made return risk not matter when rho=1. I then just experimented with raising Rport to different powers that sum (or difference) to 1 and have power=0 when rho=1 for one of the powers. I eventually got lucky.

It was a good thing I did that work with the RiskyAsset model, else I would have stumbled onto the Rfree thing in the portfolio model and then been stumped on the other ones.

I can make "visual proof by examples", which aren't proofs at all. They involve solving the problem out to extremely high values of aNrm like 1B and show that the numerically constructed cFunc really does converge to the limiting linear function defined by MPCmin and hNrm.

HARK/ConsumptionSaving/ConsPortfolioModel.py Show resolved Hide resolved
@mnwhite
Copy link
Contributor Author

mnwhite commented Apr 10, 2024

Huh, I didn't see those code comments until just now. I was looking for them because you mentioned something in the meeting today.

@llorracc
Copy link
Collaborator

llorracc commented Apr 11, 2024 via email

@alanlujan91
Copy link
Member

@mnwhite I think we can go ahead and merge this, should I?

@mnwhite
Copy link
Contributor Author

mnwhite commented Apr 12, 2024 via email

@alanlujan91 alanlujan91 merged commit e237921 into master Apr 12, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants