diff --git a/lectures/_static/lecture_specific/subjective_beliefs_business_cycles/bbh_macro_quarterly.csv b/lectures/_static/lecture_specific/subjective_beliefs_business_cycles/bbh_macro_quarterly.csv new file mode 100644 index 00000000..ef779444 --- /dev/null +++ b/lectures/_static/lecture_specific/subjective_beliefs_business_cycles/bbh_macro_quarterly.csv @@ -0,0 +1,261 @@ +YYYYQ,GDP,GDPC1,GDPPOT,PCESV,GPDI,PIRIC,PRS85006023,CPIAUCSL,PCEND,UNRATE,CUMFNS,FEDFUNDS,CE16OV,CNP16OV +19551,413.073,2815.134,2757.5375,108.459,68.702,3.7812,116.228,26.7933,,4.7333,84.4645,1.3433,60814.6667,109130.3333 +19552,421.532,2860.942,2775.4869,109.634,72.688,3.8033,116.416,26.7567,,4.4,87.4043,1.5,61643.3333,109533.6667 +19553,430.221,2899.578,2793.0734,111.288,74.747,3.8236,116.436,26.7767,,4.1,87.483,1.94,62753.3333,109883.6667 +19554,437.092,2916.985,2811.3068,114.179,78.882,3.828,116.693,26.8567,,4.2333,88.5527,2.3567,63310.6667,110186.0 +19561,439.746,2905.656,2829.9084,115.94,78.303,3.8365,116.288,26.86,,4.0333,87.5453,2.4833,63560.6667,110483.3333 +19562,446.01,2929.666,2848.3139,117.723,77.02,3.8171,115.838,27.0367,,4.2,86.4917,2.6933,63765.0,110787.6667 +19563,451.191,2927.034,2867.5765,119.958,78.267,3.8304,115.873,27.3167,,4.1333,84.154,2.81,63950.3333,111113.3333 +19564,460.463,2975.209,2887.9829,122.272,77.145,3.8457,116.08,27.55,,4.1333,86.3631,2.9267,63893.6667,111431.0 +19571,469.779,2994.259,2909.9969,123.876,77.728,3.8527,115.685,27.7767,,3.9333,86.521,2.9333,64097.6667,111720.3333 +19572,472.025,2987.699,2933.4487,125.528,77.907,3.8553,114.913,28.0133,,4.1,84.5961,3.0,64076.0,112045.3333 +19573,479.49,3016.979,2957.9832,127.521,79.339,3.8151,114.636,28.2633,,4.2333,83.8886,3.2333,64206.6667,112430.6667 +19574,474.864,2985.775,2983.7255,129.85,71.045,3.7711,113.608,28.4,,4.9333,79.4514,3.2533,63879.0,112865.6667 +19581,467.54,2908.281,3009.6601,130.63,66.73,3.6815,113.415,28.7367,,6.3,74.0657,1.8633,62949.6667,113236.3333 +19582,471.978,2927.395,3036.5506,133.0,65.065,3.6384,113.546,28.93,,7.3667,72.406,0.94,62745.0,113532.0 +19583,485.841,2995.112,3063.9644,135.471,71.999,3.6083,114.08,28.9133,,7.3333,75.4322,1.3233,62979.3333,113846.3333 +19584,499.555,3065.141,3092.7924,137.049,80.001,3.5888,114.641,28.9433,,6.3667,78.1743,2.1633,63498.0,114283.3333 +19591,510.33,3123.978,3122.0075,139.726,83.166,3.5845,115.042,28.9933,126.0667,5.8333,81.3723,2.57,63939.6667,114714.3333 +19592,522.653,3194.429,3152.2324,142.888,89.381,3.5742,115.397,29.0433,127.1667,5.1,84.5589,3.0833,64772.0,115139.0 +19593,525.034,3196.683,3184.1616,146.201,83.606,3.5454,115.039,29.1933,128.2,5.2667,80.4988,3.5767,64875.0,115550.6667 +19594,528.6,3205.79,3216.3037,149.277,86.524,3.5154,114.763,29.37,129.5,5.6,80.0757,3.99,64927.3333,115918.0 +19601,542.648,3277.847,3249.2975,151.304,96.476,3.4992,114.518,29.3967,129.6,5.1333,84.4715,3.9333,65213.3333,116707.6667 +19602,541.08,3260.177,3282.0234,153.813,87.096,3.4719,114.572,29.5733,131.9667,5.2333,81.318,3.6967,66061.3333,117036.6667 +19603,545.604,3276.133,3314.9506,154.623,86.377,3.4464,114.685,29.59,131.5667,5.5333,78.8989,2.9367,66023.6667,117411.0 +19604,540.197,3234.087,3347.584,156.921,75.963,3.4216,114.205,29.78,132.4,6.2667,75.8523,2.2967,65839.6667,117824.3333 +19611,545.018,3255.914,3380.7233,158.878,78.378,3.4126,113.975,29.84,133.4,6.8,73.8284,2.0033,65738.0,118254.3333 +19612,555.545,3311.181,3413.0685,161.891,84.108,3.4219,113.933,29.83,134.2333,7.0,76.3543,1.7333,65605.3333,118636.0 +19613,567.664,3374.742,3446.7717,163.156,90.917,3.4115,114.099,29.9467,134.6,6.7667,78.4144,1.6833,65667.0,119000.6667 +19614,580.612,3440.924,3480.8275,166.742,92.931,3.3997,114.538,29.99,136.1667,6.2,80.6409,2.4,65966.6667,119189.6667 +19621,594.013,3502.298,3515.977,169.287,98.074,3.3763,114.248,30.1067,137.8333,5.6333,81.1862,2.4567,66379.6667,119378.6667 +19622,600.366,3533.947,3552.0383,172.666,96.706,3.3598,114.784,30.22,138.7667,5.5333,81.3356,2.6067,66576.6667,119819.3333 +19623,609.027,3577.362,3589.0123,174.806,98.16,3.349,114.733,30.3067,139.9667,5.5667,81.5601,2.8467,66881.0,120368.0 +19624,612.28,3589.128,3626.7058,177.594,94.968,3.3247,114.29,30.38,141.5,5.5333,81.6345,2.9233,66969.3333,121045.6667 +19631,621.672,3628.306,3664.8477,179.236,99.689,3.3128,114.418,30.4767,142.4667,5.7667,82.3156,2.9667,67149.0,121640.0 +19632,629.752,3669.02,3703.8465,181.728,101.65,3.3031,114.65,30.5333,142.8,5.7333,83.7831,2.9633,67635.3333,122166.6667 +19633,644.444,3749.681,3743.5307,185.526,104.612,3.274,114.343,30.72,145.1667,5.5,83.5563,3.33,67995.6667,122669.6667 +19634,653.938,3774.264,3784.049,189.031,107.189,3.2583,114.636,30.8033,145.3,5.5667,84.2037,3.4533,68258.0,123188.6667 +19641,669.822,3853.835,3824.6709,192.935,110.474,3.2211,115.857,30.93,148.6333,5.4667,84.4956,3.4633,68613.6667,123708.0 +19642,678.674,3895.793,3866.1988,196.604,110.518,3.2183,116.111,30.98,151.4667,5.2,85.5447,3.49,69401.6667,124203.0 +19643,692.031,3956.657,3908.2547,200.168,112.631,3.2,115.867,31.05,154.8667,5.0,86.057,3.4567,69480.0,124739.3333 +19644,697.319,3968.878,3950.3756,203.811,114.984,3.2021,116.019,31.1933,155.7667,4.9667,86.492,3.5767,69710.3333,125289.0 +19651,717.79,4064.915,3993.8515,207.035,126.542,3.1933,116.672,31.29,157.7667,4.9,88.866,3.9767,70187.6667,125814.0 +19652,730.191,4116.267,4036.6403,211.056,127.052,3.1683,116.44,31.49,160.7667,4.6667,89.3981,4.08,70897.3333,126324.6667 +19653,749.323,4207.782,4081.0984,215.035,131.237,3.139,116.14,31.5833,164.0667,4.3667,89.9023,4.0767,71369.3333,126745.0 +19654,771.857,4304.731,4126.1029,220.065,133.752,3.123,116.194,31.75,170.5333,4.1,89.9839,4.1667,71827.0,127169.3333 +19661,795.734,4409.518,4171.8648,223.73,144.2,3.0595,116.444,32.0467,174.0667,3.8667,91.1195,4.56,72173.3333,127511.3333 +19662,804.981,4424.581,4218.3113,228.23,143.501,3.0507,116.104,32.3367,177.2667,3.8333,91.5667,4.9133,72594.0,127868.6667 +19663,819.638,4462.053,4265.4766,232.198,143.194,3.0176,115.668,32.6167,179.7667,3.7667,91.2437,5.41,73088.0,128233.6667 +19664,833.302,4498.66,4312.4901,237.04,145.855,3.0124,115.136,32.8833,180.4,3.7,90.5906,5.5633,73656.6667,128617.0 +19671,844.17,4538.498,4360.2718,240.841,142.811,3.0039,114.527,32.9667,182.3667,3.8333,88.5845,4.8233,73572.0,129043.6667 +19672,848.983,4541.28,4408.3508,244.953,137.495,2.9987,113.789,33.1667,184.0333,3.8333,86.7784,3.99,74001.3333,129527.0 +19673,865.233,4584.246,4456.8385,250.241,142.835,2.9892,113.68,33.5,185.7667,3.8,85.9545,3.8933,74713.6667,130165.6667 +19674,881.439,4618.812,4506.4596,254.743,147.653,2.9885,113.627,33.8667,187.8667,3.9,87.3031,4.1733,75216.3333,130757.3333 +19681,909.387,4713.013,4556.6781,261.903,152.288,2.9699,113.24,34.2,193.5667,3.7333,87.3264,4.79,75102.6667,131267.0 +19682,934.344,4791.758,4606.9936,269.161,158.943,2.9503,113.448,34.5333,197.8,3.5667,87.2785,5.9833,75950.0,131712.3333 +19683,950.825,4828.892,4658.5699,275.736,155.683,2.9281,113.436,35.0,202.8333,3.5333,86.6724,5.9467,76100.6667,132250.0 +19684,968.03,4847.885,4710.9814,282.144,160.76,2.9333,112.889,35.4333,205.0,3.4,87.0991,5.9167,76498.6667,132880.0 +19691,993.337,4923.76,4761.7534,288.161,172.388,2.9202,112.809,35.8667,208.8333,3.4,87.6714,6.5667,77166.3333,133476.0 +19692,1009.02,4938.728,4810.6972,295.673,172.721,2.9033,112.65,36.4333,212.2333,3.4333,86.924,8.3267,77605.0,134020.3333 +19693,1029.956,4971.349,4857.3564,302.01,177.564,2.8861,112.416,36.9333,215.9667,3.5667,86.843,8.9833,78153.0,134595.0 +19694,1038.147,4947.104,4902.0822,309.998,171.573,2.8856,111.973,37.5,219.7333,3.5667,85.1773,8.94,78575.3333,135246.6667 +19701,1051.2,4939.759,4945.1206,317.59,168.113,2.8735,111.487,38.1,224.5,4.1667,81.8428,8.5733,78780.3333,135949.6667 +19702,1067.375,4946.77,4986.3386,324.016,171.455,2.8871,110.699,38.6333,226.5667,4.7667,80.446,7.8867,78635.6667,136676.6667 +19703,1086.059,4992.357,5026.2052,331.755,173.904,2.8627,110.177,39.0333,229.7,5.1667,79.1387,6.7067,78616.0,137456.0 +19704,1088.608,4938.857,5065.0472,338.361,166.754,2.8629,110.078,39.6,234.4667,5.8333,76.4018,5.5667,78643.0,138260.3333 +19711,1135.156,5072.996,5103.9317,345.307,189.495,2.8778,110.102,39.9333,235.8,5.9333,77.5462,3.8567,78717.3333,139033.6667 +19712,1156.271,5100.447,5143.6084,353.404,197.329,2.8727,110.062,40.3,238.8333,5.9,77.6773,4.5667,78961.0,139827.3333 +19713,1177.675,5142.422,5183.8731,361.732,202.058,2.8571,109.872,40.7,240.4,6.0333,77.4289,5.4767,79511.0,140602.6667 +19714,1190.297,5154.547,5224.7492,370.843,198.411,2.8476,110.266,41.0,243.6,5.9333,79.1621,4.75,80228.6667,141401.6667 +19721,1230.609,5249.337,5266.3945,381.204,212.968,2.8542,110.289,41.3333,247.0333,5.7667,81.8018,3.5467,81213.3333,143005.3333 +19722,1266.369,5368.485,5308.6412,388.996,226.798,2.8588,110.266,41.6,254.3,5.7,82.8565,4.3,81875.0,143758.6667 +19723,1290.566,5419.184,5351.5746,398.123,233.09,2.8571,110.179,41.9333,260.1,5.5667,83.3599,4.7433,82450.3333,144522.6667 +19724,1328.904,5509.926,5395.2241,409.029,239.715,2.8505,110.034,42.3667,268.0667,5.3667,85.7091,5.1467,83002.0,145215.0 +19731,1377.49,5646.286,5439.8896,417.739,254.313,2.8328,109.922,43.0333,275.4333,4.9333,87.6636,6.5367,83841.6667,145964.3333 +19732,1413.887,5707.755,5486.6967,427.847,268.196,2.8075,109.874,43.9333,281.4,4.9333,87.6192,7.8167,84797.3333,146719.6667 +19733,1433.838,5677.738,5534.0687,438.16,264.335,2.7838,109.878,44.8,289.9,4.8,87.3638,10.56,85330.3333,147478.3333 +19734,1476.289,5731.632,5582.5199,448.011,280.858,2.7333,109.557,45.9333,297.7,4.7667,88.1166,9.9967,86236.0,148226.0 +19741,1491.209,5682.353,5631.5986,456.362,268.361,2.6739,108.769,47.3,308.8667,5.1333,86.5609,9.3233,86709.3333,148986.6667 +19742,1530.056,5695.859,5680.9704,471.822,277.391,2.6617,108.454,48.5667,318.0,5.2,85.7211,11.25,86833.6667,149746.6667 +19743,1560.026,5642.025,5729.7526,485.284,271.013,2.6938,108.142,49.9333,327.6667,5.6333,84.7454,12.09,87079.0,150498.0 +19744,1599.679,5620.126,5777.8023,501.022,281.339,2.7302,107.415,51.4667,330.9,6.6,80.4802,9.3467,86588.3333,151253.0 +19751,1616.116,5551.713,5824.5431,517.197,244.306,2.7645,106.529,52.5667,336.2,8.2667,73.5116,6.3033,85356.6667,151987.3333 +19752,1651.853,5591.382,5870.2553,532.041,243.281,2.7969,106.394,53.2,344.8333,8.8667,71.9256,5.42,85331.6667,152707.6667 +19753,1709.82,5687.087,5915.5758,544.44,265.192,2.7711,106.594,54.2667,355.9333,8.4667,73.8461,6.16,86135.6667,153579.0 +19754,1761.831,5763.665,5960.948,563.172,276.236,2.7563,107.153,55.2667,359.6667,8.3,75.3242,5.4133,86497.0,154336.3333 +19761,1820.487,5893.276,6006.2049,579.174,304.638,2.7416,107.512,55.9,367.4333,7.7333,77.3502,4.8267,87685.6667,155075.0 +19762,1852.332,5936.515,6052.324,590.571,322.303,2.7448,106.766,56.4,373.1,7.5667,78.0969,5.1967,88591.0,155773.6667 +19763,1886.558,5969.089,6099.8266,608.353,328.307,2.7215,106.57,57.3,380.6,7.7333,78.6868,5.2833,89163.0,156526.6667 +19764,1934.273,6012.356,6148.0796,627.345,337.65,2.7058,106.34,58.1333,389.5667,7.7667,79.3082,4.8733,89570.3333,157222.0 +19771,1988.648,6083.391,6197.9564,647.814,360.313,2.6821,106.198,59.2,396.4333,7.5,80.6792,4.66,90359.3333,157910.6667 +19772,2055.909,6201.659,6249.0099,663.054,389.703,2.6556,106.383,60.2333,403.7,7.1333,82.8604,5.1567,91661.3333,158652.3333 +19773,2118.473,6313.559,6300.8389,683.003,414.134,2.6552,106.152,61.0667,409.9,6.9,83.1817,5.82,92409.0,159429.6667 +19774,2164.27,6313.697,6354.0847,700.494,422.299,2.6657,105.993,61.9667,423.6333,6.6667,83.2387,6.5133,93639.3333,160140.3333 +19781,2202.76,6333.848,6408.7339,724.811,434.799,2.6718,105.219,63.0333,431.0667,6.3333,82.637,6.7567,94552.6667,160828.6667 +19782,2331.633,6578.605,6464.4701,750.386,470.584,2.6608,106.187,64.4667,444.8,6.0,84.7155,7.2833,95835.3333,161525.3333 +19783,2395.053,6644.754,6521.7113,769.856,492.368,2.65,106.015,65.9667,455.6,6.0333,84.797,8.1,96397.0,162265.0 +19784,2476.949,6734.069,6580.0271,789.689,515.755,2.6302,105.889,67.5,469.4,5.9,85.7337,9.5833,97399.6667,163024.0 +19791,2526.61,6746.176,6639.9792,809.273,525.809,2.6027,105.409,69.2,484.5667,5.8667,85.3371,10.0733,98252.3333,163756.3333 +19792,2591.247,6753.389,6697.9387,835.468,539.293,2.5661,104.811,71.4,500.0667,5.7,84.3488,10.18,98371.0,164447.3333 +19793,2667.565,6803.558,6757.0171,858.589,545.621,2.5323,105.139,73.7,522.3333,5.8667,83.5143,10.9467,99040.6667,165199.6667 +19794,2723.883,6820.572,6807.4314,886.599,547.875,2.5076,105.098,76.0333,539.4,5.9667,82.9414,13.5767,99637.0,166054.6667 +19801,2789.842,6842.024,6853.9741,910.301,554.562,2.4907,104.531,79.0333,559.9333,6.3,82.551,15.0467,99862.3333,166762.3333 +19802,2797.352,6701.046,6896.0535,926.892,519.294,2.4827,103.661,81.7,565.9,7.3333,77.8801,12.6867,98953.3333,167415.6667 +19803,2856.483,6693.082,6933.63,961.909,495.071,2.4702,103.47,83.2333,576.5,7.6667,75.7289,9.8367,98899.0,168110.6667 +19804,2985.557,6817.903,6973.2988,1004.394,551.472,2.4535,103.969,85.5667,591.2333,7.4,78.5143,15.8533,99498.6667,168693.6667 +19811,3124.206,6951.495,7017.0829,1025.643,619.381,2.4378,104.088,87.9333,614.0667,7.4333,78.0007,16.57,100239.0,169279.0 +19812,3162.532,6899.98,7064.0682,1053.74,609.843,2.4493,103.669,89.7667,622.8667,7.4,77.9005,17.78,100800.6667,169837.3333 +19813,3260.609,6982.609,7113.6671,1077.047,652.297,2.4495,103.244,92.2667,629.1333,7.4,77.2097,17.5767,100482.0,170412.6667 +19814,3280.818,6906.529,7165.8463,1101.901,643.395,2.4584,103.309,93.7667,635.7,8.2333,74.6717,13.5867,100076.6667,170990.3333 +19821,3274.302,6799.233,7219.8608,1127.862,588.318,2.4599,102.341,94.6,639.8667,8.8333,72.4271,14.2267,99708.6667,171497.0 +19822,3331.972,6830.251,7275.1977,1151.697,593.621,2.4655,103.04,95.9667,638.7,9.4333,71.6318,14.5133,99745.0,172020.0 +19823,3366.322,6804.139,7331.6461,1183.846,592.954,2.4384,103.032,97.6333,649.7333,9.9,70.6419,11.0067,99543.3333,172521.6667 +19824,3402.561,6806.857,7389.1969,1224.459,549.242,2.4175,102.93,97.9333,656.7667,10.6667,68.9264,9.2867,99119.6667,173046.0 +19831,3473.413,6896.561,7446.7071,1258.678,565.52,2.3998,103.308,98.0,657.0333,10.3667,70.2942,8.6533,99143.0,173505.0 +19832,3578.848,7053.5,7505.6008,1286.806,613.783,2.3729,103.546,99.1333,673.0333,10.1333,72.2148,8.8033,99945.0,173957.3333 +19833,3689.179,7194.504,7566.2177,1329.097,652.269,2.3397,104.3,100.1,688.4333,9.3667,74.6901,9.46,101610.6667,174449.3333 +19834,3794.706,7344.597,7629.3462,1356.704,718.496,2.3249,104.359,101.1,696.5,8.5333,76.7528,9.43,102588.0,174950.3333 +19841,3908.054,7488.167,7701.1611,1380.27,790.872,2.2808,104.713,102.5333,706.4667,7.8667,78.805,9.6867,103664.0,175678.6667 +19842,4009.601,7617.547,7768.8625,1412.233,818.894,2.2612,104.869,103.5,722.4,7.4333,79.5818,10.5567,105040.0,176125.3333 +19843,4084.25,7690.985,7838.097,1446.882,838.852,2.2434,104.475,104.4,724.6,7.4333,79.686,11.39,105362.6667,176595.3333 +19844,4148.551,7754.117,7908.4099,1475.175,831.741,2.2305,104.354,105.3,732.7667,7.3,79.5398,9.2667,105944.3333,177132.3333 +19851,4230.168,7829.26,7978.9846,1525.616,809.865,2.2096,104.402,106.2667,742.3667,7.2333,78.8659,8.4767,106615.3333,177522.3333 +19852,4294.887,7898.194,8049.7445,1555.754,827.04,2.1916,104.515,107.2333,752.7667,7.3,78.3372,7.9233,106791.0,177946.3333 +19853,4386.773,8018.809,8120.3593,1597.213,822.157,2.1729,104.308,107.9,760.4667,7.2,77.775,7.9,107186.3333,178413.3333 +19854,4444.094,8078.415,8190.5228,1622.303,859.545,2.1624,104.14,109.0,773.3667,7.0333,77.7767,8.1033,108023.3333,178940.6667 +19861,4507.894,8153.829,8260.076,1652.771,863.457,2.1462,103.889,109.5667,779.2667,7.0333,78.387,7.8267,108734.6667,179825.3333 +19862,4545.34,8190.552,8329.2905,1676.713,855.237,2.159,103.282,109.0333,767.4333,7.1667,78.1791,6.92,109205.6667,180320.6667 +19863,4607.669,8268.935,8398.3217,1700.452,835.832,2.1617,102.993,109.7,771.0667,6.9667,78.3772,6.2067,109970.0,180835.6667 +19864,4657.627,8313.338,8467.2809,1732.668,842.063,2.1573,103.12,110.4667,779.0,6.8333,78.9446,6.2667,110492.0,181365.3333 +19871,4722.156,8375.274,8535.8303,1767.634,871.196,2.1443,103.675,111.8,797.4,6.6,79.5409,6.22,111206.0,182001.3333 +19872,4806.16,8465.63,8604.0969,1801.948,874.588,2.1283,103.509,113.0667,812.2667,6.2667,80.2908,6.65,112158.0,182526.6667 +19873,4884.555,8539.075,8672.098,1836.16,876.466,2.1134,103.456,114.2667,820.6667,6.0,81.1952,6.8433,112866.6667,183016.0 +19874,5007.994,8685.694,8740.3552,1874.186,946.459,2.1067,103.663,115.3333,826.8,5.8333,82.9278,6.9167,113526.6667,183467.0 +19881,5073.372,8730.569,8808.2422,1923.07,908.569,2.0948,103.05,116.2333,838.4,5.7,83.2343,6.6633,114093.3333,183967.3333 +19882,5190.036,8845.28,8876.3763,1964.954,934.525,2.0799,103.315,117.5667,853.5667,5.4667,83.9856,7.1567,114623.0,184389.3333 +19883,5282.835,8897.107,8944.3285,2020.733,942.009,2.0619,103.127,119.0,870.7667,5.4667,84.0521,7.9833,115232.6667,184840.3333 +19884,5399.509,9015.661,9012.2245,2062.056,962.748,2.0556,103.529,120.3,886.3333,5.3333,84.7809,8.47,115947.3333,185253.3333 +19891,5511.253,9107.314,9079.7836,2101.195,1005.487,2.0385,103.707,121.6667,902.5333,5.2,84.905,9.4433,116835.3333,185772.6667 +19892,5612.463,9176.827,9146.9441,2132.674,1001.047,2.0136,103.788,123.6333,927.7667,5.2333,83.8181,9.7267,117204.6667,186178.0 +19893,5695.365,9244.816,9213.6664,2167.474,996.46,2.0056,103.951,124.6,936.2667,5.2333,82.5197,9.0833,117493.6667,186602.3333 +19894,5747.237,9263.033,9280.1172,2210.469,995.809,1.9947,103.546,125.8667,951.3333,5.3667,81.9263,8.6133,117774.3333,187017.6667 +19901,5872.701,9364.259,9345.7291,2248.382,1010.838,1.9713,103.269,128.0333,974.1667,5.3,82.1761,8.25,119114.3333,188519.6667 +19902,5960.028,9398.243,9410.6983,2304.16,1014.72,1.9527,102.795,129.3,980.8333,5.3333,82.1695,8.2433,118995.3333,188916.3333 +19903,6015.116,9404.494,9474.2945,2350.024,1000.785,1.9298,102.522,131.5333,1003.1333,5.7,81.8775,8.16,118712.0,189352.6667 +19904,6004.733,9318.876,9536.6607,2368.207,947.453,1.9068,102.515,133.7667,1018.7667,6.1333,80.1083,7.7433,118361.0,189866.3333 +19911,6035.178,9275.276,9598.1338,2387.721,924.569,1.9109,102.235,134.7667,1014.1667,6.6,77.9846,6.4267,117782.3333,190271.6667 +19912,6126.862,9347.597,9658.7723,2429.918,926.541,1.9006,102.078,135.5667,1021.7333,6.8333,78.0903,5.8633,117729.3333,190655.6667 +19913,6205.937,9394.834,9719.0523,2464.541,947.476,1.8871,102.219,136.6,1024.4,6.8667,79.2156,5.6433,117660.0,191121.3333 +19914,6264.54,9427.581,9779.6871,2501.877,978.788,1.8657,102.177,137.7333,1020.6667,7.1,79.1974,4.8167,117678.6667,191650.6667 +19921,6363.102,9540.444,9840.9574,2566.552,956.817,1.8464,102.098,138.6667,1037.6667,7.3667,78.8379,4.0233,117958.3333,192074.6667 +19922,6470.763,9643.893,9903.2242,2607.453,1013.084,1.8336,102.341,139.7333,1047.2,7.6,79.862,3.77,118406.6667,192506.6667 +19923,6566.641,9739.185,9967.1893,2653.829,1024.162,1.821,102.285,140.8,1061.0333,7.6333,80.0819,3.2567,118753.0,193024.3333 +19924,6680.803,9840.753,10032.2124,2709.331,1057.962,1.8097,102.448,142.0333,1074.8,7.3667,80.1231,3.0367,118833.6667,193615.6667 +19931,6729.459,9857.185,10098.2893,2742.534,1083.829,1.7992,102.54,143.0667,1079.1333,7.1333,80.5712,3.04,119297.3333,194106.0 +19932,6808.939,9914.565,10165.978,2784.412,1094.479,1.7921,102.872,144.1,1086.3333,7.0667,80.4369,3.0,119959.6667,194555.3333 +19933,6882.098,9961.873,10234.6817,2838.023,1095.852,1.7874,102.974,144.7667,1092.5333,6.8,80.1822,3.06,120625.6667,195068.0 +19934,7013.738,10097.362,10304.222,2873.579,1153.142,1.7828,103.161,145.9667,1105.3,6.6333,81.127,2.99,121152.0,195621.0 +19941,7115.652,10195.338,10374.3207,2915.554,1201.675,1.7815,103.025,146.7,1116.8333,6.5667,81.5218,3.2133,121994.0,196085.3333 +19942,7246.931,10333.495,10444.8719,2956.348,1264.948,1.7764,103.688,147.5333,1128.1,6.2,82.6904,3.94,122596.0,196522.0 +19943,7331.075,10393.898,10516.4034,2993.87,1251.749,1.77,103.701,148.9,1149.6,6.0,83.0831,4.4867,123245.0,197050.0 +19944,7455.288,10512.962,10588.5101,3031.873,1307.566,1.7635,103.545,149.7667,1163.0,5.6333,84.1697,5.1667,124449.6667,197600.6667 +19951,7522.289,10550.251,10660.8512,3074.329,1327.586,1.7608,103.114,150.8667,1166.8667,5.4667,84.1047,5.81,124848.6667,197882.0 +19952,7580.997,10581.723,10733.9241,3129.711,1303.988,1.749,102.535,152.1,1177.0,5.6667,83.2542,6.02,124629.3333,198295.6667 +19953,7683.125,10671.738,10807.2352,3172.427,1303.248,1.7383,103.097,152.8667,1183.6667,5.6667,82.8261,5.7967,124933.6667,198807.0 +19954,7772.586,10744.203,10882.1475,3211.762,1335.135,1.7241,103.06,153.7,1191.6333,5.5667,82.4916,5.72,125221.3333,199351.6667 +19961,7868.468,10824.674,10958.2375,3259.617,1355.353,1.7089,102.529,155.0667,1211.1667,5.5333,81.6066,5.3633,125542.0,199776.0 +19962,8032.84,11005.217,11038.9935,3304.626,1418.388,1.6858,102.847,156.4,1239.5333,5.5,82.2476,5.2433,126280.0,200279.3333 +19963,8131.408,11103.935,11125.4637,3348.618,1474.35,1.6763,103.043,157.3,1246.5,5.2667,82.452,5.3067,127218.3333,200849.6667 +19964,8259.771,11219.238,11217.5508,3394.838,1480.128,1.6576,103.233,158.6667,1268.3,5.3333,82.2956,5.28,127840.3333,201457.3333 +19971,8362.655,11291.665,11315.1161,3446.497,1522.404,1.6452,103.317,159.6333,1281.0,5.2333,82.7629,5.2767,128495.6667,202395.6667 +19972,8518.825,11479.33,11417.5148,3496.96,1590.218,1.632,103.219,160.0,1277.7333,5.0,82.6928,5.5233,129339.6667,202835.3333 +19973,8662.823,11622.911,11525.0735,3559.055,1625.251,1.6211,103.326,160.8,1297.3333,4.8667,83.1887,5.5333,129950.3333,203366.6667 +19974,8765.907,11722.722,11636.5249,3618.627,1644.529,1.6067,103.169,161.6667,1307.8,4.6667,83.7156,5.5067,130503.6667,203935.3333 +19981,8866.48,11839.876,11751.2764,3676.961,1712.324,1.5935,103.258,162.0,1306.6667,4.6333,83.1161,5.52,130782.3333,204395.0 +19982,8969.699,11949.492,11869.7764,3743.404,1695.773,1.5788,103.081,162.5333,1319.9,4.4,81.765,5.5,131259.3333,204905.0 +19983,9121.097,12099.191,11990.4282,3806.982,1741.623,1.5633,102.764,163.3667,1334.8333,4.5333,80.8646,5.5333,131568.3333,205482.6667 +19984,9293.991,12294.737,12113.2492,3847.889,1796.964,1.5499,103.522,164.1333,1355.1333,4.4333,81.0535,4.86,132293.6667,206097.6667 +19991,9411.682,12410.778,12238.5118,3897.147,1853.063,1.5377,103.168,164.7333,1384.9667,4.3,80.8521,4.7333,132943.3333,206876.0 +19992,9526.21,12514.408,12365.285,3957.569,1848.341,1.5213,103.071,165.9667,1418.2667,4.2667,80.6023,4.7467,133214.6667,207431.6667 +19993,9686.626,12679.977,12493.7035,4024.047,1893.735,1.5037,103.152,167.2,1440.0667,4.2333,80.2136,5.0933,133570.6667,208043.6667 +19994,9900.169,12888.281,12624.3398,4108.02,1953.097,1.4891,103.137,168.4333,1482.6667,4.0667,80.8811,5.3067,134275.0,208660.3333 +20001,10002.179,12935.252,12756.1978,4205.131,1950.65,1.4752,103.015,170.1,1492.2,4.0333,80.7776,5.6767,136619.3333,211586.0 +20002,10247.72,13170.749,12885.5495,4274.861,2075.786,1.4667,102.828,171.4333,1535.1,3.9333,80.772,6.2733,136946.6667,212242.0 +20003,10318.165,13183.89,13010.0471,4350.834,2059.969,1.4528,102.666,173.0,1557.4667,4.0,79.7339,6.52,136695.3333,212918.6667 +20004,10435.744,13262.25,13129.1448,4425.204,2067.227,1.4404,102.399,174.2333,1577.6,3.9,78.2838,6.4733,137341.3333,213560.3333 +20011,10470.231,13219.251,13242.6261,4496.052,1971.333,1.4222,101.933,175.9,1572.4333,4.2333,76.2684,5.5933,137724.3333,214101.0 +20012,10599.0,13301.394,13350.3998,4532.144,1973.033,1.4095,101.56,177.1333,1590.6333,4.4,74.5433,4.3267,137088.0,214735.6667 +20013,10598.02,13248.142,13452.5044,4555.089,1944.909,1.4034,101.097,177.6333,1591.3667,4.8333,72.8865,3.4967,136719.3333,215421.6667 +20014,10660.465,13284.881,13549.5783,4609.546,1850.091,1.3966,100.918,177.5,1581.9,5.5,71.7826,2.1333,136225.6667,216111.6667 +20021,10783.5,13394.91,13642.0267,4655.831,1912.659,1.3831,100.951,178.0667,1585.6333,5.7,72.1619,1.7333,136105.3333,216664.0 +20022,10887.46,13477.356,13731.5275,4721.022,1933.282,1.3657,101.335,179.4667,1609.5333,5.8333,73.0831,1.75,136360.0,217203.6667 +20023,10984.04,13531.741,13819.4217,4778.693,1933.197,1.3504,101.12,180.4333,1616.7333,5.7333,73.6318,1.74,136806.6667,217867.6667 +20024,11061.433,13549.421,13906.0731,4844.998,1942.531,1.3403,101.111,181.5,1641.8667,5.8667,73.5331,1.4433,136651.6667,218543.0 +20031,11174.129,13619.434,13992.4845,4909.826,1960.221,1.3208,100.663,183.3667,1682.8,5.8667,73.9181,1.25,137444.3333,220109.3333 +20032,11312.766,13741.107,14079.7166,4983.56,1972.386,1.3091,100.642,183.0667,1670.0667,6.1333,73.5049,1.2467,137655.6667,220774.0 +20033,11566.669,13970.157,14166.9684,5054.68,2044.304,1.2912,100.548,184.4333,1724.5,6.1333,74.0054,1.0167,137544.0,221512.6667 +20034,11772.234,14131.379,14255.2957,5124.541,2131.311,1.2823,100.712,185.1333,1741.8667,5.8333,74.8627,0.9967,138273.0,222275.6667 +20041,11923.447,14212.34,14345.4715,5205.543,2154.052,1.2769,100.907,186.7,1779.0,5.7,75.4283,1.0033,138489.0,222356.0 +20042,12112.815,14323.017,14436.9677,5278.129,2262.607,1.2741,100.404,188.1667,1803.0,5.6,76.1141,1.01,138902.0,222973.3333 +20043,12305.307,14457.832,14530.6572,5371.996,2318.272,1.2678,100.51,189.3667,1826.3,5.4333,76.8515,1.4333,139538.6667,223680.0 +20044,12527.214,14605.595,14625.5401,5464.088,2390.082,1.2626,100.516,191.4,1877.4667,5.4333,77.734,1.95,140029.3333,224418.0 +20051,12767.286,14767.846,14718.9072,5549.788,2486.068,1.2596,100.284,192.3667,1892.0,5.3,78.6482,2.47,140428.0,225038.0 +20052,12922.656,14839.707,14809.9664,5643.732,2476.474,1.2558,100.453,193.6667,1919.4667,5.1,78.6991,2.9433,141525.6667,225674.0 +20053,13142.642,14956.291,14899.7647,5730.245,2531.076,1.2438,100.385,196.6,1986.0333,4.9667,78.1064,3.46,142287.0,226422.3333 +20054,13324.204,15041.232,14987.7347,5820.809,2645.263,1.239,100.561,198.4333,2019.7333,4.9667,78.8068,3.98,142599.6667,227196.0 +20061,13599.16,15244.088,15073.6648,5903.311,2709.74,1.2356,100.672,199.4667,2042.7,4.7333,79.1551,4.4567,143449.3333,227763.6667 +20062,13753.424,15281.525,15158.0237,6001.556,2709.252,1.2243,100.662,201.2667,2076.8333,4.6333,78.9365,4.9067,144067.6667,228432.6667 +20063,13870.188,15304.517,15239.1033,6080.335,2709.42,1.2144,100.883,203.1667,2112.9,4.6333,78.7101,5.2467,144547.3333,229166.3333 +20064,14039.56,15433.643,15316.9142,6165.119,2675.406,1.2177,100.816,202.3333,2092.8333,4.4333,78.482,5.2467,145606.0,229896.0 +20071,14215.651,15478.956,15394.5913,6264.013,2664.295,1.2045,100.578,204.317,2129.1,4.5,78.7188,5.2567,146135.0,230839.3333 +20072,14402.082,15577.779,15472.7721,6326.913,2699.217,1.1889,100.714,206.631,2166.1,4.5,79.1876,5.25,145850.6667,231482.0 +20073,14564.117,15671.605,15551.6854,6418.373,2685.969,1.1752,100.556,207.939,2188.1667,4.6667,78.697,5.0733,145943.6667,232210.0 +20074,14715.058,15767.146,15630.1644,6508.952,2642.56,1.1572,100.263,210.4897,2232.6,4.8,78.5673,4.4967,146271.3333,232936.6667 +20081,14706.538,15702.906,15707.8964,6598.077,2563.701,1.1446,100.231,212.7697,2252.8667,5.0,77.977,3.1767,146206.6667,232806.6667 +20082,14865.701,15792.773,15782.9451,6686.336,2540.595,1.1276,100.055,215.5377,2305.8,5.3333,76.461,2.0867,145925.6667,233410.0 +20083,14898.999,15709.562,15854.4792,6731.171,2498.242,1.1151,99.733,218.861,2332.0667,6.0,73.9482,1.94,145270.3333,234110.3333 +20084,14608.208,15366.607,15921.6471,6731.865,2307.915,1.1368,99.202,213.8487,2167.0667,6.8667,69.7976,0.5067,144090.3333,234825.0 +20091,14430.901,15187.475,15983.2985,6694.942,2014.878,1.1346,98.628,212.3777,2115.5667,8.2667,65.2926,0.1833,141499.6667,234912.6667 +20092,14381.236,15161.772,16040.7026,6671.028,1863.65,1.1159,98.087,213.507,2139.8,9.3,63.7623,0.18,140304.3333,235459.3333 +20093,14448.882,15216.647,16094.7417,6710.929,1841.416,1.0937,97.982,215.344,2193.3667,9.6333,65.3514,0.1567,139403.6667,236093.0 +20094,14651.248,15379.155,16147.0634,6767.885,1998.71,1.0825,98.327,217.03,2222.9,9.9333,66.7467,0.12,138368.0,236739.0 +20101,14764.611,15456.059,16199.7102,6835.669,2038.161,1.07,98.612,217.374,2245.1,9.8333,68.1434,0.1333,138590.0,236996.3333 +20102,14980.193,15605.628,16253.7162,6916.911,2148.795,1.0618,99.246,217.2973,2247.3333,9.6333,70.2245,0.1933,139226.3333,237506.0 +20103,15141.605,15726.282,16310.4477,6985.635,2236.495,1.0545,99.702,217.9343,2262.7,9.4667,71.3347,0.1867,139337.6667,238103.6667 +20104,15309.471,15807.995,16369.3052,7031.511,2238.44,1.0464,99.697,219.699,2320.3667,9.5,71.942,0.1867,139154.6667,238711.3333 +20111,15351.444,15769.911,16430.4648,7089.16,2205.962,1.0374,99.364,222.0437,2381.1,9.0333,72.7261,0.1567,139427.6667,238851.6667 +20112,15557.535,15876.839,16494.1242,7158.561,2297.352,1.0291,99.808,224.5683,2431.5667,9.0667,72.8293,0.0933,139531.3333,239316.0 +20113,15647.681,15870.684,16560.1095,7227.254,2322.84,1.0225,99.776,226.0327,2437.0,9.0,73.5921,0.0833,139883.0,239871.0 +20114,15842.267,16048.702,16627.8917,7247.97,2504.095,1.0162,100.008,227.0473,2448.8,8.6333,74.1353,0.0733,140698.6667,240431.3333 +20121,16068.824,16179.968,16697.3907,7330.585,2567.75,1.009,100.136,228.326,2490.6,8.2667,74.8661,0.1033,141826.0,242436.0 +20122,16207.13,16253.726,16768.9712,7388.679,2636.863,1.0034,99.984,228.808,2482.8333,8.2,74.717,0.1533,142165.3333,242968.3333 +20123,16319.54,16282.151,16842.1603,7427.587,2644.119,0.9982,99.903,229.841,2490.1333,8.0333,74.228,0.1433,142542.3333,243564.0 +20124,16420.386,16300.035,16916.6539,7491.645,2638.282,0.9896,99.976,231.3693,2510.5,7.8,74.2101,0.16,143364.6667,244169.0 +20131,16629.05,16441.485,16992.5715,7533.987,2738.236,0.9826,99.972,232.2993,2542.8333,7.7333,74.5227,0.1433,143323.3333,244828.6667 +20132,16699.551,16464.402,17069.5661,7591.64,2775.276,0.9794,99.857,232.045,2513.8667,7.5333,74.4512,0.1167,143838.6667,245363.3333 +20133,16911.068,16594.743,17147.1508,7648.836,2879.995,0.9734,99.814,233.3,2541.4,7.2333,74.456,0.0833,144336.0,245961.0 +20134,17133.114,16712.76,17225.8024,7759.758,2910.546,0.9689,99.818,234.1627,2564.1333,6.9333,74.7773,0.0867,144264.6667,246564.3333 +20141,17144.281,16654.247,17305.4195,7828.587,2899.216,0.9635,99.868,235.621,2586.0,6.6667,74.6624,0.0733,145310.6667,247086.0 +20142,17462.703,16868.109,17385.7105,7921.739,3030.425,0.9556,100.047,236.8723,2623.5333,6.2,75.6349,0.0933,145913.0,247625.0 +20143,17743.227,17064.616,17467.5492,8036.39,3107.62,0.9523,100.093,237.4783,2642.2333,6.0667,76.1195,0.09,146569.0,248232.6667 +20144,17852.54,17141.235,17550.0148,8152.359,3139.452,0.9498,100.377,236.8883,2631.7333,5.7,76.3817,0.1,147482.0,248842.6667 +20151,17991.348,17280.647,17632.7606,8215.995,3245.05,0.9506,100.192,235.355,2584.1,5.5333,75.989,0.11,148106.0,249900.6667 +20152,18193.707,17380.875,17715.6013,8297.35,3245.825,0.9424,99.922,236.96,2617.7333,5.4333,76.0501,0.1233,148714.6667,250461.3333 +20153,18306.96,17437.08,17797.9113,8387.315,3251.67,0.9353,99.95,237.855,2641.9333,5.1,76.2634,0.1367,148945.6667,251099.0 +20154,18332.079,17462.579,17879.075,8461.274,3206.098,0.9306,99.928,237.837,2617.8,5.0333,75.6876,0.16,149612.3333,251741.3333 +20161,18425.306,17565.465,17959.5247,8560.649,3174.386,0.9237,99.821,237.6893,2605.7333,4.9,75.5328,0.36,150936.6667,252580.6667 +20162,18611.617,17618.581,18039.0586,8652.684,3178.537,0.9158,99.648,239.5903,2648.7667,4.9333,75.158,0.3733,151143.0,253180.0 +20163,18775.459,17724.489,18117.6492,8750.7,3185.73,0.9071,99.656,240.6073,2653.6333,4.9,75.0849,0.3967,151698.0,253855.0 +20164,18968.041,17812.56,18195.8769,8841.634,3281.493,0.9008,99.394,242.1347,2678.4667,4.7667,75.128,0.45,151968.0,254534.3333 +20171,19153.912,17896.623,18274.5328,8951.04,3283.481,0.8961,99.37,243.8387,2724.7333,4.5667,75.3508,0.7,152565.6667,254247.3333 +20172,19322.92,17996.802,18352.9509,9028.583,3357.362,0.8928,99.789,244.12,2738.8333,4.3667,76.2176,0.95,153214.0,254770.6667 +20173,19558.693,18126.226,18433.5035,9108.266,3413.3,0.8872,99.541,245.287,2762.1667,4.3333,76.1702,1.1533,153786.0,255356.6667 +20174,19882.965,18296.685,18516.0761,9234.339,3471.425,0.8788,99.649,247.2383,2821.9667,4.1667,77.0926,1.2033,153772.6667,255941.3333 +20181,20143.716,18436.262,18600.5425,9369.007,3550.839,0.8733,99.659,249.2543,2849.0667,4.0333,77.3251,1.4467,155018.3333,256937.0 +20182,20492.492,18590.004,18687.3221,9510.327,3603.22,0.8692,99.795,250.681,2883.2,3.9333,77.837,1.7367,155596.6667,257456.0 +20183,20659.102,18679.599,18776.0991,9629.389,3679.569,0.8655,99.775,251.7703,2894.9667,3.7667,78.1066,1.9233,155865.6667,258066.3333 +20184,20813.325,18721.281,18865.3729,9730.536,3717.518,0.8608,99.581,252.69,2911.0,3.8333,77.4994,2.22,156555.6667,258703.3333 +20191,21001.591,18833.195,18954.7646,9772.746,3801.932,0.8611,99.34,253.2927,2909.5,3.8667,76.523,2.4033,156825.0,258389.3333 +20192,21289.268,18982.528,19044.8815,9896.347,3842.963,0.855,99.188,255.283,2970.1667,3.6,75.7752,2.3967,156911.6667,258863.6667 +20193,21505.012,19112.653,19135.4193,10016.772,3858.192,0.8499,99.112,256.225,2981.2667,3.6333,75.6762,2.19,157839.0,259431.6667 +20194,21694.458,19202.31,19225.5991,10113.165,3801.943,0.8418,99.076,257.7853,3001.6,3.6,75.3778,1.6433,158544.0,260015.3333 diff --git a/lectures/_static/lecture_specific/subjective_beliefs_business_cycles/bbh_michigan_monthly.csv b/lectures/_static/lecture_specific/subjective_beliefs_business_cycles/bbh_michigan_monthly.csv new file mode 100644 index 00000000..b926586b --- /dev/null +++ b/lectures/_static/lecture_specific/subjective_beliefs_business_cycles/bbh_michigan_monthly.csv @@ -0,0 +1,508 @@ +yyyymm,px1_mean,share_more,share_same,share_less,unrate +197801,6.1,30,48,20,6.4 +197802,8.5,24,41,30,6.3 +197803,7.5,31,52,14,6.3 +197804,8.0,25,56,17,6.1 +197805,8.9,26,45,23,6.0 +197806,8.0,39,51,8,5.9 +197807,7.6,32,53,12,6.2 +197808,10.5,33,48,16,5.9 +197809,8.5,31,52,15,6.0 +197810,7.9,30,55,12,5.8 +197811,9.6,35,44,16,5.9 +197812,8.3,46,42,7,6.0 +197901,9.7,41,45,10,5.9 +197902,11.4,34,50,14,5.9 +197903,10.0,41,46,9,5.8 +197904,11.1,45,40,11,5.8 +197905,12.0,40,46,9,5.6 +197906,12.0,52,39,6,5.7 +197907,11.5,62,30,6,5.7 +197908,11.2,62,30,7,6.0 +197909,10.4,55,36,7,5.9 +197910,9.5,55,36,7,6.0 +197911,11.9,54,36,8,5.9 +197912,11.7,61,30,8,6.0 +198001,13.8,54,34,9,6.3 +198002,11.2,46,38,13,6.3 +198003,12.7,57,35,7,6.3 +198004,12.0,62,29,7,6.9 +198005,9.5,72,23,5,7.5 +198006,9.0,63,25,11,7.6 +198007,10.0,59,30,11,7.8 +198008,8.8,40,38,19,7.7 +198009,9.3,40,36,22,7.5 +198010,10.0,30,46,21,7.5 +198011,9.8,24,47,26,7.5 +198012,10.9,39,41,16,7.2 +198101,8.6,38,41,19,7.5 +198102,9.1,40,42,16,7.4 +198103,7.9,41,43,13,7.4 +198104,9.3,40,41,17,7.2 +198105,8.6,36,46,17,7.5 +198106,7.8,36,44,18,7.5 +198107,7.7,36,44,18,7.2 +198108,7.7,36,47,14,7.4 +198109,8.7,39,45,15,7.6 +198110,8.3,50,38,11,7.9 +198111,8.8,54,33,11,8.3 +198112,6.5,59,28,11,8.5 +198201,6.4,49,34,16,8.6 +198202,6.4,52,33,13,8.9 +198203,5.8,54,32,13,9.0 +198204,5.1,50,34,14,9.3 +198205,4.8,47,35,17,9.4 +198206,5.8,48,35,16,9.6 +198207,5.9,48,35,16,9.8 +198208,6.3,46,35,17,9.8 +198209,5.3,43,38,18,10.1 +198210,5.9,41,36,21,10.4 +198211,5.2,37,42,19,10.8 +198212,4.8,37,40,22,10.8 +198301,4.7,30,45,23,10.4 +198302,4.5,29,41,29,10.4 +198303,3.1,21,43,36,10.3 +198304,4.5,15,41,42,10.2 +198305,4.5,13,44,42,10.1 +198306,4.6,15,47,37,10.1 +198307,4.5,16,44,39,9.4 +198308,5.0,15,40,42,9.5 +198309,5.0,17,46,36,9.2 +198310,5.2,20,44,35,8.8 +198311,5.1,16,47,35,8.5 +198312,4.9,17,45,36,8.3 +198401,4.6,14,48,36,8.0 +198402,5.1,16,50,31,7.8 +198403,5.0,16,45,38,7.8 +198404,5.8,18,52,28,7.7 +198405,5.3,20,51,29,7.4 +198406,4.8,23,52,24,7.2 +198407,4.5,19,53,26,7.5 +198408,4.5,21,54,24,7.5 +198409,4.2,22,50,26,7.3 +198410,5.3,24,54,20,7.4 +198411,5.1,24,52,22,7.2 +198412,4.7,28,50,21,7.3 +198501,4.0,25,54,20,7.3 +198502,4.8,28,50,20,7.2 +198503,4.3,29,47,23,7.2 +198504,5.0,28,50,20,7.3 +198505,4.6,28,52,19,7.2 +198506,4.9,25,58,16,7.4 +198507,4.2,27,58,14,7.4 +198508,3.6,29,52,17,7.1 +198509,4.4,31,53,14,7.1 +198510,4.8,32,51,16,7.1 +198511,4.4,29,54,15,7.0 +198512,5.0,30,54,15,7.0 +198601,4.2,30,54,15,6.7 +198602,3.7,29,51,19,7.2 +198603,3.0,33,48,17,7.2 +198604,3.4,32,51,15,7.1 +198605,3.3,31,54,14,7.2 +198606,3.7,26,53,20,7.2 +198607,3.6,27,56,15,7.0 +198608,4.2,32,54,13,6.9 +198609,3.4,32,54,11,7.0 +198610,3.7,33,55,10,7.0 +198611,3.6,34,54,11,6.9 +198612,4.0,42,46,11,6.6 +198701,3.9,38,49,12,6.6 +198702,4.1,33,52,14,6.6 +198703,3.6,35,48,15,6.6 +198704,3.7,33,52,13,6.3 +198705,4.7,32,53,13,6.3 +198706,4.4,30,52,16,6.2 +198707,4.2,30,55,14,6.1 +198708,4.4,27,56,15,6.0 +198709,4.2,29,57,13,5.9 +198710,4.3,29,52,17,6.0 +198711,3.8,36,53,10,5.8 +198712,3.9,34,51,12,5.7 +198801,4.3,33,54,12,5.7 +198802,4.4,33,53,12,5.7 +198803,4.0,28,58,12,5.7 +198804,4.5,28,56,14,5.4 +198805,4.8,21,67,11,5.6 +198806,5.6,31,53,15,5.4 +198807,5.6,29,54,15,5.4 +198808,5.3,27,53,15,5.6 +198809,5.5,27,52,17,5.4 +198810,5.1,22,60,16,5.4 +198811,5.4,27,57,14,5.3 +198812,5.0,28,55,15,5.3 +198901,5.1,34,50,15,5.4 +198902,4.8,30,55,13,5.2 +198903,5.6,33,53,12,5.0 +198904,5.0,29,55,14,5.2 +198905,5.8,35,52,12,5.2 +198906,5.0,29,59,10,5.3 +198907,5.1,31,57,11,5.2 +198908,4.7,35,54,10,5.2 +198909,4.4,26,61,11,5.3 +198910,4.6,30,55,14,5.3 +198911,4.7,35,54,10,5.4 +198912,4.4,37,51,11,5.4 +199001,5.3,36,52,10,5.4 +199002,5.1,44,48,7,5.3 +199003,4.9,34,55,10,5.2 +199004,4.4,36,52,11,5.4 +199005,4.6,37,52,10,5.4 +199006,4.9,39,52,8,5.2 +199007,4.6,37,54,7,5.5 +199008,6.1,46,44,9,5.7 +199009,5.8,44,45,9,5.9 +199010,6.0,62,30,6,5.9 +199011,5.5,64,30,5,6.2 +199012,5.3,60,34,6,6.3 +199101,4.8,62,29,8,6.4 +199102,4.6,56,31,12,6.6 +199103,4.4,36,46,17,6.8 +199104,4.1,45,39,15,6.7 +199105,4.4,43,44,12,6.9 +199106,4.7,36,48,14,6.9 +199107,3.7,39,48,13,6.8 +199108,4.2,40,45,13,6.9 +199109,3.6,38,50,12,6.9 +199110,4.6,48,39,11,7.0 +199111,4.5,46,44,9,7.0 +199112,3.6,52,37,9,7.3 +199201,3.1,57,33,9,7.3 +199202,3.3,56,30,12,7.4 +199203,3.0,43,42,14,7.4 +199204,3.5,42,40,16,7.4 +199205,3.2,36,45,18,7.6 +199206,4.0,37,48,14,7.8 +199207,3.8,45,40,13,7.7 +199208,3.9,38,46,15,7.6 +199209,4.0,42,43,15,7.6 +199210,3.5,41,41,16,7.3 +199211,4.4,28,52,19,7.4 +199212,3.2,26,46,27,7.4 +199301,3.4,25,46,27,7.3 +199302,4.5,32,44,22,7.1 +199303,4.9,37,43,20,7.0 +199304,4.0,33,48,18,7.1 +199305,4.3,38,48,13,7.1 +199306,4.8,40,46,13,7.0 +199307,4.4,42,44,12,6.9 +199308,4.7,42,43,13,6.8 +199309,4.7,45,42,12,6.7 +199310,3.9,46,44,9,6.8 +199311,3.5,42,45,11,6.6 +199312,3.7,33,47,18,6.5 +199401,3.4,25,51,21,6.6 +199402,3.7,32,51,17,6.6 +199403,4.4,32,47,20,6.5 +199404,4.5,32,51,16,6.4 +199405,3.9,32,51,16,6.1 +199406,4.0,30,51,16,6.1 +199407,4.2,29,52,18,6.1 +199408,4.6,34,51,13,6.0 +199409,4.7,34,46,17,5.9 +199410,3.8,30,56,14,5.8 +199411,4.5,31,50,18,5.6 +199412,4.0,30,47,22,5.5 +199501,3.7,28,54,16,5.6 +199502,4.0,32,53,13,5.4 +199503,4.6,35,49,15,5.4 +199504,4.3,34,51,14,5.8 +199505,3.9,30,57,12,5.6 +199506,3.9,32,55,12,5.6 +199507,3.8,38,52,9,5.7 +199508,3.9,34,55,11,5.7 +199509,4.0,37,52,11,5.6 +199510,3.5,43,46,10,5.5 +199511,3.7,35,53,11,5.6 +199512,3.2,35,51,13,5.6 +199601,4.0,46,43,11,5.6 +199602,3.6,40,50,9,5.5 +199603,4.1,35,52,12,5.5 +199604,4.5,35,53,11,5.6 +199605,4.8,33,54,12,5.6 +199606,4.1,35,52,12,5.3 +199607,4.2,30,54,15,5.5 +199608,4.1,30,53,16,5.1 +199609,4.3,29,53,17,5.2 +199610,4.1,28,57,14,5.2 +199611,3.9,26,57,16,5.4 +199612,3.9,24,60,14,5.4 +199701,4.1,31,51,17,5.3 +199702,3.8,25,56,18,5.2 +199703,3.5,27,57,15,5.2 +199704,3.7,28,55,15,5.1 +199705,3.7,25,57,16,4.9 +199706,3.5,22,62,14,5.0 +199707,3.4,23,57,19,4.9 +199708,3.3,23,59,16,4.8 +199709,3.5,22,57,19,4.9 +199710,3.2,20,57,20,4.7 +199711,3.4,23,61,15,4.6 +199712,3.4,32,49,18,4.7 +199801,2.8,24,55,18,4.6 +199802,2.6,19,59,20,4.6 +199803,2.9,20,58,20,4.7 +199804,2.7,16,63,19,4.3 +199805,3.1,21,60,19,4.4 +199806,3.2,26,51,21,4.5 +199807,3.1,26,58,15,4.5 +199808,2.7,26,56,17,4.5 +199809,2.7,26,59,14,4.6 +199810,2.6,33,53,12,4.5 +199811,2.7,34,50,15,4.4 +199812,2.8,36,50,12,4.4 +199901,3.0,28,58,13,4.3 +199902,2.8,23,59,15,4.4 +199903,3.1,26,60,13,4.2 +199904,3.0,23,62,14,4.3 +199905,3.2,24,61,14,4.2 +199906,3.1,19,62,17,4.3 +199907,3.0,22,64,13,4.3 +199908,3.2,27,57,14,4.2 +199909,3.2,24,61,14,4.2 +199910,3.5,25,61,13,4.1 +199911,3.3,26,56,15,4.1 +199912,3.6,21,61,16,4.0 +200001,3.5,21,61,17,4.0 +200002,3.5,21,62,15,4.1 +200003,3.8,22,62,13,4.0 +200004,3.5,22,62,13,3.8 +200005,3.5,20,64,16,4.0 +200006,3.4,25,59,13,4.0 +200007,3.7,24,60,13,4.0 +200008,3.5,23,59,15,4.1 +200009,3.7,23,61,12,3.9 +200010,4.1,29,57,12,3.9 +200011,3.8,27,57,13,3.9 +200012,3.4,39,47,12,3.9 +200101,3.8,47,42,8,4.2 +200102,3.2,53,36,10,4.2 +200103,3.3,50,42,7,4.3 +200104,3.7,53,39,7,4.4 +200105,3.9,47,42,9,4.3 +200106,4.0,45,44,10,4.5 +200107,3.0,49,41,9,4.6 +200108,3.1,52,37,10,4.9 +200109,3.2,60,29,9,5.0 +200110,1.6,59,30,9,5.3 +200111,1.0,56,30,13,5.5 +200112,1.9,48,36,13,5.7 +200201,2.2,40,40,17,5.7 +200202,2.4,42,40,18,5.7 +200203,3.1,34,42,22,5.7 +200204,3.1,32,44,23,5.9 +200205,3.1,30,49,19,5.8 +200206,3.0,31,51,16,5.8 +200207,2.7,44,43,12,5.8 +200208,2.6,42,44,13,5.7 +200209,3.1,36,47,16,5.7 +200210,2.9,44,40,13,5.7 +200211,2.5,37,47,14,5.9 +200212,2.7,43,40,16,6.0 +200301,2.7,43,44,12,5.8 +200302,3.2,44,41,14,5.9 +200303,3.8,44,41,14,5.9 +200304,2.7,37,45,17,6.0 +200305,2.5,29,47,22,6.1 +200306,2.5,35,44,20,6.3 +200307,2.3,34,46,18,6.2 +200308,2.8,32,47,21,6.1 +200309,3.4,33,48,19,6.1 +200310,3.1,37,43,20,6.0 +200311,3.1,31,42,26,5.8 +200312,2.8,26,45,28,5.7 +200401,2.9,24,47,29,5.7 +200402,2.9,28,49,23,5.6 +200403,3.4,31,45,23,5.8 +200404,4.0,29,48,22,5.6 +200405,3.9,30,47,23,5.6 +200406,4.0,23,48,27,5.6 +200407,3.5,22,47,29,5.5 +200408,3.1,28,49,22,5.4 +200409,3.2,28,48,23,5.4 +200410,3.6,26,52,21,5.5 +200411,3.3,28,49,22,5.4 +200412,3.4,26,51,23,5.4 +200501,3.5,31,49,20,5.3 +200502,3.3,29,53,18,5.4 +200503,4.0,29,54,17,5.2 +200504,4.0,35,48,17,5.2 +200505,3.8,37,45,18,5.1 +200506,4.0,32,52,16,5.0 +200507,3.6,30,56,13,5.0 +200508,3.7,36,51,13,4.9 +200509,5.5,48,41,11,5.0 +200510,5.5,48,37,14,5.0 +200511,4.1,36,50,14,5.0 +200512,4.1,41,44,15,4.9 +200601,3.8,38,50,11,4.7 +200602,3.6,40,47,12,4.8 +200603,3.8,42,44,13,4.7 +200604,4.4,41,46,12,4.7 +200605,4.7,40,46,13,4.6 +200606,4.4,38,50,11,4.6 +200607,3.8,39,51,10,4.7 +200608,4.6,41,47,11,4.7 +200609,3.6,39,47,14,4.5 +200610,3.7,31,59,9,4.4 +200611,3.3,31,59,10,4.5 +200612,3.5,34,53,13,4.4 +200701,3.6,27,60,12,4.6 +200702,3.6,33,58,8,4.5 +200703,3.6,33,57,10,4.4 +200704,4.0,38,53,9,4.5 +200705,4.3,30,59,10,4.4 +200706,4.2,37,51,12,4.6 +200707,4.2,33,56,10,4.7 +200708,4.0,39,52,8,4.6 +200709,4.0,36,54,9,4.7 +200710,3.7,38,54,8,4.7 +200711,4.3,39,50,11,4.7 +200712,4.4,47,45,8,5.0 +200801,4.0,47,46,6,5.0 +200802,3.9,50,41,9,4.9 +200803,4.6,55,38,7,5.1 +200804,5.7,59,36,5,5.0 +200805,7.0,56,41,3,5.4 +200806,6.5,64,31,5,5.6 +200807,6.3,61,32,7,5.8 +200808,5.3,55,40,5,6.1 +200809,4.6,50,41,9,6.1 +200810,4.3,62,31,6,6.5 +200811,2.9,69,24,7,6.8 +200812,1.7,69,24,7,7.3 +200901,2.5,66,21,12,7.8 +200902,2.3,69,20,10,8.3 +200903,2.4,64,27,9,8.7 +200904,3.1,56,31,12,9.0 +200905,3.2,46,40,14,9.4 +200906,3.9,48,37,15,9.5 +200907,3.6,50,36,14,9.5 +200908,3.0,39,45,15,9.6 +200909,2.8,30,49,20,9.8 +200910,3.2,36,48,16,10.0 +200911,3.1,40,43,16,9.9 +200912,3.0,34,44,22,9.9 +201001,3.4,30,50,19,9.8 +201002,3.6,27,51,21,9.8 +201003,3.4,29,53,17,9.9 +201004,3.8,29,46,24,9.9 +201005,4.1,26,53,21,9.6 +201006,3.3,26,51,22,9.4 +201007,3.3,31,48,20,9.4 +201008,3.2,31,50,19,9.5 +201009,3.0,30,53,16,9.5 +201010,3.3,30,51,17,9.4 +201011,3.7,28,51,20,9.8 +201012,3.9,25,54,20,9.3 +201101,4.2,22,56,21,9.1 +201102,4.4,22,48,28,9.0 +201103,5.2,27,50,22,9.0 +201104,5.3,29,49,21,9.1 +201105,4.8,22,52,25,9.0 +201106,4.5,26,53,21,9.1 +201107,4.4,31,55,14,9.0 +201108,4.4,43,45,12,9.0 +201109,4.3,35,54,10,9.0 +201110,4.0,34,53,12,8.8 +201111,4.0,26,58,16,8.6 +201112,3.7,29,52,18,8.5 +201201,4.0,25,52,23,8.3 +201202,4.1,25,46,29,8.3 +201203,4.7,19,51,29,8.2 +201204,3.8,22,53,24,8.2 +201205,3.6,22,50,27,8.2 +201206,3.7,27,49,23,8.2 +201207,3.9,26,52,21,8.2 +201208,4.3,25,53,21,8.1 +201209,4.3,20,53,27,7.8 +201210,4.2,19,49,30,7.8 +201211,4.0,24,42,30,7.7 +201212,4.0,35,40,25,7.9 +201301,4.5,31,47,22,8.0 +201302,4.4,27,48,23,7.7 +201303,4.0,31,43,25,7.5 +201304,4.0,29,47,23,7.6 +201305,4.2,26,48,25,7.5 +201306,3.8,23,52,24,7.5 +201307,4.1,23,56,21,7.3 +201308,4.2,28,45,26,7.2 +201309,4.1,32,45,22,7.2 +201310,3.6,30,50,19,7.2 +201311,3.7,35,44,21,6.9 +201312,3.8,26,52,22,6.7 +201401,4.0,29,46,23,6.6 +201402,4.3,34,47,18,6.7 +201403,4.2,30,54,15,6.7 +201404,4.0,26,53,21,6.2 +201405,4.4,23,48,28,6.3 +201406,3.7,24,53,22,6.1 +201407,4.0,28,48,22,6.2 +201408,4.0,26,52,21,6.1 +201409,3.8,29,49,21,5.9 +201410,3.5,22,52,25,5.7 +201411,3.0,19,53,28,5.8 +201412,3.0,21,52,27,5.6 +201501,2.7,19,52,29,5.7 +201502,3.1,24,45,30,5.5 +201503,3.5,21,51,27,5.4 +201504,3.2,20,51,28,5.4 +201505,3.4,22,53,24,5.6 +201506,3.5,20,54,25,5.3 +201507,3.7,25,53,21,5.2 +201508,3.5,25,51,24,5.1 +201509,3.1,27,52,19,5.0 +201510,3.5,27,52,20,5.0 +201511,3.2,21,55,22,5.1 +201512,3.0,25,53,21,5.0 +201601,3.1,29,52,18,4.8 +201602,2.9,26,54,19,4.9 +201603,3.3,26,54,20,5.0 +201604,3.3,31,52,16,5.1 +201605,3.0,24,54,21,4.8 +201606,3.1,28,54,18,4.9 +201607,3.2,31,51,17,4.8 +201608,3.0,27,51,20,4.9 +201609,3.0,26,52,20,5.0 +201610,3.0,27,51,20,4.9 +201611,3.0,26,50,23,4.7 +201612,2.8,22,48,28,4.7 +201701,3.1,22,43,33,4.7 +201702,3.3,26,38,35,4.6 +201703,3.2,25,37,36,4.4 +201704,2.9,23,41,36,4.4 +201705,3.0,25,44,30,4.4 +201706,3.3,27,43,30,4.3 +201707,3.0,27,48,24,4.3 +201708,3.1,25,46,29,4.4 +201709,3.3,25,47,28,4.3 +201710,3.0,23,47,29,4.2 +201711,2.9,22,49,29,4.2 +201712,3.2,25,46,29,4.1 +201801,3.0,26,43,31,4.0 +201802,3.1,23,42,35,4.1 +201803,3.3,22,45,32,4.0 +201804,3.3,25,46,28,4.0 +201805,3.3,24,51,24,3.8 +201806,3.7,22,47,30,4.0 +201807,3.7,26,44,29,3.8 +201808,3.7,25,45,30,3.8 +201809,3.3,20,50,29,3.7 +201810,3.7,23,46,29,3.8 +201811,3.3,22,50,27,3.8 +201812,3.3,30,47,22,3.9 +201901,2.9,33,47,19,4.0 +201902,3.1,31,46,23,3.8 +201903,2.9,22,52,25,3.8 +201904,3.1,24,49,27,3.6 +201905,3.4,22,53,25,3.6 +201906,3.5,27,49,24,3.6 +201907,3.2,25,51,23,3.7 +201908,3.4,30,51,19,3.7 +201909,3.3,31,48,20,3.5 +201910,3.0,32,48,20,3.6 +201911,3.1,23,53,23,3.6 +201912,2.8,31,46,23,3.6 +202001,2.9,21,53,26,3.5 +202002,2.8,23,56,21,3.5 +202003,2.5,39,39,21,4.4 diff --git a/lectures/_static/quant-econ.bib b/lectures/_static/quant-econ.bib index 133a5ef4..281e3b85 100644 --- a/lectures/_static/quant-econ.bib +++ b/lectures/_static/quant-econ.bib @@ -1320,6 +1320,37 @@ @book{Roman2005 publisher={Springer} } + +@article{Spear_Srivastava_87, + author = {Stephen E. Spear and Sanjay Srivastava}, + title = {{On Repeated Moral Hazard with Discounting}}, + journal = {Review of Economic Studies}, + year = 1987, + volume = {54}, + number = {4}, + pages = {599-617}, + month = {}, + keywords = {}, + doi = {10.2307/2297484}, + abstract = {In this paper, we analyze optimal contracts in an infinitely repeated agency model in which both the principal and agent discount the future. We show that there is a stationary representation of the optimal contract when the agent's conditional discounted expected utility is used as a state variable. This representation reduces the multi-period problem to a static variational problem which can be analyzed using standard variational techniques. This reduction is used to obtain several properties of the contract.}, + url = {https://ideas.repec.org/a/oup/restud/v54y1987i4p599-617..html} +} + +@article{Phelan_Townsend_91, + author = {Christopher Phelan and Robert M. Townsend}, + title = {{Computing Multi-Period, Information-Constrained Optima}}, + journal = {Review of Economic Studies}, + year = 1991, + volume = {58}, + number = {5}, + pages = {853-881}, + month = {}, + keywords = {}, + doi = {10.2307/2297941}, + abstract = {This paper presents a detailed theoretical derivation and justification for methods used to compute solutions to a multi-period (including infinite-period), continuum-agent, unobservedeffort economy. Actual solutions are displayed illustrating cross-sectional variability in consumption and labour effort in the population at a point in time and variability for a typical individual over time. The optimal tradeoff between insurance and incentives is explored and the issue of excess variability is addressed by consideration of the analogue full-information economy and various restricted-contracting regimes.}, + url = {https://ideas.repec.org/a/oup/restud/v58y1991i5p853-881..html} +} + @article{PhelanStacchetti2001, author={Christopher Phelan and Ennio Stacchetti}, title={Sequential Equilibria in a Ramsey Tax Model}, @@ -1342,6 +1373,199 @@ @article{APS1990 pages = {1041-1063} } +@article{APS1986, + author = {Dilip Abreu and David Pearce and Ennio Stacchetti}, + title = {Optimal Cartel Equilibria with Imperfect Monitoring}, + journal = {Journal of Economic Theory}, + volume = {39}, + number = {1}, + pages = {251--269}, + year = {1986}, + month = {June}, + doi = {10.1016/0022-0531(86)90028-1} +} + +@article{AguiarGopinath2006, + author = {Mark Aguiar and Gita Gopinath}, + title = {Defaultable Debt, Interest Rates and the Current Account}, + journal = {Journal of International Economics}, + volume = {69}, + number = {1}, + pages = {64--83}, + year = {2006}, + doi = {10.1016/j.jinteco.2005.05.005} +} + +@article{AguiarGopinath2007, + author = {Mark Aguiar and Gita Gopinath}, + title = {Emerging Market Business Cycles: The Cycle Is the Trend}, + journal = {Journal of Political Economy}, + volume = {115}, + number = {1}, + pages = {69--102}, + year = {2007}, + doi = {10.1086/511283} +} + +@article{Atkeson1991, + author = {Andrew Atkeson}, + title = {International Lending with Moral Hazard and Risk of Repudiation}, + journal = {Econometrica}, + volume = {59}, + number = {4}, + pages = {1069--1089}, + year = {1991}, + doi = {10.2307/2938174} +} + +@article{AtkesonLucas1992, + author = {Andrew Atkeson and Robert E. Lucas, Jr.}, + title = {On Efficient Distribution with Private Information}, + journal = {Review of Economic Studies}, + volume = {59}, + number = {3}, + pages = {427--453}, + year = {1992}, + doi = {10.2307/2297858} +} + +@article{BulowRogoff1989a, + author = {Jeremy Bulow and Kenneth Rogoff}, + title = {A Constant Recontracting Model of Sovereign Debt}, + journal = {Journal of Political Economy}, + volume = {97}, + number = {1}, + pages = {155--178}, + year = {1989}, + doi = {10.1086/261596} +} + +@article{BulowRogoff1989b, + author = {Jeremy Bulow and Kenneth Rogoff}, + title = {Sovereign Debt: Is to Forgive to Forget?}, + journal = {American Economic Review}, + volume = {79}, + number = {1}, + pages = {43--50}, + year = {1989} +} + +@article{EatonGersowitz1981, + author = {Jonathan Eaton and Mark Gersovitz}, + title = {Debt with Potential Repudiation: Theoretical and Empirical Analysis}, + journal = {Review of Economic Studies}, + volume = {48}, + number = {2}, + pages = {289--309}, + year = {1981} +} + +@article{EichengreenPortes1986, + author = {Barry Eichengreen and Richard Portes}, + title = {Debt and Default in the 1930s: Causes and Consequences}, + journal = {European Economic Review}, + volume = {30}, + number = {3}, + pages = {599--640}, + year = {1986} +} + +@article{FudenbergHolmstromMilgrom1990, + author = {Drew Fudenberg and Bengt Holmstrom and Paul Milgrom}, + title = {Short-Term Contracts and Long-Term Agency Relationships}, + journal = {Journal of Economic Theory}, + volume = {51}, + number = {1}, + pages = {1--31}, + year = {1990}, + doi = {10.1016/0022-0531(90)90048-O} +} + +@article{GertlerRogoff1990, + author = {Mark Gertler and Kenneth Rogoff}, + title = {North-South Lending and Endogenous Domestic Capital Market Inefficiencies}, + journal = {Journal of Monetary Economics}, + volume = {26}, + number = {2}, + pages = {245--266}, + year = {1990}, + doi = {10.1016/0304-3932(90)90022-V} +} + +@article{GrossmanHart1983, + author = {Sanford J. Grossman and Oliver D. Hart}, + title = {An Analysis of the Principal-Agent Problem}, + journal = {Econometrica}, + volume = {51}, + number = {1}, + pages = {7--45}, + year = {1983}, + doi = {10.2307/1912246} +} + +@article{GrossmanVanHuyck1988, + author = {Herschel I. Grossman and John B. Van Huyck}, + title = {Sovereign Debt as a Contingent Claim: Excusable Default, Repudiation, and Reputation}, + journal = {American Economic Review}, + volume = {78}, + number = {5}, + pages = {1088--1097}, + year = {1988} +} + +@incollection{LindertMorton1989, + author = {Peter H. Lindert and Peter J. Morton}, + title = {How Sovereign Debt Has Worked}, + booktitle = {Developing Country Debt and Economic Performance, Volume 1: The International Financial System}, + editor = {Jeffrey D. Sachs}, + publisher = {University of Chicago Press}, + pages = {39--106}, + year = {1989} +} + +@article{NeuemeyerPerri2005, + author = {Andr{\'e}s Neumeyer and Fabrizio Perri}, + title = {Business Cycles in Emerging Economies: The Role of Interest Rates}, + journal = {Journal of Monetary Economics}, + volume = {52}, + number = {2}, + pages = {345--380}, + year = {2005}, + doi = {10.1016/j.jmoneco.2004.04.011} +} + +@article{Rogerson1985, + author = {William P. Rogerson}, + title = {The First-Order Approach to Principal-Agent Problems}, + journal = {Econometrica}, + volume = {53}, + number = {6}, + pages = {1357--1367}, + year = {1985}, + doi = {10.2307/1913212} +} + +@article{ThomasWorrall1990, + author = {Jonathan Thomas and Tim Worrall}, + title = {Income Fluctuation and Asymmetric Information: An Example of a Repeated Principal-Agent Problem}, + journal = {Journal of Economic Theory}, + volume = {51}, + number = {2}, + pages = {367--390}, + year = {1990} +} + +@article{Tsyrennikov2013, + author = {Viktor Tsyrennikov}, + title = {Capital Flows Under Moral Hazard}, + journal = {Journal of Monetary Economics}, + volume = {60}, + number = {1}, + pages = {92--108}, + year = {2013}, + doi = {10.1016/j.jmoneco.2012.11.006} +} + @article{HarrisonKreps1979, author={Harrison, J. Michael and Kreps, David M.}, title={Martingales and arbitrage in multiperiod securities markets}, @@ -1891,11 +2115,12 @@ @book{LasotaMackey1994 } @book{Ljungqvist2012, - author = {Ljungqvist, L and Sargent, T J}, - publisher = {MIT Press}, + author = {Lars Ljungqvist and Thomas J. Sargent}, + publisher = {The MIT Press}, title = {Recursive Macroeconomic Theory}, edition = {4}, - year = {2018} + year = {2018}, + isbn = {9780262038669} } @article{Lucas1978, @@ -2875,3 +3100,131 @@ @article{szoke2022estimating year = {2022}, doi = {10.1016/j.jet.2021.105225} } + +@article{EichengrehenPortes1986, + author = {Barry Eichengreen and Richard Portes}, + title = {Debt and Default in the 1930s: Causes and Consequences}, + journal = {European Economic Review}, + year = {1986}, + volume = {30}, + number = {3}, + pages = {599--640} +} + +@article{bhandari2025survey, + title={Survey data and subjective beliefs in business cycle models}, + author={Bhandari, Anmol and Borovi{\v{c}}ka, Jaroslav and Ho, Paul}, + journal={Review of Economic Studies}, + volume={92}, + number={3}, + pages={1375--1437}, + year={2025}, + publisher={Oxford University Press UK} +} + +@article{BhandariBorovickaHo2024, + author = {Bhandari, Anmol and Borov{\v{c}}ka, Jaroslav and Ho, Paul}, + title = {Survey Data and Subjective Beliefs in Business Cycle Models}, + journal = {Review of Economic Studies}, + year = {2024}, + volume = {91}, + number = {3}, + pages = {1359--1395}, + doi = {10.1093/restud/rdad082}, + note = {NBER Working Paper No.\ 25192} +} + +@article{IlutSchneider2014, + author = {Ilut, Cosmin L. and Schneider, Martin}, + title = {Ambiguous Business Cycles}, + journal = {American Economic Review}, + year = {2014}, + volume = {104}, + number = {8}, + pages = {2368--2399}, + doi = {10.1257/aer.104.8.2368} +} + +@article{ChristianoEichenbaumTrabandt2016, + author = {Christiano, Lawrence J. and Eichenbaum, Martin S. + and Trabandt, Mathias}, + title = {Unemployment and Business Cycles}, + journal = {Econometrica}, + year = {2016}, + volume = {84}, + number = {4}, + pages = {1523--1569}, + doi = {10.3982/ECTA11776} +} + +@article{Shimer2005, + author = {Shimer, Robert}, + title = {The Cyclical Behavior of Equilibrium Unemployment and Vacancies}, + journal = {American Economic Review}, + year = {2005}, + volume = {95}, + number = {1}, + pages = {25--49}, + doi = {10.1257/0002828053828572} +} + +@book{Shimer2010, + author = {Shimer, Robert}, + title = {Labor Markets and Business Cycles}, + publisher = {Princeton University Press}, + year = {2010}, + series = {CREI Lectures in Macroeconomics} +} + +@techreport{Schmidt2016, + author = {Schmidt, Lawrence D. W.}, + title = {Climbing and Falling Off the Ladder: Asset Pricing + Implications of Labor Market Event Risk}, + institution = {University of California, San Diego}, + type = {Working Paper}, + year = {2016} +} + +@article{MankiwReisWolfers2003, + author = {Mankiw, N. Gregory and Reis, Ricardo and Wolfers, Justin}, + title = {Disagreement about Inflation Expectations}, + journal = {NBER Macroeconomics Annual}, + year = {2003}, + volume = {18}, + pages = {209--248}, + doi = {10.1086/ma.18.3585256} +} + +@article{RavennaWalsh2008, + author = {Ravenna, Federico and Walsh, Carl E.}, + title = {Vacancies, Unemployment, and the Phillips Curve}, + journal = {European Economic Review}, + year = {2008}, + volume = {52}, + number = {8}, + pages = {1494--1521}, + doi = {10.1016/j.euroecorev.2008.03.001} +} + +@article{BorovickaHansen2014, + author = {Borov{\v{c}}ka, Jaroslav and Hansen, Lars Peter}, + title = {Examining Macroeconomic Models through the Lens of Asset Pricing}, + journal = {Journal of Econometrics}, + year = {2014}, + volume = {183}, + number = {1}, + pages = {67--90}, + doi = {10.1016/j.jeconom.2014.06.006} +} + +@article{CoibionGorodnichenko2015, + author = {Coibion, Olivier and Gorodnichenko, Yuriy}, + title = {Information Rigidity and the Expectations Formation Process: + A Simple Framework and New Facts}, + journal = {American Economic Review}, + year = {2015}, + volume = {105}, + number = {8}, + pages = {2644--2678}, + doi = {10.1257/aer.20130921} +} diff --git a/lectures/_toc.yml b/lectures/_toc.yml index a7cf3e5a..646e8f33 100644 --- a/lectures/_toc.yml +++ b/lectures/_toc.yml @@ -45,6 +45,7 @@ parts: - file: five_preferences - file: entropy - file: robustness + - file: subjective_beliefs_business_cycles - file: rob_markov_perf - caption: Time Series Models numbered: true @@ -83,6 +84,9 @@ parts: - file: amss3 - file: chang_ramsey - file: chang_credible + - file: repeat_mh + - file: atkeson_1991 + - file: tsyrennikov_2013 - file: dovis_accounting_mf - caption: Other numbered: true diff --git a/lectures/atkeson_1991.md b/lectures/atkeson_1991.md new file mode 100644 index 00000000..2b716574 --- /dev/null +++ b/lectures/atkeson_1991.md @@ -0,0 +1,1633 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +(atkeson_1991)= +# International Lending with Moral Hazard and Risk of Repudiation + +## Overview + +This lecture studies {cite:t}`Atkeson1991`, which examines the *constrained +optimal pattern of capital flows* between an international lender and a +sovereign borrower subject to two frictions: + +1. **Moral hazard**: lenders cannot observe whether the borrower invests or + simply consumes loan proceeds. +2. **Risk of repudiation**: as a sovereign, the borrower can unilaterally + renounce its debt at any time. + +A central result is that, under the informativeness and interiority +conditions stated below, an optimal contract can require the borrowing +country to *export capital* after the lowest output realizations. + +These low-output outflows are part of the mechanism that incentivizes +investment. + +The model extends recursive techniques from {cite:t}`APS1986`, {cite:t}`APS1990`, +and {cite:t}`Spear_Srivastava_87` to an environment with a *physical state +variable* that changes across periods. + +## The environment + +### Technology + +Time is discrete, $t = 0, 1, 2, \ldots$. + +In each period the borrower chooses +investment $I_t \geq 0$. + +Output next period takes values in a finite, ordered set +$\mathcal{Y} = \{Y_1,\ldots,Y_N\}$ with $0 < Y_1 < \cdots < Y_N$. + +The technology is built from two fixed probability distributions on +$\mathcal{Y}$, which we call $g_0$ and $g_1$. + +We think of $g_1$ as the output distribution when investment is lowest and +$g_0$ as the output distribution when investment is highest. + +Accordingly $g_0$ places relatively more weight on high outputs than $g_1$ +does. + +Investment does not reshape these two distributions; it only changes how likely +output is to be drawn from one rather than the other. + +With probability $\lambda(I)$ output is drawn from the favorable distribution +$g_0$, and with probability $1 - \lambda(I)$ it is drawn from $g_1$, so given +investment $I_t$ next period's output $Y_{t+1}$ has the mixture distribution + +$$ +g(Y';\,I) = \lambda(I)\,g_0(Y') + \bigl[1 - \lambda(I)\bigr]\,g_1(Y'). +$$ + +The weight $\lambda : [0,I_{\max}] \to [0,1]$ is strictly increasing and +concave, + +$$ +\lambda'(I)>0, \qquad \lambda''(I)\leq 0, +$$ + +so more investment always raises the chance of the favorable distribution, but +with diminishing returns. + +Every output level keeps strictly positive probability at every investment +level, $g(Y_i;I)>0$ for all $i$, so no single realization ever fully reveals how +much the borrower invested. + +Finally, the likelihood ratio $g_0(Y_i)/g_1(Y_i)$ is increasing in $i$, the +monotone likelihood-ratio condition. + +Since higher investment raises $\lambda(I)$, high output is relatively good +news about investment, while low output is relatively strong evidence that +the borrower invested little. + +### Agents and preferences + +**The borrower** is an infinitely-lived, risk-averse agent with normalized +discounted utility + +$$ +v(\sigma) = (1 - \beta)\,\mathbb{E}_0^{\sigma} + \sum_{t=0}^{\infty} \beta^t \, u(c_t), +$$ + +```{prf:assumption} Preferences and Autarky +:label: atkeson_assumption_preferences + +The borrower has discount factor $\beta \in (0,1)$ and period utility +$u(c)$ that is increasing, strictly concave, bounded above, and satisfies +$u'(0)=+\infty$. + +Lenders are short-lived and risk neutral. + +The borrower's autarky value is high enough to rule out equilibrium states +with arbitrarily low current consumption: + +$$ +(1-\beta)u(0) + \beta \bar u < v_{\text{aut}}(Y_1), +$$ + +where $\bar u$ is an upper bound for period utility. +``` + +The last inequality is Atkeson's lower-bound condition. + +It ensures that very low current consumption cannot be compensated by even +the best possible future utility, so the relevant state space can be bounded +away from such outcomes. + +Here $\sigma$ denotes an **allocation**, a complete state-contingent plan for +consumption, investment, and the associated loans and repayments, written out +in full in feasibility condition {eq}`eq:atkeson_feasibility`. + +$\mathbb{E}_0^{\sigma}$ is the expectation over output histories that this plan +induces, evaluated at date $0$. + +The factor $(1 - \beta)$ normalizes lifetime utility to per-period units, so +$v$ is comparable to a one-period payoff. + +**Lenders** are a sequence of short-lived, risk-neutral agents, one born each +period. + +The lender born at $t$ extends loan $b_t$ when young and collects +state-contingent repayment $d_{t+1}(Y_{t+1})$ when old. + +A lender's +participation constraint is + +$$ +-b_t + \beta \sum_{Y'} d_{t+1}(Y')\,g(Y';\,I_t) \geq 0. +$$ (eq:atkeson_lender_ir) + +```{prf:assumption} Lender Commitment and Deposit Seizure +:label: atkeson_assumption_lenders + +Young lenders can commit to honor the contract when they are old, including +states in which the repayment $d_{t+1}(Y_{t+1})$ is negative and the borrower +is withdrawing deposits. + +If a borrower repudiates a loan, the old lender whose loan was repudiated +can seize the borrower's later deposits with future lenders until the loss is +compensated. +``` + +These assumptions prevent the borrower from defaulting on one lender and +then using deposits with future lenders to smooth consumption. + +They are what make exclusion into autarky a credible punishment after +repudiation in this environment. + +### State variable and feasibility + +Define + +$$ +Q_t := Y_t - d_t(Y_t) +$$ + +as **output net of repayment**. + +It is the resources available to the borrower +after settling the old lender's claim. + +An allocation +$\sigma = \{c_t(Q^t),\,I_t(Q^t),\,b_t(Q^t),\,d_{t+1}(Y_{t+1};Q^t)\}$ +is **feasible** if for all $t$ and histories: + +$$ +c_t + I_t - b_t \leq Q_t, \quad c_t,\, I_t \geq 0, + \quad b_t, -d_{t+1}(Y') \leq M, +$$ (eq:atkeson_feasibility) + +where $M$ is the lender's endowment per period. + +### Autarky value + +The value the borrower can attain without credit access satisfies + +$$ +v_{\text{aut}}(Z) = \max_{0 \leq I \leq \min\{Z,\, I_{\max}\}} + \Bigl[ + (1-\beta)\,u(Z - I) + + \beta \sum_{Y'} v_{\text{aut}}(Y')\,g(Y';\,I) + \Bigr]. +$$ + +## Two impediments to contracting + +### Moral hazard + +The contract can condition on observable histories, but not directly on +the borrower's investment. + +An allocation is a full history-contingent plan + +$$ +\sigma = \{c_t(Q^t),\, I_t(Q^t),\, b_t(Q^t),\, + d_{t+1}(Y_{t+1}; Q^t)\}_{t \geq 0}. +$$ + +The loan and repayment schedules, $b$ and $d$, are contract terms. + +The borrower privately chooses consumption and investment. + +Hence incentive compatibility must rule out deviations in the whole +future consumption-investment plan, not just in current investment. + +The allocation $\sigma$ is **incentive compatible** if, after every +history $Q^t$, the borrower cannot improve by choosing another feasible +consumption-investment plan while keeping the same $b$ and $d$: + +$$ +\begin{aligned} +v(\sigma \mid Q^t) +&\geq +v(\tilde \sigma \mid Q^t), +\\ +&\text{for all feasible } +\tilde \sigma = \{\tilde c,\, \tilde I,\, b,\, d\}. +\end{aligned} +$$ (eq:atkeson_ic) + +The lender observes output histories and can make future loans and +repayments depend on those histories. + +The lender cannot make them depend directly on hidden investment. + +This mirrors the terminology in {doc}`Repeated Moral Hazard `, +where incentive compatibility means that the agent prefers the recommended +hidden action to a deviation. + + +### Risk of repudiation + +The borrower is sovereign and can refuse to repay. + +Suppose that after history $Q^t$, output $Y_{t+1}$ is realized and the +contract calls for repayment $d_{t+1}(Y_{t+1}; Q^t)$. + +If the borrower repays, next period's state is + +$$ +Q_{t+1} += Y_{t+1} - d_{t+1}(Y_{t+1}; Q^t). +$$ + +If the borrower repudiates instead, it keeps the whole output +$Y_{t+1}$ but is excluded from future borrowing. + +The relevant outside option is therefore autarky starting with +$Y_{t+1}$. + +This distinction matters because $Q_{t+1}$ is the state *after* +repayment, while default lets the borrower keep the resources that would +have been repaid. + +An allocation is **immune from repudiation** if + +$$ +v\bigl(\sigma \mid Q^t, Y_{t+1}\bigr) +\geq +v_{\text{aut}}(Y_{t+1}) +$$ (eq:atkeson_no_repudiation) + +for all $t$, histories $Q^t$, and output realizations $Y_{t+1}$. + +The left side is the value of continuing with the contract after repayment. + +The right side is the punishment value after default. + +## The constrained Pareto problem + +The planner chooses among allocations that satisfy five restrictions: + +1. Feasibility {eq}`eq:atkeson_feasibility` +2. Borrower individual rationality: + $v(\sigma \mid Q^t) \geq v_{\text{aut}}(Q_t)$ +3. Lender participation {eq}`eq:atkeson_lender_ir` +4. Immunity from repudiation {eq}`eq:atkeson_no_repudiation` +5. Incentive compatibility {eq}`eq:atkeson_ic` + +An allocation is **constrained Pareto optimal** if it maximizes the +borrower's initial payoff $v(\sigma)$ over this constrained set. + +Borrower individual rationality is part of the general feasible set. + +When the recursive problem maximizes the borrower's payoff on the Pareto +frontier, however, this constraint is nonbinding and can be dropped from +that maximization. + +The lender's expected payoff from its loan contract must be nonnegative. + +These restrictions are continuation restrictions. + +After every possible history, the continuation allocation must again be +feasible, satisfy individual rationality and lender participation, be immune +from repudiation, and be incentive compatible. + +## Recursive formulation + +### Self-generation and factorization + +Let $\mathcal V(Q)$ be the set of payoffs the borrower can achieve from +allocations satisfying feasibility {eq}`eq:atkeson_feasibility`, +borrower individual rationality, lender participation +{eq}`eq:atkeson_lender_ir`, no-repudiation +{eq}`eq:atkeson_no_repudiation`, and incentive compatibility +{eq}`eq:atkeson_ic` when the state is $Q$. + +The recursive formulation extends the **self-generation** and +**factorization** arguments of {cite:t}`APS1990` to a setting with a +physical state variable. + +Let $A := (c, I, b, d')$ collect the current controls: consumption, +investment, the current loan, and the next-period repayment schedule. + +A pair $(A, v)$ of current controls and a continuation value function is +*admissible with respect to* $W$ at $Q$ if it satisfies one-period +versions of these same restrictions and $v(Q') \in W(Q')$ for all $Q'$. + +Let $\mathcal B(W)(Q)$ be the set of payoffs +generated by admissible pairs. + +The operator $\mathcal B$ asks a simple question. + +Suppose future continuation payoffs must lie in the candidate set $W$. + +Which current payoffs can we generate today while respecting feasibility, +individual rationality, lender participation, no-repudiation, and incentive +compatibility? + +Those payoffs form $\mathcal B(W)(Q)$. + +A set $W$ is **self-generating** if every payoff in $W$ can be generated +again by using current controls and continuation payoffs that remain in +$W$. + +Thus a self-generating set can reproduce itself recursively. + +**Factorization** goes in the other direction. + +It says that any payoff from a valid full contract can be split into two +parts: current controls today and a continuation payoff after each +possible next state. + +Because the continuation contract must also be valid, those continuation +payoffs lie in $\mathcal V$. + +Two propositions characterize the utility possibility correspondence. + +```{prf:proposition} Self-generation +:label: atkeson_self_generation + +If $W$ is self-generating, with +$W(Q) \subseteq \mathcal B(W)(Q)$ for all $Q$, then +$\mathcal B(W)(Q) \subseteq \mathcal V(Q)$ for all $Q$. +``` + +```{prf:proposition} Factorization +:label: atkeson_factorization + +$\mathcal V(Q) \subseteq \mathcal B(\mathcal V)(Q)$ for all $Q$. +``` + +Together, {prf:ref}`atkeson_self_generation` and +{prf:ref}`atkeson_factorization` imply +$\mathcal V = \mathcal B(\mathcal V)$, characterizing the utility +possibility correspondence as the fixed point of $\mathcal B$. + +### Program P* + +The correspondence $\mathcal V$ describes all feasible continuation +payoffs. + +The constrained Pareto problem selects the upper envelope of that +correspondence: for each state $Q$, it asks for the highest borrower +payoff that can be delivered while respecting the contracting +restrictions. + +Call this frontier $\bar v(Q)$. + +```{prf:assumption} Continuity of the Frontier +:label: atkeson_assumption_continuity + +The constrained-optimal value function $\bar v(Q)$ is continuous in the +state variable $Q$ on the relevant bounded state space. +``` + +Under {prf:ref}`atkeson_assumption_continuity`, Program P* is the Bellman +equation for the frontier. + +The continuity condition is a substantive qualification: the functional +equation below is the recursive characterization once this regularity is in +place. + +```{prf:proposition} Program P* +:label: atkeson_program_p_star + +Under {prf:ref}`atkeson_assumption_continuity`, $\bar v(Q)$ satisfies the +functional equation + +$$ +\bar v(Q) = \max_{c,\,I,\,b,\,d'(\cdot)} + (1-\beta)\,u(c) + \beta \sum_{Y'} \bar v \bigl(Y' - d'(Y')\bigr)\,g(Y';\,I) +$$ (eq:atkeson_program_p_star) + +subject to feasibility {eq}`eq:atkeson_feasibility`, lender +participation {eq}`eq:atkeson_lender_ir`, no-repudiation +{eq}`eq:atkeson_no_repudiation`, and incentive compatibility +{eq}`eq:atkeson_ic`. + +Borrower individual rationality is omitted here because it is nonbinding +when the frontier maximizes the borrower's payoff. + +Moreover, the optimal *continuation* value function equals +$\bar v$ itself. +``` + +This mirrors Bellman's principle: the *continuation of the optimal contract +is itself optimal* at the updated state. + +Bellman's principle of optimality says that an optimal plan remains +optimal from any future state it reaches. + +Here that means the contract chosen today does not need a separate +continuation rule after tomorrow's output is realized. + +Once the new state $Q' = Y' - d'(Y')$ is reached, the continuation +contract is again described by the same value function $\bar v(Q')$. + +### Capital outflows after the lowest outputs + +To see where the repayment result comes from, first write the one-period +problem with continuation values as choice variables. + +This first-order argument is not unconditional. + +```{prf:assumption} First-Order Approach +:label: atkeson_assumption_first_order + +At the constrained optimum, the expected value of repayments to lenders is +nondecreasing in investment: + +$$ +\sum_{Y'} d'(Y')\,[g_0(Y')-g_1(Y')] \geq 0. +$$ + +This is the weak form of Atkeson's repayment-monotonicity condition; a strict +inequality is the stronger version that rules out degenerate cases. + +The constrained-optimal investment choice is interior: +$I^* \in (0,I_{\max})$. +``` + +Together with the concavity of $\lambda$ and the monotone likelihood-ratio +condition introduced above, +{prf:ref}`atkeson_assumption_first_order` justifies replacing the +incentive-compatibility condition by the relaxed first-order inequality used +in the Lagrangian. + +Let $v_j$ be the continuation value promised after output $Y_j'$. + +$$ +Q_j' = Y_j' - d_j, +$$ + +where $d_j$ is the repayment due after output $Y_j'$. + +Let $g_j(I) = g(Y_j'; I)$ and +$g_{I,j} = \partial g(Y_j'; I) / \partial I$. + +A Lagrangian for the relaxed one-period problem has the form + +$$ +\begin{aligned} +\mathcal L +=& (1-\beta)u(c) + \beta\sum_j v_j g_j(I) \\ +&+ \lambda_f (Q + b - c - I) \\ +&+ \lambda_\ell \left[\beta\sum_j d_j g_j(I) - b\right] \\ +&+ \beta\sum_j \mu_j g_j(I) + \left[v_j - v_{\text{aut}}(Y_j')\right] \\ +&+ \eta + \left[-(1-\beta)u'(Q+b-I) + + \beta\sum_j v_j g_{I,j}\right] \\ +&+ \beta\sum_j \xi_j g_j(I) + \left[\bar v(Y_j' - d_j) - v_j\right]. +\end{aligned} +$$ (eq:atkeson_relaxed_lagrangian) + +Here $\lambda_f$ is the feasibility multiplier, $\lambda_\ell$ is the +lender-participation multiplier, $\mu_j$ is the no-repudiation multiplier +after output $Y_j'$, $\eta$ is the multiplier on the relaxed +investment-incentive condition, and $\xi_j$ enforces consistency between +$v_j$ and the frontier value $\bar v(Q_j')$. + +In the numbered notation of {cite:t}`Atkeson1991`, $\mu_3(Y_j')$ corresponds +to $\mu_j$ and $\mu_4$ corresponds to $\eta$. + +The first-order condition with respect to $v_j$ is, up to the common +positive scale factor $\beta g_j(I)$, + +$$ +1 + \mu_j - \xi_j + + \eta\frac{g_{I,j}}{g_j(I)} = 0. +$$ (eq:atkeson_vj_foc) + +Atkeson's argument applies when the relaxed investment-incentive constraint is +active, so $\eta > 0$. + +Hence a sufficiently negative value of $g_{I,j}/g_j(I)$ forces $\mu_j > 0$. + +Thus the likelihood term $g_{I,j}/g_j(I)$ determines which output states +put the most pressure on the no-repudiation constraint. + +For low outputs, higher investment makes the realization less likely, so +$g_{I,j}/g_j(I)$ is negative. + +By the monotone likelihood ratio property, this log-likelihood derivative +is most negative for the lowest output states. + +When {eq}`eq:atkeson_vj_foc` requires $\mu_j > 0$, complementary slackness +implies that the no-repudiation constraint binds: + +$$ +\bar v(Y_j' - d_j) = v_{\text{aut}}(Y_j'). +$$ + +Repayment $d_j$ is then at its maximum and the new loan available at the +continuation state is limited. + +Thus the borrower has a **capital outflow**: + +$$ +\underbrace{d_j}_{\text{repayment to old lender}} + \geq \underbrace{b^* \bigl(Q_j'\bigr)}_{\text{new loan from young lender}}. +$$ + +Strict capital outflow requires the inequality to be strict. + +## Computation + +We now compute a grid approximation to Program P*. + +In addition to what's in Anaconda, this lecture will need the following library: + +```{code-cell} ipython3 +:tags: [hide-output] + +!pip install jax +``` + +We will use the following imports: + +```{code-cell} ipython3 +import numpy as np +from typing import NamedTuple +from scipy.interpolate import interp1d +from jax import config +config.update("jax_enable_x64", True) +import jax +import jax.numpy as jnp +import matplotlib.pyplot as plt +``` + +### Setup + +Let's start by defining the model primitives and the state grid. + +In the code the favorable distribution $g_0$ is `g_high`, the output +distribution when investment is at its maximum, and the unfavorable +distribution $g_1$ is `g_low`, the distribution when investment is zero. + +With only two outputs, the monotone likelihood-ratio property +$g_0(Y_i)/g_1(Y_i)$ increasing in $i$ reduces to +$g_0(Y_H)/g_1(Y_H) > 1 > g_0(Y_L)/g_1(Y_L)$. + +Thus $Y_L$ is evidence of low investment, while $Y_H$ is evidence of high +investment. + +```{code-cell} ipython3 +# Model parameters +class Model(NamedTuple): + β: float # discount factor + I_max: float # upper bound on investment + Y: np.ndarray # output states + M: float # lender endowment (b, -d <= M) + g_high: np.ndarray # distribution at I_max + g_low: np.ndarray # distribution at I = 0 + κ: float # curvature in the investment-probability map + + +def create_model(β=0.92, + I_max=0.6, + Y=(0.8, 1.2), + M=0.3, + g_high=(0.05, 0.95), + g_low=(0.95, 0.05), + κ=3.0): + """Build a model instance, validating the parameters.""" + + if not 0 < β < 1: + raise ValueError("β must lie in (0, 1)") + Y = np.asarray(Y, dtype=float) + + if np.any(np.diff(Y) <= 0): + raise ValueError("output states must be strictly increasing") + + g_high, g_low = np.asarray(g_high), np.asarray(g_low) + + if not (np.isclose(g_high.sum(), 1.0) + and np.isclose(g_low.sum(), 1.0)): + raise ValueError("probability vectors must sum to 1") + + return Model(β=β, I_max=I_max, Y=Y, M=M, + g_high=g_high, g_low=g_low, κ=κ) + + +model = create_model() +β, I_max, Y, M, κ = model.β, model.I_max, model.Y, model.M, model.κ +g_high, g_low = model.g_high, model.g_low +Y_L, Y_H = Y + +# State grid: Q = Y - d (resources after repaying old debt) +N_Q = 70 +N_I = 19 +Q_MIN = 0.002 +Q_MAX = 1.8 +Q_grid = np.linspace(Q_MIN, Q_MAX, N_Q) +Q_grid_j = jnp.asarray(Q_grid) +I_grid = np.linspace(0.0, I_max, N_I) +I_grid_j = jnp.asarray(I_grid) +Y_j = jnp.asarray(Y) + +Qp_L_mesh, Qp_H_mesh = np.meshgrid(Q_grid, Q_grid, indexing='ij') +Qp_flat = jnp.asarray(np.column_stack((Qp_L_mesh.ravel(), + Qp_H_mesh.ravel()))) +n_pair = Qp_flat.shape[0] + +# Utility +def u(c): + return np.log(np.maximum(c, 1e-12)) + + +def u_jax(c): + return jnp.log(jnp.maximum(c, 1e-12)) + + +def lambda_weight(I): + x = np.clip(I / I_max, 0.0, 1.0) + return (1 - np.exp(-κ * x)) / (1 - np.exp(-κ)) + + +def lambda_weight_jax(I): + x = jnp.clip(I / I_max, 0.0, 1.0) + return (1 - jnp.exp(-κ * x)) / (1 - jnp.exp(-κ)) + + +def g_of_I(I, g_high_val=None, g_low_val=None): + if g_high_val is None: + g_high_val = g_high + if g_low_val is None: + g_low_val = g_low + λ = lambda_weight(I) + return λ[..., None] * g_high_val + (1 - λ[..., None]) * g_low_val + + +def g_of_I_jax(I, g_high_val, g_low_val): + λ = lambda_weight_jax(I) + return λ[..., None] * g_high_val + (1 - λ[..., None]) * g_low_val + + +print(f"Likelihood ratios g_low / g_high : {g_low / g_high}") +print(f"Y_L signals low investment with ratio {g_low[0]/g_high[0]:.1f}x") +``` + +```{note} +The computation uses log utility, $u(c)=\log(c)$, as a numerical +illustration. + +This relaxes the bounded-above primitive utility assumption used in the +existence argument of {prf:ref}`atkeson_assumption_preferences`. + +On the finite grid, with finite resource bounds, the objective nonetheless +remains bounded. +``` + +### Autarky value function + +In autarky the borrower has no access to credit. + +Starting each period with +resources $Q$, the borrower solves + +$$ +v_{\text{aut}}(Q) = + \max_{0 \leq I \leq \min\{Q,I_{\max}\}} + \Bigl[(1-\beta)\,u(Q - I) + \beta + \sum_j g(Y_j;I)v_{\text{aut}}(Y_j)\Bigr]. +$$ + +Note that the continuation values depend only on $Y_L$ and $Y_H$, not on the +current $Q$, because next period's state is simply the realized output. + +```{code-cell} ipython3 +@jax.jit +def autarky_operator_jax(V, β_val, g_high_val, g_low_val): + """One vectorized Bellman step for the autarky problem.""" + V_Y = jnp.interp(Y_j, Q_grid_j, V) + g_I = g_of_I_jax(I_grid_j, g_high_val, g_low_val) + EV_I = g_I @ V_Y + c = Q_grid_j[:, None] - I_grid_j[None, :] + val = (1 - β_val) * u_jax(c) + β_val * EV_I[None, :] + val = jnp.where(c >= 1e-10, val, -jnp.inf) + idx = jnp.argmax(val, axis=1) + return jnp.max(val, axis=1), I_grid_j[idx] + + +def autarky_vfi(β_val=None, + g_high_val=None, + g_low_val=None, + tol=1e-8, + max_iter=3000, + verbose=True): + """Value function iteration for the autarky problem.""" + if β_val is None: + β_val = β + if g_high_val is None: + g_high_val = g_high + if g_low_val is None: + g_low_val = g_low + + V = jnp.zeros(N_Q) + g_high_j = jnp.asarray(g_high_val) + g_low_j = jnp.asarray(g_low_val) + for it in range(max_iter): + V_new, policy_I = autarky_operator_jax(V, β_val, + g_high_j, g_low_j) + diff = float(jnp.max(jnp.abs(V_new - V))) + V = V_new + if diff < tol: + if verbose: + print( + f"Autarky VFI converged in {it+1} iterations " + f"(diff={diff:.2e})" + ) + break + + return np.asarray(V), np.asarray(policy_I) + +V_aut, I_aut = autarky_vfi() +``` + +### Program P* + +We solve Program P* iteratively. + +At each state $Q$, the planner chooses current investment $I$ and +continuation states $(Q'_L,Q'_H)$, equivalently repayments +$d_j = Y_j - Q'_j$. + +With lender participation imposed as binding, the loan is determined by + +$$ +b^*(Q,I,Q') + = \beta \sum_j (Y_j - Q'_j)g(Y_j;I), +$$ + +and current consumption is $c^* = Q + b^* - I$. + +We impose lender participation as binding in the displayed calibration. + +This is valid here because the implied loan remains below $M$, and the code +discards any candidate whose zero-profit loan would exceed $M$. + +If instead $M$ binds, $b$ must be treated as a separate constrained choice, or +set to $\min\{M,\,\beta\sum_j d_j g(Y_j;I)\}$ for each candidate. + +On the two-output grid, the search is over $I$ and $(Q'_L,Q'_H)$: + +$$ +\max_{Q'_L,\,Q'_H} + \max_I \left\{(1-\beta)\,u(c^*) + + \beta\sum_j v(Q'_j)g(Y_j;I)\right\} +$$ + +subject to: + +- **(NR)** $v(Q'_j) \geq v_{\text{aut}}(Y_j)$, i.e. $Q'_j \geq Q^*_j$ +- **(IC)** $I$ solves the borrower's hidden investment problem +- **(F)** $c^* \geq 0$ + +The code enforces IC by checking every alternative investment on `I_grid`. + +```{code-cell} ipython3 +def find_Qmin(V_arr, v_thresh): + """Return min Q on grid with value above the no-repudiation bound.""" + + if v_thresh <= V_arr[0]: + return float(Q_MIN) + if v_thresh >= V_arr[-1]: + return float(Q_MAX) + + # Use searchsorted on a monotone version of V + V_mono = np.maximum.accumulate(V_arr) # enforce monotone for inversion + idx = np.searchsorted(V_mono, v_thresh) + idx = np.clip(idx, 1, N_Q - 1) + denom = V_mono[idx] - V_mono[idx-1] + + if abs(denom) < 1e-14: + return float(Q_grid[idx-1]) + + t = (v_thresh - V_mono[idx-1]) / denom + return float(Q_grid[idx-1] + t * (Q_grid[idx] - Q_grid[idx-1])) + + +@jax.jit +def program_p_bellman_step_jax(V, V_aut_arr, I_aut_arr, + β_val, g_high_val, g_low_val, Qp_min): + """ + One grid Bellman step for Program P*. + """ + g_I = g_of_I_jax(I_grid_j, g_high_val, g_low_val) + V_pair = jnp.column_stack(( + jnp.interp(Qp_flat[:, 0], Q_grid_j, V), + jnp.interp(Qp_flat[:, 1], Q_grid_j, V) + )) + + d_pair = Y_j[None, :] - Qp_flat + pair_ok = ( + (Qp_flat[:, 0] >= Qp_min[0]) + & (Qp_flat[:, 1] >= Qp_min[1]) + & jnp.all(-d_pair <= M, axis=1) + ) + + b = β_val * jnp.einsum("iy,py->ip", g_I, d_pair) + EV = jnp.einsum("iy,py->ip", g_I, V_pair) + candidate_ok = pair_ok[None, :] & (b <= M) + + resources = Q_grid_j[:, None, None] + b[None, :, :] + c = resources - I_grid_j[None, :, None] + obj = (1 - β_val) * u_jax(c) + β_val * EV[None, :, :] + + dev_best = jnp.full(obj.shape, -jnp.inf) + for i_alt in range(N_I): + I_alt = I_grid_j[i_alt] + EV_alt = jnp.einsum("y,py->p", g_I[i_alt], V_pair) + c_alt = resources - I_alt + dev = ((1 - β_val) * u_jax(c_alt) + + β_val * EV_alt[None, None, :]) + dev = jnp.where(c_alt >= 1e-10, dev, -jnp.inf) + dev_best = jnp.maximum(dev_best, dev) + + feasible = ( + candidate_ok[None, :, :] + & (c >= 1e-10) + & (obj >= dev_best - 1e-8) + ) + obj = jnp.where(feasible, obj, -jnp.inf) + + flat = obj.reshape((N_Q, -1)) + idx = jnp.argmax(flat, axis=1) + best_val = jnp.max(flat, axis=1) + has_feasible = jnp.isfinite(best_val) + + I_flat = jnp.repeat(I_grid_j, n_pair) + b_flat = b.reshape(-1) + Qp_L_flat = jnp.tile(Qp_flat[:, 0], N_I) + Qp_H_flat = jnp.tile(Qp_flat[:, 1], N_I) + + use_autarky = (~has_feasible) | (best_val < V_aut_arr) + V_new = jnp.where(use_autarky, V_aut_arr, best_val) + pol_I = jnp.where(use_autarky, I_aut_arr, I_flat[idx]) + pol_b = jnp.where(use_autarky, 0.0, b_flat[idx]) + pol_Qp = jnp.column_stack(( + jnp.where(use_autarky, Y_j[0], Qp_L_flat[idx]), + jnp.where(use_autarky, Y_j[1], Qp_H_flat[idx]) + )) + + return V_new, pol_I, pol_b, pol_Qp + + +def program_p_bellman(V, V_aut_arr, I_aut_arr, + β_val=None, + g_high_val=None, + g_low_val=None, + ε=0.0): + """ + One Bellman step for Program P*. + """ + if β_val is None: + β_val = β + if g_high_val is None: + g_high_val = g_high + if g_low_val is None: + g_low_val = g_low + + Vaut_f = interp1d(Q_grid, V_aut_arr, fill_value='extrapolate', + bounds_error=False) + Vaut_Y = np.array([float(Vaut_f(yj)) for yj in Y]) + ε + Qp_min = np.array([find_Qmin(V, v) for v in Vaut_Y]) + Qp_min = np.clip(Qp_min, Q_MIN, Q_MAX - 1e-4) + + V_new, pol_I, pol_b, pol_Qp = program_p_bellman_step_jax( + jnp.asarray(V), jnp.asarray(V_aut_arr), jnp.asarray(I_aut_arr), + β_val, jnp.asarray(g_high_val), jnp.asarray(g_low_val), + jnp.asarray(Qp_min) + ) + + return (np.asarray(V_new), np.asarray(pol_I), + np.asarray(pol_b), np.asarray(pol_Qp)) + + +def program_p_vfi(V_aut_arr, + I_aut_arr, + β_val=None, + g_high_val=None, + g_low_val=None, + ε=0.0, + tol=2e-4, + max_iter=1000, + relaxation=0.25, + verbose=True): + """Value iteration for Program P*.""" + if β_val is None: + β_val = β + if g_high_val is None: + g_high_val = g_high + if g_low_val is None: + g_low_val = g_low + + V = V_aut_arr.copy() + + for it in range(max_iter): + V_raw, pol_I, pol_b, pol_Qp = program_p_bellman( + V, V_aut_arr, I_aut_arr, β_val=β_val, + g_high_val=g_high_val, g_low_val=g_low_val, ε=ε) + V_new = (1 - relaxation) * V + relaxation * V_raw + diff = np.max(np.abs(V_new - V)) + V = V_new + if verbose and (it == 0 or (it + 1) % 10 == 0): + print(f" iter {it+1:3d}, max|ΔV| = {diff:.5f}") + if diff < tol: + if verbose: + print(f"Program P* VFI converged in {it+1} iterations.") + break + else: + if verbose: + print(f"Stopped after {max_iter} iterations " + f"(max|ΔV| = {diff:.2e}).") + + return V, pol_I, pol_b, pol_Qp + + +print("Running Program P* VFI ...") +V_pareto, pol_I, pol_b, pol_Qp = program_p_vfi(V_aut, I_aut) +``` + +### Value functions + +Let's start by plotting the autarky value and the Program P* value. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: autarky and Program P* values + name: fig-atk-value +--- +fig, ax = plt.subplots() + +ax.plot(Q_grid, V_aut, lw=2, label=r'Autarky $v_{\rm aut}(Q)$') +ax.plot(Q_grid, V_pareto, lw=2, ls='--', + label=r'Program P* value $\bar v(Q)$') + +ax.set_xlabel(r'state $Q$ (output net of repayment)') +ax.set_ylabel('normalized utility') +ax.legend() +plt.tight_layout() +plt.show() +``` + +The Program P* value dominates autarky in the plotted region. + +Access to credit lets the borrower smooth +consumption across output realizations while preserving incentives for +investment. + +The vertical distance between the two curves is the value of the lending +relationship, net of the incentive and repudiation constraints. + +The gain is not the complete-markets gain from perfect insurance. + +It is the value that remains once the contract must both induce hidden +investment and keep the borrower from preferring repudiation after each +output realization. + +### Investment + +The next figure reports the investment chosen by the contract and the +autarky investment policy. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: investment policy + name: fig-atk-investment +--- +fig, ax = plt.subplots() + +ax.plot(Q_grid, I_aut, lw=2, label='Autarky') +ax.plot(Q_grid, pol_I, lw=2, ls='--', label='Program P*') +ax.set_xlabel(r'state $Q$') +ax.set_ylabel(r'investment $I(Q)$') +ax.legend() +plt.tight_layout() +plt.show() +``` + +This figure compares investment in autarky with investment under the +optimal lending contract. + +Both policies are step functions because investment is chosen from the finite +grid `I_grid`. + +Under autarky, the borrower uses only its own current resources, so +investment rises with $Q$ once enough resources are available. + +Under Program P*, investment is disciplined by the contract. + +At low and middle states the lending relationship can support positive +investment earlier than autarky because loans relax the current resource +constraint. + +At higher states, however, the Program P* investment schedule is flatter and +lower than autarky in this calibration. + +The reason is not that resources are scarce, but that investment must be +incentive compatible: the continuation-value spread across low and high +output has to make the chosen investment privately optimal for the borrower. + +When raising investment would require too much output-contingent punishment +or reward, the optimal contract chooses a lower investment level. + +### Continuation states and the no-repudiation constraint + +Let's now look at the continuation states $Q'_L$ and $Q'_H$ after +low and high output, respectively. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: continuation states and no-repudiation floors + name: fig-atk-continuation +--- +# Compute no-repudiation floors +Vaut_at_Y = np.array([float(interp1d(Q_grid, V_aut, + fill_value='extrapolate', + bounds_error=False)(yj)) for yj in Y]) +Qp_min_L = find_Qmin(V_pareto, Vaut_at_Y[0]) +Qp_min_H = find_Qmin(V_pareto, Vaut_at_Y[1]) + +fig, axes = plt.subplots(1, 2, figsize=(8, 4), sharex=True, sharey=True) + +# Left: Q'_L (continuation state after low output) +axes[0].plot(Q_grid, pol_Qp[:, 0], lw=2, label=r"$Q'_L = Y_L - d_L$") +axes[0].axhline(Qp_min_L, ls='--', color='C3', + label=fr"NR floor $Q^*_L \approx {Qp_min_L:.3f}$") +axes[0].set_xlabel(r'state $Q$') +axes[0].set_ylabel(r"$Q'_L$") + +# Right: Q'_H (continuation state after high output) +axes[1].plot(Q_grid, pol_Qp[:, 1], lw=2, color='C1', + label=r"$Q'_H = Y_H - d_H$") +axes[1].axhline(Qp_min_H, ls='--', color='C3', + label=fr"NR floor $Q^*_H \approx {Qp_min_H:.3f}$") +axes[1].set_xlabel(r'state $Q$') +axes[1].set_ylabel(r"$Q'_H$") + +for ax in axes: + ax.set_xlim(Q_MIN, Q_MAX) + ax.set_ylim(Q_MIN, Q_MAX) + ax.set_aspect('equal', adjustable='box') + ax.legend() + +plt.tight_layout() +plt.show() +``` + +The dashed horizontal lines are no-repudiation floors. + +A continuation state cannot fall below its floor, because otherwise the +borrower would prefer repudiation. + +In this calibration, the low-output continuation state $Q'_L$ is pinned +at the floor only for low current states. + +Over that region, repayment $d_L = Y_L - Q'_L$ is as large as the +repudiation constraint allows. + +For higher current states, the no-repudiation constraint is slack and +$Q'_L$ rises with $Q$. + +The high-output continuation state $Q'_H$ is generally higher than +$Q'_L$, rewarding the high investment that makes high output more likely. + +The two panels should be read as punishment and reward schedules. + +After low output, the borrower is sent to a lower continuation state, which +reduces future utility and helps deter low investment. + +After high output, the borrower is sent to a higher continuation state, +which rewards the outcome that is more likely when investment is high. + +The horizontal dashed lines mark the smallest continuation states compatible +with no repudiation. + +When a policy curve touches one of these lines, the contract is using the +maximum feasible punishment at that output realization. + +### Loan and net capital flows + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: low-output loan and net capital flows + name: fig-atk-loan-flows +--- +# Repayment and next loan at the low-output continuation state +d_L_policy = Y_L - pol_Qp[:, 0] # d_L(Q) = Y_L - Q'_L(Q) + +pol_b_fn = interp1d(Q_grid, pol_b, + fill_value='extrapolate', bounds_error=False) +b_next_L = pol_b_fn(pol_Qp[:, 0]) + +net_out_L = d_L_policy - b_next_L +low_outflow = net_out_L > 0 + +fig, axes = plt.subplots(1, 2, figsize=(8, 4)) + +axes[0].plot(Q_grid, d_L_policy, lw=2, label=r'Repayment $d_L(Q)$') +axes[0].plot(Q_grid, b_next_L, lw=2, ls='--', + label=r"New loan $b^*(Q'_L)$") +axes[0].fill_between(Q_grid, d_L_policy, b_next_L, + where=low_outflow, interpolate=True, + color='C3', alpha=0.16, label='capital outflow') +axes[0].axhline(0, color='k', lw=0.6, ls=':') +axes[0].set_title('After low output') +axes[0].set_xlabel(r'state $Q$') +axes[0].set_ylabel('level') +axes[0].legend() + +axes[1].plot(Q_grid, net_out_L, + lw=2, label=r"$d_L(Q) - b^*(Q'_L)$") +axes[1].fill_between(Q_grid, 0, net_out_L, + where=low_outflow, interpolate=True, + color='C3', alpha=0.16) +axes[1].axhline(0, color='k', lw=0.8, ls=':') +axes[1].set_title('Low-output net flow') +axes[1].set_xlabel(r'state $Q$') +axes[1].set_ylabel(r"net outflow after $Y_L$") +axes[1].legend() + +plt.tight_layout() +plt.show() +``` + +This figure isolates the low-output branch of the contract. + +Start from current state $Q$. + +If next period's output is low, $Y_L$, the contract sends the borrower to +the continuation state + +$$ +Q'_L(Q) = Y_L - d_L(Q). +$$ + +The old lender receives the repayment $d_L(Q)$. + +At that new state, the next young lender offers the loan +$b^*(Q'_L(Q))$. + +The low-output net capital outflow is therefore + +$$ +d_L(Q) - b^*(Q'_L(Q)). +$$ + +The left panel plots the two pieces of this difference. + +The right panel plots the difference itself. + +Values above zero are capital outflows: the borrower repays more to the old +lender than it receives as a new loan. + +Values below zero are capital inflows: new borrowing more than offsets the +repayment. + +The shaded region marks the states in which + +$$ +d_L(Q) > b^*(Q'_L(Q)). +$$ + +In that region, repayment after bad news about investment is not fully offset +by new borrowing, so the borrower exports capital. + +This is the numerical analogue of Atkeson's capital-outflow condition +$d_j \geq b^*(Q'_j)$ for the lowest output realization. + +Outside the shaded region, low output is still punished through a lower +continuation state, but that punishment does not show up as a literal net +capital outflow because the next loan is larger than the repayment. + +### Simulation + +We now simulate one history generated by the computed contract. + +This is an on-contract path. + +The borrower follows the recommended investment policy, so next output is +drawn from $g(Y';I_t)$. + +The simulation does not draw deviations or defaults. + +It asks what histories look like when the contract is obeyed. + +At the start of a period, the state is $Q_t$, output net of the old +repayment. + +The policy functions choose current investment $I_t = I(Q_t)$, current loan +$b_t = b(Q_t)$, and current consumption + +$$ +c_t = Q_t + b_t - I_t. +$$ + +Then output $Y_{t+1}$ is drawn. + +If output state $j$ occurs, the policy function sends the borrower to + +$$ +Q_{t+1} = Q'_j(Q_t). +$$ + +The repayment due to the old lender is therefore + +$$ +d_{t+1}(Y_j) = Y_j - Q'_j(Q_t). +$$ + +The net capital outflow reported below is + +$$ +d_{t+1}(Y_j) - b(Q_{t+1}). +$$ + +It is repayment to the old lender minus the new loan received at the +continuation state. + +Positive values are capital outflows. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: simulated contract paths + name: fig-atk-simulation +--- +def simulate_contract(pol_I, pol_b, pol_Qp, T=150, seed=0): + """ + Simulate one on-contract history. + + The borrower follows the computed investment and loan policies. + """ + rng = np.random.default_rng(seed) + + I_fn = interp1d(Q_grid, pol_I, fill_value='extrapolate', + bounds_error=False) + Qp_fn = [interp1d(Q_grid, pol_Qp[:, j], + fill_value='extrapolate', bounds_error=False) + for j in range(2)] + b_fn = interp1d(Q_grid, pol_b, fill_value='extrapolate', + bounds_error=False) + + Q = float(np.median(Q_grid)) # start at median state + + out = {'Q': [], 'Y': [], 'I': [], 'c': [], 'b': [], 'b_next': [], + 'd': [], 'net_out': []} + + for _ in range(T): + I = float(I_fn(Q)) + b = float(b_fn(Q)) + c = Q + b - I + c = max(c, 1e-10) + + probs = np.asarray(g_of_I(np.array(I))).ravel() + j = int(rng.choice(2, p=probs)) + Yp = Y[j] + Qp = float(Qp_fn[j](Q)) # next state + + d = Yp - Qp # repayment after output is realized + b_next = float(b_fn(Qp)) + net_out = d - b_next # repayment minus new borrowing + + out['Q'].append(Q) + out['Y'].append(Yp) + out['I'].append(I) + out['c'].append(c) + out['b'].append(b) + out['b_next'].append(b_next) + out['d'].append(d) + out['net_out'].append(net_out) + + Q = Qp + + return {k: np.array(v) for k, v in out.items()} + + +sim = simulate_contract(pol_I, pol_b, pol_Qp, T=150) +t = np.arange(len(sim['Q'])) + +fig, axes = plt.subplots(4, 1, figsize=(10, 8), sharex=True) + +axes[0].plot(t, sim['Y'], alpha=0.6, label='Output $Y_{t+1}$') +axes[0].plot(t, sim['c'], lw=1.8, label='Consumption $c_t$') +axes[0].set_ylabel('level') +axes[0].legend(ncol=2, loc='upper right') + +axes[1].plot(t, sim['I'], lw=1.8, color='C2', label='Investment $I_t$') +axes[1].axhline(0, color='k', lw=0.5) +axes[1].set_ylabel('investment') +axes[1].legend() + +axes[2].plot(t, sim['d'], lw=1.8, label='Repayment $d_{t+1}$') +axes[2].plot(t, sim['b_next'], lw=1.8, ls='--', + label=r'New loan $b(Q_{t+1})$') +axes[2].axhline(0, color='k', lw=0.5) +axes[2].set_ylabel('level') +axes[2].legend(ncol=2) + +colors = ['#d73027' if x > 0 else '#4575b4' for x in sim['net_out']] +axes[3].bar(t, sim['net_out'], color=colors, label='Net capital outflow') +axes[3].axhline(0, color='k', lw=0.6) +axes[3].set_xlabel('period $t$') +axes[3].set_ylabel('net outflow') +axes[3].legend() + +fig.tight_layout(h_pad=1.0) +plt.show() + +# Atkeson's capital export operates in the constrained region, where the +# no-repudiation floor binds after low output (the shaded low-Q region above). +Q_star = float(Q_grid[net_out_L > 0].max()) + +# A longer simulation gives stable ergodic frequencies. +sim_long = simulate_contract(pol_I, pol_b, pol_Qp, T=20_000) +low = sim_long['Y'] == Y_L +constrained = sim_long['Q'] <= Q_star + +print(f"\nTime in the constrained region (Q <= {Q_star:.2f}): " + f"{np.mean(constrained):.1%}") +print(f"Low-output capital outflow frequency, constrained: " + f"{np.mean(sim_long['net_out'][low & constrained] > 0):.1%}") +print(f"Low-output capital outflow frequency, unconstrained: " + f"{np.mean(sim_long['net_out'][low & ~constrained] > 0):.1%}") +``` + +This simulated history illustrates how the contract smooths resources while +still using output-contingent continuation promises. + +Output jumps between the two possible realizations, $Y_L = 0.8$ and +$Y_H = 1.2$, but consumption moves much less sharply. + +Most of the time consumption stays near the middle of the output range rather +than matching output one for one. + +Investment is also nearly flat. + +Along this path it is usually close to $0.10$, with only a few grid-sized +adjustments when the continuation state becomes especially favorable or +especially tight. + +The third panel shows the two terms used to construct the net-flow bars. + +Repayment $d_{t+1}$ and the next-state loan $b(Q_{t+1})$ move almost +together. + +Both are often negative in this calibration, so the contract is frequently +using deposits or withdrawals rather than ordinary positive borrowing. + +The net capital flow is the difference between the repayment due after output +is realized and the loan available at the next state, + +$$ +d_{t+1} - b(Q_{t+1}). +$$ + +Red bars are periods in which this difference is positive, so on net the +borrower sends resources to the lending sector and capital flows out. + +Blue bars are periods in which it is negative, so on net the borrower receives +resources and capital flows in. + +The contract's capital flows split into two regimes. + +In the constrained region, where the borrower is poor and the no-repudiation +floor binds, low output forces repayment to exceed new lending and capital +flows out. + +This is Atkeson's result, the shaded low-$Q$ region of the loan-flow figure +above. + +Along the path, low output exports capital about 60% of the time the borrower +is in this region, against essentially never outside it. + +In the unconstrained region, where the borrower is richer and the floor is +slack, the pattern is buffer-stock saving instead. + +There the borrower deposits after high output, a capital outflow, and draws +those deposits down after low output, a capital inflow. + +The borrower spends about a fifth of its time in the constrained region, +because good output builds a buffer that lifts it out. + +## Summary + +The central friction in this lecture is moral hazard. + +The borrower privately chooses investment, while lenders observe only output. + +Low output is therefore bad news for two reasons. + +It lowers current resources and it is also evidence that the borrower may have +chosen low investment. + +Atkeson's optimal contract responds by making continuation values depend on +output. + +High output is rewarded with a better continuation state. + +Low output is punished with a tighter continuation state, subject to the +borrower's option to repudiate and live in autarky. + +This is the same logic as in {doc}`Repeated Moral Hazard `: +hidden actions are disciplined by future promised utility. + +Atkeson's contribution is to judiciously combine that incentive logic with sovereign +default risk and a physical state variable, so continuation promises must also +respect the no-repudiation constraint. + +## Exercises + +```{exercise-start} +:label: atkeson_1991_ex1 +``` + +*Patience and the severity of debt crises.* + +Redo the analysis with $\beta = 0.8$ and $\beta = 0.95$ (keep all other +parameters fixed). + +1. For each value of $\beta$, compute the autarky and Program P* value + functions. +2. Compute the no-repudiation lower bounds $Q^*_L$ and $Q^*_H$. +3. Plot $Q'_L(Q)$ for the three values of $\beta$ on a single figure. +4. Discuss: how does the borrower's patience affect how tightly the + no-repudiation constraint binds after low output? +```{exercise-end} +``` + +```{solution-start} atkeson_1991_ex1 +:class: dropdown +``` + +Here is one solution: + +```{code-cell} ipython3 +fig, ax = plt.subplots() + +for β_val, ls, color, tag in [ + (0.8, '-', 'C0', ''), + (β, '--', 'C1', ' baseline'), + (0.95, ':', 'C2', '')]: + V_a, I_a = autarky_vfi(β_val=β_val, verbose=False) + V_p, _, _, pQp = program_p_vfi( + V_a, I_a, β_val=β_val, verbose=False) + + Vaut_fn_tmp = interp1d(Q_grid, V_a, fill_value='extrapolate', + bounds_error=False) + Vaut_Y_tmp = np.array([float(Vaut_fn_tmp(yj)) for yj in Y]) + Qmin_L_tmp = find_Qmin(V_p, Vaut_Y_tmp[0]) + + ax.plot(Q_grid, pQp[:, 0], ls=ls, color=color, + label=fr'$\beta = {β_val}${tag} ' + fr'(NR floor $\approx {Qmin_L_tmp:.3f}$)') + +ax.set_xlabel(r'state $Q$') +ax.set_ylabel(r"$Q'_L$ (continuation state after low output)") +ax.set_title('continuation state across patience levels') +ax.legend() +plt.tight_layout() +plt.show() +``` + +The figure shows how the continuation state after low output changes with +the borrower's patience. + +For low current states, each curve is almost flat at its no-repudiation +floor. + +That floor falls as $\beta$ rises. + +Thus, in this calibration, a more patient borrower can be assigned a lower +continuation state after low output without choosing repudiation. + +Since + +$$ +d_L(Q) = Y_L - Q'_L(Q), +$$ + +a lower $Q'_L$ means a larger repayment after low output. + +Patience therefore lets the contract use a harsher low-output punishment. + +As current resources $Q$ rise, the low-output no-repudiation floor stops +binding. + +The curves then increase with $Q$: after a borrower enters the period with +more resources, the contract can promise a better continuation state even +after low output. + +At high values of $Q$, the schedules become close to one another and flatten +near the upper part of the grid. + +```{solution-end} +``` + +```{exercise-start} +:label: atkeson_1991_ex2 +``` + +*Signal quality and capital flows.* + +Replace the output distribution with the more symmetric values +$g_0 = (0.40, 0.60)$ and $g_1 = (0.60, 0.40)$, so that output is a +weaker signal of investment. + +1. Recompute the autarky and Program P* value functions. +2. Plot the low-output net capital outflow curve $d(Y_L) - b^*(Q'_L)$ as a + function of $Q$ for both the baseline and the weak-signal specification. +3. Explain intuitively why weaker signal quality changes the capital flow + pattern. +```{exercise-end} +``` + +```{solution-start} atkeson_1991_ex2 +:class: dropdown +``` + +Here is one solution: + +```{code-cell} ipython3 +# Weak-signal specification +g_high_ws = np.array([0.40, 0.60]) +g_low_ws = np.array([0.60, 0.40]) + +print("Weak-signal likelihood ratios g_low/g_high:", + g_low_ws / g_high_ws) + +V_aut_ws, I_aut_ws = autarky_vfi(g_high_val=g_high_ws, + g_low_val=g_low_ws, + verbose=False) +V_par_ws, _, pb_ws, pQp_ws = program_p_vfi( + V_aut_ws, I_aut_ws, g_high_val=g_high_ws, + g_low_val=g_low_ws, verbose=False) + +pb_fn_ws = interp1d(Q_grid, pb_ws, + fill_value='extrapolate', bounds_error=False) +net_L_ws = (Y_L - pQp_ws[:, 0]) - pb_fn_ws(pQp_ws[:, 0]) + +pb_fn_bl = interp1d(Q_grid, pol_b, + fill_value='extrapolate', bounds_error=False) +net_L_bl = (Y_L - pol_Qp[:, 0]) - pb_fn_bl(pol_Qp[:, 0]) + +fig, ax = plt.subplots() +ax.plot(Q_grid, net_L_bl, + lw=2, label=r'After $Y_L$, baseline (strong signal)') +ax.plot(Q_grid, net_L_ws, + lw=2, ls='--', label=r'After $Y_L$, weak signal') +ax.axhline(0, color='k', lw=0.8, ls=':') +ax.set_xlabel(r'state $Q$') +ax.set_ylabel('net capital outflow') +ax.set_title('net outflow under weak signal') +ax.legend() +plt.tight_layout() +plt.show() +``` + +The main lesson is that signal quality matters for the capital-flow +mechanism. + +When low output is a strong signal of low investment, the contract can use +the low-output state aggressively as a punishment, producing a small region +of net capital outflows. + +When the signal is weaker, low output is less informative, so the same +punishment is less useful for incentives. + +In this calibration, the visible low-output outflow region largely disappears: +after low output, new borrowing usually offsets repayment. + +```{solution-end} +``` diff --git a/lectures/chang_ramsey.md b/lectures/chang_ramsey.md index 97571036..2f0d62b0 100644 --- a/lectures/chang_ramsey.md +++ b/lectures/chang_ramsey.md @@ -3,10 +3,12 @@ jupytext: text_representation: extension: .md format_name: myst + format_version: 0.13 + jupytext_version: 1.19.1 kernelspec: - display_name: Python 3 - language: python name: python3 + display_name: Python 3 (ipykernel) + language: python --- (chang_ramsey)= @@ -22,10 +24,9 @@ kernelspec: In addition to what's in Anaconda, this lecture will need the following libraries: -```{code-cell} ipython ---- -tags: [hide-output] ---- +```{code-cell} ipython3 +:tags: [hide-output] + !pip install polytope cvxopt ``` @@ -68,7 +69,7 @@ and other lectures. We'll start with some standard imports: -```{code-cell} ipython +```{code-cell} ipython3 import numpy as np import polytope import matplotlib.pyplot as plt @@ -918,16 +919,18 @@ $\beta = 0.8$. (Here we have set the number of subgradients to 10 in order to speed up the code for now - we can increase accuracy by increasing the number of subgradients) -```{code-cell} python3 +```{code-cell} ipython3 :load: _static/lecture_specific/chang_credible/changecon.py + + ``` -```{code-cell} python3 +```{code-cell} ipython3 ch1 = ChangModel(β=0.3, mbar=30, h_min=0.9, h_max=2, n_h=8, n_m=35, N_g=10) ch1.solve_sustainable() ``` -```{code-cell} python3 +```{code-cell} ipython3 def plot_competitive(ChangModel): """ Method that only plots competitive equilibrium set @@ -960,13 +963,13 @@ def plot_competitive(ChangModel): plot_competitive(ch1) ``` -```{code-cell} python3 +```{code-cell} ipython3 ch2 = ChangModel(β=0.8, mbar=30, h_min=0.9, h_max=1/0.8, n_h=8, n_m=35, N_g=10) ch2.solve_sustainable() ``` -```{code-cell} python3 +```{code-cell} ipython3 plot_competitive(ch2) ``` @@ -1023,14 +1026,14 @@ From the figures earlier in this lecture, we know that when $\beta = 0.3$, $\Omega = [0.0088,0.0499]$, and when $\beta = 0.8$, $\Omega = [0.0395,0.2193]$ -```{code-cell} python3 +```{code-cell} ipython3 ch1 = ChangModel(β=0.3, mbar=30, h_min=0.99, h_max=1/0.3, n_h=8, n_m=35, N_g=50) ch2 = ChangModel(β=0.8, mbar=30, h_min=0.1, h_max=1/0.8, n_h=20, n_m=50, N_g=50) ``` -```{code-cell} python3 +```{code-cell} ipython3 ch1.solve_bellman(θ_min=0.01, θ_max=0.0499, order=30, tol=1e-6) ch2.solve_bellman(θ_min=0.045, θ_max=0.15, order=30, tol=1e-6) ``` @@ -1040,14 +1043,14 @@ good. We do this by calculating the residuals between iterates on the value function on a fine grid: -```{code-cell} python3 +```{code-cell} ipython3 max(abs(ch1.resid_grid)), max(abs(ch2.resid_grid)) ``` The value functions plotted below trace out the right edges of the sets of equilibrium values plotted above -```{code-cell} python3 +```{code-cell} ipython3 fig, axes = plt.subplots(1, 2, figsize=(12, 4)) for ax, model in zip(axes, (ch1, ch2)): @@ -1062,7 +1065,7 @@ plt.show() The next figure plots the optimal policy functions; values of $\theta',m,x,h$ for each value of the state $\theta$: -```{code-cell} python3 +```{code-cell} ipython3 for model in (ch1, ch2): fig, axes = plt.subplots(2, 2, figsize=(12, 6), sharex=True) @@ -1094,7 +1097,7 @@ With the first set of parameter values, this function does not intersect the 45-degree line until $\bar \theta$, whereas in the second set of parameter values, it intersects in the interior. -```{code-cell} python3 +```{code-cell} ipython3 fig, axes = plt.subplots(1, 2, figsize=(12, 4)) for ax, model in zip(axes, (ch1, ch2)): @@ -1115,7 +1118,7 @@ equilibrium. These are shown below for both sets of parameters -```{code-cell} python3 +```{code-cell} ipython3 for model in (ch1, ch2): fig, axes = plt.subplots(2, 2, figsize=(12, 6)) diff --git a/lectures/repeat_mh.md b/lectures/repeat_mh.md new file mode 100644 index 00000000..a0cfc1a4 --- /dev/null +++ b/lectures/repeat_mh.md @@ -0,0 +1,1878 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.19.1 +kernelspec: + name: python3 + display_name: Python 3 (ipykernel) + language: python +--- + +# Repeated Moral Hazard + +## Overview + +This lecture computes information-constrained optima in the repeated +moral-hazard environment of {cite:t}`Phelan_Townsend_91`. + +The environment is a continuum-agent economy with unobserved effort. + +The planner chooses lotteries over individual histories, subject to +promise-keeping and incentive-compatibility constraints, and maximizes +discounted social surplus. + +The key recursive idea comes from {cite:t}`Spear_Srivastava_87`: an +agent's promised continuation utility is a sufficient state variable. + +{cite:t}`Phelan_Townsend_91` combine that idea with lotteries, finite grids, +and linear programming to compute full-information, static +unobserved-action, and repeated unobserved-action allocations. + +The lecture proceeds from the recursive formulation to the computational +implementation: + +* We review the promised-utility recursion of + {cite:t}`Spear_Srivastava_87`. +* We formulate the Phelan-Townsend lottery problem and its finite-grid + linear-programming approximation. +* We use the static economy to isolate the surplus cost of hidden + effort and the role of output-contingent consumption. +* We use the repeated economy to show how continuation promises become + an additional incentive instrument and generate dispersion over time. + + +## Promised-utility recursion + +{cite:t}`Spear_Srivastava_87` showed how to write an infinitely repeated, +discounted principal-agent problem recursively. + +* A **principal** owns a technology that produces output $q_t$ at + time $t$ according to a conditional distribution $F(q_t \mid a_t)$ + that depends on the **effort** $a_t$ chosen by an **agent**. +* The principal does *not* observe $a_t$. +* The principal *does* observe $q_t$ at the end of period $t$ and + remembers the full history $\{q_s\}_{s=0}^t$. +* The principal is risk-neutral and has access to a loan market with + gross risk-free interest rate $\beta^{-1}$. +* The agent has preferences over random consumption streams ordered + by $E_0 \sum_{t=0}^\infty \beta^t u(c_t, a_t)$, where $u$ is + increasing in $c$ and decreasing in $a$. + +A **contract** recommends effort before output is realized and then +assigns consumption and continuation promises as functions of observed +output histories. + +The principal designs the contract to maximize expected discounted +surplus $E_0 \sum_{t=0}^\infty \beta^t \{q_t - c_t\}$. + +Let $w$ denote the discounted expected continuation utility that the +principal has promised to the agent at the start of a period. + +The promised utility $w$ summarizes payoff-relevant history. + +Given +$w$, the principal chooses a recommended action $a(w)$, an +output-contingent consumption rule $c(w,q)$, and an output-contingent +next-period promise $\tilde w(w,q)$ subject to + +$$ +w = \int \bigl\{ u[c(w,q),\, a(w)] + \beta\,\tilde{w}(w,q) + \bigr\}\, dF[q \mid a(w)] +$$ (eq:eq1) + +and, for all alternative actions $\hat a \in A$, + +$$ +\int \bigl\{ u[c(w,q),\, a(w)] + \beta\,\tilde{w}(w,q) \bigr\}\, +dF[q\mid a(w)] +\;\geq\; +\int \bigl\{ u[c(w,q),\, \hat a] + \beta\,\tilde{w}(w,q) \bigr\}\, +dF[q\mid\hat a]. +$$ (eq:eq2) + +Equation {eq}`eq:eq1` is the **promise-keeping** constraint: the +contract must deliver the promised continuation utility $w$. + +Equation {eq}`eq:eq2` is the **incentive-compatibility** constraint: +the agent must prefer the recommended action $a(w)$ over any +deviation $\hat a$. + +The principal's value function $v(w)$ is the maximum expected +discounted surplus attainable when the agent has been promised $w$. + +It satisfies the Bellman equation + +$$ +v(w) = \max_{a,\,c,\,\tilde{w}}\ +\int \bigl\{q - c(w,q) + \beta\, v[\tilde{w}(w,q)]\bigr\}\, +dF[q\mid a(w)] +$$ (eq:eq3) + +subject to the promise-keeping constraint {eq}`eq:eq1` and the +incentive-compatibility constraint {eq}`eq:eq2`. + + +## Lotteries and linear programming + +A technical difficulty in problems like {eq}`eq:eq3` is that +incentive constraints can make deterministic contract problems +non-convex. + +```{prf:example} A non-convex deterministic contract set +:label: repeat_mh_nonconvex_example +:class: dropdown + +Consider a one-period version of the problem with two outputs, +$q_H$ and $q_L$, and two actions, high effort $H$ and low effort $L$. +Suppose that + +$$ +P(q_H \mid H)=3/4, +\qquad +P(q_H \mid L)=1/4, +$$ + +and that the agent's utility is + +$$ +u(c,H)=\sqrt c - 1/2, +\qquad +u(c,L)=\sqrt c. +$$ + +A deterministic contract that recommends high effort pays $c_H$ after +$q_H$ and $c_L$ after $q_L$. +Incentive compatibility requires + +$$ +\frac34 \sqrt{c_H}+\frac14\sqrt{c_L}-\frac12 +\geq +\frac14 \sqrt{c_H}+\frac34\sqrt{c_L}, +$$ + +or + +$$ +\sqrt{c_H}-\sqrt{c_L}\geq 1. +$$ + +The two contracts $(c_H,c_L)=(1,0)$ and $(c_H,c_L)=(9,4)$ both satisfy +this constraint. + +But their midpoint, $(c_H,c_L)=(5,2)$, violates it because + +$$ +\sqrt 5-\sqrt 2 \approx 0.82 < 1. +$$ + +Thus the set of deterministic contracts satisfying incentive +compatibility is not convex. +``` + +The Phelan-Townsend approach formulates the planning problem in +terms of **lotteries** over actions, outputs, consumptions, and +continuation utilities. + +At the aggregate level these probabilities +are also population fractions, so individual randomization creates no +aggregate uncertainty in a continuum-agent economy. + +For computation, we "grid" the relevant sets of +possible utilities, allowing only finitely many points. + +With finite sets, or finite approximations to sets, $A$, $Q$, and $C$, +the planner's problem becomes a finite-dimensional optimization problem +with linear constraints. + +Each stage of the computation therefore amounts to solving a finite +**linear programming** (LP) problem. + +We begin with the finite objects in the planning problem. + +Let $P(q \mid a)$ be a family of discrete conditional probability +distributions over finite sets $Q$ (outputs) and $A$ (actions). + +Let $C$ and $W'$ be finite grids for current consumption and next-period +promised utility. + +For each current promise $w$, the planner chooses a joint probability +$\Pi^w(a, q, c, w')$ subject to: + +$$ +\sum_{c \in C}\sum_{w' \in W'} \Pi^w(\bar a, \bar q, c, w') = +P(\bar q \mid \bar a)\, +\sum_{q \in Q}\sum_{c \in C}\sum_{w' \in W'} + \Pi^w(\bar a, q, c, w'), +\quad \forall\, \bar a,\, \bar q +$$ (eq:town1a) + +$$ +\Pi^w(a, q, c, w') \geq 0, +\qquad +\sum_{a \in A}\sum_{q \in Q}\sum_{c \in C}\sum_{w' \in W'} + \Pi^w(a, q, c, w') = 1. +$$ (eq:town1b) + +Equation {eq}`eq:town1a` says that conditional on action $\bar a$, +the marginal distribution of output follows $P(q \mid \bar a)$. + +Equations {eq}`eq:town1b` require the choice variables to be proper +probabilities. + +The **promise-keeping** constraint is + +$$ +w = \sum_{A \times Q \times C \times W'} +\bigl\{u(c, a) + \beta w'\bigr\}\,\Pi^w(a, q, c, w'). +$$ (eq:eq1prime) + +The **incentive-compatibility** constraint, for each pair +$(a, \hat a)$, is + +$$ +\sum_{Q \times C \times W'} \bigl\{u(c,a) + \beta w'\bigr\}\, +\Pi^w(a, q, c, w') +\;\geq\; +\sum_{Q \times C \times W'} \bigl\{u(c,\hat a) + \beta w'\bigr\}\, +\frac{P(q\mid\hat a)}{P(q\mid a)}\,\Pi^w(a, q, c, w'). +$$ (eq:eq2prime) + +The ratio $P(q\mid\hat a)/P(q\mid a)$ is the likelihood ratio that +updates the probability of outcome $q$ when the agent deviates from +the recommended action $a$ to $\hat a$. + +The corresponding Bellman operator is also a linear program. +The principal's value function satisfies + +$$ +v(w) = \max_{\Pi}\ +\sum_{A \times Q \times C \times W'} +\bigl\{(q - c) + \beta\, v(w')\bigr\}\,\Pi^w(a, q, c, w'), +$$ (eq:bell2) + +where the maximization is over probabilities $\Pi$ satisfying +{eq}`eq:town1a`, {eq}`eq:town1b`, {eq}`eq:eq1prime`, and +{eq}`eq:eq2prime`. + +This is a **linear program**: the objective and all constraints are +linear in the decision variables $\Pi^w$. + +Because $v(w')$ on the right side of {eq}`eq:bell2` is treated as a +*fixed* vector from the previous iteration, the Bellman operator +itself is a linear program. + +The algorithm solves one LP for each grid point $w \in W$ and iterates +on the surplus function. + +In addition to what's in Anaconda, this lecture will need the following libraries: + +```{code-cell} ipython3 +:tags: [hide-output] + +!pip install quantecon cvxpy highspy +``` + +We import some Python packages. + +```{code-cell} ipython3 +from time import time + +import cvxpy as cp +import highspy as hp +import matplotlib.pyplot as plt +import numpy as np +import quantecon as qe +``` + +## The static economy + +A one-period economy is the cleanest place to isolate the +informational friction. + +This isolates the static informational friction before the dynamic +promised-utility channel is introduced. + +We first compute the full-information benchmark, where effort can be +controlled directly. + +We then make effort private information and add incentive compatibility. + +### Setting + +The **full-information problem** (FIP) maximizes expected surplus +subject only to feasibility and promise keeping: + +$$ +\max_{\Pi^w}\; \sum_{A \times Q \times C} (q - c)\,\Pi^w(a, q, c) +$$ + +subject to + +$$ +\begin{aligned} +\text{C1:}&\quad + w = \sum_{A \times Q \times C} U(a,c)\,\Pi^w(a,q,c) \\[4pt] +\text{C2:}&\quad + \sum_C \Pi^w(\bar a, \bar q, c) + = P(\bar q \mid \bar a) + \sum_{Q \times C} \Pi^w(\bar a, q, c), + \quad \forall\, \bar a,\, \bar q \\[4pt] +\text{C3:}&\quad + \sum_{A \times Q \times C} \Pi^w(a,q,c) = 1, + \quad \Pi^w(a,q,c) \geq 0 +\end{aligned} +$$ + +The **unobserved-action problem** adds incentive compatibility. + +For each recommended action $a$ and each possible deviation $\hat a$, the +utility from obeying must be at least as large as the utility from +deviating while preserving the same output-contingent consumption rule: + +$$ +\text{C4:}\quad +\sum_{Q \times C} U(a,c)\,\Pi^w(a,q,c) +\;\geq\; +\sum_{Q \times C} U(\hat a, c)\, +\frac{P(q\mid\hat a)}{P(q\mid a)}\,\Pi^w(a,q,c), +\quad \forall\, a,\, \hat a \in A. +$$ + +### Parameterization + +The baseline utility specification is + +$$ +U(a, c) = 2\sqrt{c} + 2\sqrt{1-a} +$$ + +with discrete grids + +| Variable | Values | +| :------- | :----- | +| $a \in A$ | $\{0,\; 0.2,\; 0.4,\; 0.6\}$ | +| $q \in Q$ | $\{1,\; 2\}$ | +| $c \in C$ | $81$ equally spaced points on $[0,\, 2.25]$ | + +and conditional output probabilities + +| $a$ | $P(q=1)$ | $P(q=2)$ | +| :-----: | :------: | :------: | +| 0 | 0.9 | 0.1 | +| 0.2 | 0.6 | 0.4 | +| 0.4 | 0.4 | 0.6 | +| 0.6 | 0.25 | 0.75 | + +These parameter values define the baseline numerical economy for the +static comparisons and the first dynamic calculations. + +The static grid of promised utility values below spans the +interval $[1,5]$. + +```{code-cell} ipython3 +def u(a, c): + return c**0.5 / 0.5 + (1 - a)**0.5 / 0.5 + +A = np.array([0, 0.2, 0.4, 0.6]) +Q = np.array([1, 2]) +C = np.linspace(0, 2.25, 81) +P = np.array([[0.9, 0.1], + [0.6, 0.4], + [0.4, 0.6], + [0.25, 0.75]]) +``` + +### Solving the static problem + +The function `solve_static_problem` solves one LP for each promised +utility value $w$. + +The code keeps the notation close to the mathematical problem: +`π[a_i][q_i, c_i]` is the lottery probability +$\Pi^w(a_i,q_i,c_i)$, `Φ[q_i, c_i]` is output net of consumption, +and `U[a_i, c_i]` is period utility. + +For the full-information problem we impose C1--C3. + +For the unobserved-action problem we add C4. + +```{code-cell} ipython3 +def solve_static_problem(W, u, A, Q, C, P, problem_type): + """Solve the static LP on a grid of promised utilities.""" + n_a, n_q, n_c = len(A), len(Q), len(C) + A_i, Q_i = range(n_a), range(n_q) + + Φ = np.array([[q - c for c in C] for q in Q]) + U = np.array([[u(a, c) for c in C] for a in A]) + + w = cp.Parameter() + π = [cp.Variable((n_q, n_c), nonneg=True) for _ in A_i] + + surplus = cp.sum([ + cp.sum(cp.multiply(Φ, π[a_i])) + for a_i in A_i + ]) + + promise = [ + cp.sum([ + cp.sum(cp.multiply(U[a_i], π[a_i][q_i, :])) + for a_i in A_i + for q_i in Q_i + ]) == w + ] + + output_law = [ + cp.sum(π[a_i][q_i, :]) == P[a_i, q_i] * cp.sum(π[a_i]) + for a_i in A_i + for q_i in Q_i + ] + + probability = [cp.sum([cp.sum(π[a_i]) for a_i in A_i]) == 1] + + constraints = promise + output_law + probability + + if problem_type.lower() != "full information": + incentives = [] + for a_i in A_i: + for a_hat_i in A_i: + obey = cp.sum([ + cp.sum(cp.multiply(U[a_i], π[a_i][q_i, :])) + for q_i in Q_i + ]) + deviate = cp.sum([ + cp.sum(cp.multiply(U[a_hat_i], π[a_i][q_i, :])) + * P[a_hat_i, q_i] / P[a_i, q_i] + for q_i in Q_i + ]) + incentives.append(obey >= deviate) + constraints += incentives + + problem = cp.Problem(cp.Maximize(surplus), constraints) + + s_W = np.full(len(W), np.nan) + π_W = np.full((len(W), n_a, n_q, n_c), np.nan) + for w_i, w_value in enumerate(W): + w.value = w_value + problem.solve(solver=cp.HIGHS) + if problem.status in (cp.OPTIMAL, cp.OPTIMAL_INACCURATE): + s_W[w_i] = surplus.value + for a_i in A_i: + π_W[w_i, a_i] = π[a_i].value + + return s_W, π_W +``` + +### Static allocations + +Now we solve the static problem for a grid of promised utility values. + +```{code-cell} ipython3 +W_static = np.linspace(1, 5, 100) + +s_W_full, π_full = solve_static_problem(W_static, u, A, Q, C, P, + "full information") +s_W_unobs, π_unobs = solve_static_problem(W_static, u, A, Q, C, P, + "unobserved-actions") +``` + +The arrays returned by `solve_static_problem` have a direct economic +interpretation. + +`s_W_full` and `s_W_unobs` are the optimized surplus frontiers. + +`π_full` and `π_unobs` store the optimal lotteries over +$(a,q,c)$ at each promised utility. + +The next helper turns a lottery over $(a,q,c)$ into conditional mean +consumption for each $(w,a,q)$. + +If the event $(a,q)$ has zero probability at a given $w$, the +conditional mean is undefined and is stored as `nan`. + +```{code-cell} ipython3 +def expected_consumption_static(π_W, C): + π0 = np.nan_to_num(π_W, nan=0.0) + mass = π0.sum(axis=-1) + numerator = np.einsum('c,waqc->waq', C, π0) + Ec = np.full(mass.shape, np.nan) + np.divide(numerator, mass, out=Ec, where=mass > 1e-10) + return Ec +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: static surplus frontiers + name: fig-rmh-static-surplus +--- +plt.figure() +plt.plot(W_static, s_W_full, label="Full information", lw=2) +plt.plot(W_static, s_W_unobs, label="Hidden effort", lw=2) +plt.hlines(0, 1.0, 5.0, linestyle="dashed") +plt.xlabel("w") +plt.ylabel("s(w)") +plt.xlim([1.0, 5.0]) +plt.ylim([-1.5, 2.0]) +plt.legend() +plt.show() +``` + +The full-information frontier is higher because the planner can choose +effort directly. + +The unobserved-action frontier lies below it because effort must be +induced with state-contingent rewards. + +The gap is the agency cost of private effort. + +We can also look at the expected effort and consumption. + +The next figure plots expected effort as a function of the promise $w$ +under full information and under hidden effort. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: expected effort by promise + name: fig-rmh-static-effort +--- +Ea_full = np.einsum('a,waqc->w', A, π_full) +Ea_unobs = np.einsum('a,waqc->w', A, π_unobs) + +plt.figure() +plt.plot(W_static, Ea_full, label="Full information", lw=2) +plt.plot(W_static, Ea_unobs, label="Hidden effort", lw=2) +plt.xlabel("w") +plt.ylabel(r"$E(a(w))$") +plt.xlim([1.0, 5.0]) +plt.ylim([0.0, 0.8]) +plt.legend() +plt.show() +``` + +Here the code integrates the action grid against the lottery +probabilities, producing $E(a(w))$. + +Under full information, effort is chosen to maximize surplus at each +promise. + +With unobserved action, expected effort is lower where incentives are +costly to provide. + +Now we look at expected consumption as a function of the promise $w$, +the recommended action $a$, and the realized output $q$. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: consumption under hidden effort + name: fig-rmh-static-cons-unobs +--- +Ec_unobs = expected_consumption_static(π_unobs, C) + +fig, axes = plt.subplots(1, len(Q), sharey=True) +for q_i, ax in enumerate(axes): + for a_i, a in enumerate(A): + ax.plot(W_static, Ec_unobs[:, a_i, q_i], label=f"a={a:g}", lw=2) + ax.set_xlabel(f"w, q={Q[q_i]:g}") + ax.set_xlim([1.0, 5.0]) + ax.set_ylim([0.0, 2.25]) +axes[0].set_ylabel(r"$E(c \mid w, a, q)$") +axes[-1].legend(title="action", loc="lower right") +fig.tight_layout() +plt.show() +``` + +The gaps in the figure are important. + +A line is missing where the optimal contract puts zero probability on +that action-output pair $(a,q)$ at promise $w$. + +In those places, $E[c \mid w,a,q]$ is not defined because the event +being conditioned on never occurs. + +As $w$ rises, the set of actions used by the optimal contract changes. + +That is why some lines start and stop. + +The absent curve for $a=0.6$ means that action is never used on this +grid. + +When effort is hidden, full insurance would make the agent want to choose +a lower action. + +So, when a positive action is recommended, high output must be rewarded +with higher consumption. + +That is why, on the parts of the graph where a line exists, consumption +is higher after $q=2$ than after $q=1$. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: consumption under full information + name: fig-rmh-static-cons-full +--- +Ec_full = expected_consumption_static(π_full, C) + +fig, axes = plt.subplots(1, len(Q), sharey=True) +for q_i, ax in enumerate(axes): + for a_i, a in enumerate(A): + ax.plot(W_static, Ec_full[:, a_i, q_i], label=f"a={a:g}", lw=2) + ax.set_xlabel(f"w, q={Q[q_i]:g}") + ax.set_xlim([1.0, 5.0]) + ax.set_ylim([0.0, 2.25]) +axes[0].set_ylabel(r"$E(c \mid w, a, q)$") +axes[-1].legend(title="action", loc="lower right") +fig.tight_layout() +plt.show() +``` + +With full information, output does not need to carry incentive rewards. + +Consumption therefore depends primarily on the promise $w$ rather than +on output. + +Notice also that the full-information plot has no gaps of the same kind. + +As $w$ rises, the planner switches from one action to another, but the +pieces join up and cover the promise range. + +This is because effort is observed: the planner can choose the action +directly. + +## The repeated economy + +We now move from the one-period economy to infinite-horizon contracts. + +The planner maximizes discounted social surplus. + +This can be interpreted as allowing society to borrow and lend at the constant +gross interest rate $\beta^{-1}$, so that discounted surplus is the +right feasibility criterion. + +### Formulation + +The recursive repeated problem chooses today's action, output, +consumption, and next-period promised utility. + +The surplus function +$s(w)$ satisfies + +$$ +s(w) = \max_{\Pi^w}\; +\sum_{A \times Q \times C \times W'} +\bigl\{(q - c) + \beta\, s(w')\bigr\}\,\Pi^w(a, q, c, w') +$$ + +subject to, for all $(\bar a, \bar q)$, + +$$ +\begin{aligned} +\text{C5:}&\quad + w = \sum_{A \times Q \times C \times W'} + \bigl\{U(a,c) + \beta w'\bigr\}\,\Pi^w(a,q,c,w') \\[4pt] +\text{C6:}&\quad + \sum_{C \times W'} \Pi^w(\bar a, \bar q, c, w') + = P(\bar q\mid\bar a) + \sum_{Q \times C \times W'} \Pi^w(\bar a, q, c, w') \\[4pt] +\text{C7:}&\quad + \sum_{A \times Q \times C \times W'} \Pi^w(a,q,c,w') = 1, + \quad \Pi^w(a,q,c,w') \geq 0 \\[4pt] +\text{C8:}&\quad + \sum_{Q \times C \times W'} + \bigl\{U(a,c)+\beta w'\bigr\}\Pi^w(a,q,c,w') + \;\geq\; + \sum_{Q \times C \times W'} + \bigl\{U(\hat a,c)+\beta w'\bigr\} + \frac{P(q\mid\hat a)}{P(q\mid a)}\Pi^w(a,q,c,w'), + \quad \forall\, a,\, \hat a. +\end{aligned} +$$ + +Constraints C5--C8 are the dynamic analogues of C1--C4. + +* Constraint C5 keeps the promise $w$. + +* Constraint C6 maintains the action-output law of motion. + +* Constraint C7 is a probability constraint. + +* Constraint C8 is incentive compatibility. + +For the infinite horizon, we iterate on the Bellman operator until the +surplus function is stable. + +At each iteration, a separate LP is solved for each grid point +$w \in W$. + +### The two-step algorithm + +Solving the full LP over $(a,q,c,w')$ at each grid point is +computationally demanding. + +We use a factored algorithm that splits each period into two sub-steps. + +The split exploits the additive separability of the utility function + +$$ +U(a, c) = 2\sqrt{1-a} + 2\sqrt{c}. +$$ + +In the first sub-step, the planner chooses action, output, and +intermediate promised utility. + +Let $w^m$ be the **intermediate promised utility** after the output +is observed but before consumption is allocated. + +Thus $w^m$ includes +the utility from current consumption and the discounted next-period +promise, but not the current effort utility. + +Solve + +$$ +\begin{aligned} +\max_{\Pi^w}\; & +\sum_{A \times Q \times W^m} + \bigl\{q + s^m(w^m)\bigr\}\,\Pi^w(a, q, w^m) \\[4pt] +\text{C5:}&\quad + w = \sum_{A \times Q \times W^m} + \bigl\{2\sqrt{1-a} + w^m\bigr\}\,\Pi^w(a, q, w^m) \\[4pt] +\text{C6:}&\quad + \sum_{W^m} \Pi^w(\bar a, \bar q, w^m) + = P(\bar q\mid\bar a) + \sum_{Q \times W^m} \Pi^w(\bar a, q, w^m) \\[4pt] +\text{C7:}&\quad + \Pi^w(a,q,w^m) \geq 0,\quad + \sum_{A \times Q \times W^m} \Pi^w(a,q,w^m) = 1 \\[4pt] +\text{C8:}&\quad + \sum_{Q \times W^m} + \bigl\{2\sqrt{1-a} + w^m\bigr\}\Pi^w(a,q,w^m) + \;\geq\; + \sum_{Q \times W^m} + \bigl\{2\sqrt{1-\hat a} + w^m\bigr\} + \frac{P(q\mid\hat a)}{P(q\mid a)}\Pi^w(a,q,w^m), + \quad \forall\, a,\, \hat a. +\end{aligned} +$$ + +In the second sub-step, the planner allocates current consumption and +next-period promised utility. + +Given $w^m$, solve + +$$ +\begin{aligned} +\max_{\Pi^{w^m}}\; & +\sum_{C \times W'} + \bigl\{\beta s(w') - c\bigr\}\,\Pi^{w^m}(c, w') \\[4pt] +\text{C5:}&\quad + w^m = \sum_{C \times W'} + \bigl\{2\sqrt{c} + \beta w'\bigr\}\,\Pi^{w^m}(c, w') \\[4pt] +\text{C7:}&\quad + \Pi^{w^m}(c,w') \geq 0,\quad + \sum_{C \times W'} \Pi^{w^m}(c,w') = 1. +\end{aligned} +$$ + +Step 2 is solved first computationally, for all $w^m \in W^m$, to +obtain $s^m(w^m)$. + +Step 1 then uses this intermediate surplus function +as input. + +This factored algorithm significantly reduces computation time because +each sub-LP is smaller than the original joint LP. + +The two-step formulation is an approximation when $W^m$ is +discretized: as the number of grid points $N_m$ grows, it converges +to the exact solution. + +### Functions + +We use HiGHS directly via the `highspy` bindings instead of building +each LP through CVXPY. + +HiGHS is a high-performance open-source solver for linear programming +and related optimization problems. + +`highspy` is its Python interface. + +It lets us keep the same LP model in memory and change only the small +parts that differ across grid points. + +In each Bellman iteration the two-step LPs differ only in the +promise-keeping right-hand side (and in the objective coefficients +between iterations). + +Re-building a CVXPY problem object for every grid point and every +iteration adds substantial overhead and can leak memory. + +With `highspy` we build each LP once and then mutate the objective +and one row bound between solves. + +The first subproblem is Step 2. + +For a fixed intermediate promise $w^m$, Step 2 chooses a lottery over +current consumption and tomorrow's promise, $(c,w')$. + +The two constraints are simple: deliver $w^m$ and make probabilities sum +to one. + +```{code-cell} ipython3 +def _build_step2_lp(C, W_prime, β): + """Build the Step 2 LP.""" + n_C, n_W = len(C), len(W_prime) + n_x = n_C * n_W + U_disc = np.array([2*c**0.5 + β*wp + for c in C for wp in W_prime]) + + h = hp.Highs() + h.silent() + h.setOptionValue("parallel", "off") + empty_i = np.array([], dtype=np.int32) + empty_d = np.array([], dtype=float) + h.addCols(n_x, np.zeros(n_x), np.zeros(n_x), np.full(n_x, hp.kHighsInf), + 0, empty_i, empty_i, empty_d) + + idx = np.arange(n_x, dtype=np.int32) + h.addRow(0.0, 0.0, n_x, idx, U_disc) # promise + h.addRow(1.0, 1.0, n_x, idx, np.ones(n_x)) # probability + return h, (n_C, n_W) +``` + +Step 1 uses the Step 2 value $s^m(w^m)$. + +It chooses a lottery over the recommended action, output, and +intermediate promise, $(a,q,w^m)$. + +Here the fixed constraints enforce promise keeping, probabilities, +output probabilities, and, in the hidden-effort case, incentive +compatibility. + +```{code-cell} ipython3 +def _build_step1_lp(A, Q, W_m, P, problem_type): + """Build the Step 1 LP.""" + n_A, n_Q, n_W_m = len(A), len(Q), len(W_m) + n_x = n_A * n_Q * n_W_m + + def vid(a_i, q_i, m_i): + return (a_i * n_Q + q_i) * n_W_m + m_i + + U_aw = np.empty(n_x) + for a_i, a in enumerate(A): + ua = 2*(1 - a)**0.5 + for q_i in range(n_Q): + for m_i, wm in enumerate(W_m): + U_aw[vid(a_i, q_i, m_i)] = ua + wm + + h = hp.Highs() + h.silent() + h.setOptionValue("parallel", "off") + empty_i = np.array([], dtype=np.int32) + empty_d = np.array([], dtype=float) + h.addCols(n_x, np.zeros(n_x), np.zeros(n_x), np.full(n_x, hp.kHighsInf), + 0, empty_i, empty_i, empty_d) + + idx = np.arange(n_x, dtype=np.int32) + h.addRow(0.0, 0.0, n_x, idx, U_aw) # promise + h.addRow(1.0, 1.0, n_x, idx, np.ones(n_x)) # probability + + # Output law + for a_i in range(n_A): + for q_i in range(n_Q): + row = np.zeros(n_x) + for m_i in range(n_W_m): + row[vid(a_i, q_i, m_i)] += 1.0 + for q_j in range(n_Q): + for m_i in range(n_W_m): + row[vid(a_i, q_j, m_i)] -= P[a_i, q_i] + nz = np.flatnonzero(row) + h.addRow(0.0, 0.0, nz.size, + nz.astype(np.int32), row[nz]) + + # Incentive compatibility + if problem_type.lower() != "full information": + for a_i in range(n_A): + ua = 2*(1 - A[a_i])**0.5 + for a_hat in range(n_A): + if a_hat == a_i: + continue + ua_h = 2*(1 - A[a_hat])**0.5 + row = np.zeros(n_x) + for q_i in range(n_Q): + ratio = P[a_hat, q_i] / P[a_i, q_i] + for m_i, wm in enumerate(W_m): + coef = (ua + wm) - (ua_h + wm) * ratio + row[vid(a_i, q_i, m_i)] -= coef + nz = np.flatnonzero(row) + h.addRow(-hp.kHighsInf, 0.0, nz.size, + nz.astype(np.int32), row[nz]) + return h, (n_A, n_Q, n_W_m) +``` + +Once the constraint matrices are built, each Bellman iteration only +changes objective coefficients. + +The next helpers update those coefficients in place. + +HiGHS minimizes by default, so we store the negative of the surplus +objective. + +```{code-cell} ipython3 +def _update_step2_objective(h, C, W_prime, s_W_prime, β): + n_C, n_W = len(C), len(W_prime) + obj = -(β * np.broadcast_to(s_W_prime, (n_C, n_W)) + - np.asarray(C)[:, None]).ravel() + cols = np.arange(obj.size, dtype=np.int32) + h.changeColsCost(cols.size, cols, obj) + h.clearSolver() + + +def _update_step1_objective(h, A, Q, W_m, s_W_m): + n_A, n_Q, n_W_m = len(A), len(Q), len(W_m) + Φ = np.asarray(Q)[:, None] + np.asarray(s_W_m)[None, :] + obj = -np.broadcast_to(Φ, (n_A, n_Q, n_W_m)).ravel() + cols = np.arange(obj.size, dtype=np.int32) + h.changeColsCost(cols.size, cols, obj) + h.clearSolver() +``` + +Finally we solve the two subproblems across their promise grids. + +The promise-keeping constraint is row 0 in both LPs. + +So at each grid point we only change the lower and upper bound of row 0, +then resolve the same model. + +```{code-cell} ipython3 + + +def _solve_step2(h, shape, W_m): + n_C, n_W = shape + n_W_m = len(W_m) + s_W_m = np.empty(n_W_m) + π_W_m_s2 = np.empty((n_W_m, n_C, n_W)) + for i, wm in enumerate(W_m): + h.changeRowBounds(0, wm, wm) + h.run() + st = h.getModelStatus() + if st != hp.HighsModelStatus.kOptimal: + raise RuntimeError(f"Step 2 LP not optimal at w_m={wm}: {st}") + s_W_m[i] = -h.getObjectiveValue() + π_W_m_s2[i] = np.asarray(h.getSolution().col_value).reshape(n_C, n_W) + return s_W_m, π_W_m_s2 + + +def _solve_step1(h, shape, W): + n_A, n_Q, n_W_m = shape + s_W = np.empty(len(W)) + π_W_s1 = np.empty((len(W), n_A, n_Q, n_W_m)) + for i, w in enumerate(W): + h.changeRowBounds(0, w, w) + h.run() + st = h.getModelStatus() + if st != hp.HighsModelStatus.kOptimal: + raise RuntimeError(f"Step 1 LP not optimal at w={w}: {st}") + s_W[i] = -h.getObjectiveValue() + π_W_s1[i] = (np.asarray(h.getSolution().col_value) + .reshape(n_A, n_Q, n_W_m)) + return s_W, π_W_s1 +``` + +### Repeated-economy solver + +The solver below builds the Step 1 and Step 2 LPs once. + +After that, each Bellman iteration only changes the objective +coefficients, and each promise-grid point only changes one row bound. + +It also uses Anderson acceleration with a short history of recent +surplus functions. + +This often reduces the number of Bellman iterations. + +```{code-cell} ipython3 +def solve_multi_period_economy(A=None, + Q=None, + C=None, + P=None, + problem_type=None, + β=0.95, + N=50, + N_m=50, + s_W_0=None, + tol=1e-4, + max_iter=300, + m_anderson=5, + verbose=True): + """Solve the infinite-horizon problem with reusable HiGHS models.""" + if β >= 1 or β <= 0: + raise ValueError('β must lie in (0, 1)') + + def u_fn(a, c): + return c**0.5 / 0.5 + (1 - a)**0.5 / 0.5 + + problem_type = problem_type.lower() + if problem_type == "full information": + w_l = u_fn(A.max(), C.min()) / (1 - β) + w_u = u_fn(A.min(), C.max()) / (1 - β) + else: + w_l = u_fn(A.min(), C.min()) / (1 - β) + w_u = u_fn(A.min(), C.max()) / (1 - β) + + W = np.linspace(w_l, w_u, N) + W_m = np.linspace(β * w_l + 2 * C.min()**0.5, + β * w_u + 2 * C.max()**0.5, N_m) + + step2_lp, shape2 = _build_step2_lp(C, W, β) + step1_lp, shape1 = _build_step1_lp(A, Q, W_m, P, problem_type) + + s_W_prime = (np.array(s_W_0, dtype=float) + if s_W_0 is not None else np.zeros(N)) + + hist_x, hist_fx = [], [] + err = np.inf + + for iteration in range(1, max_iter + 1): + t0 = time() + + _update_step2_objective(step2_lp, C, W, s_W_prime, β) + s_W_m, π_W_m_s2 = _solve_step2(step2_lp, shape2, W_m) + + _update_step1_objective(step1_lp, A, Q, W_m, s_W_m) + s_W, π_W_s1 = _solve_step1(step1_lp, shape1, W) + + t1 = time() + err = np.max(np.abs(s_W - s_W_prime)) + if verbose: + print(f"Iter {iteration:3d}: max|ΔsW| = {err:.2e} ({t1-t0:.1f}s)") + + if err <= tol: + if verbose: + print(f"Converged in {iteration} iterations.") + break + + # Anderson acceleration. + hist_x.append(s_W_prime.copy()) + hist_fx.append(s_W.copy()) + mk = min(len(hist_x), m_anderson) + if len(hist_x) > m_anderson: + hist_x.pop(0) + hist_fx.pop(0) + + if mk >= 2: + X = np.column_stack(hist_x[-mk:]) + FX = np.column_stack(hist_fx[-mk:]) + F = FX - X + FtF = F.T @ F + reg = max(1e-10 * np.trace(FtF) / mk, 1e-14) + ones = np.ones(mk) + try: + θ = np.linalg.solve(FtF + reg * np.eye(mk), ones) + θ /= ones @ θ + s_candidate = FX @ θ + s_next = (s_candidate + if np.all(np.isfinite(s_candidate)) + else s_W) + except np.linalg.LinAlgError: + s_next = s_W + else: + s_next = s_W + + s_W_prime = s_next + + else: + print(f"Warning: did not converge after {max_iter} iterations. " + f"Final max|ΔsW| = {err:.2e}") + + return s_W, π_W_s1, π_W_m_s2, W +``` + +### Dynamic allocations + +We use the same parameters as for the static economy, plus a +discount factor $\beta = 0.8$ and grids of $N = N_m = 100$ points. + +We initialize the value function iteration with the one-period +(static) solution, scaled to discounted-sum units. + +```{code-cell} ipython3 +β = 0.8 +N = 100 +N_m = 100 + +W_l = u(A.min(), C.min()) / (1 - β) +W_u = u(A.min(), C.max()) / (1 - β) +W = np.linspace(W_l, W_u, N) + +W_m_l = β * W_l + 2 * C.min()**0.5 +W_m_u = β * W_u + 2 * C.max()**0.5 +W_m = np.linspace(W_m_l, W_m_u, N_m) + +with qe.Timer(): + s_W_0, π_0 = solve_static_problem(W * (1 - β), u, + A, Q, C, P, + "unobserved-actions") +``` + +The next cell solves the infinite-horizon hidden-effort problem using +the static solution as the initial value. + +```{code-cell} ipython3 +:tags: [hide-output] + +with qe.Timer(): + s_W, π_W_s1, π_W_m_s2, _ = solve_multi_period_economy( + A, Q, C, P, "unobserved-actions", + β=β, N=N, N_m=N_m, + s_W_0=s_W_0 / (1 - β), + tol=1e-8, verbose=False) +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: infinite-horizon surplus function + name: fig-rmh-infinite-surplus +--- +plt.figure() +plt.plot(W, s_W, "k-.", label="Infinite-horizon hidden effort", lw=2) +plt.xlim([5.0, 25.0]) +plt.ylim([-7.5, 10.0]) +plt.xlabel("w") +plt.ylabel("s(w)") +plt.legend() +plt.show() +``` + +At each promised utility, the surplus function is the fixed point of the +Bellman operator. + +The current lottery and the continuation promise are jointly chosen so +that tomorrow's promise is priced by the same surplus function plotted +here. + +### Recovering $\Pi^w(a, q, c, w')$ + +The two-step algorithm returns +$\Pi^w(a, q, w^m)$ and $\Pi^{w^m}(c, w')$ separately. + +We recover the full joint distribution by using + +$$ +\Pi^w(a,q,c,w') += \sum_{w^m} + \Pi^w(a,q,w^m)\,\Pi^{w^m}(c,w'). +$$ + +```{code-cell} ipython3 +π = np.einsum("waqm,mcx->waqcx", π_W_s1, π_W_m_s2) +``` + +The `einsum` line is just the law of total probability. + +It sums over the intermediate promise $w^m$ and reconstructs the full +lottery over $(a,q,c,w')$. + +To measure the gain from history dependence, we compare three surplus +frontiers: full information, static hidden effort, and repeated hidden +effort. + +```{code-cell} ipython3 +W_full = np.linspace(5, 25, N) + +with qe.Timer(): + s_W_1, π_1 = solve_static_problem(W_full*(1-β), u, A, + Q, C, P, "full information") +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: surplus functions compared + name: fig-rmh-surplus-compare +--- +plt.figure() +plt.plot(W_full, s_W_1/(1 - β), label="Full information", lw=2) +plt.plot(W, s_W, "k-.", label="Repeated hidden effort", lw=2) +plt.plot(W, s_W_0/(1 - β), label="Static hidden effort", lw=2) +plt.xlim([5.0, 25.0]) +plt.ylim([-7.5, 10.0]) +plt.hlines(0, 5.0, 25.0, linestyle="dashed") +plt.xlabel("w") +plt.ylabel("s(w)") +plt.legend() +plt.show() +``` + +This comparison separates two forces. + +The full-information frontier is highest because effort can be controlled +directly. + +The infinite-horizon hidden-effort frontier is below it because +incentive constraints remain, but it lies above the frontier obtained by +repeating the one-period hidden-effort contract. + +The difference between the two unobserved-action curves is the gain from +history dependence. + +The next figure compares expected effort in the static hidden-effort +contract and in the repeated contract. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: effort and history dependence + name: fig-rmh-effort-compare +--- +Ea_1 = np.einsum('a,waqc->w', A, π_0) +Ea_inf = np.einsum('a,waqcx->w', A, π) + +plt.figure() +plt.plot(W, Ea_inf, label="Repeated hidden effort", lw=2) +plt.plot(W, Ea_1, label="Static hidden effort", lw=2) +plt.xlabel("w") +plt.ylabel(r"$E\{a(w)\}$") +plt.xlim([5.0, 25.0]) +plt.ylim([0.0, 0.8]) +plt.legend() +plt.show() +``` + +History dependence also raises effort relative to repeated one-period +contracts. + +Near the lower utility bound, incentive compatibility forces +low effort, but away from that bound continuation promises help provide +incentives without relying only on current consumption. + +The full lottery $\pi(w,a,q,c,w')$ is high-dimensional. + +To read it, we summarize it by conditional means. + +The next helper computes $E[c \mid w,a,q]$, first summing over +continuation promises and then normalizing by the probability of the +conditioning event. + +```{code-cell} ipython3 +def expected_consumption(π, C): + """Compute E[c | w, a, q].""" + if π.ndim == 4: + mass = π.sum(axis=3) + total = np.einsum("c,waqc->waq", C, π) + else: + mass = π.sum(axis=(3, 4)) + total = np.einsum("c,waqcx->waq", C, π) + + return np.divide(total, mass, + out=np.full_like(total, np.nan, dtype=float), + where=mass > 1e-12) +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: consumption in repeated contract + name: fig-rmh-dynamic-cons +--- +Ec_inf = expected_consumption(π, C) + +fig, axes = plt.subplots(1, len(Q), sharey=True) +for q_i, ax in enumerate(axes): + for a_i, a in enumerate(A): + ax.plot(W, Ec_inf[:, a_i, q_i], label=f"a={a:g}", lw=2) + ax.set_xlabel(f"w, q={Q[q_i]:g}") + ax.set_xlim([5.0, 25.0]) + ax.set_ylim([0.0, 2.25]) +axes[0].set_ylabel(r"$E(c \mid w, a, q)$") +axes[-1].legend(title="action", loc="lower right") +fig.tight_layout() +plt.show() +``` + +The repeated contract smooths current consumption relative to the static +hidden-effort economy. + +Output still affects rewards, +but a large part of the reward and punishment is shifted into future +promised utility. + +The parallel statistic for the dynamic margin is +$E[w' \mid w,a,q]$. + +This is the object that reveals how the contract uses future utility as a +reward or punishment. + +```{code-cell} ipython3 +def expected_promise(π, W): + """Compute E[w' | w, a, q].""" + mass = π.sum(axis=(3, 4)) + total = np.einsum("x,waqcx->waq", W, π) + return np.divide(total, mass, + out=np.full_like(total, np.nan, dtype=float), + where=mass > 1e-12) +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: expected continuation promises + name: fig-rmh-promises +--- +Ew_inf = expected_promise(π, W) + + +fig, axes = plt.subplots(1, len(Q), sharey=True) +for q_i, ax in enumerate(axes): + for a_i, a in enumerate(A): + ax.plot(W, Ew_inf[:, a_i, q_i], label=f"a={a:g}", lw=2) + ax.plot(W, W, "k-.", label="45-degree line", lw=2) + ax.set_xlabel(f"w, q={Q[q_i]:g}") + ax.set_xlim([10.0, 25.0]) + ax.set_ylim([10.0, 25.0]) +axes[0].set_ylabel(r"$E(w' \mid w, a, q)$") +axes[-1].legend(title="action", loc="lower right") +fig.tight_layout() +plt.show() +``` + +This plot displays the expected next-period promise conditional on +current $w$, recommended action $a$, and realized output $q$. + +High output generally raises continuation utility and low output lowers it. + +At the endpoints of the feasible promise set, the transition stays on the +45-degree line because only the corresponding extreme plan can deliver +that endpoint. + +For the simulations, we use a higher discount factor, $\beta = 0.95$. + +The higher discount factor makes promised utility a stronger incentive +instrument and makes the evolution of individual histories easier to +see. + +We solve the infinite-horizon economy again at $\beta = 0.95$ with a +grid of $N = N_m = 50$ points. + +Starting from the static solution rescaled to discounted-sum units, the +iteration converges to tolerance $10^{-4}$. + +```{code-cell} ipython3 +β_95 = 0.95 +N_95 = 50 +N_m95 = 50 + +w_l_95 = u(A.min(), C.min()) / (1 - β_95) +w_u_95 = u(A.min(), C.max()) / (1 - β_95) +W_95 = np.linspace(w_l_95, w_u_95, N_95) + +s_W_0_95, _ = solve_static_problem(W_95 * (1 - β_95), u, A, Q, C, P, + "unobserved-actions") + +with qe.Timer(): + s_W_new, π_W_s1_new, π_W_m_s2_new, W_new = solve_multi_period_economy( + A, Q, C, P, "unobserved-actions", + β=β_95, N=N_95, N_m=N_m95, + s_W_0=s_W_0_95 / (1 - β_95), + tol=1e-4, max_iter=300, m_anderson=5, + verbose=True) +``` + +```{code-cell} ipython3 +π_new = np.einsum("waqm,mcx->waqcx", π_W_s1_new, π_W_m_s2_new) +``` + +For the simulation, a state is a current promise grid point. + +Given that state, the code draws an action, then output, then a pair +$(c,w')$ from the joint lottery. + +The next period's state is the realized $w'$. + +```{code-cell} ipython3 +def draw_from(probabilities, rng): + probabilities = np.maximum(np.asarray(probabilities, dtype=float), 0.0) + total = probabilities.sum() + if total <= 1e-12: + return rng.integers(len(probabilities)) + probabilities = probabilities / total + return min(np.searchsorted(np.cumsum(probabilities), rng.random()), + len(probabilities) - 1) + + +def simulation(W, C, s_W, T, π, seed=12345): + w_index = np.nanargmin(np.abs(s_W)) + rng = np.random.default_rng(seed) + + w_series = np.empty(T + 1) + c_series = np.empty(T) + w_series[0] = W[w_index] + + for i in range(T): + joint = np.maximum(π[w_index], 0.0) + + a_index = draw_from(joint.sum(axis=(1, 2, 3)), rng) + q_index = draw_from(joint[a_index].sum(axis=(1, 2)), rng) + + cw_prob = joint[a_index, q_index] + cw_index = draw_from(cw_prob.ravel(), rng) + c_index, w_next_index = np.unravel_index(cw_index, cw_prob.shape) + + c_series[i] = C[c_index] + w_index = w_next_index + w_series[i + 1] = W[w_index] + + return c_series, w_series +``` + +```{code-cell} ipython3 +c_series = np.zeros((80, 4)) +w_series = np.zeros((81, 4)) +for i in range(4): + c_series[:, i], w_series[:, i] = simulation( + W_new, C, s_W_new, 80, π_new, seed=(12345 + i)) +``` + +The simulations start from the grid point at which surplus is closest to +zero. + +This corresponds to the ex ante symmetric, or "fair", allocation. + +It is the highest common promised utility that can be assigned while +keeping discounted social surplus nonnegative. + +The next figure plots four simulated consumption histories from the +same initial promised utility. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: simulated consumption histories + name: fig-rmh-sim-consumption +--- +date_c = np.arange(80) + 1 +plt.figure() +plt.plot(date_c, c_series[:, 0], lw=2) +plt.plot(date_c, c_series[:, 1], lw=2) +plt.plot(date_c, c_series[:, 2], lw=2) +plt.plot(date_c, c_series[:, 3], lw=2) +plt.xlabel("date") +plt.ylabel("consumption") +plt.xlim([0, 80]) +plt.ylim([0.00, 2.25]) +plt.show() +``` + +The four consumption paths differ even though all agents begin with the +same promised utility. + +Different output histories move agents to different continuation +promises, so the contract gradually creates heterogeneous consumption +histories. + +The next figure plots the corresponding promised-utility histories. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: simulated promised utilities + name: fig-rmh-sim-promises +--- +date_w = np.arange(81) +plt.figure() +plt.plot(date_w, w_series[:, 0], lw=2) +plt.plot(date_w, w_series[:, 1], lw=2) +plt.plot(date_w, w_series[:, 2], lw=2) +plt.plot(date_w, w_series[:, 3], lw=2) +plt.xlabel("date") +plt.ylabel("expected utility") +plt.ylim([40.0, 100.0]) +plt.show() +``` + +The promised-utility paths show the state variable moving directly. + +High-output histories tend to move the agent upward, while low-output +histories move the agent downward. + +This is the dynamic incentive mechanism in the model. + +For distributional plots, it is cleaner to propagate population mass +directly instead of drawing more individual histories. + +```{code-cell} ipython3 +def population_distributions(W, C, s_W, T, π): + w_index = np.nanargmin(np.abs(s_W)) + μ = np.zeros(len(W)) + μ[w_index] = 1.0 + + π_pos = np.maximum(π, 0.0) + row_sums = π_pos.sum(axis=(1, 2, 3, 4), keepdims=True) + π_pos = np.divide(π_pos, row_sums, + out=np.zeros_like(π_pos), + where=row_sums > 1e-12) + + π_c = np.zeros((T, len(C))) + π_w = np.zeros((T, len(W))) + + for t in range(T): + joint = np.tensordot(μ, π_pos, axes=(0, 0)) + π_c[t] = joint.sum(axis=(0, 1, 3)) + μ = joint.sum(axis=(0, 1, 2)) + μ = μ / μ.sum() + π_w[t] = μ + + return π_c, π_w + + +π_c, π_w = population_distributions(W_new, C, s_W_new, 80, π_new) +``` + +The distribution calculation above keeps the whole population rather +than drawing individual sample paths. + +Starting from a point mass over $w$, it applies the optimal lottery each +period and records the implied marginal distributions of consumption and +promised utility. + +The next wireframe plots the cross-sectional consumption distribution +over time. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: consumption distribution over time + name: fig-rmh-dist-consumption +--- +date_mat_c = np.reshape(np.arange(80) + 1, (80, 1)) * \ + np.ones((1, len(C))) +c_mat = np.ones((80, 1)) @ np.reshape(C, (1, len(C))) + +fig = plt.figure() +ax = fig.add_subplot(projection='3d') +plt.xlabel('date') +plt.ylabel('consumption') +ax.set_zlabel('percentage') + +ax.set_zlim(0.0, 1.0) +ax.view_init(elev=25, azim=-65) +ax.plot_wireframe(date_mat_c, c_mat, π_c, + rstride=1, cstride=2, + color="black", linewidth=0.35) +plt.show() +``` + +The consumption distribution spreads out over time because histories +receive different rewards and punishments. + +On the finite grid, some mass eventually reaches the edges of the +feasible promise set. + +The final wireframe plots the distribution of promised utility itself. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: promise distribution over time + name: fig-rmh-dist-promises +--- +date_mat_w = np.reshape(np.arange(80) + 1, (80, 1)) * \ + np.ones((1, len(W_new))) +W_mat_12 = np.ones((80, 1)) @ np.reshape(W_new, (1, len(W_new))) + +fig = plt.figure() +ax = fig.add_subplot(projection='3d') +plt.xlabel('date') +plt.ylabel('w') +ax.set_zlabel('percentage') + +ax.set_zlim(0.0, 1.0) +ax.view_init(elev=25, azim=-65) +ax.plot_wireframe(date_mat_w, W_mat_12, π_w, + rstride=1, cstride=2, + color="black", linewidth=0.35) +plt.show() +``` + +The promise distribution is the deeper state-space picture behind the +consumption distribution. + +It shows how repeated incentives convert a common initial promise into a +distribution of continuation utilities. + +## Concluding remarks + +### Economics + +#### Moral hazard and the cost of private information + +When the principal cannot observe the agent's effort, the optimal contract +must balance two competing objectives: *insurance* (smoothing the agent's +consumption across output realizations) and *incentives* (rewarding high +output to make effort attractive). + +The hidden-effort surplus frontier lies below the full-information +frontier, and the gap between them measures the surplus cost of +unobserved effort. + +Restricting the contract to one period raises consumption variability and +lowers average output relative to the multi-period optimum, the gain visible in +{numref}`fig-rmh-surplus-compare`. + +#### Dynamic contracts and promised utility + +The recursive formulation of {cite:t}`Spear_Srivastava_87` compresses all +payoff-relevant history into a single scalar state: the discounted +expected continuation utility $w$ that the principal has promised the +agent. + +By tracking $w$ rather than the full history of outputs, the dynamic +contracting problem becomes tractable. + +In the optimal infinite-horizon contract, the principal rewards high +output by granting the agent a higher continuation utility and punishes +low output by lowering it. + +Continuation promises +therefore substitute partly for large contemporaneous consumption +spreads. + +#### Diversity over time + +The simulations illustrate the central computational message: +starting from a common initial promise, dynamic incentives generate +non-trivial individual histories and cross-sectional dispersion in +consumption and promised utility. + +On the finite grid used here, the lowest and highest promised-utility values act +as traps: once an agent's promise reaches either end, the only feasible contract +keeps it there, so the agent never moves back. + +The simulations should therefore be read as finite-grid +illustrations of how history dependence spreads the distribution over +time, not as a separate theorem about the limiting distribution. + +### Technical tricks + +#### Lotteries and convexification + +Incentive constraints can render the set of feasible contracts non-convex, +making standard optimization techniques unreliable. + +{cite:t}`Phelan_Townsend_91` circumvented this by allowing the planner to +choose a joint *lottery* $\Pi(a, q, c, w')$ over actions, outputs, +consumptions, and continuation values. + +Because any mixture of feasible lotteries is itself feasible, the +constraint set becomes convex, and global optima are well-defined. + +#### Linear programming + +With finite grids, the convexified Bellman equation is a linear program: +the objective $(q - c + \beta v(w'))$ and every constraint are linear in +$\Pi$. + +Treating $v(w')$ as a fixed vector from the previous iteration, value +function iteration reduces to solving one LP per grid point per +iteration, a task handled efficiently by modern LP solvers such as +HiGHS. + +#### Dynamic programming squared + +This lecture is closely related to what {cite:t}`Ljungqvist2012` call *dynamic programming squared*. + +The phrase refers to recursive problems in which one continuation object +is carried as a state variable inside another recursive problem. + +Here the surplus function $s(w)$, the solution to the principal's +outer dynamic program, has the agent's continuation utility $w$ as its +state variable, while feasible movements in $w$ are governed by +promise-keeping and incentive constraints. + +The same architecture reappears throughout this lecture series. + +In {doc}`Stackelberg plans ` the Stackelberg leader's +value function takes the followers' competitive-equilibrium value +function as an argument. + +In {doc}`Optimal Taxation with State-Contingent Debt `, +a Ramsey planner's outer Bellman equation uses the household's +marginal utility of wealth $x$, itself defined by an inner +implementability constraint, as its state variable. + +In the {doc}`Calvo model ` and the two Chang lectures +({doc}`Ramsey plans ` and +{doc}`credible policies `), a government's value +function takes the private sector's continuation value $\theta$ +as an argument, with $\theta$ governed by its own Bellman equation. + +In {doc}`Unemployment Insurance ` the planner's +contract-design problem embeds the worker's continuation utility +as the state variable in an outer surplus-maximization program, +producing a closely related nested recursive structure. + +In all of these settings, the inner dynamic program defines a +state variable (a promised utility, a marginal value, or a +continuation value) that restricts what the outer dynamic +program can promise or deliver. + +The same recursive idea --- carry promised utility as the state and restrict which +promises the contract may offer --- reappears in the {doc}`atkeson_1991` lecture. + +There a sovereign borrower can repudiate its debt and walk away to autarky at any +time, so the contract must always promise at least the value of that outside +option. + +## Exercises + +```{exercise-start} +:label: repeat_mh_ex1 +``` + +Using the surplus arrays `s_W_full` and `s_W_unobs` computed in the +static section, define the **agency cost function** + +$$ +\delta(w) = s^{FI}(w) - s^{UA}(w), \quad w \in W_{static}. +$$ + +1. Plot $\delta(w)$ over $W_{static} = [1, 5]$. +2. Report the value $\hat{w}$ at which $\delta$ is largest. +3. Explain intuitively why agency costs are highest at that level of + promised utility. +```{exercise-end} +``` + +```{solution-start} repeat_mh_ex1 +:label: repeat_mh_ex1_sol +:class: dropdown +``` + +```{code-cell} ipython3 +δ_W = s_W_full - s_W_unobs + +plt.figure() +plt.plot(W_static, δ_W, lw=2) +plt.xlabel("w") +plt.ylabel(r"$\delta(w) = s^{FI}(w) - s^{UA}(w)$") +plt.xlim([1.0, 5.0]) +plt.ylim(bottom=0.0) +plt.title("agency cost function") +plt.show() + +max_i = np.nanargmax(δ_W) +w_hat = W_static[max_i] +print(f"Largest agency cost at w = {w_hat:.3f}, δ = {δ_W[max_i]:.4f}") +``` + +Agency costs are highest near intermediate levels of promised utility +because at those values the principal most values inducing high effort +(output is valuable) while the agent still requires meaningful +consumption-state variation to be incentivized. + +At low $w$ the agent is near subsistence and effort is low anyway; +at high $w$ the agent is nearly fully insured and the marginal incentive +cost of each additional unit of effort is small. + +```{solution-end} +``` + +```{exercise-start} +:label: repeat_mh_ex2 +``` + +The output probability matrix $P$ governs how informative output is about +effort. +Define a **flatter** probability matrix + +$$ +P_{flat} = \begin{bmatrix} +0.70 & 0.30 \\ +0.55 & 0.45 \\ +0.45 & 0.55 \\ +0.30 & 0.70 +\end{bmatrix} +$$ + +in which output is less informative about effort than in the baseline $P$. + +1. Re-solve the static unobserved-action problem with $P_{flat}$ and the + same grid $W_{static}$. +2. On a single figure with two panels, compare the surplus functions + $s^{UA}(w)$ and expected effort levels $E\{a(w)\}$ under $P$ and + $P_{flat}$. +3. Explain the economic intuition for any differences you find. +```{exercise-end} +``` + +```{solution-start} repeat_mh_ex2 +:label: repeat_mh_ex2_sol +:class: dropdown +``` + +```{code-cell} ipython3 +P_flat = np.array([[0.70, 0.30], + [0.55, 0.45], + [0.45, 0.55], + [0.30, 0.70]]) + +s_W_flat, π_flat = solve_static_problem(W_static, u, A, Q, C, P_flat, + "unobserved-actions") +Ea_flat = np.einsum('a,waqc->w', A, π_flat) + +fig, axes = plt.subplots(1, 2) + +axes[0].plot(W_static, s_W_unobs, label="Baseline $P$", lw=2) +axes[0].plot(W_static, s_W_flat, label="Flat $P$", lw=2) +axes[0].hlines(0, 1.0, 5.0, linestyle="dashed") +axes[0].set_xlabel("w") +axes[0].set_ylabel("s(w)") +axes[0].set_xlim([1.0, 5.0]) +axes[0].legend() + +axes[1].plot(W_static, Ea_unobs, label="Baseline $P$", lw=2) +axes[1].plot(W_static, Ea_flat, label="Flat $P$", lw=2) +axes[1].set_xlabel("w") +axes[1].set_ylabel(r"$E\{a(w)\}$") +axes[1].set_xlim([1.0, 5.0]) +axes[1].set_ylim([0.0, 0.8]) +axes[1].legend() + +fig.suptitle("surplus and effort under flatter probabilities") +plt.tight_layout() +plt.show() +``` + +With $P_{flat}$ output carries less statistical information about effort: +the likelihood ratio $P(q \mid \hat{a}) / P(q \mid a)$ is closer to 1 +for all deviations $\hat{a} \neq a$. + +The incentive-compatibility constraint {eq}`eq:eq2prime` therefore becomes +harder to satisfy: large consumption rewards for high output must be +offered to deter deviations, crowding out insurance. + +As a result the principal extracts less surplus and induces less effort +than under the baseline $P$: the surplus function shifts down and +expected effort falls. + +```{solution-end} +``` diff --git a/lectures/subjective_beliefs_business_cycles.md b/lectures/subjective_beliefs_business_cycles.md new file mode 100644 index 00000000..0477c2fb --- /dev/null +++ b/lectures/subjective_beliefs_business_cycles.md @@ -0,0 +1,2474 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.17.1 +kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +--- + +(subjective_beliefs_bc)= +# Survey Data and Subjective Beliefs in Business Cycle Models + +```{index} single: Subjective Beliefs; Business Cycles +``` + +## Overview + +This lecture studies whether household survey data on macroeconomic +expectations can discipline business cycle models, following +{cite:t}`bhandari2025survey`. + +A central finding is that household forecasts of unemployment and inflation +exhibit **systematic upward biases** relative to rational forecasts. + +These biases, called *belief wedges*, are: + +* **Persistent and countercyclical**: they are larger during recessions. +* **Positively correlated**: optimism/pessimism about unemployment and inflation + move together. +* **One-factor in structure**: a single latent state accounts for most + variation across wedges. + +We follow {cite:t}`bhandari2025survey` in interpreting this evidence using +**robust preferences** ({cite:t}`HansenSargent2001`; {cite:t}`HansenSargent2008`). + +Robust preferences provide a natural way to model pessimism and optimism: +a pessimistic agent acts as if states that deliver low continuation values are +more likely than they really are, while an optimistic agent overweights states +that deliver high continuation values. + +This is done through the distortion studied in {doc}`five_preferences`. + +When calibrated to the Michigan Survey of +Consumers (1982Q1-2019Q4), this mechanism yields a time-varying *belief shock* +that substantially reduces the **unemployment volatility puzzle** --- +the fact that standard New Keynesian models with only technology and monetary +policy shocks generate far too little unemployment volatility. + +In this lecture, we will cover: + +* How to define and measure belief wedges from household survey data. +* Why optimal pessimism is a mean shift of the shock distribution, + proportional to the shock's exposure to continuation values. +* How belief distortions propagate through a linearized DSGE model. +* Why a calibrated belief shock helps resolve the unemployment volatility + puzzle. + +We start with the following imports + +```{code-cell} ipython3 +import datetime +from typing import NamedTuple + +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +import matplotlib.dates as mdates +from scipy.linalg import solve_discrete_lyapunov +from scipy.stats import norm +``` + +## Measuring belief wedges + +### Definition + +Let $E_t[\cdot]$ denote expectations under the **data-generating** (objective) +probability measure and $\tilde{E}_t[\cdot]$ denote **subjective** (survey) +expectations. + +For any scalar variable $z_{t+1}$, the **one-period belief wedge** is + +$$ + +\Delta_t^{(1)}(z) \;=\; \tilde{E}_t[z_{t+1}] - E_t[z_{t+1}]. + +$$ + +A positive wedge means households expect $z_{t+1}$ to be higher than the +data-generating forecast. + +For unemployment and inflation, this sign convention corresponds to an upward +forecast bias. + +The Michigan Survey asks about outcomes one year ahead, so the empirical +objects below are four-quarter wedges + +$$ + +\Delta_t^{(4)}(z) += \tilde E_t[z_{t+4}] - E_t[z_{t+4}]. + +$$ + +We work mostly with the one-period wedge because it is the cleanest way to +explain the theory; the appendix derives the multi-period version. + +In the data, $\tilde{E}_t[\cdot]$ is measured from the Michigan Survey of +Consumers. + +The benchmark $E_t[\cdot]$ is computed from a quarterly VAR, with Survey of +Professional Forecasters (SPF) forecasts used as a robustness check. + +In the structural model, the same object is interpreted as the difference +between subjective and data-generating expectations. + +Thus the lecture uses one object in two related ways: empirically, a belief +wedge is a survey forecast minus a statistical benchmark forecast; in the +model, it is a subjective expectation minus an objective expectation. + +The raw Michigan unemployment question is categorical, so {cite:t}`bhandari2025survey` +convert it into a quantitative forecast using the Carlson--Parkin procedure as +adapted by {cite:t}`MankiwReisWolfers2003`. + +### Replicating the wedges + +{cite:t}`bhandari2025survey` document the properties of the belief wedges on +the sample 1982Q1--2019Q4, and we now replicate that evidence. + +The raw inputs are extracted from the paper's publicly +available replication package +([doi:10.5281/zenodo.10194324](https://doi.org/10.5281/zenodo.10194324), +licensed CC-BY-4.0). + +The first file contains the quarterly macroeconomic series for the +forecasting VAR. + +(Monthly series are averaged to quarterly frequency.) + +The second file contains monthly Michigan Survey aggregates: the mean +one-year-ahead inflation forecast and the shares of households answering +"more unemployment," "about the same," and "less unemployment," together with +the monthly unemployment rate. + +```{code-cell} ipython3 +data_path = '_static/lecture_specific/subjective_beliefs_business_cycles/' +macro_q = pd.read_csv(data_path + 'bbh_macro_quarterly.csv', + index_col='YYYYQ') +mich_m = pd.read_csv(data_path + 'bbh_michigan_monthly.csv', + index_col='yyyymm') +``` + +Quarters are indexed as `YYYYQ`, so `19821` means 1982Q1, and months as +`yyyymm`. + +#### The VAR benchmark forecast + +The data-generating forecast $E_t[\cdot]$ comes from a quarterly VAR with two +lags in nine variables: + + - CPI inflation over the past year, + - annualized real GDP growth, + - the unemployment rate, + - the log change in the relative price of investment goods, + - capacity utilization, + - log hours worked per capita, + - the consumption rate, + - the investment rate, and + - the federal funds rate. + +```{code-cell} ipython3 +q = macro_q +var_data = pd.DataFrame({ + 'infl_yoy': 100 * (q.CPIAUCSL / q.CPIAUCSL.shift(4) - 1), + 'gdp_gr': 400 * np.log(q.GDPC1 / q.GDPC1.shift(1)), + 'unrate': q.UNRATE, + 'dpiric': 100 * np.log(q.PIRIC / q.PIRIC.shift(1)), + 'cumfns': q.CUMFNS, + 'hours_pc': 100 * np.log(q.PRS85006023 * q.CE16OV / q.CNP16OV), + 'cons_r': 100 * (q.PCEND + q.PCESV) / q.GDP, + 'inv_r': 100 * q.GPDI / q.GDP, + 'ffr': q.FEDFUNDS, +}) +output_gap = 100 * np.log(q.GDPC1 / q.GDPPOT) +``` + +The VAR is estimated by least squares on 1960Q1--2019Q4, and forecasts are +iterated four quarters ahead from every quarter. + +```{code-cell} ipython3 +def var_forecasts(data, first=19601, last=20194, horizon=4): + """OLS VAR(2) and iterated `horizon`-step-ahead forecasts.""" + X, idx = data.values, data.index.values + est = (idx >= first) & (idx <= last) + Y_est = np.array([X[t] for t in range(2, len(idx)) if est[t]]) + X_est = np.array([np.concatenate([X[t-1], X[t-2], [1.0]]) + for t in range(2, len(idx)) if est[t]]) + B = np.linalg.lstsq(X_est, Y_est, rcond=None)[0] + + forecast = np.full_like(X, np.nan) + for t in range(1, len(idx)): + z1, z2 = X[t], X[t-1] + if np.isnan(z1).any() or np.isnan(z2).any(): + continue + for h in range(horizon): + z1, z2 = np.concatenate([z1, z2, [1.0]]) @ B, z1 + forecast[t] = z1 + return pd.DataFrame(forecast, index=idx, columns=data.columns) + +E_t4 = var_forecasts(var_data) # E_t[x_{t+4}], info through quarter t +``` + +#### From categorical answers to a forecast + +The Michigan unemployment question is categorical, so we convert the answer +shares into a mean forecast with the Carlson--Parkin procedure. + +Assume household forecasts of the change in unemployment over the next year +are normally distributed across households, $N(\mu_t, \sigma_t^2)$, and that a +household answers "about the same" when its forecast lies in $[-a, a]$. + +The shares answering "more" ($q_t^u$) and "less" ($q_t^d$) then satisfy + +$$ + +q_t^u = 1 - \Phi\!\left(\frac{a - \mu_t}{\sigma_t}\right), +\qquad +q_t^d = \Phi\!\left(\frac{-a - \mu_t}{\sigma_t}\right), + +$$ + +where $\Phi$ is the standard normal cdf, and inverting the two equations +gives + +$$ + +\sigma_t = \frac{2a}{\Phi^{-1}(1 - q_t^u) - \Phi^{-1}(q_t^d)}, +\qquad +\mu_t = a - \sigma_t\, \Phi^{-1}(1 - q_t^u). + +$$ + +The threshold $a$ scales the whole series; {cite:t}`bhandari2025survey` pin it +down by comparing the implied cross-sectional dispersion with that of SPF +forecasts. + +We set $a = 1.045$, which reproduces their fitted forecast series. + +```{code-cell} ipython3 +def carlson_parkin(share_more, share_less, a=1.045): + """Mean forecast implied by categorical shares (normal cross-section).""" + z_up, z_down = norm.ppf(1 - share_more), norm.ppf(share_less) + σ = 2 * a / (z_up - z_down) + return a - σ * z_up +``` + +#### Constructing the wedges + +Timing follows the paper: responses from the first month of quarter $t+1$ are +treated as forecasts made with information through quarter $t$. + +The unemployment *level* forecast adds the expected change to the +unemployment rate in the month the forecast is made. + +Each wedge then subtracts the corresponding VAR forecast: year-over-year +inflation, and the unemployment rate four quarters ahead. + +Both wedges are measured in percentage points (pp), the unit we use for them +throughout the lecture. + +```{code-cell} ipython3 +def build_wedges(mich_m, E_t4, first=19821, last=20194): + """Survey minus VAR forecasts for unemployment and inflation.""" + rows = [] + for yq in E_t4.index: + if not first <= yq <= last: + continue + y, qq = yq // 10, yq % 10 + mm = (y + (qq == 4)) * 100 + (qq % 4) * 3 + 1 # 1st month of qtr t+1 + s = mich_m.loc[mm] + total = s.share_more + s.share_same + s.share_less + du = carlson_parkin(s.share_more / total, s.share_less / total) + rows.append((yq, + s.unrate + du - E_t4.loc[yq, 'unrate'], + s.px1_mean - E_t4.loc[yq, 'infl_yoy'])) + return pd.DataFrame(rows, columns=['YYYYQ', 'unemp', 'infl'] + ).set_index('YYYYQ') + +wedges = build_wedges(mich_m, E_t4) +wedge_u, wedge_π = wedges.unemp, wedges.infl +W = np.column_stack([wedge_u, wedge_π]) +eigvals = np.linalg.eigvalsh(np.cov(W, rowvar=False)) +pc1_share = eigvals[-1] / eigvals.sum() + +# Quarterly dates and NBER recessions for plotting +quarters = [datetime.date(yq // 10, 3 * (yq % 10) - 2, 1) + for yq in wedges.index] + + +def fred_recession_spans(start, end): + """NBER recession spans from FRED's monthly USREC indicator.""" + fetch_start = pd.Timestamp(start) - pd.DateOffset(years=1) + fetch_end = pd.Timestamp(end) + pd.DateOffset(months=1) + rec = pd.read_csv( + 'https://fred.stlouisfed.org/graph/fredgraph.csv?id=USREC', + parse_dates=['observation_date'], + index_col='observation_date' + )['USREC'].loc[fetch_start:fetch_end] + rec = pd.to_numeric(rec, errors='coerce').fillna(0).astype(bool) + + starts = rec.index[rec & ~rec.shift(fill_value=False)] + ends = rec.index[~rec & rec.shift(fill_value=False)] + if rec.iloc[-1]: + ends = ends.append(pd.DatetimeIndex([rec.index[-1] + + pd.offsets.MonthBegin()])) + return [(s.date(), e.date()) for s, e in zip(starts, ends)] + + +recessions = fred_recession_spans(quarters[0], quarters[-1]) +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: replicated belief wedges, 1982Q1-2019Q4 + name: fig-sbbc-belief-wedges +--- +fig, axes = plt.subplots(2, 1, figsize=(11, 6), sharex=True) + +axes[0].plot(quarters, wedge_u, color='steelblue', linewidth=2, + label='Unemployment belief wedge') +axes[0].axhline(np.mean(wedge_u), color='steelblue', linestyle='--', + linewidth=0.8, alpha=0.7) +axes[0].set_ylabel('percentage points') +axes[0].legend(loc='upper left') + +axes[1].plot(quarters, wedge_π, color='darkorange', linewidth=2, + label='Inflation belief wedge') +axes[1].axhline(np.mean(wedge_π), color='darkorange', linestyle='--', + linewidth=0.8, alpha=0.7) +axes[1].set_ylabel('percentage points') +axes[1].legend(loc='upper left') + +for ax in axes: + for start, end in recessions: + ax.axvspan(start, end, color='grey', alpha=0.25, linewidth=0) + ax.axhline(0, color='black', linewidth=0.6, linestyle=':') + ax.xaxis.set_major_locator(mdates.YearLocator(5)) + ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y')) + ax.set_xlim(quarters[0], quarters[-1]) + +plt.tight_layout() +plt.show() +``` + +Both wedges are positive most of the time and both rise during the shaded +NBER recessions. + +It suggests that households persistently +overpredict unemployment and inflation. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: one-factor structure of belief wedges + name: fig-sbbc-wedge-scatter +--- +fig, ax = plt.subplots() +sc = ax.scatter(wedge_u, wedge_π, c=range(len(wedges)), cmap='RdYlGn_r', + alpha=0.7, s=20) +plt.colorbar(sc, ax=ax, label='quarter (dark = recent)') +ax.set_xlabel('unemployment wedge (pp)') +ax.set_ylabel('inflation wedge (pp)') +corr = np.corrcoef(wedge_u, wedge_π)[0, 1] +ax.text(0.05, 0.90, + f'correlation = {corr:.2f}\nPC1 share = {pc1_share:.3f}', + transform=ax.transAxes, fontsize=11) +plt.tight_layout() +plt.show() +``` + +The scatter plot shows the one-factor structure. + +Each point is one quarter, with the horizontal coordinate equal to the +unemployment wedge and the vertical coordinate equal to the inflation wedge. + +The points form an upward-sloping cloud rather than a line: a common +pessimism factor drives both wedges, while survey noise and other +idiosyncratic variation keep them from being collinear. + +The reported PC1 share of 0.809 is computed from the raw covariance matrix of +the two replicated wedges, so it reflects both their correlation and the +larger variance of the inflation wedge. + +{cite:t}`bhandari2025survey` report 78.6% under their preferred +normalization; depending on the normalization, the first component explains +roughly 79--81% of the joint variation. + +### Empirical facts + +The figures above show three key empirical facts about the belief wedges: + +- Both wedges are **positive on average**: households expect higher unemployment +and higher inflation than the rational forecast. + +- Both wedges are **countercyclical**: they rise during every NBER recession in +the sample. + +- The wedges are **positively correlated and share one dominant factor**: the +first principal component explains about four-fifths of their joint +variation. + +A positive unemployment wedge is naturally read as pessimism, since +unemployment is high in bad times. + +The positive inflation wedge carries the same interpretation because +households regard high inflation as a feature of bad times. + +The same one-factor pattern appears in the cross section. + +This evidence supports the interpretation that the wedges reflect a common +pessimism/optimism component rather than two unrelated forecast mistakes. + +These moments are the calibration targets for the belief shock $\theta_t$, +the pessimism parameter formalized in the next section. + +The code below also defines two *wedge loadings*, $c_u$ and $c_\pi$, that the +model illustrations later in the lecture use to map $\theta_t$ into wedges. + +In the full model these loadings are endogenous equilibrium objects, +covariances between shocks and continuation values, but here we set them +directly so that the implied wedges equal the empirical means of 0.52 and +1.22 pp at $\theta_t = \mu_\theta$. + +```{code-cell} ipython3 +# Belief-shock calibration from the paper +μ_θ = 5.64 # mean of belief-shock parameter θ +ρ_θ = 0.714 # AR(1) persistence: autocorrelation of the wedges' first PC +σ_θ = 4.3 # innovation volatility + +# Wedge loadings used later in the model illustrations +# In the full model these are equilibrium objects +c_u = 0.52 / μ_θ +c_π = 1.22 / μ_θ +``` + +## A model of pessimism + +Before turning to the theory, the table below collects the notation used in +the rest of the lecture. + +| Symbol | Meaning | +|---|---| +| $x_t$ | state (a vector in general; log consumption in the scalar example) | +| $\bar x$ | steady state of the state; $x_{1t}$ is the first-order deviation from $\bar x$ | +| $w_{t+1}$ | standard normal innovation under the data-generating measure | +| $m_{t+1}$ | likelihood ratio that distorts the data-generating measure | +| $\theta_t$ | belief factor: $\theta_t > 0$ is pessimism, $\theta_t < 0$ is optimism | +| $\bar\theta$ | loading of the belief factor on the state, $\theta_t = \bar\theta x_t$ | +| $\mu_\theta$, $\rho_\theta$, $\sigma_\theta$ | mean ($\mu_\theta = \bar\theta \bar x$), persistence, and innovation volatility of $\theta_t$ | +| $v_t$, $v_x$, $v_q$ | continuation value, its slope in the state, and its constant term | +| $\nu_t$ | subjective mean shift of the innovation $w_{t+1}$ | +| $\Delta_t^{(\tau)}(z)$ | $\tau$-period belief wedge for variable $z$ | + +### Robust preferences + +Why would households have systematically biased beliefs? + +One answer comes from **robust control** or **multiplier preferences** +({cite:t}`HansenSargent2001`, {cite:t}`HansenSargent2008`). + +Recall that an agent represented by multiplier preferences solves + +$$ +v_t \;=\; \min_{\substack{m_{t+1} > 0 \\ E_t[m_{t+1}] = 1}} +\Bigl\{ + u(x_t) + + \beta E_t\!\left[m_{t+1} v_{t+1}\right] + + \frac{\beta}{\theta_t}\, E_t\!\left[m_{t+1} \log m_{t+1}\right] +\Bigr\}. +$$ + +Here $m_{t+1}$ is a **likelihood ratio** (Radon–Nikodym derivative) that +distorts the reference measure, and the last term is an entropy penalty that +keeps the distortion from being too extreme. + +Assume state vector $x_t \in \mathbb{R}^n$ follows a Markov law of motion + +$$ +x_{t+1} = \psi(x_t, w_{t+1}), +$$ + +where $w_{t+1} \sim N(0, I_k)$ is i.i.d. under the data-generating measure, +and the penalty parameter is linear in the state: + +$$ +\theta_t = \bar\theta x_t, +$$ + +for a $1 \times n$ vector of parameters $\bar\theta$. + +The scalar $\theta_t$ controls the direction and strength of the belief tilt. + +The minimization problem above corresponds to $\theta_t > 0$: larger +$\theta_t$ means more pessimism. + +Because $\theta_t$ is linear in the state, it can turn negative, in which case +the inner problem becomes a maximization that tilts probability toward +high-continuation-value states, which corresponds to optimism. + +The inner minimization has a closed-form solution. + +Minimizing $E_t[m_{t+1} v_{t+1}] + \frac{1}{\theta_t} E_t[m_{t+1} \log m_{t+1}]$ +state by state subject to $E_t[m_{t+1}] = 1$ gives the first-order condition + +$$ +v_{t+1} + \frac{1}{\theta_t}\left(\log m_{t+1} + 1\right) + \lambda_t = 0, +$$ + +where $\lambda_t$ is the multiplier on the constraint, so +$m_{t+1} \propto \exp(-\theta_t v_{t+1})$ and the constraint pins down the +normalization: + +$$ +m_{t+1}^* \;=\; +\frac{\exp(-\theta_t v_{t+1})}{E_t[\exp(-\theta_t v_{t+1})]}. +$$ + +Since $m_{t+1}^*$ assigns higher weight to states where $v_{t+1}$ is low (bad +outcomes), pessimistic agents effectively over-weight recessions in their +probability assessments. + +Substituting $m_{t+1}^*$ back into the recursion gives the equivalent +**risk-sensitive** form + +$$ +v_t = u(x_t) - \frac{\beta}{\theta_t} +\log E_t\!\left[\exp(-\theta_t v_{t+1})\right], +$$ + +which replaces the expected continuation value with a soft minimum: as +$\theta_t \to 0$ it reduces to $u(x_t) + \beta E_t[v_{t+1}]$, and as +$\theta_t \to \infty$ it approaches the worst case over states in a bounded +or finite-state setting. + + +(With unbounded Gaussian shocks, the soft minimum +instead falls without bound.) + +In the robust-control literature, this distortion expresses fear of model +misspecification. + +{cite:t}`bhandari2025survey` instead take the recursion as a model of +pessimism and optimism, and let survey data discipline the process for +$\theta_t$. + +Survey data also resolve an identification problem. + +With log period utility, these preferences are mathematically equivalent to +Epstein--Zin preferences with time-varying risk aversion +$\gamma_t = \theta_t + 1$, so asset prices and macroeconomic aggregates alone +cannot distinguish time-varying pessimism from time-varying risk premia. + +Survey forecasts can: fluctuations in rational risk premia leave forecasts +unbiased, whereas subjective beliefs show up directly as belief wedges. + +### Connection to the belief wedge + +The belief wedge is the expected deviation between subjective and objective +forecasts. + +Using $\tilde{E}_t[\cdot] = E_t[m_{t+1}^* \cdot]$: + +$$ +\Delta_t^{(1)}(z) += \tilde{E}_t[z_{t+1}] - E_t[z_{t+1}] += E_t\!\left[m_{t+1}^* z_{t+1}\right] - E_t[z_{t+1}] += \operatorname{Cov}_t(m_{t+1}^*, z_{t+1}). +$$ + +The last equality holds because $E_t[m_{t+1}^*] = 1$, so +$E_t[m^*_{t+1} z_{t+1}] - E_t[z_{t+1}] += E_t[m^*_{t+1} z_{t+1}] - E_t[m^*_{t+1}]\,E_t[z_{t+1}]$. + +So the belief wedge equals the covariance between the distorted likelihood +ratio and the variable of interest. + +When $v_{t+1}$ is high in states where +$z_{t+1}$ is also high, $m_{t+1}^*$ will be low in those states, making the +covariance negative (i.e. the agent *underestimates* good-state variables). + +For unemployment (which varies inversely with good economic outcomes), the +wedge is positive: pessimists expect higher unemployment than the model predicts. + +### Illustration: optimal belief distortion + +We now compute, in the smallest model that can carry them, the +risk-sensitive recursion, the optimal distortion $m_{t+1}^*$, and the belief +wedge in closed form. + +The payoff is the central formula of the lecture: optimal pessimism is a +**mean shift** of the shock distribution, equal to $-\theta_t$ times the +exposure of the continuation value to the shock. + +Consider an endowment economy in which log consumption $x_t$ follows the +linear process below. + +$$ +x_{t+1} = \rho_x x_t + \sigma_x w_{t+1}, \qquad w_{t+1} \sim N(0,1). +$$ + +Here $\rho_x$ is the persistence of the state, $\sigma_x$ is the standard +deviation of its innovation, and period utility is $u(x_t) = (1 - \beta)x_t$ +for log utility in consumption. + +The calculation has four steps: + +1. guess an affine continuation value; +2. evaluate the risk-sensitive recursion in closed form; +3. derive the optimal belief distortion and the wedge it implies; +4. solve for the value-function slope, first with a fixed $\theta$ and then + with a state-dependent $\theta_t$. + +Steps 1--3 treat the current value of $\theta_t$ as given. + +With linear dynamics, Gaussian shocks, and linear utility, the continuation +value is affine in the state, so we guess $v_t = v_x x_t + v_q$ and verify +the guess by substitution. + +(The affine guess with constant coefficients is exact when $\theta$ is +constant; when $\theta_t$ varies over time, it requires the first-order +approximation of {cite:t}`bhandari2025survey`.) + +The slope $v_x = \partial v_t / \partial x_t$ measures how much the agent +values an extra unit of the state. + +We solve it from the recursion in Step 4. + +Since $-\theta_t v_{t+1} = -\theta_t(v_x \rho_x x_t + v_q) - \theta_t v_x +\sigma_x w_{t+1}$ is linear in the standard normal shock, the moment +generating function $E[\exp(a w)] = \exp(a^2/2)$ evaluates the expectation in +closed form: + +$$ +-\frac{1}{\theta_t} \log E_t\!\left[\exp(-\theta_t v_{t+1})\right] += v_x \rho_x x_t + v_q - \frac{\theta_t}{2}\, v_x^2 \sigma_x^2. +$$ + +Pessimism subtracts half of $\theta_t$ times the conditional variance of +continuation values from the ordinary expectation, so it acts like an +endogenous discount on risky continuation utilities. + + +The same linearity pins down the optimal distortion. + +Writing $\alpha_t = -\theta_t v_x \sigma_x$, the likelihood ratio becomes + +$$ +m_{t+1}^* = \frac{\exp(\alpha_t w_{t+1})} + {E_t[\exp(\alpha_t w_{t+1})]} += \exp\!\left(\alpha_t w_{t+1} - \tfrac{1}{2} \alpha_t^2\right), +$$ + +which is exactly the density of $N(\alpha_t, 1)$ divided by the density of +$N(0, 1)$. + +Pessimism is therefore a **mean shift**: under the subjective measure, + +$$ +w_{t+1} \;\sim\; N(\nu_t, 1), +\qquad +\nu_t = -\theta_t v_x \sigma_x. +$$ + +The drift $\nu_t$ is the negative of the pessimism parameter times the +exposure of the continuation value to the shock. + +The agent tilts beliefs exactly in the +direction that hurts most, by an amount the entropy penalty limits. + +The belief wedge for the state follows immediately: + +$$ +\Delta_t^{(1)}(x) = \tilde E_t[x_{t+1}] - E_t[x_{t+1}] += \sigma_x \nu_t = -\theta_t v_x \sigma_x^2. +$$ + +Here $\tilde E_t$ denotes expectation under the subjective measure implied by +$m_{t+1}^*$, while $E_t$ denotes expectation under the data-generating measure. + +When $v_x > 0$ (high consumption states are good) and $\theta_t > 0$ +(pessimism), the wedge is negative, so the agent *underestimates* future +consumption. + +For a variable that enters the value function with a negative sign, such as +unemployment, the same pessimism generates a *positive* wedge. + +It remains to pin down the slope $v_x$ (Step 4), and here the distinction +between a fixed and a state-dependent pessimism parameter matters. + +*Case 1: fixed $\theta$.* + +Suppose first that $\theta_t = \theta$ is a constant. + +Substituting the closed-form recursion back into the Bellman equation gives + +$$ +v_x x_t + v_q += (1 - \beta)\, x_t ++ \beta\left(v_x \rho_x x_t + v_q - \frac{\theta}{2}\, v_x^2 \sigma_x^2\right). +$$ + +The variance penalty $-\frac{\theta}{2} v_x^2 \sigma_x^2$ does not involve +$x_t$, so matching coefficients on $x_t$ gives the rational-expectations +slope $v_x = u_x / (1 - \beta\rho_x)$ with $u_x = 1 - \beta$, while the +penalty only lowers the constant $v_q$. + +With constant pessimism, the agent tilts beliefs, but the marginal value of +the state is unchanged. + +*Case 2: state-dependent $\theta_t$.* + +Now suppose, as in {cite:t}`bhandari2025survey`, that pessimism moves with +the state, + +$$ +\theta_t = \bar\theta(\bar x + x_t), +$$ + +where $\bar x$ is the steady state and $x_t$ the deviation from it; we +normalize $\bar x = 1$, so that the steady-state pessimism level is +$\mu_\theta = \bar\theta \bar x = \bar\theta$. + +The variance penalty is now proportional to the state, + +$$ +-\frac{\beta}{2}\,\bar\theta(\bar x + x_t)\, v_x^2 \sigma_x^2, +$$ + +so it contributes to the coefficient on $x_t$, and matching coefficients +yields the **Riccati equation** + +$$ +v_x = u_x + \beta \rho_x v_x - \frac{\beta}{2}\,\mu_\theta\, \sigma_x^2 v_x^2, +\qquad u_x = 1 - \beta. +$$ + +The quadratic term is the price of state-dependent pessimism: it lowers the +marginal value of the state relative to the rational-expectations value +$v_x^{RE} = u_x / (1 - \beta\rho_x)$. + +If $\theta_t$ were fixed, the same variance penalty would affect only the +constant term, as in Case 1; the Riccati term in the slope exists precisely +because pessimism varies with the state. + +We now turn this illustration into code, building it up from small pieces. + +Because the quantitative model uses the state-dependent specification, the +code implements Case 2. + +The first ingredient is the slope $v_x$ of the continuation value. + +It solves the scalar Riccati equation, which we write as a quadratic +$a v_x^2 + b v_x + c = 0$ and solve with the quadratic formula. + +We keep the root that collapses to the rational-expectations value +$v_x^{RE} = u_x / (1 - \beta\rho_x)$ as the pessimism parameter $\mu_\theta \to 0$. + +```{code-cell} ipython3 +def solve_vx(β, ρ_x, σ_x, μ_θ): + """ + Solve the scalar Riccati equation for the value-function slope vx: + + vx = u_x - (β/2) μ_θ σ_x**2 vx**2 + β ρ_x vx, with u_x = 1 - β. + """ + u_x = 1.0 - β # marginal utility of log consumption + vx_re = u_x / (1.0 - β * ρ_x) # rational-expectations (θ = 0) value + + # Coefficients of a vx**2 + b vx + c = 0 + a = 0.5 * β * σ_x**2 * μ_θ + b = 1.0 - β * ρ_x + c = -u_x + + if abs(a) < 1e-14: # no pessimism: equation is linear + return vx_re + + disc = b**2 - 4.0 * a * c + if disc < 0: # no real root: fall back to RE + return vx_re + + # Keep the root closest to the rational-expectations value + r1 = (-b + np.sqrt(disc)) / (2.0 * a) + r2 = (-b - np.sqrt(disc)) / (2.0 * a) + return r1 if abs(r1 - vx_re) < abs(r2 - vx_re) else r2 +``` + +We store the primitives in a `NamedTuple`, together with the solved slope +$v_x$, and use `create_belief_model` to build an instance. + +```{code-cell} ipython3 +class BeliefModel(NamedTuple): + β: float # discount factor + ρ_x: float # persistence of log consumption + σ_x: float # volatility of the consumption innovation + μ_θ: float # mean of the belief-shock parameter θ + ρ_θ: float # AR(1) persistence of θ + σ_θ: float # volatility of the θ innovation + vx: float # slope of the linearized continuation value + + +def create_belief_model(β=0.994, ρ_x=0.85, σ_x=0.005, + μ_θ=5.64, ρ_θ=0.714, σ_θ=4.3): + """Build a belief model, solving the Riccati equation for vx.""" + vx = solve_vx(β, ρ_x, σ_x, μ_θ) + return BeliefModel(β=β, ρ_x=ρ_x, σ_x=σ_x, + μ_θ=μ_θ, ρ_θ=ρ_θ, σ_θ=σ_θ, vx=vx) +``` + +Two functions map a value of $\theta_t$ into the implied distortion. + +The drift $\nu_t = -\theta_t v_x \sigma_x$ is the mean shift of the shock under +the subjective measure; the wedge $\Delta_t^{(1)}(x) = \sigma_x \nu_t$ is the +resulting forecast bias for the state. + +```{code-cell} ipython3 +def belief_drift(model, θ): + """Mean shift of the shock under subjective beliefs: ν = -θ vx σ_x.""" + return -θ * model.vx * model.σ_x + + +def belief_wedge(model, θ): + """One-period belief wedge for the state: Δ = σ_x ν = -θ vx σ_x**2.""" + return model.σ_x * belief_drift(model, θ) +``` + +A last helper simulates the AR(1) belief shock $\theta_t$. + +This is a third specification of pessimism, distinct from the fixed $\theta$ +of Case 1 and the state-dependent $\theta_t$ of Case 2: the quantitative +model treats $\theta_t$ as an exogenous AR(1) process calibrated to the +survey wedges, and the appendix shows how it fits into the perturbation +solution. + +```{code-cell} ipython3 +def simulate_θ(model, T=200, seed=42): + """Simulate the AR(1) belief shock θ_t.""" + rng = np.random.default_rng(seed) + θ = np.zeros(T) + θ[0] = model.μ_θ + for t in range(1, T): + θ[t] = ((1 - model.ρ_θ) * model.μ_θ + + model.ρ_θ * θ[t-1] + + model.σ_θ * rng.standard_normal()) + return θ +``` + +Building the model at the baseline calibration, we compare the robust slope +$v_x$ with its rational-expectations counterpart, and report the mean belief +drift and wedge. + +```{code-cell} ipython3 +model = create_belief_model() + +vx_re = (1 - model.β) / (1 - model.β * model.ρ_x) +print(f"RE value of v_x: {vx_re:.8f}") +print(f"Robust value of v_x: {model.vx:.8f}") +print(f"Belief drift at θ_bar: ν = {belief_drift(model, model.μ_θ):.5f} " + "(standard deviations of w)") +print(f"Belief wedge at θ_bar: Δ = {belief_wedge(model, model.μ_θ) * 100:.5f} " + "(% of consumption)") +``` + +Both the drift and the wedge are tiny at this calibration: log consumption +has a small innovation standard deviation, so the exposure $v_x \sigma_x$ of +the continuation value to the shock is small, and the entropy penalty allows +only a small tilt. + +In the full model, the corresponding exposures of continuation values to +shocks are much larger, and they generate the percentage-point wedges seen in +the surveys. + +The next figure illustrates the tilt. + +Because the true drift $\nu_t = -\theta_t v_x \sigma_x \approx -0.001$ is +invisible on a density plot, the figure instead shifts each curve by the +**scaled drift** $\nu_t / \sigma_x = -\theta_t v_x$, magnifying the true +shift by a factor of $1/\sigma_x = 200$. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: objective and subjective shock distributions (drift scaled for + visibility) + name: fig-sbbc-shock-distributions +--- +θ_vals = [0, model.μ_θ, 2 * model.μ_θ] +labels = ['θ = 0', + f'θ = θ_bar = {model.μ_θ:.1f} (mean)', + f'θ = 2θ_bar (pessimistic)'] +colors = ['black', 'steelblue', 'firebrick'] + +# True drift ν = -θ vx σ_x, and the version scaled by 1/σ_x +# that the figure plots so that the shift is visible +ν_true = [belief_drift(model, θ) for θ in θ_vals] +ν_scaled = [ν / model.σ_x for ν in ν_true] + +x_grid = np.linspace(-4, 4, 500) + +fig, ax = plt.subplots() +for μ, label, color in zip(ν_scaled, labels, colors): + ax.plot(x_grid, norm.pdf(x_grid, loc=μ), + label=label, color=color, linewidth=2) + +ax.axvline(0, color='grey', linestyle=':', linewidth=0.8) +ax.set_xlabel( + 'scaled innovation shift: ' + '$\\nu_t / \\sigma_x = -\\theta_t v_x$' +) +ax.set_ylabel('density') +ax.legend() +plt.tight_layout() +plt.show() + +print("True and scaled subjective drifts:") +for ν, ν_s, label in zip(ν_true, ν_scaled, labels): + print(f" {label:35s} ν = {ν:9.5f} ν/σ_x = {ν_s:.4f}") +``` + +The figure shows how pessimism (higher $\theta_t$) shifts the perceived +distribution of future shocks to the left. + +The black curve is the objective distribution, centered at zero. + +The blue and red curves are subjective distributions for progressively larger +values of $\theta_t$, with the mean shift drawn at the scaled drift +$\nu_t / \sigma_x$ rather than at the (tiny) true drift $\nu_t$. + +An agent with $\theta_t > 0$ +believes bad shocks are more likely than they actually are. + +### Subjective dynamics + +The mean shift changes the law of motion that agents perceive. + +Substituting $w_{t+1} = \nu_t + \tilde w_{t+1}$, where +$\tilde w_{t+1} \sim N(0, 1)$ under the subjective measure, into the dynamics +of $x_t$ gives + +$$ +x_{t+1} = -\theta_t v_x \sigma_x^2 + \rho_x x_t + \sigma_x \tilde w_{t+1}. +$$ + +With the state-dependent specification of Case 2, +$\theta_t = \bar\theta(\bar x + x_t)$, collecting terms shows that subjective +beliefs change both the intercept and the slope of the perceived dynamics: + +$$ +\tilde\rho_x = \rho_x - \bar\theta\, v_x \sigma_x^2, +\qquad +\tilde\psi_q = -\bar\theta \bar{x}\, v_x \sigma_x^2. +$$ + +For a good state ($v_x > 0$), pessimism adds a negative drift. + +For a bad state such as unemployment ($v_x < 0$), the same formula raises the +subjective persistence, so pessimists believe bad times last longer. + +The code below compares objective and subjective forecast paths of the +consumption state, starting from the steady state, holding the pessimism +level fixed along the forecast path. + +```{code-cell} ipython3 +def forecast_paths(model, θ, x0=0.0, τ_max=20): + """Objective and subjective forecast paths of the state x.""" + ν = belief_drift(model, θ) # subjective mean of the shock + obj = np.empty(τ_max + 1) + subj = np.empty(τ_max + 1) + obj[0] = subj[0] = x0 + for τ in range(τ_max): + obj[τ+1] = model.ρ_x * obj[τ] + subj[τ+1] = model.ρ_x * subj[τ] + model.σ_x * ν + return obj, subj +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: objective and subjective forecasts of consumption + name: fig-sbbc-subjective-forecasts +--- +τ_max = 20 +horizons = np.arange(τ_max + 1) + +θ_levels = [model.μ_θ, 2 * model.μ_θ] +labels_f = [f'subjective, θ = θ_bar', f'subjective, θ = 2θ_bar'] +colors_f = ['steelblue', 'firebrick'] + +fig, ax = plt.subplots() +obj, _ = forecast_paths(model, 0.0, τ_max=τ_max) +ax.plot(horizons, obj * 100, color='black', linewidth=2, + label='objective forecast') +for θ, lab, c in zip(θ_levels, labels_f, colors_f): + _, subj = forecast_paths(model, θ, τ_max=τ_max) + ax.plot(horizons, subj * 100, color=c, linewidth=2, + linestyle='--', label=lab) + +ax.axhline(0, color='grey', linewidth=0.7, linestyle=':') +ax.set_xlabel('forecast horizon $\\tau$ (quarters)') +ax.set_ylabel('forecast of $x_{t+\\tau}$ (%)') +ax.legend() +plt.tight_layout() +plt.show() +``` + +Starting at the steady state, the objective forecast of consumption is flat +at zero. + +The subjective forecasts drift persistently downward, and twice as fast when +pessimism is twice as high, because the constant drift +$\sigma_x \nu_t$ accumulates at the persistence $\rho_x$. + +On average the feared bad times never arrive, so subjective forecasts are +systematically wrong, and that systematic error is exactly the belief wedge +measured in the surveys. + +This figure is the scalar version of a key picture in +{cite:t}`bhandari2025survey`: after a pessimism shock in the structural +model, households expect consumption to fall further and recover far more +slowly than it actually does. + +## Linear approximation with belief distortions + +### The perturbation method + +For quantitative analysis, {cite:t}`bhandari2025survey` extend the standard +first-order perturbation method to accommodate time-varying belief distortions. + +Let the state vector be $x_t \in \mathbb{R}^n$ with **objective** law of +motion + +$$ +x_{t+1} = \psi_q + \psi_x x_t + \psi_w w_{t+1}, \qquad +w_{t+1} \sim N(0, I_k). +$$ + +To first order, the belief factor $\theta_t = \bar\theta x_t$ equals +$\bar\theta(\bar{x} + x_{1t})$. + +Under the optimal belief distortion the shocks are re-centered: + +$$ +w_{t+1} \;\sim\; N\!\left(- \theta_t (v_x \psi_w)',\; I_k\right), +$$ + +where $v_x$ is the row vector of first derivatives of the continuation value +and $\bar{x}$ is the steady state. + +In a standard first-order perturbation, belief distortions would vanish from +the solution. + +The reason is the certainty equivalence of first-order approximations: the +expansion scales shock volatility by a parameter $\mathsf{q}$ and keeps only +terms linear in $\mathsf{q}$, so any object that works through the *variance* +of shocks --- risk premia, precautionary saving, or belief distortions --- is +second order and gets truncated away. + +The scalar example makes the orders visible. + +The optimal drift $\nu_t = -\theta_t v_x \sigma_x$ shrinks linearly with +volatility: when there is less to fear, the entropy penalty permits only a +smaller tilt. + +The implied wedge $\Delta_t^{(1)}(x) = -\theta_t v_x \sigma_x^2$ is therefore +*quadratic* in volatility --- halve $\sigma_x$ and the wedge falls by a +factor of four --- so it is one order smaller than the dynamics themselves +and drops out of a linear solution. + +A naive linearization would thus behave exactly like its +rational-expectations twin: no wedges, no belief shock, and nothing for the +survey data to discipline. + +{cite:t}`bhandari2025survey` avoid this by scaling $\theta_t$ jointly with +the shock volatility, letting it grow like $1/\mathsf{q}$ as volatility +shrinks. + +The drift $-\theta_t (v_x \psi_w)'$ then stays of order one, the wedge +becomes first order --- the same order as everything else in the linear +solution --- and the subjective law of motion survives as an object distinct +from the data-generating process; the appendix gives details. + +The wedge formula comes directly from comparing the one-step-ahead objective +and subjective conditional means. + +Under the objective measure, $E_t[w_{t+1}] = 0$, so + +$$ +E_t[x_{t+1}] = \psi_q + \psi_x x_t. +$$ + +Under the subjective measure, the shock mean is +$\tilde E_t[w_{t+1}] = -\theta_t (v_x \psi_w)'$, so + +$$ +\tilde E_t[x_{t+1}] += \psi_q + \psi_x x_t + - \theta_t \psi_w (v_x \psi_w)'. +$$ + +For any linear variable $z_t = \bar{z}' x_t$, the one-period belief wedge is +therefore + +$$ +\Delta_t^{(1)}(z) +\;=\; \tilde E_t[z_{t+1}] - E_t[z_{t+1}] +\;=\; -\theta_t\, \bar{z}' (\psi_w \psi_w') v_x'. +$$ + +Because the drift moves with $\theta_t$, subjective beliefs change both the +conditional mean and the persistence of the state: adverse states are more +persistent under the subjective measure than under the data-generating +measure. + +### Riccati equation for $v_x$ + +The key object is $v_x$, which solves + +$$ +v_x +\;=\; u_x + - \frac{\beta}{2}\, v_x \psi_w \psi_w' v_x' \bar\theta + + \beta\, v_x \psi_x. +$$ + +This is a modified Riccati equation: like the Riccati equations of +linear-quadratic control, it is quadratic in the unknown $v_x$, and the +middle term vanishes under rational expectations ($\bar\theta = 0$), leaving +the linear equation $v_x = u_x + \beta v_x \psi_x$ with the familiar solution +$v_x = u_x (I - \beta\psi_x)^{-1}$. + +Each term has an economic reading. + +The first term, $u_x$, is the marginal flow utility of the state. + +The last term, $\beta v_x \psi_x$, is the discounted marginal continuation +value: an extra unit of the state today raises next period's state by +$\psi_x$ and hence next period's value by $v_x \psi_x$. + +The middle term is the price of state-dependent pessimism: an extra unit of +state component $j$ raises the belief factor by $\bar\theta_j$, and each unit +of the belief factor discounts the continuation value by half its conditional +variance, $v_x \psi_w \psi_w' v_x'$. + +To see why the extra term has this form, focus on the continuation value. + +Locally, write it as linear in next period's state, + +$$ + +v_{t+1} \approx v_x x_{t+1}. + +$$ + +Since $x_{t+1} = \psi_q + \psi_x x_t + \psi_w w_{t+1}$, the risky part of +continuation value is $v_x \psi_w w_{t+1}$, with conditional variance + +$$ + +v_x \psi_w \psi_w' v_x'. + +$$ + +Robust preferences replace the ordinary expected continuation value with an +entropy-adjusted one. + +For a Gaussian linear payoff, the minimization over distorted beliefs gives + +$$ + +E_t[v_{t+1}] +- \frac{\theta_t}{2}\,\operatorname{Var}_t(v_{t+1}). + +$$ + +Thus the agent subtracts a variance penalty from continuation value. + +Because $\theta_t = \bar\theta x_t$, the penalty is linear in the state --- +the mechanism of Case 2 in the scalar illustration --- so matching the +coefficient on $x_t$ in the Bellman equation adds the term + +$$ + +- \frac{\beta}{2}\, + \left(v_x \psi_w \psi_w' v_x'\right)\bar\theta. + +$$ + +The term is quadratic in $v_x$ because the variance of the continuation value +depends on the square of its exposure to shocks, $v_x \psi_w$. + +Indeed, this equation is the vector version of the scalar Riccati equation: +setting $\psi_x = \rho_x$, $\psi_w = \sigma_x$, and $\bar\theta = \mu_\theta$ +(all scalars) recovers the equation solved by `solve_vx`. + +And if $\theta_t$ were a constant, the variance penalty would not depend on +$x_t$, the middle term would disappear from the coefficient-matching +equation, and only the constant term of the value function would change. + +In that case, it collapses to the linear equation of Case 1. + +### One-factor structure + +An important consequence of the formula for $\Delta_t^{(1)}(z)$ is that the +*time variation* in all belief wedges is driven by the **single scalar** belief +factor $\theta_t \approx \bar\theta(\bar{x} + x_{1t})$. + +The cross-sectional loadings $-\bar{z}'(\psi_w\psi_w')v_x'$ are +fixed by the model's structural parameters. + +The loadings are not free parameters: they equal covariances of shocks with +the continuation value, objects that the equilibrium of the model determines. + +The only free parameters describing beliefs are the three governing the +$\theta_t$ process, so every additional surveyed variable adds an +overidentifying restriction on the model. + +This theoretical prediction matches the empirical finding that one principal +component explains about four-fifths of the joint variation in the +unemployment and inflation wedges. + +The code below illustrates the one-factor structure, but with a shortcut: in +place of the model-implied loadings $-\bar{z}'(\psi_w\psi_w')v_x'$, it uses +the empirical loadings $c_u$ and $c_\pi$ defined earlier, so the lines pass +through the empirical mean wedges at $\theta = \mu_\theta$. + +In the full model the loadings are endogenous, and the paper's structural +benchmark implies mean wedges of 0.55 and 0.90 rather than the data values +0.52 and 1.22. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: wedge loadings implied by one factor + name: fig-sbbc-one-factor-loadings +--- +θ_grid = np.linspace(0, 20, 200) + +# Empirical loadings +loading_u = c_u # 0.52 / 5.64 pp per unit of θ (unemployment) +loading_π = c_π # 1.22 / 5.64 pp per unit of θ (inflation) + +wedge_u_grid = loading_u * θ_grid +wedge_π_grid = loading_π * θ_grid + +fig, axes = plt.subplots(1, 2, figsize=(11, 4)) + +axes[0].plot(θ_grid, wedge_u_grid, color='steelblue', linewidth=2, + label='$\\Delta^{(1)}(u)$') +axes[0].plot(θ_grid, wedge_π_grid, color='darkorange', linewidth=2, + label='$\\Delta^{(1)}(\\pi)$') +axes[0].axvline(μ_θ, color='grey', linestyle='--', linewidth=0.9, + label=f'$\\bar{{\\theta}} = {μ_θ}$') +axes[0].set_xlabel('belief-shock level $\\theta$') +axes[0].set_ylabel('belief wedge (pp)') +axes[0].legend() + +θ_sim = simulate_θ(model, T=400, seed=7) +wu_sim = loading_u * θ_sim +w_π_sim = loading_π * θ_sim +axes[1].scatter(wu_sim, w_π_sim, c=θ_sim, cmap='Blues', alpha=0.6, s=12) +axes[1].set_xlabel('unemployment wedge (pp)') +axes[1].set_ylabel('inflation wedge (pp)') + +plt.tight_layout() +plt.show() +``` + +The left panel plots the two one-period wedge formulas as functions of the +belief shock. + +Both lines slope upward, but the inflation line is steeper because the +calibration assigns inflation a larger loading on $\theta_t$. + +The vertical dashed line marks the average value $\bar{\theta}$, where the +lines match the empirical mean wedges of 0.52 and 1.22 percentage points. + +The right panel simulates $\theta_t$ and plots the resulting unemployment and +inflation wedges against each other. + +Since both are driven by the same scalar state, the simulated points trace out +an almost exact positive relation. + +In the data the relation is looser because survey responses contain +measurement error; a hidden-factor model that allows for such error recovers a +belief factor whose path is close to the first principal component. + +## A reduced-form emulator of the New Keynesian model + +We now use the empirical belief factor to generate impulse responses. + +The full model in {cite:t}`bhandari2025survey` is a New Keynesian model with +households, Calvo price setting, search-and-matching labor frictions, Nash +wage bargaining, TFP shocks, monetary-policy shocks, and the belief shock, +all calibrated inside the full equilibrium system. + +Beliefs matter there because consumption decisions, vacancy posting, wage +bargaining, and price setting are forward-looking. + +Rather than solve that system here, we use a small linear emulator that keeps +only the pieces needed for transparent impulse responses: + +$$ + +s_{t+1} = A\, s_t + B\, \epsilon_{t+1}, + +$$ + +where $s_t = (u_t, \pi_t, y_t, \theta_t, a_t)'$ collects unemployment, +inflation, output, the belief shock, and TFP, and +$\epsilon_{t+1} \sim N(0, I_3)$ contains the three structural shocks. + +The matrices below are chosen to reproduce selected signs and moments from +the paper; they are not obtained by solving the structural equilibrium +conditions. + +The belief shock follows the persistence estimated from the survey wedges, +$\rho_\theta = 0.714$. + +The two wedge loadings are chosen so that $c_u \mu_\theta = 0.52$ and +$c_\pi \mu_\theta = 1.22$ at $\mu_\theta = 5.64$. + +The entries that connect $\theta_t$ to $u_t$, $\pi_t$, and $y_t$ should be +read as reduced-form summaries of the full subjective-expectations channel, +calibrated to give the right signs and a reasonable scale for the +belief-shock IRF: higher pessimism raises unemployment, temporarily raises +inflation, and lowers output. + +One difference from the structural model deserves emphasis. + +Unlike the full model, this reduced-form system makes $\theta_t$ move the +wedges and the macroeconomic variables directly. + +In the structural model, $\theta_t$ matters through distorted probabilities +over payoff-relevant shocks, so the presence and propagation of fundamental +shocks are part of the mechanism. + +We index the five state variables with named constants, so that later code can +refer to, say, the belief shock as `I_THETA` rather than a bare number. + +```{code-cell} ipython3 +# Position of each variable in the state vector s_t +I_U, I_PI, I_Y, I_THETA, I_A = 0, 1, 2, 3, 4 +``` + +The object below stores the transition matrix, shock loadings, and the two +wedge loadings. That is enough to compute the impulse responses. + +```{code-cell} ipython3 +class NKModel(NamedTuple): + A: np.ndarray # state transition matrix + B: np.ndarray # shock loadings (columns: w_θ, w_a, w_r) + c_u: float # loading of the unemployment wedge on θ + c_π: float # loading of the inflation wedge on θ + + +def create_nk_model(): + """Build the reduced-form NK emulator (state and shock matrices).""" + # Exogenous-process parameters from bhandari2025survey. + ρ_θ, σ_θ = 0.714, 4.3 + ρ_a, σ_a = 0.840, 0.00568 + + # Belief-wedge loadings on θ (match the mean empirical wedges) + c_u = 0.52 / 5.64 + c_π = 1.22 / 5.64 + + # Impact of the belief shock θ on the endogenous variables (per unit of θ) + φ_u_θ = 0.00648 / σ_θ + φ_π_θ = 0.00063 / σ_θ + φ_y_θ = -0.00807 / σ_θ + + # Impact of TFP on the endogenous variables + φ_u_a, φ_π_a, φ_y_a = -0.362, -0.1306, 1.0236 + + # Endogenous persistence (quarterly) + ρ_u, ρ_π, ρ_y = 0.35, 0.50, 0.35 + + A = np.array([ + [ρ_u, 0, 0, φ_u_θ, φ_u_a], # unemployment + [0, ρ_π, 0, φ_π_θ, φ_π_a], # inflation + [0, 0, ρ_y, φ_y_θ, φ_y_a], # output + [0, 0, 0, ρ_θ, 0 ], # belief shock + [0, 0, 0, 0, ρ_a ], # TFP + ]) + + # Columns: [w_θ, w_a, w_r] + B = np.array([ + [0, 0, 0.5e-3], # MP -> unemployment + [0, 0, -0.1e-3], # MP -> inflation + [0, 0, -0.5e-3], # MP -> output + [σ_θ, 0, 0 ], # θ innovation + [0, σ_a, 0 ], # TFP innovation + ]) + return NKModel(A=A, B=B, c_u=c_u, c_π=c_π) +``` + +Impulse responses are computed by iterating $s_{t+1} = A s_t$ from the impact +column of $B$, and the two belief wedges are read off as $c_u \theta_t$ and +$c_\pi \theta_t$. + +```{code-cell} ipython3 +def irf(model, shock_idx, T=25): + """ + Impulse responses to a one-standard-deviation shock. + + shock_idx : 0 = belief shock, 1 = TFP shock, 2 = monetary policy shock. + + Returns the state responses together with the unemployment and inflation + wedge responses. + """ + A, B = model.A, model.B + resp = np.zeros((A.shape[0], T)) + s = B[:, shock_idx].copy() # impact response + for t in range(T): + resp[:, t] = s + s = A @ s + + wu = model.c_u * resp[I_THETA, :] + w_π = model.c_π * resp[I_THETA, :] + return resp, wu, w_π +``` + +```{code-cell} ipython3 +nk = create_nk_model() +``` + +## Quantitative results + +### Impulse responses to the belief shock + +A positive innovation to $\theta_t$ makes households more pessimistic. + +In the full structural model, higher pessimism makes households and firms act +as if bad future states are more likely. + +Vacancy posting weakens, output +falls, unemployment rises, and the two survey wedges jump together. + +The reduced-form system below is calibrated to reproduce those signs and to +make the belief wedges decay with $\rho_\theta = 0.714$. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: impulse responses to a belief shock + name: fig-sbbc-belief-shock-irfs +--- +T_irf = 25 +periods = np.arange(T_irf) + +resp_θ, wu_θ, w_π_θ = irf(nk, shock_idx=0, T=T_irf) + +fig, axes = plt.subplots(2, 3, figsize=(13, 7)) +axes = axes.flatten() + +ylabels = ['unemployment (pp)', 'inflation (pp, ann.)', 'output (%)', + 'belief shock θ', 'unemployment wedge Δ(u) (pp)', + 'inflation wedge Δ(π) (pp)'] +series = [resp_θ[0] * 100, # unemployment in pp (fraction * 100) + resp_θ[1] * 400, # inflation ann. pp (quarterly frac * 400) + resp_θ[2] * 100, # output in % (fraction * 100) + resp_θ[3], # belief shock θ + wu_θ, # unemp. wedge (pp): c_u * θ, already in pp + w_π_θ] # infl. wedge (pp): c_π * θ, already in pp +colors = ['steelblue'] * 3 + ['purple', 'steelblue', 'darkorange'] + +for ax, ylabel, y, color in zip(axes, ylabels, series, colors): + ax.plot(periods, y, color=color, linewidth=2) + ax.axhline(0, color='grey', linewidth=0.7, linestyle='--') + ax.set_ylabel(ylabel) + ax.set_xlabel('quarters') + +plt.tight_layout() +plt.show() +``` + +The first row shows the macroeconomic responses, and the second row shows the +belief shock and the two implied survey wedges. + +The shock raises unemployment, lowers output, and generates comoving +unemployment and inflation wedges. + +Inflation rises temporarily in this +calibration, then decays back toward zero. + +### The unemployment volatility puzzle + +A long-standing challenge for New Keynesian models is that standard TFP and +monetary policy shocks generate far too little unemployment volatility {cite}`Shimer2005`. + +In the paper's no-belief-shock economy, TFP and monetary policy shocks +produce unemployment volatility of only 0.55, compared to 1.70 in the data. + +Adding the belief shock substantially closes the gap. + +The emulator is calibrated to reproduce this experiment: we compute its +unconditional standard deviations from the discrete Lyapunov equation, with +and without the belief shock. + +```{code-cell} ipython3 +def simulate_nk(model, T=200, seed=42): + """Simulate the model for T periods under the data-generating measure.""" + rng = np.random.default_rng(seed) + A, B = model.A, model.B + k = B.shape[1] + s = np.zeros((A.shape[0], T)) + for t in range(1, T): + s[:, t] = A @ s[:, t-1] + B @ rng.standard_normal(k) + return s + + +def unconditional_stds(model, include_θ_shock=True): + """Unconditional standard deviations from the discrete Lyapunov equation.""" + B_use = model.B.copy() + if not include_θ_shock: + B_use[:, 0] = 0.0 # shut down the belief shock + Σ = solve_discrete_lyapunov(model.A, B_use @ B_use.T) + return np.sqrt(np.diag(Σ)) +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: model and data volatility comparison + name: fig-sbbc-volatility-comparison +--- +std_full = unconditional_stds(nk, include_θ_shock=True) +std_no_θ = unconditional_stds(nk, include_θ_shock=False) + +labels_vol = ['Unemployment', 'Inflation', 'Output'] +idx = [I_U, I_PI, I_Y] +scale = [100, 400, 100] # convert to pp (unemployment, annualized inflation, %) + +std_full_scaled = [std_full[i] * scale[j] for j, i in enumerate(idx)] +std_no_θ_scaled = [std_no_θ[i] * scale[j] for j, i in enumerate(idx)] + +# Data standard deviations reported by bhandari2025survey. +data_std = [1.70, 1.37, 2.00] # unemployment, inflation, output + +x = np.arange(len(labels_vol)) +width = 0.25 + +fig, ax = plt.subplots() +ax.bar(x - width, std_no_θ_scaled, width, label='No $\\theta_t$', + color='steelblue', alpha=0.7) +ax.bar(x, std_full_scaled, width, label='Benchmark', + color='firebrick', alpha=0.7) +ax.bar(x + width, data_std, width, label='Data', + color='grey', alpha=0.7) + +ax.set_xticks(x) +ax.set_xticklabels(labels_vol) +ax.set_ylabel('standard deviation (% or pp, ann.)') +ax.legend() +plt.tight_layout() +plt.show() +``` + +The bar chart compares three standard deviations: the emulator without the +belief shock, the emulator with it (labeled "Benchmark" after the paper's +benchmark economy), and the data. + +The main message is visible in the unemployment bars. + +Without the belief shock, unemployment volatility is far below its empirical +counterpart. + +Adding the calibrated belief shock raises unemployment volatility from about +0.55 to about 1.39, moving the model much closer to the data value 1.70. + +The belief shock also improves the model's fit to the historical record. + +### Role of firms' beliefs + +In the benchmark model, *firms* as well as +households hold subjective beliefs. + +What changes when firms instead have rational beliefs? + +The key channel is through the price-setting equation. + +Price-setting firms that share the household's pessimism put extra probability +weight on states with lower productivity and higher marginal costs. + +The rational-firms experiment turns off belief distortions in firms' +forward-looking equations while keeping household beliefs subjective and +recalibrating $\theta_t$ so that the mean and volatility of the unemployment +wedge remain comparable. + +If firms have rational beliefs, they see the household pessimism shock mainly +as a contraction in demand. + +Inflation falls on impact, and the inflation wedge is too small. + +Wages also fall by less. + +Under Nash bargaining, the wage splits the gap between the firm's subjective +valuation of the match and the worker's subjective value of unemployment; a +rational firm does not mark down its valuation when $\theta_t$ rises, so the +perceived surplus stays larger and the bargained wage declines less. + +Firm beliefs therefore strengthen the comovement between the unemployment +wedge and the inflation wedge, which is needed to match the data. + +### Countercyclicality of wedges + +A final important prediction is that belief wedges are countercyclical. + +Recessions are periods of high $\theta_t$, which raises both the unemployment +wedge and the inflation wedge simultaneously. + +The code below simulates a +long run of the model and shows this property: + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: simulated countercyclicality of belief wedges + name: fig-sbbc-countercyclical-wedges +--- +sim = simulate_nk(nk, T=400, seed=99) +θ_sim = sim[I_THETA] +y_sim = sim[I_Y] * 100 + +wu_sim_series = nk.c_u * θ_sim +w_π_sim_series = nk.c_π * θ_sim + +fig, axes = plt.subplots(3, 1, figsize=(11, 8), sharex=True) + +axes[0].plot(y_sim, color='steelblue', linewidth=2, label='Output gap (%)') +axes[0].axhline(0, color='grey', linestyle='--', linewidth=0.7) +axes[0].set_ylabel('%') +axes[0].legend(loc='upper right') + +axes[1].plot(wu_sim_series, color='firebrick', linewidth=2, + label='Unemployment belief wedge (pp)') +axes[1].set_ylabel('pp') +axes[1].legend(loc='upper right') + +axes[2].plot(w_π_sim_series, color='darkorange', linewidth=2, + label='Inflation belief wedge (pp)') +axes[2].set_ylabel('pp') +axes[2].legend(loc='upper right') +axes[2].set_xlabel('quarter') + +plt.tight_layout() +plt.show() + +corr_u = np.corrcoef(y_sim, wu_sim_series)[0, 1] +corr_π = np.corrcoef(y_sim, w_π_sim_series)[0, 1] +print(f"Corr(output gap, unemployment wedge) = {corr_u:.3f} " + f"(data: -0.49)") +print(f"Corr(output gap, inflation wedge) = {corr_π:.3f} " + f"(data: -0.30)") +``` + +The top panel plots the simulated output gap, while the middle and bottom +panels plot the unemployment and inflation wedges generated by the same +simulation. + +Periods with weak output tend to coincide with elevated wedges. + +The simulated correlations are negative, confirming the countercyclicality +predicted by the model and documented in the survey data. + +## Extensions + +Several extensions of the benchmark model are worth noting: + +**Heterogeneous beliefs:** The solution method allows the belief distortion +to be switched on or off equation by equation, so different agents can hold +different subjective beliefs. + +The rational-firms variant above is one example, and the relative sizes of the +unemployment and inflation wedges identify whose beliefs are distorted. + +With incomplete markets, heterogeneous exposures of continuation values to +shocks would generate belief heterogeneity across households endogenously, +with implications for saving, portfolio choice, and the design of social +insurance. + +**Pessimism induced by TFP:** The benchmark treats $\theta_t$ as an +exogenous AR(1) process. + +Another specification makes negative TFP shocks raise pessimism. + +This variant matches many unconditional moments: the inflation wedge mean is +$0.85$, the unemployment wedge mean is $0.56$, and unemployment volatility is +$1.49$. + +Its weakness is dynamic: responses to TFP shocks become counterfactually large +relative to the VAR evidence, and the correlations of model-implied paths with +the data fall to 0.22 (unemployment), 0.20 (unemployment wedge), and 0.35 +(inflation wedge), compared with 0.51, 0.83, and 0.79 in the benchmark. + +{cite:t}`bhandari2025survey` read this as evidence that quantitatively +important movements in pessimism are orthogonal to productivity. + +**Wage rigidity:** Wage rigidity is important for amplification. + +With flexible wages ($\chi_w = 0$), bargained wages absorb shocks, firm values +move less, and unemployment volatility falls from $1.39$ to $0.77$ --- the +Shimer-style amplification problem in another form. + +Lower macroeconomic volatility feeds back into beliefs: with less to fear, the +covariance between forecasted variables and continuation values shrinks, and +unemployment-wedge volatility falls from $0.45$ to $0.13$. + +**Beyond the first-order homoskedastic case:** The approximation is designed +to keep subjective-belief effects alive in a linear solution. + +In richer nonlinear or stochastic-volatility settings, belief wedges could also +move because the dispersion of continuation values changes. + +We do not pursue those extensions here. + +**Idiosyncratic risk:** The benchmark model takes fluctuations in $\theta_t$ as +exogenous, but they can also be endogenized. + +In a variant where households face uninsurable idiosyncratic risk, a rise in +that risk makes adverse states more likely from each household's viewpoint, so +pessimism and the belief wedges increase without any exogenous shock to +$\theta_t$. + +The supporting empirical idea is that belief wedges comove with the +{cite}`Schmidt2016` index of idiosyncratic labor-income skewness, which +proxies for the risk of large losses such as job loss. + +## Appendix: the series expansion method + +This appendix gives the computational and theoretical details underlying the +linearization presented in the main lecture. + +The formulas follow {cite:t}`bhandari2025survey`, but the notation needed for the +calculations below is introduced here. + +### Multi-period belief wedges + +The main text focused on the one-period belief wedge +$\Delta_t^{(1)}(z)$. + +Longer-horizon survey forecasts require $\tau$-period-ahead wedges +$\Delta_t^{(\tau)}(z) = \tilde E_t[z_{t+\tau}] - E_t[z_{t+\tau}]$, +so we now derive their linear representation. + +Under linear dynamics + +$$ + +x_{t+1} = \psi_q + \psi_x x_t + \psi_w w_{t+1}, +\qquad w_{t+1} \sim N(0, I_k), + +$$ + +the $\tau$-period-ahead expectation of the state deviation under the +data-generating measure is + +$$ + +E_t[x_{1,t+\tau}] = G_x^{(\tau)} x_{1t} + G_0^{(\tau)}, +\qquad +G_x^{(\tau)} = \psi_x G_x^{(\tau-1)}, +\quad +G_0^{(\tau)} = \psi_x G_0^{(\tau-1)} + \psi_q, + +$$ + +with initial conditions $G_x^{(0)} = I$ and $G_0^{(0)} = 0$. + +Under the **subjective** measure, the mean of $w_{t+1}$ is shifted to +$\nu_t = \bar H + HF x_{1t}$. + +For the stationary model the relevant identifications are + +$$ + +F = \bar\theta, +\qquad +H = -(v_x \psi_w)', +\qquad +\bar H = -\bar\theta\,\bar x\,(v_x \psi_w)'. + +$$ + +The shift is equivalent to replacing the transition matrices by their +subjective counterparts + +$$ + +\tilde\psi_x = \psi_x + \psi_w H F, +\qquad +\tilde\psi_q = \psi_q + \psi_w \bar H, + +$$ + +so the subjective loadings $\tilde G_x^{(\tau)}$ and $\tilde G_0^{(\tau)}$ +satisfy the same recursions with $\tilde\psi_x$ and $\tilde\psi_q$ in place +of $\psi_x$ and $\psi_q$. + +The $\tau$-period belief wedge is then + +$$ + +\Delta_t^{(\tau)} = \bigl(\tilde G_x^{(\tau)} - G_x^{(\tau)}\bigr) x_{1t} + + \tilde G_0^{(\tau)} - G_0^{(\tau)}, + +$$ + +which reduces to the one-period wedge formula at $\tau = 1$. + +The code below implements these recursions and shows how belief wedges grow +with the forecast horizon. + +```{code-cell} ipython3 +def compute_tau_wedge_loadings(ψ_x, ψ_w, H, H_bar, F, τ_max=20): + """ + Compute tau-period belief wedge loadings. + + For simplicity we work with the scalar stationary case (all quantities + are scalars or 1-d arrays). + """ + n = ψ_x.shape[0] + ψ_x_tild = ψ_x + ψ_w @ (H @ F) # subjective transition matrix + ψ_q_tild = (ψ_w @ H_bar).ravel() # subjective intercept + + Gx = np.eye(n) + Gx_tild = np.eye(n) + G0 = np.zeros(n) + G0_tild = np.zeros(n) + + wedge_const = np.zeros(τ_max) + wedge_slope = np.zeros((τ_max, n)) + + for τ in range(1, τ_max + 1): + Gx = ψ_x @ Gx + Gx_tild = ψ_x_tild @ Gx_tild + G0 = ψ_x @ G0 # ψ_q = 0 under the objective measure + G0_tild = ψ_x_tild @ G0_tild + ψ_q_tild + + wedge_slope[τ - 1] = (Gx_tild - Gx)[0] + wedge_const[τ - 1] = float((G0_tild - G0)[0]) + + return wedge_const, wedge_slope +``` + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: multi-period belief wedge profile + name: fig-sbbc-horizon-wedge +--- +# Scalar model objects +ψ_x_sc = np.array([[model.ρ_x]]) +ψ_w_sc = np.array([[model.σ_x]]) +F_sc = np.array([[model.μ_θ]]) # θ-bar +H_sc = np.array([[-model.vx * model.σ_x]]) # -(vx ψ_w)' +x_bar_sc = 1.0 +H_bar_sc = -model.μ_θ * x_bar_sc * np.array([[model.vx * model.σ_x]]) + +τ_max = 20 +wc, ws = compute_tau_wedge_loadings(ψ_x_sc, ψ_w_sc, H_sc, H_bar_sc, F_sc, τ_max) + +# State deviation that raises the belief factor by one +# unconditional standard deviation of the belief shock. +θ_std = model.σ_θ / np.sqrt(1 - model.ρ_θ**2) +x_dev = θ_std / model.μ_θ + +fig, ax = plt.subplots() +τ_grid = np.arange(1, τ_max + 1) +ax.plot(τ_grid, wc * 100, + color='steelblue', linewidth=2, label='Wedge at mean ($x_{1t}=0$)') +ax.plot(τ_grid, (wc + ws[:, 0] * x_dev) * 100, + color='firebrick', linewidth=2, linestyle='--', + label='Wedge at $+1\\,\\sigma_\\theta$ deviation') +ax.axhline(0, color='grey', linewidth=0.7, linestyle=':') +ax.set_xlabel('forecast horizon $\\tau$ (quarters)') +ax.set_ylabel('belief wedge (pp)') +ax.legend() +plt.tight_layout() +plt.show() +``` + +The horizontal axis is the forecast horizon, from one quarter ahead to twenty +quarters ahead. + +The blue line is the multi-period wedge evaluated at the mean state, and the +red dashed line evaluates the same wedge after a one-standard-deviation +increase in the belief shock. + +Both wedges are negative because the state is consumption-like ($v_x > 0$): +pessimists *under*-forecast consumption, the mirror image of the positive +unemployment and inflation wedges in the survey data. + +The blue line isolates the constant-term gap $\tilde G_0^{(\tau)} - +G_0^{(\tau)}$: each period the subjective law of motion adds the drift +$\tilde\psi_q$, compounded through the subjective persistence, so the wedge +deepens with the horizon and flattens toward the long-run limit +$\tilde\psi_q / (1 - \tilde\psi_x)$ --- the gap between the subjective and +objective unconditional means. + +Pessimism therefore distorts long-horizon forecasts more than short ones, +until mean reversion saturates the accumulation. + +The red line adds the slope contribution +$(\tilde\psi_x^{\tau} - \psi_x^{\tau})\, x_{1t}$, and this term explains its +hump shape: a difference of two geometric decays is zero at $\tau = 0$, +largest in magnitude at intermediate horizons, and vanishing again as both +powers die out. + +The extra pessimism thus deepens the wedge most at medium horizons (around +ten quarters here), after which the red line climbs back toward the *same* +asymptote as the blue line: today's elevated pessimism is transitory, so it +cannot move very-long-horizon forecasts, which are pinned down by the +steady-state wedge. + +### The series expansion + +The main text used three results without full derivation: the re-centred +shock distribution, the Riccati equation for $v_x$, and the claim that +belief distortions survive linearization only if $\theta_t$ is scaled +jointly with shock volatility. + +This section derives them and explains how {cite:t}`bhandari2025survey` +solve the full general-equilibrium model, using a **series expansion** +(perturbation) method in the tradition of {cite:t}`BorovickaHansen2014`. + +Three problems have to be solved in turn. + +First, the model must be expanded around its deterministic steady state in a +parameter that scales shock volatility; this step is standard. + +Second, the expansion must be modified so that the belief distortion appears +at first order instead of being truncated away by certainty equivalence. + +This is the **joint perturbation** of the shock volatility $\mathsf{q}$ and +the penalty parameter $\theta_t$, the paper's key methodological innovation. + +Third, the distorted expectations must be embedded in the model's +equilibrium conditions, which couples the unknown policy matrices to the +continuation value and modifies the standard Blanchard--Kahn solution. + +A final subsection extends the method to the specification the quantitative +model actually uses, in which the belief factor is an exogenous AR(1) +process. + +#### Law of motion + +The first step is to parameterize how volatile the world is. + +Index the model by a scalar perturbation parameter $\mathsf{q}$ that +scales shock volatility: + +$$ + +x_{t+1}(\mathsf{q}) = \psi\!\left(x_t(\mathsf{q}),\, + \mathsf{q}\, w_{t+1},\, \mathsf{q}\right). + +$$ + +Here $\mathsf{q} = 1$ is the model of interest and $\mathsf{q} = 0$ is a +deterministic economy. + +Expanding around $\mathsf{q} = 0$ gives + +$$ + +x_t(\mathsf{q}) \approx \bar x + \mathsf{q}\, x_{1t} + + \tfrac{\mathsf{q}^2}{2}\, x_{2t} + \cdots + +$$ + +The first-order dynamics are + +$$ + +x_{1,t+1} = \psi_q + \psi_x x_{1t} + \psi_w w_{t+1}. + +$$ + +A first-order solution keeps only $\bar x$ and $x_{1t}$, and the main text +explained the cost of that truncation: the optimal belief distortion works +through the variance of continuation values, so it is second order in +$\mathsf{q}$ and would be discarded. + +#### Continuation value and the Riccati equation + +The fix makes the agent's taste for distortion grow exactly as fast as the +scope for distortion shrinks. + +The penalty parameter is **jointly scaled** with $\mathsf{q}$: the effective +penalization in the perturbed recursion is +$\mathsf{q}/[\bar\theta(\bar x + x_{1t})]$, which shrinks together with +shock volatility. + +Because the optimal drift is the product of the penalty parameter and the +shock exposure of continuation values, scaling one up as the other scales +down keeps the drift at order one, so the subjective model remains distinct +from the data-generating process in the first-order solution. + +Guessing $v_{1t} = v_x x_{1t} + v_q$ and matching coefficients yields +the **Riccati equation for $v_x$**: + +$$ + +v_x = u_x - \frac{\beta}{2}\, v_x \psi_w \psi_w' v_x' \bar\theta + + \beta\, v_x \psi_x, + +$$ + +and the constant + +$$ + +v_q = u_q - \frac{\beta}{2}\,\bar\theta\, \bar x\, + v_x \psi_w \psi_w' v_x' + \beta\, v_x \psi_q + \beta v_q. + +$$ + +The slope equation is the modified Riccati equation read term by term in +the main text. + +The constant equation shows where any *fixed* component of pessimism goes: +the variance penalty evaluated at the steady state, proportional to +$\bar\theta \bar x$, shifts only $v_q$ --- Case 1 of the scalar illustration +again. + +The Riccati equation is quadratic in $v_x$. + +For the stationary scalar case it reduces to + +$$ + +a\, v_x^2 + b\, v_x + c = 0, +\qquad +a = \frac{\beta}{2}\sigma_x^2 \bar\theta,\quad +b = 1 - \beta\rho_x,\quad +c = -u_x. + +$$ + +#### Shock distribution under subjective beliefs + +With $v_x$ in hand, the optimal distortion +$m_{t+1}^* \propto \exp(-\theta_t v_{t+1})$ can be evaluated along the +expansion. + +Substituting the first-order expansion into the distortion formula shows that +the leading term $m_{0,t+1}$ is a lognormal change of measure. + +With Gaussian shocks, this is equivalent to shifting the innovation mean +as follows: + +$$ + +w_{t+1} \;\sim\; +N\!\left(-\bar\theta(\bar x + x_{1t})(v_x \psi_w)',\; I_k\right). + +$$ + +Belief wedges for the state vector follow immediately: + +$$ + +\Delta_t^{(1)} = \tilde E_t[x_{t+1}] - E_t[x_{t+1}] += \psi_w\, \tilde E_t[w_{t+1}] += -\bar\theta(\bar x + x_{1t})(\psi_w \psi_w') v_x'. + +$$ + +These are the mean-shift and wedge formulas of the main text, now derived +with the belief factor evaluated at its first-order expansion +$\theta_t = \bar\theta(\bar x + x_{1t})$. + +#### Equilibrium conditions with subjective beliefs + +So far the law of motion $\psi$ was taken as given, but in equilibrium it is +itself determined by optimality and market-clearing conditions, some of +which involve subjective expectations. + +The full model's equilibrium conditions take the form + +$$ + +0 = E_t\!\left[\mathbb{M}_{t+1}\, g(x_{t+1}, x_t, x_{t-1}, w_{t+1}, w_t)\right], + +$$ + +where $\mathbb{M}_{t+1} = \mathrm{diag}(m_{t+1}^{\sigma_1}, \ldots, +m_{t+1}^{\sigma_n})$ selects which equations involve subjective +expectations ($\sigma_i = 1$) versus objective ones ($\sigma_i = 0$). + +This equation-by-equation switch is what makes experiments like the +rational-firms variant possible: belief distortions can be turned off in +firms' equations while households remain pessimistic. + +First-order expansion of these conditions gives a system in the unknown +policy matrices $\psi_x, \psi_w, \psi_q$: + +$$ + +0 = (g_{x^+}\psi_x + g_x - \mathbb{D})\,\psi_x + g_{x^-} + +$$ + +$$ + +0 = (g_{x^+}\psi_x + g_x - \mathbb{D})\,\psi_w + g_w + +$$ + +$$ + +0 = (g_{x^+}\psi_x + g_{x^+} + g_x)\,\psi_q + g_q + - \mathbb{D}(\bar x + \psi_q), + +$$ + +where the **belief distortion matrix** $\mathbb{D}$ collects the impact +of subjective expectations on each equation: + +$$ + +\mathbb{D} = \operatorname{stack}\Bigl\{ + \sigma_i\, [g_{x^+}\psi_w + g_{w^+}]^i\, + (v_x \psi_w)'\, \bar\theta +\Bigr\}. + +$$ + +(We write $\mathbb{D}$ for this matrix to avoid confusion with the +expectation operator $E_t$.) + +Row $i$ of $\mathbb{D}$ is nonzero only if equation $i$ uses subjective +expectations, and it equals that equation's exposure to next period's +shocks, $[g_{x^+}\psi_w + g_{w^+}]^i$, times the vector +$(v_x \psi_w)' \bar\theta$ that governs the optimal mean shift. + +These equations are solved jointly with the Riccati equation for $v_x$: the +policy matrices determine the continuation value's exposure to shocks, and +that exposure feeds back into the policy matrices through $\mathbb{D}$. + +Compared with the standard Blanchard–Kahn solution, +the only modification is the additive term $-\mathbb{D}$ that shifts the +characteristic matrix; when $\bar\theta = 0$ we recover the standard +rational-expectations solution. + +## Summary + +The lecture has built the mechanism from the survey object to the model object. + +A belief wedge is the difference between a subjective forecast and an objective +forecast. + +In the data, the unemployment and inflation wedges are positive on average, +countercyclical, and well described by one common factor. + +Multiplier preferences generate exactly this kind of common factor: a higher +$\theta_t$ makes agents overweight states with low continuation value. + +With Gaussian shocks, the optimal change of measure is especially simple: it +shifts the mean of the innovation by +$-\theta_t (v_x \psi_w)'$. + +This mean shift implies belief wedges that are proportional to $\theta_t$ and +to the covariance between shocks and continuation values. + +In the New Keynesian application, the same belief shock raises unemployment, +creates comoving unemployment and inflation forecast wedges, and helps close +the unemployment volatility gap left by TFP and monetary-policy shocks alone. + +The survey wedges do double duty: they calibrate the belief-shock process, and +their joint behavior across variables, means, comovement, cyclicality, and +forecast-error predictability, over-identifies and thereby tests the model. + +## Exercises + +```{exercise-start} +:label: sbbc_ex1 +``` + +*Belief wedge sign* + +In the simple endowment economy built by `create_belief_model`, suppose the +state variable is log consumption $x_t$ with $\rho_x = 0.90$, $\sigma_x = 0.01$, +$\beta = 0.99$. + +1. Compute $v_x$ under rational expectations and under pessimism + $\mu_\theta = 4$. +2. What is the sign of the belief wedge for consumption growth? +3. If instead the agent forecasts unemployment (which enters the value + function with a negative sign, so $u_x < 0$), what is the sign of the + unemployment belief wedge? +```{exercise-end} +``` + +```{solution-start} sbbc_ex1 +:label: sbbc_ex1_sol +:class: dropdown +``` + +*Part 1.* Under rational expectations ($\theta = 0$): + +$$ + +v_x^{RE} = \frac{u_x}{1 - \beta \rho_x} + = \frac{1 - \beta}{1 - \beta \rho_x}. + +$$ + +```{code-cell} ipython3 +β_ex = 0.99 +ρ_x_ex = 0.90 +σ_x_ex = 0.01 +μ_θ_ex = 4.0 + +vx_re_ex = (1 - β_ex) / (1 - β_ex * ρ_x_ex) +print(f"v_x (rational expectations): {vx_re_ex:.6f}") + +m_ex = create_belief_model(β=β_ex, ρ_x=ρ_x_ex, + σ_x=σ_x_ex, μ_θ=μ_θ_ex) +print(f"v_x (with pessimism θ_bar={μ_θ_ex}): {m_ex.vx:.6f}") +print(f"difference: {m_ex.vx - vx_re_ex:.2e}") +``` + +The two slopes differ only in the fifth decimal place: the quadratic term in +the Riccati equation is scaled by $\sigma_x^2$, so at this calibration +pessimism only reduces the marginal value of the state by a small amount. + +*Part 2.* Under pessimism ($\theta_t > 0$), the consumption wedge is + +$$ +\Delta_t^{(1)}(x) += -\theta_t v_x \sigma_x^2. +$$ + +Since $v_x > 0$ and $\theta_t > 0$, the wedge is **negative**: pessimistic +agents underestimate consumption growth relative to the model. + +*Part 3.* For unemployment, $u_x < 0$, so $v_x^u < 0$. + +The belief wedge becomes + +$$ +\Delta_t^{(1)}(u) += -\theta_t v_x^u \sigma_x^2 > 0 +$$ + +(positive, because pessimism makes agents over-estimate unemployment). +This matches the empirical finding of a positive mean unemployment wedge. + +```{solution-end} +``` + +```{exercise-start} +:label: sbbc_ex2 +``` + +*Persistence and wedge volatility* + +Using `create_belief_model`, vary $\rho_\theta$ from 0.3 to +0.95 (holding $\sigma_\theta = 4.3$ fixed) and plot how the standard +deviation of the belief wedge changes. + +Explain the economic intuition. +```{exercise-end} +``` + +```{solution-start} sbbc_ex2 +:label: sbbc_ex2_sol +:class: dropdown +``` + +```{code-cell} ipython3 +ρ_vals = np.linspace(0.3, 0.95, 30) +wedge_stds = [] + +for ρ in ρ_vals: + m_temp = create_belief_model(ρ_θ=ρ) + θ_sim_temp = simulate_θ(m_temp, T=5000, seed=0) + wedge_sim_temp = belief_wedge(m_temp, θ_sim_temp) + wedge_stds.append(np.std(wedge_sim_temp)) + +fig, ax = plt.subplots() +ax.plot(ρ_vals, np.array(wedge_stds) * 100, color='steelblue', linewidth=2) +ax.set_title('Persistence and belief-wedge volatility') +ax.set_xlabel('persistence $\\rho_\\theta$') +ax.set_ylabel('standard deviation of belief wedge (pp)') +plt.tight_layout() +plt.show() +``` + +The figure plots the persistence parameter $\rho_\theta$ on the horizontal +axis and the simulated standard deviation of the belief wedge on the vertical +axis. + +The curve slopes upward. + +Higher persistence $\rho_\theta$ means that a given innovation to $\theta_t$ +has more prolonged effects: the unconditional variance of an AR(1) with +volatility $\sigma$ is $\sigma^2 / (1 - \rho^2)$, which increases in $\rho$. + +Since the wedge is proportional to $\theta_t$, its standard deviation +inherits this relationship and rises with $\rho_\theta$. + +```{solution-end} +``` + +```{exercise-start} +:label: sbbc_ex3 +``` + +*Unemployment volatility decomposition* + +Using the reduced-form NK model built by `create_nk_model`: + +1. Compute the fraction of unemployment variance explained by each of the + three shocks. + +2. Show that the belief shock is the dominant driver of unemployment + fluctuations, while TFP shocks matter much more for inflation and + output than they do for unemployment. +```{exercise-end} +``` + +```{solution-start} sbbc_ex3 +:label: sbbc_ex3_sol +:class: dropdown +``` + +```{code-cell} ipython3 +shock_names = ['Belief shock (θ)', 'TFP shock', 'MP shock'] +var_labels = ['Unemployment', 'Inflation', 'Output'] + +nk2 = create_nk_model() + +n_states = nk2.A.shape[0] +var_by_shock = np.zeros((n_states, 3)) + +for j in range(3): + B_j = np.outer(nk2.B[:, j], nk2.B[:, j]) + Σ_j = solve_discrete_lyapunov(nk2.A, B_j) + var_by_shock[:, j] = np.diag(Σ_j) + +var_total = var_by_shock.sum(axis=1) + +print(f"{'Variable':<16}", *[f"{s:>20}" for s in shock_names]) +print('-' * 77) +for i, label in zip([I_U, I_PI, I_Y], var_labels): + shares = var_by_shock[i] / var_total[i] * 100 + print(f"{label:<16}", *[f"{s:>19.1f}%" for s in shares]) +``` + +The belief shock accounts for the large majority of unemployment variance in +this calibrated emulator. + +Technology shocks drive most of the inflation variance, and output variance +is split roughly evenly between the belief and TFP shocks. + +Monetary policy shocks play a negligible role for all three variables. + +This pattern matches the variance decomposition of the structural model, in +which the belief shock dominates unemployment while technology shocks account +for most of the variation in inflation. + +```{solution-end} +``` + +```{exercise-start} +:label: sbbc_ex4 +``` + +*Changing the degree of pessimism* + +Solve the Riccati equation (`solve_vx`) for a grid of +$\mu_\theta$ values from 0 (rational expectations) to 15. + +For each value, +compute the scaled subjective drift $\nu / \sigma_x = -\mu_\theta v_x$ +and the steady-state belief wedge. + +Discuss how the robust value function differs from +the rational-expectations value function. +```{exercise-end} +``` + +```{solution-start} sbbc_ex4 +:label: sbbc_ex4_sol +:class: dropdown +``` + +```{code-cell} ipython3 +μ_grid = np.linspace(0, 15, 100) +drift_norm = [] +wedge_ss = [] + +for μ in μ_grid: + m_temp = create_belief_model(μ_θ=μ) + drift_norm.append(-μ * m_temp.vx) + wedge_ss.append(belief_wedge(m_temp, μ) * 100) # in pp + +fig, axes = plt.subplots(1, 2, figsize=(11, 4)) +fig.suptitle('Subjective drift and steady-state wedge') + +axes[0].plot(μ_grid, drift_norm, color='steelblue', linewidth=2) +axes[0].axhline(0, color='grey', linestyle='--', linewidth=0.8) +axes[0].set_xlabel('mean pessimism $\\mu_\\theta$') +axes[0].set_ylabel('scaled drift $\\nu / \\sigma_x$') + +axes[1].plot(μ_grid, np.array(wedge_ss), color='firebrick', linewidth=2) +axes[1].set_xlabel('mean pessimism $\\mu_\\theta$') +axes[1].set_ylabel('steady-state wedge (pp)') + +plt.tight_layout(rect=[0, 0, 1, 0.94]) +plt.show() +``` + +The left panel plots the scaled subjective drift +$\nu / \sigma_x = -\mu_\theta v_x$. + +The right panel plots the corresponding steady-state belief wedge. + +The scaled drift is the horizontal shift used in the shock-distribution +figure above, so it is easier to see than the tiny movement in $v_x$ itself. + +The steady-state consumption wedge becomes more negative, approximately +linearly in magnitude, since +$\Delta^{(1)} \propto -\mu_\theta v_x \sigma_x^2$ and $v_x$ is approximately +constant for small $\mu_\theta$. + +Finally, consider how the robust value function itself changes with +$\mu_\theta$. + +```{code-cell} ipython3 +vx_0 = create_belief_model(μ_θ=0).vx +vx_15 = create_belief_model(μ_θ=15).vx +print(f"v_x at μ_θ = 0: {vx_0:.8f}") +print(f"v_x at μ_θ = 15: {vx_15:.8f}") +print(f"relative change: {(vx_15 - vx_0) / vx_0:.2e}") +``` + +The slope $v_x$ falls as $\mu_\theta$ rises --- the quadratic term in the +Riccati equation lowers the marginal value of the state --- but the change is +on the order of $10^{-5}$ in relative terms, because the quadratic term is +scaled by $\sigma_x^2$. + +The robust value function therefore differs from its rational-expectations +counterpart mainly through the constant $v_q$, which falls as the variance +penalty grows. + +Because $v_x$ is nearly constant, the drift and the wedge are approximately +linear in $\mu_\theta$, which is what both panels show. + +```{solution-end} +``` diff --git a/lectures/tsyrennikov_2013.md b/lectures/tsyrennikov_2013.md new file mode 100644 index 00000000..6d018d82 --- /dev/null +++ b/lectures/tsyrennikov_2013.md @@ -0,0 +1,2335 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.19.1 +kernelspec: + name: python3 + display_name: Python 3 (ipykernel) + language: python +--- + +(tsyrennikov_2013)= +# Capital Flows Under Moral Hazard + +## Overview + +This lecture studies {cite:t}`Tsyrennikov2013`, which revisits the +infinite-horizon moral-hazard and limited-enforcement model of +{cite:t}`Atkeson1991` (see {doc}`atkeson_1991`) and makes two main +contributions: + +1. **First-order approach**: it proves ({prf:ref}`tsyrennikov_foa_lemma`) that + the borrower's incentive-compatibility constraint can be replaced by its + first-order condition, which makes the optimal contract with continuous + investment tractable to compute. +2. **Calibration and quantitative analysis**: it calibrates the model to + Argentina's business cycle and compares moral hazard against a pure + limited-enforcement benchmark. Unlike standard sovereign-default models, + contracts here are allowed to be fully state contingent. + +The central finding is that *moral hazard, not limited enforcement, does most +of the work* in matching several key features of emerging market economies: +high, volatile and countercyclical interest rate spreads, limited consumption +risk-sharing, and crisis-like dynamics in which capital inflows halt and +interest rates spike. + +The mechanism is that moral hazard severely restricts the amount of +*state contingency* that repayment schedules can provide. + +As a result, the optimal repayment is nearly *non-contingent* on output. + +This explains why non-contingent debt is an optimal way to finance an emerging +economy. + +Moral hazard also gives the model a strong internal propagation mechanism: even +i.i.d. output shocks generate persistent movements in output through +investment. + +Tsyrennikov is also explicit about the model's main weakness. + +The mechanism improves the behavior of consumption, output and spreads, but it +does not fully match the observed current-account dynamics. + +### The mechanism in brief + +The borrower can use foreign funds for either current consumption or +investment. + +Investment is costly today, but it raises the probability of high output +tomorrow. + +If investment were observable and contracts were fully enforceable, the lender +could insure the borrower almost completely. + +The contract would make the borrower's continuation net worth nearly the same +after low and high output, smoothing consumption across states. + +Moral hazard prevents this. + +When investment is hidden, full insurance gives the borrower too little reason +to invest. + +To make investment privately attractive, the contract must reward high output +with a higher continuation value than low output: + +$$ +v(n_2') > v(n_1'). +$$ + +This continuation-value spread is the borrower's incentive to invest. + +It also means that the risk-averse borrower must bear output risk. + +The optimal contract therefore cannot use much state-contingent repayment to +smooth consumption. + +It ends up looking close to non-contingent debt: repayments vary little across +output states, and insurance comes mainly from access to borrowing rather than +from repayments that adjust strongly to output. + +The same force creates persistence. + +After a low-output realization, the borrower's net worth falls. + +Lower net worth reduces investment, lower investment lowers the probability of +high output, and the economy becomes more likely to remain weak. + +Thus even i.i.d. output shocks generate persistent output dynamics through the +investment channel. + +When net worth is low, the borrower is also closer to its borrowing limit and +continuation values are more distorted by incentive provision. + +This raises the implied interest rate, making spreads high, volatile and +countercyclical. + +Limited enforcement works differently. + +It restricts repayments because the borrower must prefer repayment to default, +but by itself it can still allow substantial state-contingent insurance. + +Tsyrennikov's main quantitative result is that moral hazard, rather than +limited enforcement, is the friction that makes the optimal contract resemble +non-contingent debt and that generates the crisis-like dynamics. + +## Empirical motivation + +The paper starts from three facts about Argentina, viewed as a representative +emerging market economy over 1993--2005. + +First, consumption is almost perfectly correlated with output and is at least as +volatile as output. + +Second, interest rate spreads are high, volatile and countercyclical. + +Third, after a sequence of bad output realizations, capital inflows stop or +reverse. + +For comparison, Canada displays much smoother consumption and much weaker +spread-output comovement. + +The following table, condensed from the paper, highlights the contrast. + +Here and in the moments table below, $E(\cdot)$ is a mean, $\sigma(\cdot)$ a +standard deviation, and $\rho(\cdot,\cdot)$ a correlation, while $\rho(y)$ is the +first-order autocorrelation of output. + +The variables are consumption $c$, output $y$, the trade balance $tb$, and the +interest-rate spread $r$ over the world risk-free rate, in annualized percentage +points. + +| country and period | $\sigma(c)/\sigma(y)$ | $\rho(c,y)$ | $E(r)$ | $\sigma(r)$ | $\rho(r,y)$ | $\rho(tb,y)$ | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| Canada, 1993:Q1--2001:Q4 | 0.55 | 0.62 | 1.51 | 0.33 | 0.23 | 0.27 | +| Argentina, 1993:Q1--2001:Q4 | 1.11 | 0.97 | 8.18 | 4.73 | -0.58 | -0.81 | +| Argentina, 1993:Q1--2005:Q4 | 1.15 | 0.99 | 7.86 | 4.78 | -0.68 | -0.82 | + +So $\sigma(c)/\sigma(y)$ is consumption volatility relative to output, +$\rho(c,y)$ is consumption-output comovement, and a negative $\rho(r,y)$ or +$\rho(tb,y)$ means the spread or trade balance is countercyclical. + +The moral-hazard interpretation is that foreign creditors cannot fully observe +the use of borrowed funds. + +This is plausible when national accounts are noisy, when governments can blur +the line between consumption and investment, or when the level of investment is +observable but its effective quality is distorted by misallocation, corruption +or weak institutions. + +## The environment + +### Technology and preferences + +The environment is a small open economy with an infinitely lived borrower. + +The borrower starts each period with net worth $n$ (output net of debt +repayment), borrows $b$ from a short-lived risk-neutral lender, invests $I$, +and consumes + +$$ +c = n + b - \theta I, \qquad \theta > 0. +$$ (eq:tsyrennikov_budget) + +Given investment $I$, next period's output is random and takes one of two +ordered values $Y_1 < Y_2$. + +Following {cite:t}`Atkeson1991`, output is drawn from a mixture of two fixed +distributions $g_0$ and $g_1$, where $g_{kj}$ denotes the probability that +distribution $g_k$ assigns to output state $Y_j$. + +Here $g_0$ is the *favorable* distribution: it places more weight on high output +than $g_1$ does, so $g_0$ first-order stochastically dominates $g_1$. + +Investment controls the mixing weight $\lambda(I) \in [0,1]$, the probability +that output is drawn from the favorable distribution $g_0$: + +$$ +g(Y_j \mid I) = \lambda(I)\,g_{0j} + + \bigl(1 - \lambda(I)\bigr)\,g_{1j}, \qquad j = 1, 2, +$$ (eq:tsyrennikov_output_law) + +so $g(Y_j \mid I)$ is the probability of output state $Y_j$ given investment +$I$. + +The weight $\lambda : \mathbb{R}_+ \to [0,1]$ is strictly increasing and strictly +concave. + +Higher investment therefore raises the weight on $g_0$, so the output +distribution under higher investment first-order stochastically dominates that +under lower investment, with diminishing returns. + +```{note} +This is the same mixture technology, and uses the same labeling, as in +{doc}`atkeson_1991`: the weight $\lambda(I)$ multiplies the favorable +distribution $g_0$, so more investment makes high output more likely. +``` + +Tsyrennikov restricts to two output states, so the favorable distribution puts +all its mass on high output and the unfavorable one on low output: + +$$ +g_0 = (g_{0,1},\,g_{0,2}) = (0,\,1), \qquad +g_1 = (g_{1,1},\,g_{1,2}) = (1,\,0). +$$ + +The output probabilities then reduce to + +$$ +\Pr(Y_1 \mid I) = 1 - \lambda(I), \qquad +\Pr(Y_2 \mid I) = \lambda(I). +$$ (eq:tsyrennikov_two_state_output) + +It is convenient to record how investment moves the output distribution. + +Let $\Delta g_j \equiv g_{0j} - g_{1j}$, so that +$\partial g(Y_j \mid I)/\partial I = \lambda'(I)\,\Delta g_j$. + +In the two-state model $\Delta g = (-1,\,1)$: a marginal increase in investment +shifts probability away from low output and toward high output. + +The functional form $\lambda(I) = \min(I^\nu, 1)$ with $\nu \in (0,1)$ is +strictly concave and gives an interior optimum. + +The borrower's preferences are CRRA: + +$$ +U = \mathbb{E}_0 \sum_{t=0}^\infty \beta^t \, u(c_t), + \quad u(c) = \frac{c^{1-\gamma}}{1-\gamma}, \quad \gamma > 1. +$$ (eq:tsyrennikov_preferences) + +Lenders are risk-neutral and discount the future at factor $\beta_c \geq \beta$, +so they lend at the international gross risk-free rate $1/\beta_c$. + +Each lender lives for two periods with endowment $M$, so the loan cannot exceed +it: $b \leq M$. + +The assumption $\beta \leq \beta_c$ captures the idea that the government of an +emerging economy may have a shorter planning horizon than a typical +international lender. + +A contract between the two parties specifies the loan $b$ and the repayment +$d_j$ that the borrower makes after each output state $Y_j$. + +### Two frictions + +There are two frictions that limit the contract's ability to smooth consumption and investment across states. + +**Moral hazard**: lenders observe output but neither investment nor +consumption. + +The incentive-compatibility (IC) constraint {eq}`eq:tsyrennikov_ic` requires +that the borrower finds the recommended investment $I$ privately optimal. + +**Limited enforcement**: the borrower can default, suffering a one-time +output penalty. + +If default occurs when output is $Y_j$, the borrower retains only +$\delta Y_j$, with $\delta \in (0,1)$, and then lives in autarky forever. + +Let $v_{\text{aut}}^{\delta}(Y_j)$ denote the value after default in state +$j$: + +$$ +v_{\text{aut}}^{\delta}(Y_j) + = + \max_{0 \leq I \leq Y_j} + \left\{ + u(\delta Y_j - \theta I) + + \beta \sum_k g(Y_k \mid I) v_{\text{aut}}(Y_k) + \right\}. +$$ (eq:tsyrennikov_default_value) + +Here $v_{\text{aut}}$ is the borrower's autarky value, defined in the next +subsection. + +The superscript $\delta$ marks the one-time output loss incurred on entering +autarky. + +The enforcement constraint requires + +$$ +v(Y_j - d_j) \geq v_{\text{aut}}^{\delta}(Y_j), \qquad j = 1,2, +$$ (eq:tsyrennikov_enforcement) + +where $v$ is the contract value function. + +A larger $\delta$ means a milder default penalty and hence a better outside +option after default. + +### The autarky value function + +Without access to credit ($b = 0$), the borrower solves + +$$ +v_{\text{aut}}(n) = + \max_{0 \leq I \leq n} + \Bigl[u(n - \theta I) + \beta\,\bigl[(1-\lambda(I))\,v_{\text{aut}}(Y_1) + + \lambda(I)\,v_{\text{aut}}(Y_2)\bigr]\Bigr]. +$$ (eq:tsyrennikov_autarky) + +Note that the continuation values depend only on $Y_1$ and $Y_2$, not on $n$. + +### The frictionless benchmark + +If investment is contractible and contracts are fully enforceable, the borrower +can trade a full set of Arrow securities with the risk-neutral lender. + +The optimal repayment schedule then delivers state-independent continuation net +worth: + +$$ +Y_j - d_j = n' \qquad \text{for all } j. +$$ + +Net worth converges to a constant, so consumption and investment are eventually +constant, and the risk-sharing index defined below equals one. + +This benchmark implies full risk sharing and a strongly procyclical +current account --- the opposite of what the data show. + +It also shows what moral hazard limits: the ability to make repayments +strongly state contingent without weakening investment incentives. + +### The recursive contract + +The state variable is net worth $n$. + +{cite:t}`Atkeson1991` establishes that the optimal long-term contract can be +represented recursively with this single state, and we take this result as +given. + +Let + +$$ +n_j' := Y_j - d_j +$$ + +be next period's net worth after output $Y_j$ and repayment $d_j$. + +The contract value satisfies the Bellman equation + +$$ +v(n) = \max_{b,\,d,\,I} + \Bigl[u(n+b-\theta I) + \beta\,\sum_j g(Y_j\mid I)\,v(Y_j - d_j)\Bigr] +$$ (eq:tsyrennikov_bellman) + +subject to the following constraints. + +Feasibility requires the budget constraint {eq}`eq:tsyrennikov_budget` with +nonnegative consumption $c = n + b - \theta I \geq 0$ and nonnegative +investment $I \geq 0$. + +Lender participation requires the loan not to exceed the discounted expected +value of repayments, + +$$ +b \leq \beta_c \sum_j g(Y_j \mid I)\,d_j, +$$ (eq:tsyrennikov_lender_ir) + +while the lender endowment constraint caps the loan at the lender's resources, +$b \leq M$. + +The contract must also satisfy incentive compatibility {eq}`eq:tsyrennikov_ic` +and the enforcement constraint {eq}`eq:tsyrennikov_enforcement`. + +The incentive constraint says that the recommended investment must be the +borrower's own best choice from the feasible set: + +$$ +I \in \arg\max_{0 \leq \hat I \leq n+b} + \left\{ + u(n+b-\theta \hat I) + + \beta \sum_j g(Y_j\mid \hat I) v(Y_j-d_j) + \right\}. +$$ (eq:tsyrennikov_ic) + +Since $v$ is strictly increasing, limited enforcement can also be written as an +endogenous borrowing limit: + +$$ +d_j \leq \bar d_j + := Y_j - v^{-1}\!\left(v_{\text{aut}}^{\delta}(Y_j)\right). +$$ (eq:tsyrennikov_borrowing_limit) + +## The first-order approach + +The incentive constraint {eq}`eq:tsyrennikov_ic` is awkward to impose directly, +because it requires re-solving the borrower's investment problem inside the +contracting problem. + +Following {cite:t}`Rogerson1985`, {cite:t}`Tsyrennikov2013` replaces it with the +borrower's first-order condition + +$$ +-\theta\,u'(c) + \beta\,\lambda'(I)\,\sum_j \Delta g_j\,v(Y_j-d_j) \geq 0, +$$ (eq:tsyrennikov_relaxed_ic) + +which holds with equality whenever investment is interior. + +```{prf:lemma} Validity of the first-order approach +:label: tsyrennikov_foa_lemma + +Replacing the incentive constraint {eq}`eq:tsyrennikov_ic` with the relaxed +first-order condition {eq}`eq:tsyrennikov_relaxed_ic` does not change the +solution of the contract problem {eq}`eq:tsyrennikov_bellman`. +``` + +This is Lemma 1 of {cite:t}`Tsyrennikov2013`. + +```{prf:proof} +Fix a contract $(b, d_1, d_2)$, write $n_j' = Y_j - d_j$ and +$c(\hat I) = n + b - \theta\hat I$, and let + +$$ +S \;:=\; \sum_j \Delta g_j\, v(n_j') \;=\; v(n_2') - v(n_1') +$$ + +be the continuation-value spread. + +Using $g(Y_j\mid\hat I) = \lambda(\hat I)\,g_{0j} + (1-\lambda(\hat I))\,g_{1j}$, +a borrower who is offered this contract and invests $\hat I$ obtains + +$$ +\begin{aligned} +W(\hat I) +&:= u(c(\hat I)) + \beta \sum_j g(Y_j\mid\hat I)\, v(n_j') \\ +&\;= u(c(\hat I)) + \beta\bigl[v(n_1') + \lambda(\hat I)\, S\bigr], +\end{aligned} +$$ + +with first two derivatives + +$$ +W'(\hat I) = -\theta\, u'(c(\hat I)) + \beta\,\lambda'(\hat I)\, S, +\qquad +W''(\hat I) = \theta^2\, u''(c(\hat I)) + \beta\,\lambda''(\hat I)\, S . +$$ + +The incentive constraint {eq}`eq:tsyrennikov_ic` is +$I \in \arg\max_{\hat I \in [0,\, n+b]} W(\hat I)$, while the relaxed condition +{eq}`eq:tsyrennikov_relaxed_ic` is $W'(I) \geq 0$. + +First, we show that *a nonnegative spread makes the first-order condition sufficient.* + +Suppose $S \geq 0$. + +Since $u'' < 0$ and $\lambda'' < 0$, we have $\theta^2 u''(c) < 0$ and +$\beta\,\lambda''(\hat I)\, S \leq 0$, so $W''(\hat I) < 0$ for every $\hat I$. + +Hence $W$ is strictly concave on $[0,\, n+b]$ and has a unique maximizer. + +The Inada condition $u'(0) = +\infty$ rules out the upper corner +$\hat I = (n+b)/\theta$, where $c = 0$, so the maximizer is either interior, with +$W'(I) = 0$, or the lower corner $I = 0$. + +In either case the borrower's choice is characterized by its first-order +condition, so {eq}`eq:tsyrennikov_ic` and {eq}`eq:tsyrennikov_relaxed_ic` --- the +latter holding with equality at an interior optimum --- select the same +investment. + +Next, we show that *every optimal contract can be replaced by an equivalent one +with $S \geq 0$.* + +Suppose an optimal contract had $S < 0$, that is $v(n_2') < v(n_1')$. + +Then for every $\hat I \in (0,\, n+b]$ both terms of $W'(\hat I)$ are negative, +because $u' > 0$, $\lambda' > 0$ and $S < 0$, so $W$ is strictly decreasing and +the borrower invests $I = 0$. + +At $I = 0$ we have $\lambda(0) = 0$, so output equals $Y_1$ with probability one +and the borrower's payoff $u(n+b) + \beta\, v(n_1')$ is independent of $n_2'$. + +Now raise the high-output continuation to $\tilde n_2' = n_1'$ --- equivalently set +$\tilde d_2 = Y_2 - n_1'$ --- leaving $b$, $d_1$ and the recommended $I = 0$ +unchanged. + +This contract is still feasible: lender participation +{eq}`eq:tsyrennikov_lender_ir` weights $d_2$ by $g(Y_2\mid 0) = 0$ and is +unaffected, while the state-$2$ enforcement constraint +{eq}`eq:tsyrennikov_enforcement` is relaxed because +$v(\tilde n_2') = v(n_1') > v(n_2')$. + +It delivers the same borrower payoff but now has spread +$\tilde S = v(n_1') - v(n_1') = 0 \geq 0$. + +This gives one direction: an optimal contract can always be taken to have +$S \geq 0$, and then the argument above makes its first-order condition coincide +with incentive compatibility, so the original optimum is feasible for the +relaxed problem. + +For the other direction, the relaxed constraint +$W'(I) = -\theta u'(c) + \beta\lambda'(I)\, S \geq 0$ can hold only when $S > 0$, +because $u' > 0$ and $\lambda' > 0$. + +So the relaxed problem only ever considers contracts with $S \geq 0$, where its +first-order condition is genuine incentive compatibility. + +The relaxed problem therefore neither loses the original optimum nor admits a +contract that is not incentive compatible, which proves the lemma. +``` + +The subtle part of the argument is why the planner may freely raise $n_2'$. + +If $S < 0$, then the high-output continuation value is lower than the low-output +continuation value: + +$$ +v(n_2') < v(n_1'). +$$ + +But investment raises the probability of the high-output state. + +So with $S < 0$, investment has two private costs for the borrower. + +It lowers current consumption, because $c = n + b - \theta I$, and it also +raises the probability of receiving the worse continuation value. + +The borrower therefore has no reason to invest and chooses $I=0$. + +At $I=0$, the two-state technology gives $\lambda(0)=0$, so the high-output +state occurs with probability zero. + +This means that the promised high-output continuation $n_2'$ is off the +equilibrium path. + +Changing it does not affect current consumption, the borrower's payoff along the +realized path, or the lender's participation constraint, because the repayment +$d_2$ receives zero probability weight. + +Raising $n_2'$ also relaxes the high-state enforcement constraint, since the +borrower is promised more value in that state. + +Thus the planner can raise $n_2'$ up to $n_1'$ without changing the allocation +that actually occurs. + +After this change the spread is $S=0$ instead of $S<0$. + +Hence any candidate optimum with a negative spread can be replaced by an +equivalent candidate with a nonnegative spread. + +## The Euler equation and implied interest rate + +To characterize the optimal contract, attach multipliers to the constraints of +problem {eq}`eq:tsyrennikov_bellman`. + +Let $\kappa \geq 0$ be the multiplier on lender participation +{eq}`eq:tsyrennikov_lender_ir`, $\phi \geq 0$ the multiplier on the endowment +limit $b \leq M$, $\mu \geq 0$ the multiplier on the relaxed incentive +constraint {eq}`eq:tsyrennikov_relaxed_ic`, and $\xi_j \geq 0$ the multiplier on +each enforcement constraint {eq}`eq:tsyrennikov_enforcement`. + +The Lagrangian is + +$$ +\begin{aligned} +\mathcal{L} =\ & u(n+b-\theta I) + \beta \sum_j g(Y_j\mid I)\,v(Y_j-d_j) + + \kappa\Bigl(\beta_c \sum_j g(Y_j\mid I)\,d_j - b\Bigr) + \phi\,(M-b) \\ + &+ \mu\Bigl(-\theta u'(n+b-\theta I) + + \beta\lambda'(I)\sum_j \Delta g_j\,v(Y_j-d_j)\Bigr) + + \beta \sum_j g(Y_j\mid I)\,\xi_j\bigl(v(Y_j-d_j) + - v_{\text{aut}}^{\delta}(Y_j)\bigr). +\end{aligned} +$$ (eq:tsyrennikov_lagrangian) + +The envelope theorem gives $v'(n) = u'(c) - \mu\theta u''(c)$ +({ref}`Exercise 3 `). + +The first-order condition for $b$ then yields + +$$ +v'(n) = \kappa + \phi, +$$ + +and the first-order condition for each $d_j$ yields + +$$ +\kappa = \frac{\beta}{\beta_c}\,v'(n_j') + \left[(1+\xi_j) + \mu\,\frac{\lambda'(I)\,\Delta g_j}{g(Y_j\mid I)}\right]. +$$ + +Combining the two delivers the **Euler equation** + +$$ +v'(n) = \frac{\beta}{\beta_c}\,v'(n_j') + \left[(1+\xi_j) + \mu\,\frac{\lambda'(I)\,\Delta g_j}{g(Y_j\mid I)}\right] + + \phi. +$$ (eq:tsyrennikov_euler) + +As a useful special case, in the pure moral-hazard economy the enforcement +multipliers vanish ($\xi_j = 0$), leaving + +$$ +v'(n) = \frac{\beta}{\beta_c}\,v'(n_j') + \left[1 + \mu\,\frac{\lambda'(I)\,\Delta g_j}{g(Y_j\mid I)}\right] + \phi. +$$ + +In the calibration $\beta/\beta_c \approx 0.99$, so the leading factor is just +below one, which on its own makes net worth drift down slowly. + +Because $\Delta g_1 = -1 < 0$, the low-output likelihood term is negative. + +When the endowment constraint is slack ($\phi = 0$) and the bracket is positive, +the equation pushes $v'(n_1')$ above $v'(n)$. + +By concavity of $v$, the borrower's net worth then falls in the low state. + +This is the **immiseration** property: incentive provision drags the borrower's +net worth down over time, a force also present in the private-information +economies of {cite:t}`ThomasWorrall1990` and {cite:t}`AtkesonLucas1992`. + +To isolate this force, set $\beta = \beta_c$, $\phi = 0$ and $\xi_j = 0$. + +Multiplying the Euler equation by $g(Y_j\mid I)$ and summing over $j$ gives + +$$ +v'(n) = \mathbb{E}\,v'(n_j') + \mu\,\lambda'(I)\sum_j \Delta g_j\,v'(n_j') + \;\leq\; \mathbb{E}\,v'(n_j'), +$$ + +because the last term is nonpositive: $\Delta g$ shifts probability toward high +output, where continuation net worth is higher and $v'$ is lower. + +So $v'(n)$ is a submartingale and, by concavity, expected net worth drifts +downward. + +Limited enforcement without moral hazard reverses the sign. + +Setting $\mu = 0$ (again with $\beta = \beta_c$ and $\phi = 0$) leaves +$v'(n) = \mathbb{E}\,v'(n_j') + \sum_j g(Y_j\mid I)\,\xi_j v'(n_j') +\geq \mathbb{E}\,v'(n_j')$, since $\xi_j \geq 0$, which implies upward drift in +continuation net worth under concavity. + +The optimal contract can be decentralized: instead of signing a contract, the +government-borrower faces an **implied interest rate** schedule $R(n)$ on each +unit borrowed: + +$$ +R(n) \;=\; \frac{u'(c(n))}{\beta\,\sum_j g(Y_j\mid I(n))\,u'(c(n_j'(n)))}, +$$ + +where $c(n_j'(n))$ is next period's consumption if state $j$ is realized. + + +This rate is countercyclical: when $n$ is low, past incentive provision has +depressed the continuation values, raising the marginal utility spread and +increasing $R$. + +## Computation + +We now implement a lightweight numerical illustration using the +parameterization from {cite:t}`Tsyrennikov2013`. + +The code solves three economies: + +1. **MH**: moral hazard only, with the lender endowment constraint $b \leq M$. +2. **MH+LE**: moral hazard and limited enforcement, without the exogenous + lender endowment constraint. +3. **LE**: limited enforcement only, again without the exogenous lender + endowment constraint. + +In the two limited-enforcement economies, the value constraint +{eq}`eq:tsyrennikov_enforcement` is imposed through the endogenous borrowing +limit {eq}`eq:tsyrennikov_borrowing_limit`. + +### Algorithm + +The state is current net worth $n$. + +For each $n$, the code searches over continuation net worths +$(n_1', n_2')$. + +In the moral-hazard economies, the first-order approach determines the +recommended investment for each candidate continuation pair. + +In the LE economy, investment is contractible, so the planner chooses it +directly from its first-order condition. + +For the pure MH economy, the loan is the smaller of the lender-participation +amount and the endowment $M$. + +For MH+LE and LE, borrowing is limited endogenously by the borrower's default +value. + +The resulting policy functions illustrate the economic mechanism and +approximate Figures 3 and 4 of {cite:t}`Tsyrennikov2013`, but they are not a +full replication of the paper's numerical algorithm. + +The paper solves the Bellman equation iteratively, approximates the value +function by a cubic spline on $[0.2, 1.2]$ with 100 nodes, and stops when the +sup-norm change in the value function is below $10^{-5}$. + +For the limited-enforcement economies, Appendix B updates the endogenous +borrowing limits with a damped rule that gives one-half weight to the previous +limit and one-half weight to the new limit implied by the current value +function. + +The code below solves the same recursive problem with a simpler two-stage +approximation. + +First, it computes the fixed point quickly with JAX, Howard policy iteration, +linear interpolation of the value function, and a finite mesh of continuation +net worth pairs $(n_1', n_2')$. + +Second, it polishes the resulting policy functions by re-optimizing each +state's contract locally with SciPy, using a cubic spline approximation to the +converged value function. + +The polishing step parameterizes the continuation pair by the low-state +continuation $n_1'$ and the risk-sharing index + +$$ +\operatorname{RSI} + = \frac{d_2-d_1}{Y_2-Y_1} + = 1 - \frac{n_2'-n_1'}{Y_2-Y_1}. +$$ + +This makes the near-zero risk-sharing index under moral hazard representable +even though the fixed-point step uses a coarse mesh. + +To reach a fixed point quickly, all three economies are solved by +**Howard policy iteration**. + +Each outer iteration takes one greedy Bellman step, which re-optimizes the +contract, and then holds that contract fixed while iterating the value a fixed +number of times. + +### Parameters + +In addition to what's in Anaconda, this lecture will need the following library: + +```{code-cell} ipython3 +:tags: [hide-output] + +!pip install jax +``` + +The computation uses JAX to vectorize the Bellman updates and SciPy for the +cubic splines and local policy-polishing problems. + +We will use the following imports: + +```{code-cell} ipython3 +import numpy as np +from typing import NamedTuple +from jax import config +config.update("jax_enable_x64", True) +import jax +import jax.numpy as jnp +import matplotlib.pyplot as plt +from scipy.interpolate import CubicSpline +from scipy.optimize import minimize +``` + +We store the parameters in a `NamedTuple`, with defaults calibrated to Argentina +as in {cite:t}`Tsyrennikov2013`. + +In the paper's calibration, $\beta_c$ matches a world interest rate of 4%, +$\ln Y_j = \pm 0.054$ matches output volatility, $\theta$ normalizes mean +output to one, and $\delta$ and $M$ match average debt-to-output ratios of +0.420 and 0.410 in the full and MH-only models. + +```{code-cell} ipython3 +class Model(NamedTuple): + β: float # borrower discount factor + β_c: float # lender discount factor + γ: float # CRRA coefficient + θ: float # resource cost of investment + ν: float # curvature in λ(I) = I^ν + δ: float # fraction of output retained after default + M: float # lender endowment + Y1: float # low output state + Y2: float # high output state + + +def create_model(β=0.980, β_c=0.990, γ=2.0, θ=0.105, ν=0.950, + δ=0.795, M=0.465, Y1=np.exp(-0.054), Y2=np.exp(+0.054)): + """Build a model instance, validating the parameters.""" + if not 0 < β < 1: + raise ValueError("β must lie in (0, 1)") + if not 0 < β_c < 1: + raise ValueError("β_c must lie in (0, 1)") + if γ <= 0: + raise ValueError("γ must be positive") + if not 0 < ν < 1: + raise ValueError("ν must lie in (0, 1)") + if not 0 < δ < 1: + raise ValueError("δ must lie in (0, 1)") + if Y1 >= Y2: + raise ValueError("require Y1 < Y2") + return Model(β=β, β_c=β_c, γ=γ, θ=θ, ν=ν, δ=δ, M=M, Y1=Y1, Y2=Y2) + + +model = create_model() +β, β_c, γ, θ, ν, δ, M, Y1, Y2 = (model.β, model.β_c, model.γ, model.θ, + model.ν, model.δ, model.M, model.Y1, model.Y2) +output_states = np.array([Y1, Y2]) + +print(f"Output states: Y1 = {Y1:.4f}, Y2 = {Y2:.4f}") +print(f"β = {β}, β_c = {β_c}, γ = {γ}, θ = {θ}, ν = {ν}") +``` + +Next we define the model primitives. + +The probability of high output is $\lambda(I) = \min(I^\nu, 1)$, period utility +`u` is CRRA, and `u_prime` is its derivative $u'(c) = c^{-\gamma}$. + +```{code-cell} ipython3 +def λ(I): + """Probability of high output, λ(I) = min{I^ν, 1}.""" + return jnp.minimum(I**ν, 1.0) + + +def u(c): + """CRRA period utility.""" + c = jnp.maximum(c, 1e-12) + return c**(1.0 - γ) / (1.0 - γ) + + +def u_prime(c): + """Marginal utility u'(c) = c^{-γ}.""" + return jnp.maximum(c, 1e-12)**(-γ) +``` + +Finally we build the grids. + +`n_grid` discretizes net worth, `I_search_grid` is the investment grid used by +the autarky step, and the mesh `(n1p_candidates, n2p_candidates)` holds the +candidate continuation pairs $(n_1', n_2')$ searched by the moral-hazard step. + +```{code-cell} ipython3 +# Net-worth grid +n_grid_size = 100 +n_lo = 0.20 +n_hi = 1.20 +n_grid = np.linspace(n_lo, n_hi, n_grid_size) +n_grid_j = jnp.asarray(n_grid) + +# Investment search grid used by the autarky Bellman step +investment_grid_size = 350 +I_search_grid = np.linspace(0.0, 1.0, investment_grid_size) +I_search_grid_j = jnp.asarray(I_search_grid) + +# Mesh of candidate continuation pairs (n_1', n_2') for the MH step +policy_grid_size = 90 +n1p_candidates = np.linspace(n_lo, n_hi, policy_grid_size) +n2p_candidates = np.linspace(n_lo, n_hi, policy_grid_size) +n1p_mesh, n2p_mesh = np.meshgrid(n1p_candidates, n2p_candidates, + indexing='ij') +n1p_flat_j = jnp.asarray(n1p_mesh.ravel()) +n2p_flat_j = jnp.asarray(n2p_mesh.ravel()) +``` + +The net-worth grid matches the paper's 100 nodes on $[0.2, 1.2]$. + +The policy mesh is deliberately modest so the lecture can execute quickly, and +it spans the full value-function domain to avoid artificial upper-bound +corners. + +### Autarky value function + +We solve the autarky problem {eq}`eq:tsyrennikov_autarky` by value function +iteration. + +The Bellman step is vectorized: it evaluates every net-worth state against the +whole investment search grid at once and keeps the best investment. + +Because the borrower has no credit in autarky, next period's net worth is just +the realized output, so the continuation values are simply $v(Y_1)$ and +$v(Y_2)$. + +```{code-cell} ipython3 +@jax.jit +def autarky_step(v, β_val): + """One vectorized Bellman step for the autarky problem.""" + # Continuation values: next-period net worth is the realized output + Ev1 = jnp.interp(Y1, n_grid_j, v) + Ev2 = jnp.interp(Y2, n_grid_j, v) + + # Evaluate every (net worth, investment) pair on the search grid + I = I_search_grid_j[None, :] + c = n_grid_j[:, None] - θ * I + l = λ(I) + obj = u(c) + β_val * ((1.0 - l) * Ev1 + l * Ev2) + + # Investment cannot exceed net worth and consumption must be positive + feasible = (I <= n_grid_j[:, None]) & (c > 1e-10) + obj = jnp.where(feasible, obj, -jnp.inf) + + idx = jnp.argmax(obj, axis=1) + return jnp.max(obj, axis=1), I_search_grid_j[idx] + + +def autarky_policy(v_arr, β_val=None): + """Return the autarky value update and investment policy on n_grid.""" + if β_val is None: + β_val = β + v_new, I_pol = autarky_step(jnp.asarray(v_arr), β_val) + return np.asarray(v_new), np.asarray(I_pol) +``` + +We iterate the step to convergence. + +```{code-cell} ipython3 +def autarky_vfi(β_val=None, tol=1e-8, max_iter=3000, verbose=False): + """Value function iteration for the autarky problem.""" + if β_val is None: + β_val = β + + v = jnp.zeros(n_grid_size) + for it in range(max_iter): + v_new, _ = autarky_step(v, β_val) + diff = float(jnp.max(jnp.abs(v_new - v))) + v = v_new + if diff < tol: + if verbose: + print( + f"Autarky VFI converged in {it+1} iterations " + f"(diff = {diff:.2e})" + ) + break + + return np.asarray(v) + + +v_aut = autarky_vfi(verbose=True) +``` + +### Default values and borrowing limits + +Limited enforcement is imposed by updating the minimum continuation net worth +that keeps the borrower from defaulting. + +If $V$ is the current contract value and +$v_{\text{aut}}^\delta(Y_j)$ is the value of defaulting in state $j$, the +borrowing-limit form of the enforcement constraint is + +$$ +n_j' \geq V^{-1}\!\left(v_{\text{aut}}^\delta(Y_j)\right). +$$ + +The code below computes the two default values and updates these two lower +boundaries during value function iteration. + +```{code-cell} ipython3 +_, I_aut = autarky_policy(v_aut) + + +def default_values(v_aut_arr, β_val=None): + """Values after default, including the one-period output loss δ.""" + if β_val is None: + β_val = β + + Ev1 = np.interp(Y1, n_grid, v_aut_arr) + Ev2 = np.interp(Y2, n_grid, v_aut_arr) + vals = [] + for Yj in output_states: + I = I_search_grid + c = δ * Yj - θ * I + l = np.minimum(I**ν, 1.0) + c_safe = np.maximum(c, 1e-12) + util = c_safe**(1.0 - γ) / (1.0 - γ) + obj = util + β_val * ((1 - l) * Ev1 + l * Ev2) + feasible = (I <= Yj) & (c > 1e-10) + vals.append(float(np.max(np.where(feasible, obj, -np.inf)))) + return np.asarray(vals) + + +def inverse_value(v_arr, target): + """Approximate V^{-1}(target) on the net-worth grid.""" + v_mono = np.maximum.accumulate(v_arr) + return float(np.interp(target, v_mono, n_grid, + left=n_grid[0], right=n_grid[-1])) + + +def borrowing_limit_nbars(v_arr, v_default): + """Minimum feasible continuation net worths implied by enforcement.""" + return np.asarray([inverse_value(v_arr, val) for val in v_default]) + + +v_aut_delta = default_values(v_aut) +print("Default values:", np.round(v_aut_delta, 4)) +``` + +### Contracting models + +For moral hazard, we use the first-order approach. + +The pure MH economy evaluates two loan regimes for each candidate +continuation pair: + +$$ +b = \beta_c\,\mathbb E[d_j] +\quad\text{and}\quad +b = M. +$$ + +In the first regime lender participation binds; in the second the lender +endowment constraint binds. + +For MH+LE, we set the exogenous cap to a very large value and rely on the +endogenous borrowing limits instead. + +For LE, investment is observable, so the planner chooses it directly. + +Each Bellman step returns both the improved value and the greedy contract, and a +shared routine `policy_eval` then performs the Howard policy-evaluation +sweeps that hold that contract fixed. + +In the two limited-enforcement economies, the endogenous borrowing limits are +refreshed once per outer iteration with a damped update. + +```{code-cell} ipython3 +big_loan_cap = 1e6 +contract_tol = 1e-6 +contract_max_iter = 1_000 +howard_eval_steps = 80 + + +def contract_initial_upper(β_val, loan_upper): + """High initial value; starting too low can converge back to autarky.""" + c_upper = n_hi + loan_upper + return np.full(n_grid_size, float(u(c_upper)) / (1.0 - β_val)) + + +@jax.jit +def mh_bellman_step(v, v_aut_arr, I_aut_arr, nbar1, nbar2, + loan_cap, β_val, β_c_val): + """One Bellman step for MH, with optional LE bounds and loan cap.""" + v1 = jnp.interp(n1p_flat_j, n_grid_j, v) + v2 = jnp.interp(n2p_flat_j, n_grid_j, v) + Δv = v2 - v1 + + d1 = Y1 - n1p_flat_j + d2 = Y2 - n2p_flat_j + enforce_feasible = ((n1p_flat_j[None, :] >= nbar1 - 1e-10) + & (n2p_flat_j[None, :] >= nbar2 - 1e-10)) + shape_ref = n_grid_j[:, None] + 0.0 * n1p_flat_j[None, :] + I_hi_base = jnp.ones_like(shape_ref) * (1.0 - 1e-6) + + def lender_value(I): + l = λ(I) + return β_c_val * ((1 - l) * d1[None, :] + l * d2[None, :]) + + def solve_mh_root(c_of_I): + I_hi = I_hi_base + + def shrink_hi(_, I_hi_val): + return jnp.where(c_of_I(I_hi_val) < 1e-8, + 0.9 * I_hi_val, I_hi_val) + + I_hi = jax.lax.fori_loop(0, 35, shrink_hi, I_hi) + I_lo = jnp.full_like(I_hi, 1e-7) + + def foa(I): + λ_prime = ν * jnp.maximum(I, 1e-12)**(ν - 1.0) + return θ * u_prime(c_of_I(I)) - β_val * λ_prime * Δv[None, :] + + foa_lo = foa(I_lo) + foa_hi = foa(I_hi) + valid = ((Δv[None, :] > 1e-10) & (I_hi > 1e-6) + & (foa_lo < 0.0) & (foa_hi > 0.0) + & (c_of_I(I_hi) > 1e-8)) + + def bisect_body(_, state): + lo, hi = state + mid = 0.5 * (lo + hi) + f_mid = foa(mid) + hi = jnp.where(f_mid > 0.0, mid, hi) + lo = jnp.where(f_mid > 0.0, lo, mid) + return lo, hi + + I_lo, I_hi = jax.lax.fori_loop(0, 35, bisect_body, (I_lo, I_hi)) + return 0.5 * (I_lo + I_hi), valid + + # Regime 1: lender participation binds. + def c_lp(I): + return n_grid_j[:, None] + lender_value(I) - θ * I + + I_lp, valid_lp = solve_mh_root(c_lp) + b_lp = lender_value(I_lp) + + # Regime 2: the exogenous loan cap binds. This regime is inactive when + # loan_cap is set to big_loan_cap. + def c_cap(I): + return n_grid_j[:, None] + loan_cap - θ * I + + I_cap, valid_cap = solve_mh_root(c_cap) + b_cap = jnp.full_like(I_cap, loan_cap) + + def evaluate(I_star, b, valid): + c = n_grid_j[:, None] + b - θ * I_star + l = λ(I_star) + Ev = (1 - l) * v1[None, :] + l * v2[None, :] + obj = u(c) + β_val * Ev + ic_feasible = I_star <= n_grid_j[:, None] + b + 1e-6 + feasible = (valid & enforce_feasible & ic_feasible + & (c > 1e-10) & (b >= -1e-8)) + return jnp.where(feasible, obj, -jnp.inf) + + obj_lp = evaluate(I_lp, b_lp, valid_lp & (b_lp <= loan_cap + 1e-7)) + cap_has_resources = lender_value(I_cap) >= loan_cap - 1e-7 + obj_cap = evaluate(I_cap, b_cap, valid_cap & cap_has_resources) + + use_cap = obj_cap > obj_lp + obj = jnp.where(use_cap, obj_cap, obj_lp) + I_all = jnp.where(use_cap, I_cap, I_lp) + b_all = jnp.where(use_cap, b_cap, b_lp) + + idx = jnp.argmax(obj, axis=1) + best_val = jnp.max(obj, axis=1) + has_feasible = jnp.isfinite(best_val) + use_fallback = (~has_feasible) | (best_val <= v_aut_arr) + + pol_n1p = jnp.where(use_fallback, Y1, n1p_flat_j[idx]) + pol_n2p = jnp.where(use_fallback, Y2, n2p_flat_j[idx]) + pol_I = jnp.where(use_fallback, I_aut_arr, + jnp.take_along_axis(I_all, idx[:, None], axis=1)[:, 0]) + pol_b = jnp.where(use_fallback, 0.0, + jnp.take_along_axis(b_all, idx[:, None], axis=1)[:, 0]) + v_new = jnp.where(use_fallback, v_aut_arr, best_val) + + return v_new, pol_n1p, pol_n2p, pol_I, pol_b, use_fallback + + +@jax.jit +def le_bellman_step(v, v_aut_arr, I_aut_arr, nbar1, nbar2, + β_val, β_c_val): + """One Bellman step for the limited-enforcement-only economy.""" + v1 = jnp.interp(n1p_flat_j, n_grid_j, v) + v2 = jnp.interp(n2p_flat_j, n_grid_j, v) + Δv = v2 - v1 + + d1 = Y1 - n1p_flat_j + d2 = Y2 - n2p_flat_j + Δd = d2 - d1 + A = n_grid_j[:, None] + β_c_val * d1[None, :] + ΔB = β_c_val * Δd + enforce_feasible = ((n1p_flat_j[None, :] >= nbar1 - 1e-10) + & (n2p_flat_j[None, :] >= nbar2 - 1e-10)) + + def c_of_I(I): + return A + (I**ν) * ΔB[None, :] - θ * I + + shape_ref = n_grid_j[:, None] + 0.0 * n1p_flat_j[None, :] + I_hi = jnp.ones_like(shape_ref) * (1.0 - 1e-6) + + def shrink_hi(_, I_hi_val): + return jnp.where(c_of_I(I_hi_val) < 1e-8, + 0.9 * I_hi_val, I_hi_val) + + I_hi = jax.lax.fori_loop(0, 35, shrink_hi, I_hi) + I_lo = jnp.full_like(I_hi, 1e-7) + + def marginal(I): + λ_prime = ν * jnp.maximum(I, 1e-12)**(ν - 1.0) + current_gain = u_prime(c_of_I(I)) * (λ_prime * ΔB[None, :] - θ) + continuation_gain = β_val * λ_prime * Δv[None, :] + return current_gain + continuation_gain + + f_lo = marginal(I_lo) + f_hi = marginal(I_hi) + has_root = (f_lo > 0.0) & (f_hi < 0.0) + + def bisect_body(_, state): + lo, hi = state + mid = 0.5 * (lo + hi) + f_mid = marginal(mid) + lo = jnp.where(f_mid > 0.0, mid, lo) + hi = jnp.where(f_mid > 0.0, hi, mid) + return lo, hi + + I_lo_b, I_hi_b = jax.lax.fori_loop(0, 35, bisect_body, (I_lo, I_hi)) + I_root = 0.5 * (I_lo_b + I_hi_b) + I_star = jnp.where(f_lo <= 0.0, 0.0, + jnp.where(f_hi >= 0.0, I_hi, I_root)) + + l = λ(I_star) + b = β_c_val * ((1 - l) * d1[None, :] + l * d2[None, :]) + c = n_grid_j[:, None] + b - θ * I_star + Ev = (1 - l) * v1[None, :] + l * v2[None, :] + obj = u(c) + β_val * Ev + feasible = (enforce_feasible & (c > 1e-10) & (b >= -1e-8) + & ((has_root | (f_lo <= 0.0) | (f_hi >= 0.0)))) + obj = jnp.where(feasible, obj, -jnp.inf) + + idx = jnp.argmax(obj, axis=1) + best_val = jnp.max(obj, axis=1) + has_feasible = jnp.isfinite(best_val) + use_fallback = (~has_feasible) | (best_val <= v_aut_arr) + + pol_n1p = jnp.where(use_fallback, Y1, n1p_flat_j[idx]) + pol_n2p = jnp.where(use_fallback, Y2, n2p_flat_j[idx]) + pol_I = jnp.where(use_fallback, I_aut_arr, + jnp.take_along_axis(I_star, idx[:, None], axis=1)[:, 0]) + pol_b = jnp.where(use_fallback, 0.0, + jnp.take_along_axis(b, idx[:, None], axis=1)[:, 0]) + v_new = jnp.where(use_fallback, v_aut_arr, best_val) + + return v_new, pol_n1p, pol_n2p, pol_I, pol_b, use_fallback + + +@jax.jit +def policy_eval(v, v_aut_arr, pol_n1p, pol_n2p, pol_I, pol_b, + use_fallback, β_val): + """Howard policy evaluation: iterate the value under a fixed policy. + """ + R = u(n_grid_j + pol_b - θ * pol_I) + l = λ(pol_I) + + def eval_step(_, v): + v1 = jnp.interp(pol_n1p, n_grid_j, v) + v2 = jnp.interp(pol_n2p, n_grid_j, v) + v_pol = R + β_val * ((1.0 - l) * v1 + l * v2) + return jnp.where(use_fallback, v_aut_arr, v_pol) + + return jax.lax.fori_loop(0, howard_eval_steps, eval_step, v) + + +def update_nbars(v_arr, nbars, v_default, relaxation=0.5): + """Damped update of endogenous borrowing limits.""" + target = borrowing_limit_nbars(v_arr, v_default) + target = np.clip(target, n_lo, n_hi) + return (1 - relaxation) * nbars + relaxation * target + + +def mh_vfi(v_aut, β_val=None, β_c_val=None, tol=contract_tol, + max_iter=contract_max_iter, + limited_enforcement=False, loan_cap=M, + verbose=False, return_limits=False): + """Howard policy iteration for MH and MH+LE.""" + if β_val is None: + β_val = β + if β_c_val is None: + β_c_val = β_c + + _, I_aut_arr = autarky_policy(v_aut, β_val=β_val) + I_aut_j = jnp.asarray(I_aut_arr) + v_aut_j = jnp.asarray(v_aut) + v_default = default_values(v_aut, β_val=β_val) + nbars = np.array([n_lo, n_lo]) + loan_upper = loan_cap if loan_cap < big_loan_cap / 2 else Y2 - n_lo + v = jnp.asarray(contract_initial_upper(β_val, loan_upper)) + label = 'MH+LE' if limited_enforcement else 'MH' + + for it in range(max_iter): + # Policy improvement: one greedy Bellman step. + (v_greedy, pol_n1p, pol_n2p, pol_I, pol_b, + use_fb) = mh_bellman_step( + v, v_aut_j, I_aut_j, nbars[0], nbars[1], + loan_cap, β_val, β_c_val) + # Policy evaluation: iterate the value under the fixed policy. + v_new = policy_eval(v_greedy, v_aut_j, pol_n1p, pol_n2p, + pol_I, pol_b, use_fb, β_val) + limit_diff = 0.0 + if limited_enforcement: + nbars_new = update_nbars(np.asarray(v_new), nbars, v_default) + limit_diff = np.max(np.abs(nbars_new - nbars)) + nbars = nbars_new + diff = max(float(jnp.max(jnp.abs(v_new - v))), limit_diff) + v = v_new + if verbose and ((it + 1) % 5 == 0 or diff < tol): + print(f" iter {it+1:3d}, diff = {diff:.2e}, " + f"nbars = {nbars}") + if diff < tol: + break + + v = np.asarray(v) + if diff >= tol: + raise RuntimeError( + f"{label} HPI failed to converge after {max_iter} iterations " + f"(diff = {diff:.3e})" + ) + if verbose: + print( + f"{label} HPI converged after {it + 1} iterations: " + f"diff = {diff:.3e}, nbars = {np.round(nbars, 4)}" + ) + + _, pol_n1p, pol_n2p, pol_I, pol_b, _ = mh_bellman_step( + jnp.asarray(v), v_aut_j, I_aut_j, + nbars[0], nbars[1], loan_cap, β_val, β_c_val) + + result = (v, np.asarray(pol_n1p), np.asarray(pol_n2p), + np.asarray(pol_I), np.asarray(pol_b)) + if return_limits: + return result + (nbars,) + return result + + +def le_vfi(v_aut, β_val=None, β_c_val=None, tol=contract_tol, + max_iter=contract_max_iter, + verbose=False, return_limits=False): + """Howard policy iteration for the LE-only economy.""" + if β_val is None: + β_val = β + if β_c_val is None: + β_c_val = β_c + + _, I_aut_arr = autarky_policy(v_aut, β_val=β_val) + I_aut_j = jnp.asarray(I_aut_arr) + v_aut_j = jnp.asarray(v_aut) + v_default = default_values(v_aut, β_val=β_val) + nbars = np.array([n_lo, n_lo]) + v = jnp.asarray(contract_initial_upper(β_val, Y2 - n_lo)) + + for it in range(max_iter): + # Policy improvement: one greedy Bellman step + (v_greedy, pol_n1p, pol_n2p, pol_I, pol_b, + use_fb) = le_bellman_step( + v, v_aut_j, I_aut_j, nbars[0], nbars[1], β_val, β_c_val) + + # Policy evaluation: iterate the value under the fixed policy + v_new = policy_eval(v_greedy, v_aut_j, pol_n1p, pol_n2p, + pol_I, pol_b, use_fb, β_val) + nbars_new = update_nbars(np.asarray(v_new), nbars, v_default) + limit_diff = np.max(np.abs(nbars_new - nbars)) + nbars = nbars_new + diff = max(float(jnp.max(jnp.abs(v_new - v))), limit_diff) + v = v_new + if verbose and ((it + 1) % 5 == 0 or diff < tol): + print(f" iter {it+1:3d}, diff = {diff:.2e}, " + f"nbars = {nbars}") + if diff < tol: + break + + v = np.asarray(v) + if diff >= tol: + raise RuntimeError( + f"LE HPI failed to converge after {max_iter} iterations " + f"(diff = {diff:.3e})" + ) + if verbose: + print( + f"LE HPI converged after {it + 1} iterations: " + f"diff = {diff:.3e}, nbars = {np.round(nbars, 4)}" + ) + + _, pol_n1p, pol_n2p, pol_I, pol_b, _ = le_bellman_step( + jnp.asarray(v), v_aut_j, I_aut_j, + nbars[0], nbars[1], β_val, β_c_val) + + result = (v, np.asarray(pol_n1p), np.asarray(pol_n2p), + np.asarray(pol_I), np.asarray(pol_b)) + if return_limits: + return result + (nbars,) + return result + + +v_mh, pol_n1p, pol_n2p, pol_I, pol_b = mh_vfi(v_aut, verbose=True) + +(v_mhle, pol_n1p_mhle, pol_n2p_mhle, pol_I_mhle, pol_b_mhle, + nbars_mhle) = mh_vfi(v_aut, limited_enforcement=True, + loan_cap=big_loan_cap, verbose=True, + return_limits=True) + +(v_le, pol_n1p_le, pol_n2p_le, pol_I_le, pol_b_le, + nbars_le) = le_vfi(v_aut, verbose=True, return_limits=True) +``` + +### Policy diagnostics + +The helper functions below convert policies into repayments, risk-sharing +indices, capital-outflow schedules and implied interest rates. + +Before constructing the figures, the raw finite-mesh policies are polished by +continuous local optimization. + +This step is controlled by `polish_policies` and can be turned off when exact +grid policies are desired. + +The small NumPy versions of the primitives below are used only in this +SciPy-based polishing step. + +The JAX versions defined earlier are used inside JIT-compiled Bellman updates, +while SciPy's optimizer works most cleanly with ordinary NumPy arrays and Python +floats. + +```{code-cell} ipython3 +def λ_np(I): + """NumPy version of λ for plotting and simulation.""" + return np.minimum(np.asarray(I)**ν, 1.0) + + +def λ_prime_np(I): + """Derivative of λ(I)=I^ν on the interior.""" + return ν * np.maximum(np.asarray(I), 1e-12)**(ν - 1.0) + + +def u_np(c): + """NumPy CRRA utility.""" + c = np.maximum(np.asarray(c), 1e-12) + return c**(1.0 - γ) / (1.0 - γ) + + +def u_prime_np(c): + """NumPy marginal utility.""" + return np.maximum(np.asarray(c), 1e-12)**(-γ) + + +polish_policies = True +polish_tol = 1e-9 +plot_grid = np.linspace(n_lo, n_hi, 400) +rsi_bounds = (-0.25, 1.00) +y_gap = Y2 - Y1 + + +def continuation_from_rsi(n1p, rsi): + """Recover n_2' from n_1' and the risk-sharing index.""" + return n1p + (1.0 - rsi) * y_gap + + +def rsi_from_continuations(n1p, n2p): + """Risk-sharing index implied by continuation net worths.""" + return 1.0 - (n2p - n1p) / y_gap + + +def value_spline(v_arr): + """Cubic spline approximation to a converged value function.""" + return CubicSpline(n_grid, np.asarray(v_arr), bc_type='natural') + + +def eval_v(vs, n): + """Evaluate a value spline on the supported domain.""" + return float(vs(np.clip(n, n_lo, n_hi))) + + +def bisect_mh_investment(f, lo=1e-7, hi=1.0 - 1e-7, max_iter=70): + """Solve the scalar MH first-order condition robustly.""" + flo = f(lo) + fhi = f(hi) + if not np.isfinite(flo) or not np.isfinite(fhi): + return None + if flo >= 0.0: + return lo + if fhi <= 0.0: + return hi + + for _ in range(max_iter): + mid = 0.5 * (lo + hi) + fmid = f(mid) + if not np.isfinite(fmid): + hi = mid + elif fmid > 0.0: + hi = mid + else: + lo = mid + return 0.5 * (lo + hi) + + +def mh_contract_value(n, n1p, rsi, vs, nbar1, nbar2, + loan_cap, β_val=β, β_c_val=β_c): + """Best MH contract value for a candidate (n_1', RSI).""" + n2p = continuation_from_rsi(n1p, rsi) + if not (nbar1 <= n1p <= n_hi and nbar2 <= n2p <= n_hi): + return None + + v1 = eval_v(vs, n1p) + v2 = eval_v(vs, n2p) + Δv = v2 - v1 + if Δv <= 1e-10: + return None + + d1 = Y1 - n1p + d2 = Y2 - n2p + + def lender_value(I): + l = λ_np(I) + return β_c_val * ((1.0 - l) * d1 + l * d2) + + candidates = [] + + def add_candidate(b_fun, participation_check): + def foc(I): + c = n + b_fun(I) - θ * I + if c <= 1e-10: + return np.inf + return θ * u_prime_np(c) - β_val * λ_prime_np(I) * Δv + + I_star = bisect_mh_investment(foc) + if I_star is None: + return + b_star = b_fun(I_star) + c_star = n + b_star - θ * I_star + if c_star <= 1e-10 or b_star < -1e-8: + return + if not participation_check(I_star, b_star): + return + l_star = λ_np(I_star) + val = u_np(c_star) + β_val * ((1.0 - l_star) * v1 + l_star * v2) + candidates.append((float(val), n1p, n2p, float(I_star), float(b_star))) + + add_candidate( + lender_value, + lambda I, b: b <= loan_cap + 1e-8 + ) + + if loan_cap < big_loan_cap / 2: + add_candidate( + lambda I: loan_cap, + lambda I, b: lender_value(I) >= b - 1e-8 + ) + + if not candidates: + return None + return max(candidates, key=lambda x: x[0]) + + +def le_contract_value(n, n1p, rsi, I, vs, nbar1, nbar2, + β_val=β, β_c_val=β_c): + """LE contract value for a candidate (n_1', RSI, I).""" + n2p = continuation_from_rsi(n1p, rsi) + if not (nbar1 <= n1p <= n_hi and nbar2 <= n2p <= n_hi): + return None + + d1 = Y1 - n1p + d2 = Y2 - n2p + l = λ_np(I) + b = β_c_val * ((1.0 - l) * d1 + l * d2) + c = n + b - θ * I + if c <= 1e-10 or b < -1e-8: + return None + + v1 = eval_v(vs, n1p) + v2 = eval_v(vs, n2p) + val = u_np(c) + β_val * ((1.0 - l) * v1 + l * v2) + return float(val), n1p, n2p, float(I), float(b) + + +def polish_mh_state(n, raw_n1p, raw_n2p, vs, nbar1, nbar2, loan_cap): + """Local continuous re-optimization of one MH policy point.""" + raw_rsi = rsi_from_continuations(raw_n1p, raw_n2p) + starts = [ + (raw_n1p, raw_rsi), + (raw_n1p, 0.0), + (raw_n1p, 0.005), + (max(nbar1, raw_n1p - 0.03), 0.0), + (min(n_hi, raw_n1p + 0.03), 0.0) + ] + + def objective(x): + out = mh_contract_value(n, x[0], x[1], vs, nbar1, nbar2, loan_cap) + return 1e8 if out is None else -out[0] + + best = None + for start in starts: + x0 = np.array([np.clip(start[0], nbar1, n_hi), + np.clip(start[1], *rsi_bounds)]) + out0 = mh_contract_value(n, x0[0], x0[1], vs, nbar1, nbar2, loan_cap) + if out0 is not None and (best is None or out0[0] > best[0]): + best = out0 + res = minimize(objective, x0, method='Nelder-Mead', + options={'xatol': polish_tol, 'fatol': polish_tol, + 'maxiter': 500}) + x = np.array([np.clip(res.x[0], nbar1, n_hi), + np.clip(res.x[1], *rsi_bounds)]) + out = mh_contract_value(n, x[0], x[1], vs, nbar1, nbar2, loan_cap) + if out is not None and (best is None or out[0] > best[0]): + best = out + + return best + + +def polish_le_state(n, raw_n1p, raw_n2p, raw_I, vs, nbar1, nbar2): + """Local continuous re-optimization of one LE policy point.""" + raw_rsi = rsi_from_continuations(raw_n1p, raw_n2p) + starts = [ + (raw_n1p, raw_rsi, raw_I), + (raw_n1p, 0.80, raw_I), + (raw_n1p, 1.00, raw_I), + (max(nbar1, raw_n1p - 0.03), 0.80, raw_I), + (min(n_hi, raw_n1p + 0.03), 0.80, raw_I) + ] + + def objective(x): + out = le_contract_value(n, x[0], x[1], x[2], vs, nbar1, nbar2) + return 1e8 if out is None else -out[0] + + best = None + for start in starts: + x0 = np.array([np.clip(start[0], nbar1, n_hi), + np.clip(start[1], *rsi_bounds), + np.clip(start[2], 0.0, 1.0 - 1e-7)]) + out0 = le_contract_value(n, x0[0], x0[1], x0[2], vs, nbar1, nbar2) + if out0 is not None and (best is None or out0[0] > best[0]): + best = out0 + res = minimize(objective, x0, method='Nelder-Mead', + options={'xatol': polish_tol, 'fatol': polish_tol, + 'maxiter': 700}) + x = np.array([np.clip(res.x[0], nbar1, n_hi), + np.clip(res.x[1], *rsi_bounds), + np.clip(res.x[2], 0.0, 1.0 - 1e-7)]) + out = le_contract_value(n, x[0], x[1], x[2], vs, nbar1, nbar2) + if out is not None and (best is None or out[0] > best[0]): + best = out + + return best + + +def polish_mh_policy(v_arr, n1p_arr, n2p_arr, I_arr, b_arr, + nbars=None, loan_cap=M): + """Polish all MH or MH+LE policy points with continuous local search.""" + vs = value_spline(v_arr) + nbar1, nbar2 = (n_lo, n_lo) if nbars is None else nbars + out_v, out_n1p, out_n2p, out_I, out_b = [], [], [], [], [] + failures = 0 + + for n, n1_raw, n2_raw, I_raw, b_raw, v_raw in zip( + n_grid, n1p_arr, n2p_arr, I_arr, b_arr, v_arr): + best = polish_mh_state(n, n1_raw, n2_raw, vs, + nbar1, nbar2, loan_cap) + if best is None: + failures += 1 + best = (v_raw, n1_raw, n2_raw, I_raw, b_raw) + val, n1p, n2p, I, b = best + out_v.append(val) + out_n1p.append(n1p) + out_n2p.append(n2p) + out_I.append(I) + out_b.append(b) + + if failures: + print(f"MH polish fallback points: {failures}") + return map(np.asarray, (out_v, out_n1p, out_n2p, out_I, out_b)) + + +def polish_le_policy(v_arr, n1p_arr, n2p_arr, I_arr, b_arr, nbars): + """Polish all LE policy points with continuous local search.""" + vs = value_spline(v_arr) + nbar1, nbar2 = nbars + out_v, out_n1p, out_n2p, out_I, out_b = [], [], [], [], [] + failures = 0 + + for n, n1_raw, n2_raw, I_raw, b_raw, v_raw in zip( + n_grid, n1p_arr, n2p_arr, I_arr, b_arr, v_arr): + best = polish_le_state(n, n1_raw, n2_raw, I_raw, vs, nbar1, nbar2) + if best is None: + failures += 1 + best = (v_raw, n1_raw, n2_raw, I_raw, b_raw) + val, n1p, n2p, I, b = best + out_v.append(val) + out_n1p.append(n1p) + out_n2p.append(n2p) + out_I.append(I) + out_b.append(b) + + if failures: + print(f"LE polish fallback points: {failures}") + return map(np.asarray, (out_v, out_n1p, out_n2p, out_I, out_b)) + + +def make_policy(name, n1p, n2p, I, b, v, nbars=None): + """Collect a regime's policy arrays and derived schedules.""" + d1 = Y1 - n1p + d2 = Y2 - n2p + l = λ_np(I) + policy = { + 'name': name, + 'n1p': n1p, + 'n2p': n2p, + 'I': I, + 'b': b, + 'v': v, + 'nbars': nbars, + 'd1': d1, + 'd2': d2, + 'λ': l, + 'Enp': (1 - l) * n1p + l * n2p, + 'RSI': (d2 - d1) / (Y2 - Y1), + 'ca1': Y1 - n_grid - b, + 'ca2': Y2 - n_grid - b + } + spline_keys = ['n1p', 'n2p', 'I', 'b', 'v', 'd1', 'd2', 'λ', + 'Enp', 'RSI', 'ca1', 'ca2'] + policy['splines'] = { + key: CubicSpline(n_grid, policy[key], bc_type='natural') + for key in spline_keys + } + return policy + + +if polish_policies: + v_mh, pol_n1p, pol_n2p, pol_I, pol_b = polish_mh_policy( + v_mh, pol_n1p, pol_n2p, pol_I, pol_b, loan_cap=M) + v_mhle, pol_n1p_mhle, pol_n2p_mhle, pol_I_mhle, pol_b_mhle = ( + polish_mh_policy(v_mhle, pol_n1p_mhle, pol_n2p_mhle, + pol_I_mhle, pol_b_mhle, nbars_mhle, + loan_cap=big_loan_cap)) + v_le, pol_n1p_le, pol_n2p_le, pol_I_le, pol_b_le = polish_le_policy( + v_le, pol_n1p_le, pol_n2p_le, pol_I_le, pol_b_le, nbars_le) + + +policies = { + 'MH': make_policy('MH', pol_n1p, pol_n2p, pol_I, pol_b, v_mh), + 'MH+LE': make_policy('MH+LE', pol_n1p_mhle, pol_n2p_mhle, + pol_I_mhle, pol_b_mhle, v_mhle, nbars_mhle), + 'LE': make_policy('LE', pol_n1p_le, pol_n2p_le, + pol_I_le, pol_b_le, v_le, nbars_le) +} + + +def policy_at(policy, key, n): + """Cubic-spline interpolation of a policy at net worth n.""" + n_clip = np.clip(n, n_grid[0], n_grid[-1]) + return float(policy['splines'][key](n_clip)) + + +def policy_curve(policy, key): + """Evaluate a policy on the dense grid used in the figures.""" + return policy['splines'][key](plot_grid) + + +def next_period_c(policy, n_next): + """Consumption at the start of next period given continuation net worth.""" + b_next = policy_at(policy, 'b', n_next) + I_next = policy_at(policy, 'I', n_next) + return n_next + b_next - θ * I_next + + +def implied_R(policy, n): + """Implied one-period gross interest rate at net worth n.""" + b = policy_at(policy, 'b', n) + I = policy_at(policy, 'I', n) + n1p = policy_at(policy, 'n1p', n) + n2p = policy_at(policy, 'n2p', n) + c = n + b - θ * I + l = λ_np(I) + c1p = next_period_c(policy, n1p) + c2p = next_period_c(policy, n2p) + denom = β * ((1 - l) * float(u_prime(c1p)) + + l * float(u_prime(c2p))) + return float(u_prime(c)) / denom if denom > 1e-10 else np.nan + + +def implied_R_schedule(policy): + return np.asarray([implied_R(policy, n) for n in n_grid]) + + +def repeated_low_limit(policy, T=100): + """Approximate the lowest net worth reached after repeated low outputs.""" + n = Y1 + path = [n] + for _ in range(T): + n = policy_at(policy, 'n1p', n) + path.append(n) + return float(np.min(path[-20:])) + + +n_low_mh = repeated_low_limit(policies['MH']) +n_low_mhle = repeated_low_limit(policies['MH+LE']) +n_low_le = repeated_low_limit(policies['LE']) +print(f"Approximate low-state limit, MH: {n_low_mh:.4f}") +print(f"Approximate low-state limit, MH+LE: {n_low_mhle:.4f}") +print(f"Approximate low-state limit, LE: {n_low_le:.4f}") + +low_limits = {'MH': n_low_mh, 'MH+LE': n_low_mhle, 'LE': n_low_le} +``` + +### Value functions and insurance + +Now let's plot the value functions and risk-sharing indices. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: value functions and risk-sharing index + name: fig-tsy-value-rsi +--- +fig, axes = plt.subplots(1, 2, figsize=(12, 4)) + +axes[0].plot(plot_grid, CubicSpline(n_grid, v_aut, bc_type='natural')(plot_grid), + lw=2, color='0.45', label='Autarky') +for name, style in [('MH', '-'), ('MH+LE', '--'), ('LE', ':')]: + axes[0].plot(plot_grid, policy_curve(policies[name], 'v'), + lw=2, ls=style, label=name) +axes[0].set_xlabel('net worth $n$') +axes[0].set_ylabel('value') +axes[0].legend() + +for name, style in [('MH', '-'), ('MH+LE', '--'), ('LE', ':')]: + λ_plot = policy_curve(policies[name], 'λ') + rsi_plot = np.where(λ_plot > 0.01, + policy_curve(policies[name], 'RSI'), np.nan) + axes[1].plot(plot_grid, rsi_plot, lw=2, ls=style, label=name) +axes[1].axhline(1.0, ls=':', color='k', lw=1, + label='Full insurance') +axes[1].axhline(0.0, ls='--', color='k', lw=1, + label='Non-contingent debt') +axes[1].set_xlabel('net worth $n$') +axes[1].set_ylabel('risk-sharing index') +axes[1].set_ylim(-0.15, 1.15) +axes[1].legend() + +plt.tight_layout() +plt.show() + +for name in ['MH', 'MH+LE', 'LE']: + active = policies[name]['λ'] > 0.01 + rsi_active = policies[name]['RSI'][active] + support_lo = max(0.38, low_limits[name] - 1e-8) + support = ((n_grid >= support_lo) & (n_grid <= 1.02) + & (policies[name]['λ'] > 0.01) & (policies[name]['λ'] < 0.99)) + rsi_support = policies[name]['RSI'][support] + print(f"{name:5s}: mean RSI = {np.mean(rsi_active): .4f}, " + f"max RSI on support = {np.max(np.abs(rsi_support)): .4f}") +``` + +In {cite:t}`Tsyrennikov2013`, the moral-hazard economy has essentially +state non-contingent repayment: the maximal risk-sharing index is below 0.01. + +In the limited-enforcement economy, by contrast, the same index is about 0.80 +on average, so the contract offers a significant amount of insurance. + +Our computed schedules show the same pattern: under moral hazard the repayment +schedule $\{d_1(n), d_2(n)\}$ is nearly state non-contingent on the relevant +range of net worth, while under limited enforcement it is much more state +contingent. + +Small irregularities near the low net worth and high net worth are mainly +numerical effects of the finite grid and the local optimization. + +This is the paper's central result about the optimal contract: under moral +hazard nearly all the risk is assumed by the risk-averse borrower, and +insurance comes mainly through access to borrowing rather than through +state-contingent repayment. + +### Policy functions + +The next figure follows the structure and terminology of Figure 3 in +{cite:t}`Tsyrennikov2013`. + +Panel F multiplies the MH and MH+LE risk-sharing indices by 10, as in the +paper, so their near-zero variation is visible on the same scale as LE. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: policy functions in the MH, MH+LE and LE economies + name: fig-tsy-policy-functions +--- +fig, axes = plt.subplots(3, 2, figsize=(10, 15), sharex=True) +ax = axes.ravel() + +paper_ylims = [(0.0, 0.6), (0.4, 1.1), + (0.05, 0.5), (0.0, 0.6), + (-0.15, 0.2), (0.0, 1.1)] +paper_yticks = [np.arange(0.0, 0.61, 0.1), np.arange(0.4, 1.01, 0.1), + np.arange(0.05, 0.51, 0.05), np.arange(0.0, 0.61, 0.1), + np.arange(-0.15, 0.21, 0.05), np.arange(0.0, 1.01, 0.2)] +paper_xticks = np.arange(0.4, 1.01, 0.1) + +for a, ylim, yticks in zip(ax, paper_ylims, paper_yticks): + a.set_box_aspect(1) + a.axvline(n_low_mh, color='0.25', lw=1, ls='--') + a.axvline(n_low_le, color='0.55', lw=1, ls=':') + a.set_xlim(0.4, 1.0) + a.set_xticks(paper_xticks) + a.set_ylim(*ylim) + a.set_yticks(yticks) + +ax[0].plot(plot_grid, policy_curve(policies['MH'], 'λ'), lw=2, label='MH') +ax[0].plot(plot_grid, policy_curve(policies['MH+LE'], 'λ'), + lw=2, ls='--', label='MH+LE') +ax[0].plot(plot_grid, policy_curve(policies['LE'], 'λ'), + lw=2, ls=':', label='LE') +ax[0].set_ylabel(r'$\lambda(I)$') +ax[0].set_title('A. investment') +ax[0].legend(fontsize=9) + +for name, style in [('MH', '-'), ('MH+LE', '--'), ('LE', ':')]: + ax[1].plot(plot_grid, policy_curve(policies[name], 'Enp'), + lw=2, ls=style, label=name) +ax[1].plot(plot_grid, plot_grid, color='k', lw=1, ls=':', + label='45-degree') +ax[1].set_ylabel(r"$E[n']$") +ax[1].set_title('B. expected future net worth') +ax[1].legend(fontsize=9) + +ax[2].plot(plot_grid, policy_curve(policies['MH'], 'b'), + lw=2, label=r'$b_{MH}$') +ax[2].plot(plot_grid, policy_curve(policies['MH'], 'd1'), + lw=2, ls='--', label=r'$d_{1,MH}$') +ax[2].plot(plot_grid, policy_curve(policies['MH'], 'd2'), + lw=2, ls=':', label=r'$d_{2,MH}$') +ax[2].set_ylabel('loan and repayment') +ax[2].set_title('C. MH contract') +ax[2].legend(fontsize=9) + +ax[3].plot(plot_grid, policy_curve(policies['LE'], 'b'), + lw=2, label=r'$b_{LE}$') +ax[3].plot(plot_grid, policy_curve(policies['LE'], 'd1'), + lw=2, ls='--', label=r'$d_{1,LE}$') +ax[3].plot(plot_grid, policy_curve(policies['LE'], 'd2'), + lw=2, ls=':', label=r'$d_{2,LE}$') +ax[3].set_ylabel('loan and repayment') +ax[3].set_title('D. LE contract') +ax[3].legend(fontsize=9) + +ax[4].plot(plot_grid, policy_curve(policies['MH'], 'ca1'), + lw=2, label=r'$ca_{1,MH}$') +ax[4].plot(plot_grid, policy_curve(policies['MH'], 'ca2'), + lw=2, ls='--', label=r'$ca_{2,MH}$') +ax[4].plot(plot_grid, policy_curve(policies['LE'], 'ca1'), + lw=2, ls=':', label=r'$ca_{1,LE}$') +ax[4].plot(plot_grid, policy_curve(policies['LE'], 'ca2'), + lw=2, ls='-.', label=r'$ca_{2,LE}$') +ax[4].axhline(0, color='k', lw=0.8) +ax[4].set_xlabel('net worth $n$') +ax[4].set_ylabel('capital outflows') +ax[4].set_title('E. capital outflows') +ax[4].legend(fontsize=8) + +ax[5].plot(plot_grid, 10 * policy_curve(policies['MH'], 'RSI'), + lw=2, label=r'$10\times$ MH') +ax[5].plot(plot_grid, 10 * policy_curve(policies['MH+LE'], 'RSI'), + lw=2, ls='--', + label=r'$10\times$ MH+LE') +ax[5].plot(plot_grid, policy_curve(policies['LE'], 'RSI'), + lw=2, ls=':', label='LE') +ax[5].axhline(0, color='k', lw=0.8) +ax[5].set_xlabel('net worth $n$') +ax[5].set_ylabel('risk-sharing index') +ax[5].set_title('F. risk sharing') +ax[5].legend(fontsize=9) + +plt.tight_layout() +plt.show() +``` + +Panel A plots the optimal weight on the high-output outcome, +$\lambda(I(n))$. + +In the MH economy, investment is sensitive to the financial position of the +borrower at low levels of net worth. + +This positive-slope part of the investment policy is the paper's internal +propagation mechanism: after a low-output realization, net worth declines, +investment declines, and probability weight shifts toward the low-output +outcome. + +Panel B plots expected future net worth, +$E[Y_j-d_j(n)]$. + +The MH schedule lies below the LE schedule at high net worth, so net worth +drifts down faster in the MH economy. + +In the paper this slope difference is only 0.005, yet it is equivalent to +raising the borrower's discount rate by 2% per annum. + +At low net worth, expected future net worth can decrease with current net worth +because the endogenous improvement in the output distribution raises the +probability of the large repayment. + +Panels C and D plot the optimal loan and repayment schedules. + +In the MH economy, $b(n)$, $d_1(n)$ and $d_2(n)$ are close to one another: the +contract is close to state non-contingent debt. + +In the LE economy, repayment varies much more across output states, reflecting +the larger amount of insurance provided by the contract. + +The LE investment schedule is also higher and less volatile: investment is +observable, so the creditor can dictate more investment than the borrower would +choose under moral hazard. + +For this reason the LE economy's internal propagation mechanism is weak. + +Panel E plots capital outflows, denoted $ca_j(n)$ in the paper. + +Current output matters because it determines the repayment due on the previous +contract. + +At high net worth, the insurance effect dominates, so capital outflows are more +positively related to output. + +At low net worth, the incentive effect becomes stronger: a low-output realization +must reduce the borrower's net worth, which can increase capital outflows. + +Panel F is the risk-sharing index. + +This panel is the visual counterpart to the state +non-contingency result: RSI is close to zero in the MH economy and much larger +in the LE economy. + +### Crisis dynamics + +{cite:t}`Tsyrennikov2013` shows that a string of low output realizations +generates gradual debt accumulation followed by a sudden stop in which capital +inflows cease and interest rates spike --- a pattern consistent with the +Argentina 2001 experience. + +```{code-cell} ipython3 +--- +mystnb: + figure: + caption: simulated crisis dynamics + name: fig-tsy-crisis +--- +def simulate_crisis(policy, T_crisis=8): + """ + Simulate T_crisis periods of low output Y1, starting with zero debt. + """ + n = Y1 + records = {'n': [n], 'debt_over_Y': [], 'R': [], 'ca_over_Y': [], + 'λ': []} + + for _ in range(T_crisis): + b = policy_at(policy, 'b', n) + I = policy_at(policy, 'I', n) + n1p = policy_at(policy, 'n1p', n) + + debt_Y = (Y1 - n) / Y1 + ca = Y1 - n - b + R = implied_R(policy, n) + + records['debt_over_Y'].append(debt_Y) + records['R'].append(R) + records['ca_over_Y'].append(ca / Y1) + records['λ'].append(λ_np(I)) + + n = n1p + records['n'].append(n) + + return records + + +crises = {name: simulate_crisis(policy, T_crisis=8) + for name, policy in policies.items()} +t_ax = np.arange(8) +styles = {'MH': ('o-', 'C0'), 'MH+LE': ('s--', 'C1'), 'LE': ('^:', 'C2')} + +fig, axes = plt.subplots(2, 2, figsize=(12, 8), sharex=True) + +for name, crisis in crises.items(): + marker, color = styles[name] + axes[0, 0].plot(t_ax, crisis['debt_over_Y'], marker, + lw=2, color=color, label=name) +axes[0, 0].set_ylabel('debt / output') +axes[0, 0].legend(fontsize=9) + +for name, crisis in crises.items(): + marker, color = styles[name] + axes[0, 1].plot(t_ax, np.asarray(crisis['R'])**4, marker, + lw=2, color=color, label=name) +axes[0, 1].axhline((1 / β_c)**4, ls='--', color='k', lw=0.8, + label='World rate') +axes[0, 1].set_ylabel('annualized gross rate') +axes[0, 1].legend(fontsize=9) + +for name, crisis in crises.items(): + marker, color = styles[name] + axes[1, 0].plot(t_ax, crisis['ca_over_Y'], marker, + lw=2, color=color, label=name) +axes[1, 0].axhline(0, ls='--', color='k', lw=0.8) +axes[1, 0].set_xlabel('quarter') +axes[1, 0].set_ylabel('current account / output') + +for name, crisis in crises.items(): + marker, color = styles[name] + axes[1, 1].plot(t_ax, crisis['λ'], marker, + lw=2, color=color, label=name) +axes[1, 1].set_xlabel('quarter') +axes[1, 1].set_ylabel(r'$\lambda(I) = \Pr(Y_2 \mid I)$') + +plt.tight_layout() +plt.show() + +for name, crisis in crises.items(): + low_path_prob = np.prod(1 - np.asarray(crisis['λ'])) + print(f"{name:5s}: probability of this low-output path = " + f"{low_path_prob:.4f}") +``` + +The simulation parallels Figure 4 of the paper. + +Starting from zero debt, a path of low-output realizations makes the MH economy +steadily accumulate obligations. + +Debt/output and the current account move before the interest rate does. + +When the borrower nearly exhausts borrowing capacity, the interest rate jumps. + +Thus the interest rate gives a **late warning** about the economy's health, +unlike debt and the current account. + +The bottom-left panel shows the current account first increasing gradually, +meaning that capital inflows gradually shrink, and then moving sharply when +borrowing capacity is nearly exhausted. + +The bottom-right panel shows the probability of the high-output outcome. + +As the borrower's net worth deteriorates, investment falls and +$\lambda(I)$ falls, making the low-output path more likely than it would be in +the frictionless or LE economies. + +### MH versus limited enforcement + +A crucial result of {cite:t}`Tsyrennikov2013` is that limited enforcement +contributes almost nothing: on its own it leaves the model close to the +frictionless benchmark, and added to moral hazard it barely changes the MH +results. + +The reason is visible in the Euler equations. + +Moral hazard and limited enforcement push the dynamics in opposite directions. + +Moral hazard requires the creditor to spread the continuation value of the +borrower across future states, which shifts risk onto the borrower and produces +immiseration. + +Limited enforcement without moral hazard pushes expected net worth upward until +the enforcement constraints no longer bind. + +When both frictions are present, limited enforcement can **turn off** moral +hazard near the borrowing limits: the borrowing limits already spread +continuation values enough to provide incentives, so the incentive multiplier +collapses to zero. + +These are visible in the figures we showed above: the MH and MH+LE policies are very close to each other, while the LE policy is quite different, while +LE is closer to the frictionless benchmark. + +## Exercises + +```{exercise-start} +:label: tsyrennikov_2013_ex1 +``` + +*Effect of the default penalty.* The parameter $\delta \in (0,1)$ is the +fraction of output retained after default. + +1. Using $v_{\text{aut}}$, compute + $v_{\text{aut}}^{\delta}(Y_j)$ for + $\delta \in \{0.5,\, 0.795,\, 0.95\}$ and $j=1,2$. +2. For each $\delta$, compare the two enforcement thresholds. +3. Discuss: how does a milder default penalty, corresponding to a larger + $\delta$, affect the tightness of the enforcement constraint and, via the + Euler equation, the interest rate spread? At $\delta = 1$ default carries no + output penalty, so the enforcement constraint is tightest; as + $\delta \to 0$ the penalty is harsh and the constraint rarely binds. +```{exercise-end} +``` + +```{solution-start} tsyrennikov_2013_ex1 +:class: dropdown +``` + +Here is one solution: + +```{code-cell} ipython3 +fig, ax = plt.subplots() + +def default_values_for_delta(δ_val, v_aut_arr, β_val=None): + """Compute v_aut^δ(Y_j), j=1,2, from the paper's default problem.""" + if β_val is None: + β_val = β + + Ev1 = np.interp(Y1, n_grid, v_aut_arr) + Ev2 = np.interp(Y2, n_grid, v_aut_arr) + out = [] + + for Yj in (Y1, Y2): + I = I_search_grid[I_search_grid <= min(Yj, 1.0)] + c = δ_val * Yj - θ * I + l = λ(I) + obj = u(c) + β_val * ((1.0 - l) * Ev1 + l * Ev2) + obj = np.where(c > 1e-10, obj, -np.inf) + out.append(float(np.max(obj))) + + return out + +for δ_val in (0.50, 0.795, 0.95): + thresh1, thresh2 = default_values_for_delta(δ_val, v_aut) + print( + f"δ={δ_val:.3f}: v_aut^δ(Y1)={thresh1:.3f}, " + f"v_aut^δ(Y2)={thresh2:.3f}" + ) + +ax.plot(n_grid, v_aut, lw=2) +for δ_val, color in [(0.50, 'C0'), (0.795, 'C1'), (0.95, 'C2')]: + t1, t2 = default_values_for_delta(δ_val, v_aut) + ax.axhline(t1, ls=':', color=color, lw=1.5, + label=fr'$\delta={δ_val}$: $v_{{\rm aut}}^\delta(Y_1)$') + ax.axhline(t2, ls='--', color=color, lw=1.0, + label=fr'$\delta={δ_val}$: $v_{{\rm aut}}^\delta(Y_2)$') + +ax.set_xlabel('net worth $n$') +ax.set_ylabel(r'$v_{\rm aut}(n)$') +ax.set_title('autarky value and enforcement thresholds') +ax.legend(fontsize=9) +plt.tight_layout() +plt.show() +``` + +A larger $\delta$ means a milder default penalty. + +It raises the enforcement thresholds, tightens the enforcement constraints, +and reduces the scope for state-contingent repayment. + +In the full model, this can make limited enforcement bind before the +moral-hazard constraint does. + +Near the borrowing limit, limited enforcement can already force enough +continuation-value dispersion to reduce the incentive multiplier. + +As $\delta \to 0$ the enforcement constraint rarely binds and the model +approaches pure moral hazard. + +```{solution-end} +``` + +```{exercise-start} +:label: tsyrennikov_2013_ex2 +``` + +*Discounting wedge and impatience.* + +1. Re-solve the MH model for $\beta = \beta_c = 0.990$ (equal discounting --- + no impatience wedge) and for $\beta = 0.950$ (larger wedge). +2. For each case, plot the expected continuation net worth + $\mathbb{E}[n'] = (1-\lambda(I^*))n_1' + \lambda(I^*)n_2'$ against $n$. +3. Discuss: how does the discount wedge $\beta_c - \beta$ interact with moral + hazard in determining the stationary distribution of net worth? + +*Hint*: When $\beta = \beta_c$ the only force pushing net worth down is moral +hazard (immiseration). When $\beta < \beta_c$ there is an additional +front-loading incentive that the lender can exploit. +```{exercise-end} +``` + +```{solution-start} tsyrennikov_2013_ex2 +:class: dropdown +``` + +Here is one solution: + +```{code-cell} ipython3 +fig, ax = plt.subplots() + +for β_val, ls, color in [ + (0.990, '-', 'C0'), + (0.980, '--', 'C1'), + (0.950, ':', 'C2') +]: + v_a_tmp = autarky_vfi(β_val=β_val) + v_mh_tmp, pol_n1p_tmp, pol_n2p_tmp, pol_I_tmp, _ = mh_vfi( + v_a_tmp, β_val=β_val) + + E_np = ((1 - λ(pol_I_tmp)) * pol_n1p_tmp + + λ(pol_I_tmp) * pol_n2p_tmp) + ax.plot(n_grid, E_np, ls=ls, color=color, + label=fr'$\beta={β_val}$') + +ax.plot(n_grid, n_grid, lw=1, ls=':', color='k', label='45° line') +ax.set_xlabel('net worth $n$') +ax.set_ylabel("$E[n']$") +ax.set_title('continuation net worth across discount factors') +ax.legend() +plt.tight_layout() +plt.show() +``` + +The larger the discount wedge $\beta_c - \beta$, the faster net worth drifts +toward the borrowing limit. + +When $\beta = \beta_c$, moral hazard alone drives the downward drift; a +positive wedge adds a front-loading motive that accelerates it. + +Tsyrennikov notes that even small differences in discounting significantly +speed up convergence to the stationary distribution of net worth, which is why +the calibration keeps the wedge small ($\beta = 0.980$ against +$\beta_c = 0.990$). + +```{solution-end} +``` + +```{exercise-start} +:label: tsyrennikov_2013_ex3 +``` + +*The envelope condition.* In deriving the Euler equation +{eq}`eq:tsyrennikov_euler` we used the envelope result + +$$ +v'(n) = u'(c) - \mu\,\theta\, u''(c), +\qquad c = n + b - \theta I . +$$ + +Derive it from the Lagrangian {eq}`eq:tsyrennikov_lagrangian`. + +*Hint*: By the envelope theorem, differentiate $\mathcal{L}$ with respect to the +state $n$, holding the controls $(b, d, I)$ and the multipliers fixed. Identify +which terms of $\mathcal{L}$ actually depend on $n$. +```{exercise-end} +``` + +```{solution-start} tsyrennikov_2013_ex3 +:class: dropdown +``` + +Here is one solution: + +The state $n$ enters the Lagrangian {eq}`eq:tsyrennikov_lagrangian` only through +current consumption $c = n + b - \theta I$, and only in two terms: the period +utility $u(n+b-\theta I)$ and the incentive term $-\mu\theta\,u'(n+b-\theta I)$. + +By the envelope theorem we differentiate with respect to $n$ holding the controls +and multipliers fixed. + +Since $\partial c/\partial n = 1$, + +$$ +v'(n) = \frac{\partial \mathcal{L}}{\partial n} + = u'(c)\cdot 1 + \mu\bigl(-\theta\, u''(c)\cdot 1\bigr) + = u'(c) - \mu\,\theta\, u''(c). +$$ + +Every other term depends only on the controls $(b, d, I)$ and on the +continuation values $v(Y_j - d_j)$, none of which involve the current state $n$, +so each contributes zero to $\partial \mathcal{L}/\partial n$. + +```{solution-end} +```