Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Making Structural Estimates From Empirical Results

badge

This notebook conducts a quick and dirty structural estimation based on Table 9 of “MPC Heterogeneity and Household Balance Sheets” by Fagereng, Holm, and Natvik , who use Norweigian administrative data on income, household assets, and lottery winnings to examine the MPC from transitory income shocks (lottery prizes). Their Table 9 reports an estimated MPC broken down by quartiles of bank deposits and prize size; this table is reproduced here as MPC_target_base\texttt{MPC\_target\_base}. In this demo, we use the Table 9 estimates as targets in a simple structural estimation, seeking to minimize the sum of squared differences between simulated and estimated MPCs by changing the (uniform) distribution of discount factors. The essential question is how well their results be rationalized by a simple one-asset consumption-saving model. (Note that the paper was later published under a different version which unfortunately excluded table 9.)

The function that estimates discount factors includes several options for estimating different specifications:

  1. TypeCount : Integer number of discount factors in discrete distribution; can be set to 1 to turn off ex ante heterogeneity (and to discover that the model has no chance to fit the data well without such heterogeneity).

  2. AdjFactor : Scaling factor for the target MPCs; user can try to fit estimated MPCs scaled down by (e.g.) 50%.

  3. T_kill : Maximum number of years the (perpetually young) agents are allowed to live. Because this is quick and dirty, it’s also the number of periods to simulate.

  4. Splurge : Amount of lottery prize that an individual will automatically spend in a moment of excitement (perhaps ancient tradition in Norway requires a big party when you win the lottery), before beginning to behave according to the optimal consumption function. The patterns in Table 9 can be fit much better when this is set around $700 --> 0.7. That doesn’t seem like an unreasonable amount of money to spend on a memorable party.

  5. do_secant : Boolean indicator for whether to use “secant MPC”, which is average MPC over the range of the prize. MNW believes authors’ regressions are estimating this rather than point MPC. When False, structural estimation uses point MPC after receiving prize. NB: This is incompatible with Splurge > 0.

  6. drop_corner : Boolean for whether to include target MPC in the top left corner, which is greater than 1. Authors discuss reasons why the MPC from a transitory shock could exceed 1. Option is included here because this target tends to push the estimate around a bit.

# Import python tools
import numpy as np
from copy import deepcopy
# Import needed tools from HARK

from HARK.distributions import Uniform
from HARK.utilities import get_percentiles
from HARK.estimation import minimize_nelder_mead
from HARK.ConsumptionSaving.ConsIndShockModel import IndShockConsumerType


init_infinite = {
    "CRRA": 1.0,  # Coefficient of relative risk aversion
    "Rfree": 1.01 / (1.0 - 1.0 / 160.0),  # Survival probability,
    # Permanent income growth factor (no perm growth),
    "PermGroFac": [1.000**0.25],
    "PermGroFacAgg": 1.0,
    "BoroCnstArt": 0.0,
    "CubicBool": False,
    "vFuncBool": False,
    "PermShkStd": [
        (0.01 * 4 / 11) ** 0.5
    ],  # Standard deviation of permanent shocks to income
    "PermShkCount": 5,  # Number of points in permanent income shock grid
    "TranShkStd": [
        (0.01 * 4) ** 0.5
    ],  # Standard deviation of transitory shocks to income,
    "TranShkCount": 5,  # Number of points in transitory income shock grid
    "UnempPrb": 0.07,  # Probability of unemployment while working
    "IncUnemp": 0.15,  # Unemployment benefit replacement rate
    "UnempPrbRet": 0.07,
    "IncUnempRet": 0.15,
    "aXtraMin": 0.00001,  # Minimum end-of-period assets in grid
    "aXtraMax": 40,  # Maximum end-of-period assets in grid
    "aXtraCount": 32,  # Number of points in assets grid
    "aXtraExtra": [None],
    "aXtraNestFac": 3,  # Number of times to 'exponentially nest' when constructing assets grid
    "LivPrb": [1.0 - 1.0 / 160.0],  # Survival probability
    "DiscFac": 0.97,  # Default intertemporal discount factor; dummy value, will be overwritten
    "cycles": 0,
    "T_cycle": 1,
    "T_retire": 0,
    "IndL": 10.0 / 9.0,  # Labor supply per individual (constant),
    "kLogInitMean": np.log(0.00001),
    "kLogInitStd": 0.0,
    "pLogInitMean": 0.0,
    "pLogInitStd": 0.0,
    "AgentCount": 10000,
}
# Set key problem-specific parameters

