From 1540b2c7e7b0c3baedf3adf16045bb88db36d854 Mon Sep 17 00:00:00 2001 From: Michael Tiemann Date: Wed, 29 Apr 2026 22:16:54 +1200 Subject: [PATCH 1/5] These patches compute non-negative capital gains These changes use a simple last-in/first-out model of stock acquisition and disposal. If an agent should choose to sell stock before max acquisition period is upon them, we presume it's hardship due to a shock, and that they basically liquidate stock acquired the previous period. For stocks sold peak acquisition, we calculate the distance from the agent's age to the peak and presume they are selling stock purchased proportionally prior to the peak acquisition period. This could be improved by noticing whether there's a wide peak (in which case we should center the peak). But the general concept is to capture the taxes that fall on a symmetric buy/sell pattern. We have to adjust the initial price of the stock because older agents who acquired the stock when they were younger buy in at a very low cost (relatively speaking). The price is selected so that the stock value balances to 1, meaning younger agents pay above 1 and older agents paid below one when they "bought" the stock they hold. --- OLGModel14.m | 29 ++++++++++++++++-------- OLGModel14_HouseholdCapitalGainsFn.m | 28 +++++++++++++++++++++++ OLGModel14_HouseholdConsumptionFn.m | 20 ++++++++++++---- OLGModel14_HouseholdIncomeFn.m | 19 ++++++++++++---- OLGModel14_HouseholdReturnFn.m | 34 ++++++++++++++++++++++++---- 5 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 OLGModel14_HouseholdCapitalGainsFn.m diff --git a/OLGModel14.m b/OLGModel14.m index e446a1f..d6145b8 100644 --- a/OLGModel14.m +++ b/OLGModel14.m @@ -133,8 +133,13 @@ Params.G=0.1; % Government expenditure Params.firmbeta=1/(1+Params.r/(1-Params.tau_cg)); % 1/(1+r) but returns net of capital gains tax Params.D=0.2; % Dividends rate expected/received by households -Params.P0=1; -Params.Lhscale=0.21; % Scaling the household labor supply +Params.P0=2.22; % This price is not 1 because we need price for older and younger agents to balance +Params.Lhscale=0.22; % Scaling the household labor supply + +% We build a simple model of acquiring and disposing of stock over a lifetime +Params.S_agej_first=20; % the age at which we start acquiring more stock than noise +Params.S_agej_peak=Params.Jr+1; % the age of peak acquisition +Params.S_agej_last=Params.J-10; % the age of final disposal %% Grids for household @@ -184,8 +189,8 @@ % For households DiscountFactorParamNames.household={'beta','sj'}; % Notice we use 'OLGModel14_HouseholdReturnFn' -ReturnFn.household=@(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg)... - OLGModel14_HouseholdReturnFn(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg); +ReturnFn.household=@(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last)... + OLGModel14_HouseholdReturnFn(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last); % For firms DiscountFactorParamNames.firm={'firmbeta'}; @@ -243,8 +248,14 @@ FnsToEvaluate.S.household = @(h,sprime,s,z,e) s; % Aggregate share holdings FnsToEvaluate.PensionSpending.household = @(h,sprime,s,z,e,pension,agej,Jr) (agej>=Jr)*pension; % Total spending on pensions FnsToEvaluate.PayrollTaxRevenue.household = @(h,sprime,s,z,e,agej,Jr,tau_l,w,kappa_j,Lhscale) (agej0.2,1,'first')-1,1); +Params.S_agej_last=min(find(AgeConditionalStats.household.Mean>0.5,1,'last')+1,length(AgeConditionalStats.household.Mean)); +[~,Params.S_agej_peak]=max(AgeConditionalStats.household.Mean); + % From firms FnsToEvaluate.Output.firm = @(d,kprime,k,z,w,alpha_k,alpha_l) z*(k^alpha_k)*((w/(alpha_l*z*(k^alpha_k)))^(1/(alpha_l-1)))^alpha_l; % Production function z*(k^alpha_k)*(l^alpha_l) (substituting for l) FnsToEvaluate.L_f.firm = @(d,kprime,k,z,w,alpha_k,alpha_l) (w/(alpha_l*z*(k^alpha_k)))^(1/(alpha_l-1)); % (effective units of) labor demanded by firm @@ -314,8 +325,8 @@ %% Calculate some aggregates and print findings about them % Add consumption to the FnsToEvaluate -FnsToEvaluate.Consumption.household=@(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg) OLGModel14_HouseholdConsumptionFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg); -FnsToEvaluate.Income.household=@(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg) OLGModel14_HouseholdIncomeFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg); +FnsToEvaluate.Consumption.household=@(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last) OLGModel14_HouseholdConsumptionFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last); +FnsToEvaluate.Income.household=@(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last) OLGModel14_HouseholdIncomeFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last); AggVars=EvalFnOnAgentDist_AggVars_FHorz_Case1_PType(StationaryDist, Policy, FnsToEvaluate, Params, n_d, n_a, n_z,N_j, Names_i, d_grid, a_grid, z_grid,simoptions); @@ -335,7 +346,7 @@ fprintf('Output: Y=%8.2f \n',AggVars.Output.Mean) fprintf('Aggregate TFP: Y=%8.2f \n',AggregateTFP) fprintf('Capital-Output ratio (firm side): K/Y=%8.2f \n',AggVars.K.Mean/Y) -fprintf('Total asset value (HH side): P*S=%8.2f \n',P*AggVars.S.Mean) +fprintf('Total asset value (HH side): P*S=%8.2f; n.b.: P*S/P0=%8.2f \n',P*AggVars.S.Mean,P*AggVars.S.Mean/Params.P0) fprintf('Total firm value (firm side): Value of firm=%8.2f \n',TotalValueOfFirms) fprintf('Consumption-Output ratio: C/Y=%8.2f \n',AggVars.Consumption.Mean/Y) fprintf('Government-to-Output ratio: G/Y=%8.2f \n', Params.G/Y) diff --git a/OLGModel14_HouseholdCapitalGainsFn.m b/OLGModel14_HouseholdCapitalGainsFn.m new file mode 100644 index 0000000..5a292a6 --- /dev/null +++ b/OLGModel14_HouseholdCapitalGainsFn.m @@ -0,0 +1,28 @@ +function cg=OLGModel14_HouseholdCapitalGainsFn(h,sprime,s,z,e,agej,P0,AccidentBeq,r,tau_cg,S_agej_first,S_agej_peak,S_agej_last) +% Replace assets with 'share holdings' +% Get rid of progressive taxes +% Add Lhnormalize + +% We can get P from the equation that defines r as the return to the mutual fund +% 1+r = (P0 +(1-tau_d)D - tau_cg(P0-P))/Plag +% We are looking at stationary general eqm, so +% Plag=P; +% And thus we have P=((1-tau_cg)*P0 + (1-tau_d)*D)/(1+r-tau_cg); + +if sprime>=s + Plag=P0; % We are holding or buying, so no capital gains +else + if agej<=S_agej_peak + Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently + else + % Estimate where we are past peak accumulation and mirror around to + % proportional acquisition point + agej_selling_pct=(agej-S_agej_peak)/(S_agej_last-S_agej_peak); + agej_bought=S_agej_peak-ceil(agej_selling_pct*(S_agej_peak-S_agej_first)); + Plag=P0*(1-2*r)^(agej-agej_bought); + end +end + +cg=tau_cg*(P0-Plag)*(s+AccidentBeq); + +end diff --git a/OLGModel14_HouseholdConsumptionFn.m b/OLGModel14_HouseholdConsumptionFn.m index 2dfbfd6..54e7e04 100644 --- a/OLGModel14_HouseholdConsumptionFn.m +++ b/OLGModel14_HouseholdConsumptionFn.m @@ -1,4 +1,4 @@ -function c=OLGModel14_HouseholdConsumptionFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg) +function c=OLGModel14_HouseholdConsumptionFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last) % Replace assets with 'share holdings' % Get rid of progressive taxes % Add Lhnormalize @@ -7,10 +7,22 @@ % 1+r = (P0 +(1-tau_d)D - tau_cg(P0-P))/Plag % We are looking at stationary general eqm, so % Plag=P; -% And thus we have -P=((1-tau_cg)*P0 + (1-tau_d)*D)/(1+r-tau_cg); +% And thus we have P=((1-tau_cg)*P0 + (1-tau_d)*D)/(1+r-tau_cg); -Plag=P; % As stationary general eqm +P=P0; +if sprime>=s + Plag=P0; % We are holding or buying, so no capital gains +else + if agej<=S_agej_peak + Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently + else + % Estimate where we are past peak accumulation and mirror around to + % proportional acquisition point + agej_selling_pct=(agej-S_agej_peak)/(S_agej_last-S_agej_peak); + agej_bought=S_agej_peak-ceil(agej_selling_pct*(S_agej_peak-S_agej_first)); + Plag=P0*(1-2*r)^(agej-agej_bought); + end +end if agej=s + Plag=P0; % We are holding or buying, so no capital gains +else + if agej<=S_agej_peak + Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently + else + % Estimate where we are past peak accumulation and mirror around to + % proportional acquisition point + agej_selling_pct=(agej-S_agej_peak)/(S_agej_last-S_agej_peak); + agej_bought=S_agej_peak-ceil(agej_selling_pct*(S_agej_peak-S_agej_first)); + Plag=P0*(1-2*r)^(agej-agej_bought); + end +end if agej=s + Plag=P0; % We are holding or buying, so no capital gains +else + if agej<=S_agej_peak + Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently + else + % Estimate where we are past peak accumulation and mirror around to + % proportional acquisition point + agej_selling_pct=(agej-S_agej_peak)/(S_agej_last-S_agej_peak); + agej_bought=S_agej_peak-ceil(agej_selling_pct*(S_agej_peak-S_agej_first)); + Plag=P0*(1-2*r)^(agej-agej_bought); + end +end F=-Inf; if agej Date: Thu, 30 Apr 2026 07:30:59 +1200 Subject: [PATCH 2/5] Better shaping of cost curves; pay cg upon death Also (as proposed in a different PR), fix Bequest calculations (and don't make them part of the GE, they are an artifact of the stationary distribution). --- OLGModel14.m | 49 +++++++++++++++++++--------- OLGModel14_HouseholdCapitalGainsFn.m | 18 ++++++---- OLGModel14_HouseholdConsumptionFn.m | 16 +++++---- OLGModel14_HouseholdIncomeFn.m | 16 +++++---- OLGModel14_HouseholdReturnFn.m | 16 +++++---- 5 files changed, 75 insertions(+), 40 deletions(-) diff --git a/OLGModel14.m b/OLGModel14.m index d6145b8..ec0008b 100644 --- a/OLGModel14.m +++ b/OLGModel14.m @@ -129,17 +129,18 @@ % Some initial values/guesses for variables that will be determined in general eqm Params.pension=0.4; % Initial guess (this will be determined in general eqm) Params.w=1; % Wages, determines (household) labor supply and (firm) demand -Params.AccidentBeq=0.02; % Accidental bequests (this is the lump sum transfer) +Params.AccidentBeq=0.02; % Accidental bequests (this is the lump sum transfer received after capital gains taxes and dilution due to population growth, but before estate taxes) Params.G=0.1; % Government expenditure Params.firmbeta=1/(1+Params.r/(1-Params.tau_cg)); % 1/(1+r) but returns net of capital gains tax Params.D=0.2; % Dividends rate expected/received by households -Params.P0=2.22; % This price is not 1 because we need price for older and younger agents to balance +Params.P0=2.18; % This price is not 1 because we need price for older and younger agents to balance Params.Lhscale=0.22; % Scaling the household labor supply % We build a simple model of acquiring and disposing of stock over a lifetime Params.S_agej_first=20; % the age at which we start acquiring more stock than noise -Params.S_agej_peak=Params.Jr+1; % the age of peak acquisition -Params.S_agej_last=Params.J-10; % the age of final disposal +Params.S_agej_peak_first=Params.Jr-1; % the age of first peak acquisition +Params.S_agej_peak_last=Params.Jr+5; % the age of last peak acquisition +Params.S_agej_last=Params.J-5; % the age of final disposal %% Grids for household @@ -189,8 +190,8 @@ % For households DiscountFactorParamNames.household={'beta','sj'}; % Notice we use 'OLGModel14_HouseholdReturnFn' -ReturnFn.household=@(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last)... - OLGModel14_HouseholdReturnFn(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last); +ReturnFn.household=@(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last)... + OLGModel14_HouseholdReturnFn(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last); % For firms DiscountFactorParamNames.firm={'firmbeta'}; @@ -229,7 +230,7 @@ StationaryDist=StationaryDist_Case1_FHorz_PType(jequaloneDist,AgeWeightsParamNames,PTypeDistParamNames,Policy,n_d,n_a,n_z,N_j,Names_i,pi_z,Params,simoptions); %% General eqm variables -GEPriceParamNames={'pension','AccidentBeq','G','w','firmbeta','D','P0'}; +GEPriceParamNames={'pension','G','w','firmbeta','D','P0'}; % We don't need P % We can get P from the equation that defines r as the return to the mutual fund % 1+r = (P0 +(1-tau_d)D - tau_cg(P0-P))/Plag @@ -248,13 +249,27 @@ FnsToEvaluate.S.household = @(h,sprime,s,z,e) s; % Aggregate share holdings FnsToEvaluate.PensionSpending.household = @(h,sprime,s,z,e,pension,agej,Jr) (agej>=Jr)*pension; % Total spending on pensions FnsToEvaluate.PayrollTaxRevenue.household = @(h,sprime,s,z,e,agej,Jr,tau_l,w,kappa_j,Lhscale) (agej0.2,1,'first')-1,1); -Params.S_agej_last=min(find(AgeConditionalStats.household.Mean>0.5,1,'last')+1,length(AgeConditionalStats.household.Mean)); -[~,Params.S_agej_peak]=max(AgeConditionalStats.household.Mean); +Params.S_agej_first=max(find(AgeConditionalStats.household.Mean>0.1,1,'first')-1,1); +Params.S_agej_last=min(find(AgeConditionalStats.household.Mean>0.5,1,'last')+1,length(AgeConditionalStats.household.Mean)); % warmglow creates extra long tail we want to ignore +[~,S_agej_peak]=max(AgeConditionalStats.household.Mean); +S_peak_inflection_value=(1-1/Params.J)*AgeConditionalStats.household.Mean(S_agej_peak); +for S_agej_peak_first=S_agej_peak:-1:Params.S_agej_first + if AgeConditionalStats.household.Mean(Params.S_agej_peak_first)S_peak_inflection_value + break + end +end +Params.S_agej_peak_last=S_agej_peak_last; % From firms FnsToEvaluate.Output.firm = @(d,kprime,k,z,w,alpha_k,alpha_l) z*(k^alpha_k)*((w/(alpha_l*z*(k^alpha_k)))^(1/(alpha_l-1)))^alpha_l; % Production function z*(k^alpha_k)*(l^alpha_l) (substituting for l) @@ -268,7 +283,7 @@ GeneralEqmEqns.sharemarket = @(S) S-1; % mass of all shares equals one GeneralEqmEqns.labormarket = @(L_h,L_f) L_h-L_f; % labor supply of households equals labor demand of firms GeneralEqmEqns.pensions = @(PensionSpending,PayrollTaxRevenue) PensionSpending-PayrollTaxRevenue; % Retirement benefits equal Payroll tax revenue: pension*fractionretired-tau*w*H -GeneralEqmEqns.bequests = @(AccidentalBeqLeft,AccidentBeq,n) AccidentalBeqLeft/(1+n)-AccidentBeq; % Accidental bequests received equal accidental bequests left +% GeneralEqmEqns.bequests = @(AccidentalBeqLeft,AccidentBeq,n) AccidentalBeqLeft/(1+n)-AccidentBeq; % Accidental bequests received equal accidental bequests left GeneralEqmEqns.govbudget = @(G,tau_d,D,CapitalGainsTaxRevenue,CorpTaxRevenue) G-tau_d*D-CapitalGainsTaxRevenue-CorpTaxRevenue; % G is equal to the target, GdivYtarget*Y GeneralEqmEqns.firmdiscounting = @(firmbeta,r,tau_cg) firmbeta-1/(1+r/(1-tau_cg)); % Firms discount rate is related to market return rate GeneralEqmEqns.dividends = @(D,DividendPaid) D-DividendPaid; % That the dividend households receive equals that which firms give @@ -291,6 +306,8 @@ fprintf('Check: ShareIssuance GE condition \n') Params.P0-((((1-Params.tau_cg)*Params.P0 + (1-Params.tau_d)*Params.D)/(1+Params.r-Params.tau_cg))-AggVars.S.Mean) +% Update estimate to value from initial stationary distribution +Params.AccidentBeq=AggVars.AccidentalBeq.household.Mean; %% Solve for the General Equilibrium % heteroagentoptions.fminalgo=4 % CMA-ES algorithm @@ -300,7 +317,7 @@ % p_eqm contains the general equilibrium parameter values % Put this into Params so we can calculate things about the initial equilibrium Params.pension=p_eqm.pension; -Params.AccidentBeq=p_eqm.AccidentBeq; +% Params.AccidentBeq=p_eqm.AccidentBeq; Params.G=p_eqm.G; Params.w=p_eqm.w; Params.firmbeta=p_eqm.firmbeta; @@ -325,8 +342,8 @@ %% Calculate some aggregates and print findings about them % Add consumption to the FnsToEvaluate -FnsToEvaluate.Consumption.household=@(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last) OLGModel14_HouseholdConsumptionFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last); -FnsToEvaluate.Income.household=@(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last) OLGModel14_HouseholdIncomeFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last); +FnsToEvaluate.Consumption.household=@(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last) OLGModel14_HouseholdConsumptionFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last); +FnsToEvaluate.Income.household=@(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last) OLGModel14_HouseholdIncomeFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last); AggVars=EvalFnOnAgentDist_AggVars_FHorz_Case1_PType(StationaryDist, Policy, FnsToEvaluate, Params, n_d, n_a, n_z,N_j, Names_i, d_grid, a_grid, z_grid,simoptions); diff --git a/OLGModel14_HouseholdCapitalGainsFn.m b/OLGModel14_HouseholdCapitalGainsFn.m index 5a292a6..0925626 100644 --- a/OLGModel14_HouseholdCapitalGainsFn.m +++ b/OLGModel14_HouseholdCapitalGainsFn.m @@ -1,4 +1,4 @@ -function cg=OLGModel14_HouseholdCapitalGainsFn(h,sprime,s,z,e,agej,P0,AccidentBeq,r,tau_cg,S_agej_first,S_agej_peak,S_agej_last) +function cg=OLGModel14_HouseholdCapitalGainsFn(h,sprime,s,z,e,agej,at_death,P0,AccidentBeq,r,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last) % Replace assets with 'share holdings' % Get rid of progressive taxes % Add Lhnormalize @@ -10,19 +10,25 @@ % And thus we have P=((1-tau_cg)*P0 + (1-tau_d)*D)/(1+r-tau_cg); if sprime>=s - Plag=P0; % We are holding or buying, so no capital gains + agej_bought=agej; + cg=0; % We are holding or buying, so no capital gains else - if agej<=S_agej_peak + if agej<=S_agej_peak_first + agej_bought=agej-1; Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently else % Estimate where we are past peak accumulation and mirror around to % proportional acquisition point - agej_selling_pct=(agej-S_agej_peak)/(S_agej_last-S_agej_peak); - agej_bought=S_agej_peak-ceil(agej_selling_pct*(S_agej_peak-S_agej_first)); + agej_selling_pct=(agej-S_agej_peak_last)/(S_agej_last-S_agej_peak_last); + agej_bought=S_agej_peak_first-ceil(agej_selling_pct*(S_agej_peak_first-S_agej_first)); Plag=P0*(1-2*r)^(agej-agej_bought); end + cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime); end -cg=tau_cg*(P0-Plag)*(s+AccidentBeq); +if at_death && agej_bought>=S_agej_first + % Sell all remaining shares from first acquisition to buy-point (using geometric mean to average acquisition cost) + Plag=P0*(1-2*r)^(agej_bought-sqrt(agej_bought-S_agej_first)); + cg=cg+tau_cg*(P0-Plag)*sprime; end diff --git a/OLGModel14_HouseholdConsumptionFn.m b/OLGModel14_HouseholdConsumptionFn.m index 54e7e04..fb3f1ac 100644 --- a/OLGModel14_HouseholdConsumptionFn.m +++ b/OLGModel14_HouseholdConsumptionFn.m @@ -1,4 +1,4 @@ -function c=OLGModel14_HouseholdConsumptionFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last) +function c=OLGModel14_HouseholdConsumptionFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last) % Replace assets with 'share holdings' % Get rid of progressive taxes % Add Lhnormalize @@ -11,24 +11,28 @@ P=P0; if sprime>=s + agej_bought=agej; Plag=P0; % We are holding or buying, so no capital gains + cg=0; else - if agej<=S_agej_peak + if agej<=S_agej_peak_first + agej_bought=agej-1; Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently else % Estimate where we are past peak accumulation and mirror around to % proportional acquisition point - agej_selling_pct=(agej-S_agej_peak)/(S_agej_last-S_agej_peak); - agej_bought=S_agej_peak-ceil(agej_selling_pct*(S_agej_peak-S_agej_first)); + agej_selling_pct=(agej-S_agej_peak_last)/(S_agej_last-S_agej_peak_last); + agej_bought=S_agej_peak_first-ceil(agej_selling_pct*(S_agej_peak_first-S_agej_first)); Plag=P0*(1-2*r)^(agej-agej_bought); end + cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime); end if agej=0 is being implicitly imposed by grid on s diff --git a/OLGModel14_HouseholdIncomeFn.m b/OLGModel14_HouseholdIncomeFn.m index 8659682..ed97fd1 100644 --- a/OLGModel14_HouseholdIncomeFn.m +++ b/OLGModel14_HouseholdIncomeFn.m @@ -1,4 +1,4 @@ -function income=OLGModel14_HouseholdIncomeFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last) +function income=OLGModel14_HouseholdIncomeFn(h,sprime,s,z,e,agej,Jr,pension,w,P0,D,kappa_j,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last) % Replace assets with 'share holdings' % Get rid of progressive taxes % Add Lhnormalize @@ -10,25 +10,29 @@ % And thus we have P=((1-tau_cg)*P0 + (1-tau_d)*D)/(1+r-tau_cg); if sprime>=s + agej_bought=agej; Plag=P0; % We are holding or buying, so no capital gains + cg=0; else - if agej<=S_agej_peak + if agej<=S_agej_peak_first + agej_bought=agej-1; Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently else % Estimate where we are past peak accumulation and mirror around to % proportional acquisition point - agej_selling_pct=(agej-S_agej_peak)/(S_agej_last-S_agej_peak); - agej_bought=S_agej_peak-ceil(agej_selling_pct*(S_agej_peak-S_agej_first)); + agej_selling_pct=(agej-S_agej_peak_last)/(S_agej_last-S_agej_peak_last); + agej_bought=S_agej_peak_first-ceil(agej_selling_pct*(S_agej_peak_first-S_agej_first)); Plag=P0*(1-2*r)^(agej-agej_bought); end + cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime); end if agej=0 is being implicitly imposed by grid on s diff --git a/OLGModel14_HouseholdReturnFn.m b/OLGModel14_HouseholdReturnFn.m index f1b8e44..0d4ef9c 100644 --- a/OLGModel14_HouseholdReturnFn.m +++ b/OLGModel14_HouseholdReturnFn.m @@ -1,4 +1,4 @@ -function F=OLGModel14_HouseholdReturnFn(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak,S_agej_last) +function F=OLGModel14_HouseholdReturnFn(h,sprime,s,z,e,sigma,psi,eta,agej,Jr,J,pension,w,P0,D,kappa_j,warmglow1,warmglow2,AccidentBeq,r,tau_l,tau_d,tau_cg,S_agej_first,S_agej_peak_first,S_agej_peak_last,S_agej_last) % Replace assets with 'share holdings' % Get rid of progressive taxes % Add Lhnormalize @@ -25,25 +25,29 @@ P=P0; if sprime>=s + agej_bought=agej; Plag=P0; % We are holding or buying, so no capital gains + cg=0; else - if agej<=S_agej_peak + if agej<=S_agej_peak_first + agej_bought=agej-1; Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently else % Estimate where we are past peak accumulation and mirror around to % proportional acquisition point - agej_selling_pct=(agej-S_agej_peak)/(S_agej_last-S_agej_peak); - agej_bought=S_agej_peak-ceil(agej_selling_pct*(S_agej_peak-S_agej_first)); + agej_selling_pct=(agej-S_agej_peak_last)/(S_agej_last-S_agej_peak_last); + agej_bought=S_agej_peak_first-ceil(agej_selling_pct*(S_agej_peak_first-S_agej_first)); Plag=P0*(1-2*r)^(agej-agej_bought); end + cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime); end F=-Inf; if agej0 From b608f5d5bb9ce0000057cc6d5706bd86d330cc1c Mon Sep 17 00:00:00 2001 From: Michael Tiemann Date: Thu, 30 Apr 2026 08:19:55 +1200 Subject: [PATCH 3/5] refine and fix --- OLGModel14.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OLGModel14.m b/OLGModel14.m index ec0008b..f49144c 100644 --- a/OLGModel14.m +++ b/OLGModel14.m @@ -257,15 +257,15 @@ Params.S_agej_first=max(find(AgeConditionalStats.household.Mean>0.1,1,'first')-1,1); Params.S_agej_last=min(find(AgeConditionalStats.household.Mean>0.5,1,'last')+1,length(AgeConditionalStats.household.Mean)); % warmglow creates extra long tail we want to ignore [~,S_agej_peak]=max(AgeConditionalStats.household.Mean); -S_peak_inflection_value=(1-1/Params.J)*AgeConditionalStats.household.Mean(S_agej_peak); +S_peak_inflection_value=0.95*AgeConditionalStats.household.Mean(S_agej_peak); for S_agej_peak_first=S_agej_peak:-1:Params.S_agej_first - if AgeConditionalStats.household.Mean(Params.S_agej_peak_first)S_peak_inflection_value +for S_agej_peak_last=S_agej_peak:Params.S_agej_last + if AgeConditionalStats.household.Mean(S_agej_peak_last) Date: Thu, 30 Apr 2026 17:12:07 +1200 Subject: [PATCH 4/5] minor --- OLGModel14_HouseholdCapitalGainsFn.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OLGModel14_HouseholdCapitalGainsFn.m b/OLGModel14_HouseholdCapitalGainsFn.m index 0925626..f14840f 100644 --- a/OLGModel14_HouseholdCapitalGainsFn.m +++ b/OLGModel14_HouseholdCapitalGainsFn.m @@ -26,9 +26,11 @@ cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime); end - if at_death && agej_bought>=S_agej_first % Sell all remaining shares from first acquisition to buy-point (using geometric mean to average acquisition cost) Plag=P0*(1-2*r)^(agej_bought-sqrt(agej_bought-S_agej_first)); cg=cg+tau_cg*(P0-Plag)*sprime; end + + +end From 628887b094a24c031ea4224ef5d5d12221d4a943 Mon Sep 17 00:00:00 2001 From: Michael Tiemann Date: Fri, 1 May 2026 07:52:10 +1200 Subject: [PATCH 5/5] Handle case where S is liquidated in a single period Avoid division by zero when there's no ramp to the end liquidation. Also, no reason to use ceil in usual calculation--we can exponentiate with non-integer numbers just fine. --- OLGModel14_HouseholdCapitalGainsFn.m | 7 ++++++- OLGModel14_HouseholdConsumptionFn.m | 6 +++++- OLGModel14_HouseholdIncomeFn.m | 6 +++++- OLGModel14_HouseholdReturnFn.m | 6 +++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/OLGModel14_HouseholdCapitalGainsFn.m b/OLGModel14_HouseholdCapitalGainsFn.m index f14840f..49443ae 100644 --- a/OLGModel14_HouseholdCapitalGainsFn.m +++ b/OLGModel14_HouseholdCapitalGainsFn.m @@ -16,11 +16,16 @@ if agej<=S_agej_peak_first agej_bought=agej-1; Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently + elseif S_agej_peak_last==S_agej_last % Bulk liquidation + % Sell all remaining shares from first acquisition to buy-point (using geometric mean to average acquisition cost) + agej_bought=S_agej_peak_first-sqrt(S_agej_peak_first-S_agej_first); + Plag=P0*(1-2*r)^(agej-agej_bought); + at_death=0; % Don't bulk liquidate twice else % Estimate where we are past peak accumulation and mirror around to % proportional acquisition point agej_selling_pct=(agej-S_agej_peak_last)/(S_agej_last-S_agej_peak_last); - agej_bought=S_agej_peak_first-ceil(agej_selling_pct*(S_agej_peak_first-S_agej_first)); + agej_bought=S_agej_peak_first-agej_selling_pct*(S_agej_peak_first-S_agej_first); Plag=P0*(1-2*r)^(agej-agej_bought); end cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime); diff --git a/OLGModel14_HouseholdConsumptionFn.m b/OLGModel14_HouseholdConsumptionFn.m index fb3f1ac..de983be 100644 --- a/OLGModel14_HouseholdConsumptionFn.m +++ b/OLGModel14_HouseholdConsumptionFn.m @@ -18,11 +18,15 @@ if agej<=S_agej_peak_first agej_bought=agej-1; Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently + elseif S_agej_peak_last==S_agej_last % Bulk liquidation + % Sell all remaining shares from first acquisition to buy-point (using geometric mean to average acquisition cost) + agej_bought=S_agej_peak_first-sqrt(S_agej_peak_first-S_agej_first); + Plag=P0*(1-2*r)^(agej-agej_bought); else % Estimate where we are past peak accumulation and mirror around to % proportional acquisition point agej_selling_pct=(agej-S_agej_peak_last)/(S_agej_last-S_agej_peak_last); - agej_bought=S_agej_peak_first-ceil(agej_selling_pct*(S_agej_peak_first-S_agej_first)); + agej_bought=S_agej_peak_first-agej_selling_pct*(S_agej_peak_first-S_agej_first); Plag=P0*(1-2*r)^(agej-agej_bought); end cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime); diff --git a/OLGModel14_HouseholdIncomeFn.m b/OLGModel14_HouseholdIncomeFn.m index ed97fd1..ea25b79 100644 --- a/OLGModel14_HouseholdIncomeFn.m +++ b/OLGModel14_HouseholdIncomeFn.m @@ -17,11 +17,15 @@ if agej<=S_agej_peak_first agej_bought=agej-1; Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently + elseif S_agej_peak_last==S_agej_last % Bulk liquidation + % Sell all remaining shares from first acquisition to buy-point (using geometric mean to average acquisition cost) + agej_bought=S_agej_peak_first-sqrt(S_agej_peak_first-S_agej_first); + Plag=P0*(1-2*r)^(agej-agej_bought); else % Estimate where we are past peak accumulation and mirror around to % proportional acquisition point agej_selling_pct=(agej-S_agej_peak_last)/(S_agej_last-S_agej_peak_last); - agej_bought=S_agej_peak_first-ceil(agej_selling_pct*(S_agej_peak_first-S_agej_first)); + agej_bought=S_agej_peak_first-agej_selling_pct*(S_agej_peak_first-S_agej_first); Plag=P0*(1-2*r)^(agej-agej_bought); end cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime); diff --git a/OLGModel14_HouseholdReturnFn.m b/OLGModel14_HouseholdReturnFn.m index 0d4ef9c..22f166f 100644 --- a/OLGModel14_HouseholdReturnFn.m +++ b/OLGModel14_HouseholdReturnFn.m @@ -32,11 +32,15 @@ if agej<=S_agej_peak_first agej_bought=agej-1; Plag=P0*(1-2*r); % Dispose of shares presumably acquired recently + elseif S_agej_peak_last==S_agej_last % Bulk liquidation + % Sell all remaining shares from first acquisition to buy-point (using geometric mean to average acquisition cost) + agej_bought=S_agej_peak_first-sqrt(S_agej_peak_first-S_agej_first); + Plag=P0*(1-2*r)^(agej-agej_bought); else % Estimate where we are past peak accumulation and mirror around to % proportional acquisition point agej_selling_pct=(agej-S_agej_peak_last)/(S_agej_last-S_agej_peak_last); - agej_bought=S_agej_peak_first-ceil(agej_selling_pct*(S_agej_peak_first-S_agej_first)); + agej_bought=S_agej_peak_first-agej_selling_pct*(S_agej_peak_first-S_agej_first); Plag=P0*(1-2*r)^(agej-agej_bought); end cg=tau_cg*(P0-Plag)*(s+AccidentBeq-sprime);