TypeCount = 8  # Number of consumer types with heterogeneous discount factors
AdjFactor = 1.0  # Factor by which to scale all of MPCs in Table 9
T_kill = 100  # Don't let agents live past this age
Splurge = 0.0  # Consumers automatically spend this amount of any lottery prize
do_secant = True  # If True, calculate MPC by secant, else point MPC
drop_corner = False  # If True, ignore upper left corner when calculating distance
# Set standard HARK parameter values

base_params = deepcopy(init_infinite)
base_params["LivPrb"] = [0.975]
base_params["Rfree"] = [1.04 / base_params["LivPrb"][0]]
base_params["PermShkStd"] = [0.1]
base_params["TranShkStd"] = [0.1]
base_params[
    "T_age"
] = T_kill  # Kill off agents if they manage to achieve T_kill working years
base_params["AgentCount"] = 10000
# From Table 1, in thousands of USD
base_params["pLogInitMean"] = np.log(23.72)
base_params[
    "T_sim"
] = T_kill  # No point simulating past when agents would be killed off
# Define the MPC targets from Fagereng et al Table 9; element i,j is lottery quartile i, deposit quartile j

MPC_target_base = np.array(
    [
        [1.047, 0.745, 0.720, 0.490],
        [0.762, 0.640, 0.559, 0.437],
        [0.663, 0.546, 0.390, 0.386],
        [0.354, 0.325, 0.242, 0.216],
    ]
)
MPC_target = AdjFactor * MPC_target_base
# Define the four lottery sizes, in thousands of USD; these are eyeballed centers/averages

lottery_size = np.array([1.625, 3.3741, 7.129, 40.0])
# Make several consumer types to be used during estimation

BaseType = IndShockConsumerType(**base_params)
EstTypeList = []
for j in range(TypeCount):
    EstTypeList.append(deepcopy(BaseType))
    EstTypeList[-1].seed = j
    EstTypeList[-1].initialize_sim()
    EstTypeList[-1].make_shock_history()
# Define the objective function
def FagerengObjFunc(center, spread, verbose=False):
    """
    Objective function for the quick and dirty structural estimation to fit
    Fagereng, Holm, and Natvik's Table 9 results with a basic infinite horizon
    consumption-saving model (with permanent and transitory income shocks).

    Parameters
    ----------
    center : float
        Center of the uniform distribution of discount factors.
    spread : float
        Width of the uniform distribution of discount factors.
    verbose : bool
        When True, print to screen MPC table for these parameters.  When False,
        print (center, spread, distance).

    Returns
    -------
    distance : float
        Euclidean distance between simulated MPCs and (adjusted) Table 9 MPCs.
    """
    # Give our consumer types the requested discount factor distribution
    beta_set = (
        Uniform(bot=center - spread, top=center + spread)
        .discretize(N=TypeCount)
        .atoms.flatten()
    )
    for j in range(TypeCount):
        EstTypeList[j].DiscFac = beta_set[j]

    # Solve and simulate all consumer types, then gather their wealth levels
    for EstType in EstTypeList:
        EstType.solve()
        EstType.initialize_sim()
        EstType.simulate(99)
        EstType.unpack("cFunc")
    WealthNow = np.concatenate([ThisType.state_now["aLvl"] for ThisType in EstTypeList])

    # Get wealth quartile cutoffs and distribute them to each consumer type
    quartile_cuts = get_percentiles(WealthNow, percentiles=[0.25, 0.50, 0.75])
    for ThisType in EstTypeList:
        WealthQ = np.zeros(ThisType.AgentCount, dtype=int)
        for n in range(3):
            WealthQ[ThisType.state_now["aLvl"] > quartile_cuts[n]] += 1
        ThisType.WealthQ = WealthQ

    # Keep track of MPC sets in lists of lists of arrays
    MPC_set_list = [
        [[], [], [], []],
        [[], [], [], []],
        [[], [], [], []],
        [[], [], [], []],
    ]

    # Calculate the MPC for each of the four lottery sizes for all agents
    for ThisType in EstTypeList:
        ThisType.simulate(1)
        c_base = ThisType.controls["cNrm"]
        MPC_this_type = np.zeros((ThisType.AgentCount, 4))
        for k in range(4):  # Get MPC for all agents of this type
            Llvl = lottery_size[k]
            Lnrm = Llvl / ThisType.state_now["pLvl"]
            if do_secant:
                SplurgeNrm = Splurge / ThisType.state_now["pLvl"]
                mAdj = ThisType.state_now["mNrm"] + Lnrm - SplurgeNrm
                cAdj = ThisType.cFunc[0](mAdj) + SplurgeNrm
                MPC_this_type[:, k] = (cAdj - c_base) / Lnrm
            else:
                mAdj = ThisType.state_now["mNrm"] + Lnrm
                MPC_this_type[:, k] = cAdj = ThisType.cFunc[0].derivative(mAdj)

        # Sort the MPCs into the proper MPC sets
        for q in range(4):
            these = ThisType.WealthQ == q
            for k in range(4):
                MPC_set_list[k][q].append(MPC_this_type[these, k])

    # Calculate average within each MPC set
    simulated_MPC_means = np.zeros((4, 4))
    for k in range(4):
        for q in range(4):
            MPC_array = np.concatenate(MPC_set_list[k][q])
            simulated_MPC_means[k, q] = np.mean(MPC_array)

    # Calculate Euclidean distance between simulated MPC averages and Table 9 targets
    diff = simulated_MPC_means - MPC_target
    if drop_corner:
        diff[0, 0] = 0.0
    distance = np.sqrt(np.sum((diff) ** 2))
    if verbose:
        print(simulated_MPC_means)
    else:
        print(center, spread, distance)
    return distance
# Specify initial guess and wrapper function

guess = [0.92, 0.03]

def f_temp(x):
    return FagerengObjFunc(x[0], x[1])
# Conduct the estimation

opt_params = minimize_nelder_mead(f_temp, guess, verbose=False)
print(
    "Finished estimating for scaling factor of "
    + str(AdjFactor)
    + ' and "splurge amount" of $'
    + str(1000 * Splurge)
)
print("Optimal (beta,nabla) is " + str(opt_params) + ", simulated MPCs are:")
dist = FagerengObjFunc(opt_params[0], opt_params[1], True)
print("Distance from Fagereng et al Table 9 is " + str(dist))
0.92 0.03 1.161567658678912
0.9660000000000001 0.03 1.8955268089635904
0.92 0.0315 1.1627915847611228
0.874 0.0315 0.7993379026474928
0.8280000000000001 0.03225 0.6926895097038578
0.8280000000000002 0.03075 0.6945787061276202
0.7360000000000001 0.033 0.7883058351827853
0.782 0.03225 0.7058479694295096
0.8740000000000001 0.03075 0.8000024517145232
0.805 0.031875 0.6872472693901164
0.8049999999999998 0.033375 0.6854824317511029
0.7934999999999997 0.03468750000000001 0.6906370199890395
0.7819999999999998 0.033 0.7050539230132115
0.8165 0.0324375 0.686122238394885
0.8164999999999997 0.033937499999999995 0.6842099387950249
0.8222499999999997 0.03496874999999999 0.685034767376448
0.8049999999999995 0.034874999999999996 0.6836902347998023
0.7992499999999993 0.036093749999999994 0.6846783885874899
0.8164999999999994 0.0354375 0.682077517306895
0.8222499999999993 0.036468749999999994 0.6829588658637175
0.8049999999999992 0.036375000000000005 0.6817044067016875
0.7992499999999987 0.03759375000000001 0.6827165766361232
0.8164999999999991 0.036937500000000005 0.6800455496390396
0.822249999999999 0.03796875000000001 0.6810274155458707
0.8049999999999989 0.037875000000000006 0.6796113409270762
0.7992499999999987 0.03909375000000001 0.6807630847727714
0.8164999999999988 0.0384375 0.6780766753859806
0.8222499999999988 0.03946875 0.6788719109806697
0.8049999999999986 0.039375 0.6775589760015324
0.7992499999999985 0.04059375 0.6786942280985504
0.8164999999999986 0.0399375 0.6759071756227429
0.8222499999999984 0.04096875 0.6767534830681056
0.8049999999999983 0.04087500000000001 0.6755398343117404
0.7992499999999978 0.04209375000000001 0.6765381510905921
0.8164999999999982 0.04143750000000001 0.6738608574595243
0.8222499999999981 0.04246875000000001 0.6745985090564294
0.804999999999998 0.04237500000000001 0.6731709512462832
0.7992499999999978 0.043593750000000014 0.6742094516300106
0.8164999999999979 0.0429375 0.671624915774519
0.8222499999999979 0.04396875 0.6723564484675323
0.8049999999999977 0.043875000000000004 0.6709106833586012
0.7992499999999976 0.04509375 0.6718756815897126
0.8164999999999977 0.044437500000000005 0.6693611227612127
0.8222499999999975 0.04546875 0.670042915552423
0.8049999999999974 0.04537500000000001 0.6686378627174009
0.7992499999999969 0.04659375000000002 0.6695827225552013
0.8164999999999973 0.04593750000000001 0.6670282879315055
0.8222499999999973 0.04696875000000002 0.6677526974538208
0.8049999999999972 0.046875000000000014 0.6662696259212237
0.7992499999999969 0.04809375000000002 0.6672795894887904
0.816499999999997 0.04743750000000001 0.6646466542187277
0.822249999999997 0.048468750000000005 0.665432076320829
0.8049999999999968 0.04837500000000001 0.663955822871874
0.7992499999999967 0.049593750000000006 0.6649007243069327
0.8164999999999968 0.04893750000000001 0.6622832735258631
0.8222499999999966 0.049968750000000006 0.6627486892750879
0.8049999999999965 0.04987500000000002 0.6615068012669069
0.799249999999996 0.05109375000000002 0.6626254502564843
0.8164999999999965 0.05043750000000002 0.6598085982331637
0.8222499999999964 0.05146875000000002 0.6603160814783152
0.8049999999999963 0.05137500000000002 0.6591112210818588
0.799249999999996 0.05259375000000002 0.6603446144605231
0.8164999999999961 0.05193750000000001 0.6572884031474537
0.8222499999999962 0.05296875000000001 0.6577919688938263
0.8049999999999959 0.05287500000000001 0.6567655855112414
0.7992499999999958 0.05409375000000001 0.657827471907522
0.8164999999999959 0.05343750000000001 0.6547915809576194
0.8222499999999957 0.05446875000000001 0.6554018621268936
0.8049999999999956 0.05437500000000002 0.6542991275116093
0.7992499999999951 0.055593750000000025 0.6553170569848484
0.8164999999999956 0.05493750000000002 0.6523598712814302
0.8222499999999955 0.055968750000000025 0.6529101326307181
0.8049999999999954 0.05587500000000002 0.6518672962459888
0.7992499999999951 0.057093750000000026 0.6527641059326879
0.8164999999999952 0.056437500000000015 0.6499865239926728
0.8222499999999953 0.05746875000000001 0.6505143705346391
0.804999999999995 0.057375000000000016 0.649232793330021
0.7992499999999949 0.058593750000000014 0.6501314056720757
0.816499999999995 0.05793750000000002 0.6472612205983747
0.8222499999999948 0.058968750000000014 0.6479122422147863
0.8049999999999947 0.058875000000000025 0.6469372707583798
0.7992499999999942 0.06009375000000003 0.6474973943932917
0.8164999999999947 0.059437500000000025 0.6448554679770131
0.8222499999999946 0.06046875000000003 0.6453973734106638
0.8049999999999945 0.060375000000000026 0.6442624226192264
0.7992499999999942 0.06159375000000003 0.6449568467873467
0.8164999999999943 0.06093750000000002 0.642440180444802
0.8222499999999944 0.06196875000000002 0.6429870976772812
0.8049999999999942 0.06187500000000002 0.6417346094249946
0.799249999999994 0.06309375000000002 0.6422209852529013
0.8164999999999941 0.06243750000000002 0.639811556335962
0.8222499999999939 0.06346875000000002 0.640320842328579
0.8049999999999938 0.06337500000000003 0.6390207986274137
0.7992499999999934 0.06459375000000003 0.6394435773984618
0.8164999999999938 0.06393750000000004 0.6370436115712486
0.8222499999999937 0.06496875000000003 0.6377029845013296
0.8049999999999936 0.06487500000000003 0.6362417928932618
0.7992499999999934 0.06609375000000003 0.6366685402302759
0.8164999999999935 0.06543750000000002 0.6342781998551447
0.8222499999999935 0.06646875000000002 0.6349360242944366
0.8049999999999933 0.06637500000000002 0.6334991072422921
0.7992499999999931 0.06759375000000001 0.6337775406786461
0.8164999999999932 0.06693750000000002 0.6316264495267542
0.822249999999993 0.06796875000000002 0.6322522410730694
0.8049999999999929 0.06787500000000003 0.6306817427195589
0.7992499999999925 0.06909375000000004 0.6311408179855955
0.8164999999999929 0.06843750000000004 0.6290300331729118
0.8222499999999928 0.06946875000000005 0.6296705278071887
0.8049999999999927 0.06937500000000003 0.6280085949673846
0.7992499999999925 0.07059375000000004 0.6282616416857378
0.8164999999999926 0.06993750000000003 0.6264480563560539
0.8222499999999926 0.07096875000000002 0.6270114473974542
0.8049999999999924 0.07087500000000002 0.6252700641961703
0.7992499999999922 0.07209375000000001 0.625552598456469
0.8164999999999923 0.07143750000000003 0.6238539835317578
0.8222499999999922 0.07246875000000003 0.6244215504433229
0.804999999999992 0.07237500000000004 0.622574035858387
0.7992499999999916 0.07359375000000004 0.6229101295414593
0.816499999999992 0.07293750000000004 0.6211511303818763
0.8222499999999919 0.07396875000000006 0.6217647910107723
0.8049999999999918 0.07387500000000004 0.6198928409715336
0.7992499999999916 0.07509375000000004 0.6198746220148368
0.8107499999999915 0.07565625000000005 0.6154562548504666
0.8136249999999914 0.07729687500000007 0.6125664353091619
0.796374999999991 0.07945312500000007 0.6130758436686015
0.8107499999999908 0.0816562500000001 0.6041341555914
0.8164999999999902 0.08493750000000011 0.5990697841390716
0.8337499999999907 0.08278125000000011 0.6189827499847638
0.8057187499999909 0.08028515625000007 0.6072362594471487
0.8085937499999898 0.08792578125000011 0.5922491888977237
0.8060781249999889 0.09324023437500012 0.5823066654830136
0.8168593749999882 0.09789257812500016 0.5765325423821662
0.8224296874999868 0.10669628906250023 0.5682527424248119
0.8120078124999854 0.11499902343750024 0.5455436405533701
0.809761718749983 0.1300297851562503 0.5264924079128815
0.826113281249981 0.1434858398437504 0.5935653360367749
0.8110869140624869 0.10580163574218769 0.5597993629531685
0.7984189453124833 0.12913513183593778 0.5215473720399832
0.7864135742187317 0.14035455322265655 0.5137695617211898
0.7850883789062278 0.16458270263671915 0.4998027933644577
0.7720891113280983 0.1939732360839849 0.537182331735834
0.7617402343749764 0.1749074707031254 0.5358874353906725
0.7977563476562314 0.14124920654296907 0.5075068146498205
0.7964311523437275 0.16547735595703167 0.5113337032070822
0.7939267578124785 0.1591966552734379 0.5014398882554982
0.7812587890624748 0.18253015136718798 0.5176059700222663
0.7936319580077922 0.15156944274902379 0.5013077923026297
0.7847935791015415 0.15695549011230503 0.5008288516232128
0.776249999999977 0.1699687500000004 0.5076536196722159
0.7892864685058384 0.15616926956176794 0.499510389894512
0.7895812683105248 0.16379648208618203 0.5000089678830175
0.7883843460082789 0.16208623409271278 0.4989685380167076
0.7925824356078895 0.1536728010177616 0.5003835866553474
0.7869618930816433 0.16185522723197976 0.4989898569405227
0.7860597705840838 0.1677721917629246 0.5011187591177632
0.7884797940253998 0.1590700001120571 0.4989048362290442
0.7899022469520354 0.15930100697279015 0.4991583173494994
0.7876969815492414 0.16121667216718236 0.4988852359007574
0.7877924295663623 0.15820043818652668 0.49909820570916763
0.7882363668977997 0.16111478511616625 0.49891190520145523
0.7879404086768415 0.1591718871630732 0.49897806150956625
0.7881623773425601 0.160629060627893 0.4988796946364241
0.7873795648664017 0.16277573268301823 0.49903263040874907
0.7882047367356503 0.1599964332547974 0.4988615834721641
0.7886701325289689 0.15940882171550805 0.4988855518750153
0.7879402692941733 0.16076470955426378 0.4988711517003441
0.7879826286872635 0.1601320821811682 0.49889373790830543
0.788117440178736 0.1605048160162118 0.49890213748405265
0.7880725030149118 0.1603805714045306 0.4989066865748037
0.7881835570391051 0.1603127469413452 0.498883885127274
0.7883157907598435 0.15992860879161203 0.49885757000621594
0.7884374346323093 0.15970262748515274 0.4988952617646437
0.7883369704563887 0.15961229510506422 0.49892193483607966
0.788221910393426 0.16013763398227496 0.4988531601605149
0.7883329644176192 0.16006980951908956 0.4988701827533543
0.7882367936561425 0.16001477732087044 0.49886057353798813
0.788300907497127 0.16005146545301652 0.4988678899750719
0.7882528221163887 0.16002394935390696 0.49886803432154925
0.7882688505766348 0.16003312138694348 0.4988685430368566
0.7882293520247843 0.1600762056515727 0.49886350418915953
0.7881824118415756 0.16018071824690416 0.4988675637999965
0.7882040215253403 0.160143819031914 0.49885924329010395
Finished estimating for scaling factor of 1.0 and "splurge amount" of $0.0
Optimal (beta,nabla) is [0.78822191 0.16013763], simulated MPCs are:
[[0.77197513 0.68022941 0.56312258 0.40737706]
 [0.74226653 0.66098717 0.55142921 0.39920105]
 [0.70071168 0.62979675 0.52903479 0.3828982 ]
 [0.55771694 0.4984192  0.41248255 0.29872891]]
Distance from Fagereng et al Table 9 is 0.4988531601605149
References
  1. Fagereng, A., Holm, M. B., & Natvik, G. J. (2021). MPC Heterogeneity and Household Balance Sheets. American Economic Journal: Macroeconomics, 13(4), 1–54. 10.1257/mac.20190211