::p_load(
pacman
tidyverse,
kableExtra,
latex2exp )
4 Comparison: Risk sharing with limited commitment but without storage
Finally, I compare the model with a risk-sharing model under limited commitment but without storage, as in Kocherlakota (1996) and Ligon, Thomas, and Worrall (2002). I use the simulation code I wrote on this webstie. After I show that, in contrast to the model with saving, the model here gives consumption whcih takes a finite number of values in limit, I demonstrate that the welfare actually can decrease due to the existence of storage.
4.1 Code
4.1.1 Utility functions
<- function(cons, sigma) {
calculateUtility if (sigma != 1) {
= (cons^(1 - sigma) - 1) / (1 - sigma)
utility else if (sigma == 1) {
} = log(cons)
utility
}return(utility)
}<- function(cons, sigma) cons^(- sigma) calculateMarginalUtility
4.1.2 Consumption function
<- function(
calculateHH1Consumption
aggregateResources,
relativeParetoWeight,
sigma
) {/ (1 + (relativeParetoWeight^(1 / sigma)))
aggregateResources }
4.1.3 Value under autarky
<- function(
calculateAutarkyValue
incomeGridPoints,
sigma,
delta,
punishment,
incomeTransitionMatrix
) {
<- numeric(length = length(incomeGridPoints))
autarkyValue <- 1
i <- 1
diff while (diff > 1e-12) {
<- (
autarkyValueNew calculateUtility(incomeGridPoints * (1 - punishment), sigma)
+ delta * incomeTransitionMatrix %*% autarkyValue
)<- max(abs(autarkyValueNew - autarkyValue))
diff <- autarkyValueNew
autarkyValue <- i + 1
i
}return(autarkyValue)
}
<- function(
getRelativeParetoWeightsGridPoints
sigma,
punishment,
householdIncomeGridPoints,
villageIncomeGridPoints,
numRelativeParetoWeights
) {
<- (
minRelativeParetoWeights calculateMarginalUtility(max(villageIncomeGridPoints), sigma)
/ calculateMarginalUtility(min(householdIncomeGridPoints * (1 - punishment)), sigma)
)<- (
maxRelativeParetoWeights calculateMarginalUtility(min(villageIncomeGridPoints * (1 - punishment)), sigma)
/ calculateMarginalUtility(max(householdIncomeGridPoints), sigma)
)<- exp(
relativeParetoWeightsGridPoints seq(
log(minRelativeParetoWeights),
log(maxRelativeParetoWeights),
length.out = numRelativeParetoWeights)
)return(relativeParetoWeightsGridPoints)
}
<- function(
calculateHouseholdConsumption
aggregateIncome,
relativeParetoWeight,
numHouseholds,
sigma
) {/ (1 + (numHouseholds - 1) * (relativeParetoWeight^(- 1 / sigma)))
aggregateIncome }
<- function(
calculateValueFullRiskSharing
incomeTransitionMatrix,
aggregateIncomeGridPoints,
delta,
sigma,
autarkyValueMatrix,
consumptionOnRelativeParetoWeightGrid,
numRelativeParetoWeights,
numHouseholds
) {
# Initial guess is expected utilities under autarky
<- outer(
householdValueFullRiskSharing 1], rep(1, numRelativeParetoWeights)
autarkyValueMatrix[,
)<- outer(
villageValueFullRiskSharing 2], rep(1, numRelativeParetoWeights)
autarkyValueMatrix[,
)
<- 1
iteration <- 1
diff while (diff > 1e-10 & iteration < 500) {
<- (
householdValueFullRiskSharingNew calculateUtility(consumptionOnRelativeParetoWeightGrid, sigma)
+ delta * incomeTransitionMatrix %*% householdValueFullRiskSharing
)<- (
villageValueFullRiskSharingNew calculateUtility(
- consumptionOnRelativeParetoWeightGrid) / (numHouseholds - 1),
(aggregateIncomeGridPoints
sigma
) + delta * incomeTransitionMatrix %*% villageValueFullRiskSharing
)
<- max(
diff max(abs(householdValueFullRiskSharing - householdValueFullRiskSharingNew)),
max(abs(villageValueFullRiskSharing - villageValueFullRiskSharingNew))
)<- householdValueFullRiskSharingNew
householdValueFullRiskSharing <- villageValueFullRiskSharingNew
villageValueFullRiskSharing <- iteration + 1
iteration
}
return(list(
householdValueFullRiskSharing = householdValueFullRiskSharing,
villageValueFullRiskSharing = villageValueFullRiskSharing
)) }
<- function(
interpolateValueFunction
relativeParetoWeight,
relativeParetoWeightsGridPoints,
valueFunctionMatrix
) {apply(
valueFunctionMatrix,1,
function(x) {
approx(
relativeParetoWeightsGridPoints,
x,
relativeParetoWeight,rule = 2
$y
)
}
)
}
<- function(
calculateDiffLCRiskSharingAndAutarky
relativeParetoWeight,
relativeParetoWeightsGridPoints,
delta,
sigma,
aggregateIncome,
householdValueLCRiskSharing,
villageValueLCRiskSharing,
incomeTransitionProbVec,
householdAutarkyValue,
villageAutarkyValue,
numHouseholds
) {
<- calculateHouseholdConsumption(
householdConsumption
aggregateIncome,
relativeParetoWeight,
numHouseholds,
sigma
)
<- interpolateValueFunction(
householdValueLCRiskSharingAtRelativeParetoWeight
relativeParetoWeight,
relativeParetoWeightsGridPoints,
householdValueLCRiskSharing
)<- interpolateValueFunction(
villageValueLCRiskSharingAtRelativeParetoWeight
relativeParetoWeight,
relativeParetoWeightsGridPoints,
villageValueLCRiskSharing
)
<- (
householdDiffLCRiskSharingAndAutarky calculateUtility(householdConsumption, sigma)
+ delta * incomeTransitionProbVec %*% householdValueLCRiskSharingAtRelativeParetoWeight
- householdAutarkyValue
%>% as.numeric
) <- (
villageDiffLCRiskSharingAndAutarky calculateUtility((aggregateIncome - householdConsumption) / (numHouseholds - 1), sigma)
+ delta * incomeTransitionProbVec %*% villageValueLCRiskSharingAtRelativeParetoWeight
- villageAutarkyValue
%>% as.numeric
)
return(list(
householdDiffLCRiskSharingAndAutarky = householdDiffLCRiskSharingAndAutarky,
villageDiffLCRiskSharingAndAutarky = villageDiffLCRiskSharingAndAutarky
))
}
<- function(
calculateValueLCRiskSharing
valueFullRiskSharing,
consumptionOnRelativeParetoWeightGrid,
aggregateIncomeGridPoints,
incomeTransitionMatrix,
autarkyValueMatrix,
relativeParetoWeightsGridPoints,
numRelativeParetoWeights,
delta,
sigma,
numIncomeStates,
numHouseholds,
iterationLimit,
diffLimit
) {
# Initial guess is expected utilities under full risk sharing
<- valueFullRiskSharing$householdValueFullRiskSharing
householdValueLCRiskSharing <- valueFullRiskSharing$villageValueFullRiskSharing
villageValueLCRiskSharing
<- 1
diff <- 1
iteration while ((diff > diffLimit) && (iteration <= iterationLimit)) {
# First, ignore enforceability and just update the value functions
# using the values at the previous iteration
<- (
householdValueLCRiskSharingNew calculateUtility(consumptionOnRelativeParetoWeightGrid, sigma)
+ delta * incomeTransitionMatrix %*% householdValueLCRiskSharing
)<- (
villageValueLCRiskSharingNew calculateUtility(
- consumptionOnRelativeParetoWeightGrid) / (numHouseholds - 1),
(aggregateIncomeGridPoints
sigma
)+ delta * incomeTransitionMatrix %*% villageValueLCRiskSharing
)
# Now check enforceability at each state
for (incomeStateIndex in seq(1, numIncomeStates)) {
<- autarkyValueMatrix[incomeStateIndex, 1]
householdAutarkyValue <- autarkyValueMatrix[incomeStateIndex, 2]
villageAutarkyValue
if (any(householdValueLCRiskSharingNew[incomeStateIndex, ] <= householdAutarkyValue)) {
villageValueLCRiskSharingNew[
incomeStateIndex,<= householdAutarkyValue
householdValueLCRiskSharingNew[incomeStateIndex, ] <- villageValueLCRiskSharingNew[
]
incomeStateIndex,<= householdAutarkyValue
householdValueLCRiskSharingNew[incomeStateIndex, ] %>% min
]
householdValueLCRiskSharingNew[
incomeStateIndex,<= householdAutarkyValue
householdValueLCRiskSharingNew[incomeStateIndex, ] <- householdAutarkyValue
]
}
if (any(villageValueLCRiskSharingNew[incomeStateIndex, ] <= villageAutarkyValue)) {
householdValueLCRiskSharingNew[
incomeStateIndex,<= villageAutarkyValue
villageValueLCRiskSharingNew[incomeStateIndex, ] <- householdValueLCRiskSharingNew[
]
incomeStateIndex,<= villageAutarkyValue
villageValueLCRiskSharingNew[incomeStateIndex, ] %>% min
]
villageValueLCRiskSharingNew[
incomeStateIndex,<= villageAutarkyValue
villageValueLCRiskSharingNew[incomeStateIndex, ] <- villageAutarkyValue
]
}
}
<- max(
diff max(abs(householdValueLCRiskSharingNew - householdValueLCRiskSharing)),
max(abs(villageValueLCRiskSharingNew - villageValueLCRiskSharing))
)<- householdValueLCRiskSharingNew
householdValueLCRiskSharing <- villageValueLCRiskSharingNew
villageValueLCRiskSharing <- iteration + 1
iteration
}
<- matrix(NA, nrow = numIncomeStates, ncol = 2)
relativeParetoWeightBounds
for (incomeStateIndex in seq(1, numIncomeStates)) {
<- aggregateIncomeGridPoints[incomeStateIndex]
aggregateIncome <- incomeTransitionMatrix[incomeStateIndex,]
incomeTransitionProbVec <- autarkyValueMatrix[incomeStateIndex, 1]
householdAutarkyValue <- autarkyValueMatrix[incomeStateIndex, 2]
villageAutarkyValue
if (
calculateDiffLCRiskSharingAndAutarky(
min(relativeParetoWeightsGridPoints),
relativeParetoWeightsGridPoints,
delta,
sigma,
aggregateIncome,
householdValueLCRiskSharing,
villageValueLCRiskSharing,
incomeTransitionProbVec,
householdAutarkyValue,
villageAutarkyValue,
numHouseholds$householdDiffLCRiskSharingAndAutarky < 0) {
)<- uniroot(
relativeParetoWeightLowerBound function(x) {calculateDiffLCRiskSharingAndAutarky(
x,
relativeParetoWeightsGridPoints,
delta,
sigma,
aggregateIncome,
householdValueLCRiskSharing,
villageValueLCRiskSharing,
incomeTransitionProbVec,
householdAutarkyValue,
villageAutarkyValue,
numHouseholds$householdDiffLCRiskSharingAndAutarky},
)c(min(relativeParetoWeightsGridPoints), max(relativeParetoWeightsGridPoints)),
tol = 1e-10,
maxiter = 300
$root
)else {
} <- min(relativeParetoWeightsGridPoints)
relativeParetoWeightLowerBound
}
if (
calculateDiffLCRiskSharingAndAutarky(
max(relativeParetoWeightsGridPoints),
relativeParetoWeightsGridPoints,
delta,
sigma,
aggregateIncome,
householdValueLCRiskSharing,
villageValueLCRiskSharing,
incomeTransitionProbVec,
householdAutarkyValue,
villageAutarkyValue,
numHouseholds$villageDiffLCRiskSharingAndAutarky < 0) {
)<- uniroot(
relativeParetoWeightUpperBound function(x) {calculateDiffLCRiskSharingAndAutarky(
x,
relativeParetoWeightsGridPoints,
delta,
sigma,
aggregateIncome,
householdValueLCRiskSharing,
villageValueLCRiskSharing,
incomeTransitionProbVec,
householdAutarkyValue,
villageAutarkyValue,
numHouseholds$villageDiffLCRiskSharingAndAutarky},
)c(min(relativeParetoWeightsGridPoints), max(relativeParetoWeightsGridPoints)),
tol = 1e-10,
maxiter = 300
$root
)else {
} <- max(relativeParetoWeightsGridPoints)
relativeParetoWeightUpperBound
}1] <- relativeParetoWeightLowerBound
relativeParetoWeightBounds[incomeStateIndex, 2] <- relativeParetoWeightUpperBound
relativeParetoWeightBounds[incomeStateIndex,
}
if (iteration == iterationLimit) {
print("Reached the maximum limit of iterations!")
}
return(list(
householdValueLCRiskSharing = householdValueLCRiskSharing,
villageValueLCRiskSharing = villageValueLCRiskSharing,
relativeParetoWeightBounds = relativeParetoWeightBounds))
}
<- function(
solveLCRiskSharing
delta,
sigma,
punishment,
householdIncomeGridPoints,
villageIncomeGridPoints,
incomeTransitionMatrix,
incomeGridPointsMatrix,
numIncomeStates,
numHouseholds,numRelativeParetoWeights = 2000,
iterationLimit = 100,
diffLimit = 1e-8
) {
<- (
aggregateIncomeGridPoints 1] + incomeGridPointsMatrix[, 2] * (numHouseholds - 1)
incomeGridPointsMatrix[,
)
<- cbind(
autarkyValueMatrix calculateAutarkyValue(
householdIncomeGridPoints,
sigma,
delta,
punishment,
incomeTransitionMatrix
),calculateAutarkyValue(
villageIncomeGridPoints,
sigma,
delta,
punishment,
incomeTransitionMatrix
)
)
<- getRelativeParetoWeightsGridPoints(
relativeParetoWeightsGridPoints
sigma,
punishment,
householdIncomeGridPoints,
villageIncomeGridPoints,
numRelativeParetoWeights
)
<- matrix(
consumptionOnRelativeParetoWeightGrid NA, nrow = numIncomeStates, ncol = numRelativeParetoWeights
)for (incomeStateIndex in seq_along(aggregateIncomeGridPoints)) {
for (relativeParetoWeightIndex in seq_along(relativeParetoWeightsGridPoints)) {
consumptionOnRelativeParetoWeightGrid[
incomeStateIndex,
relativeParetoWeightIndex<- calculateHouseholdConsumption(
]
aggregateIncomeGridPoints[incomeStateIndex],
relativeParetoWeightsGridPoints[relativeParetoWeightIndex],
numHouseholds,
sigma
)
}
}
<- calculateValueFullRiskSharing(
valueFullRiskSharing
incomeTransitionMatrix,
aggregateIncomeGridPoints,
delta,
sigma,
autarkyValueMatrix,
consumptionOnRelativeParetoWeightGrid,
numRelativeParetoWeights,
numHouseholds
)
<- calculateValueLCRiskSharing(
valueLCRiskSharing
valueFullRiskSharing,
consumptionOnRelativeParetoWeightGrid,
aggregateIncomeGridPoints,
incomeTransitionMatrix,
autarkyValueMatrix,
relativeParetoWeightsGridPoints,
numRelativeParetoWeights,
delta,
sigma,
numIncomeStates,
numHouseholds,
iterationLimit,
diffLimit
)
return(valueLCRiskSharing)
}
<- 1.0
sigma <- 0.0
punishment
<- 0.8
delta <- 1
sigma <- 2
numHouseholds <- 3
numIncomeStates
<- c(0.353, 0.5, 0.647)
incomeGridPointsHH1 <- 1 - incomeGridPointsHH1
incomeGridPointsHH2 <- cbind(incomeGridPointsHH1, incomeGridPointsHH2)
incomeGridPointsMatrix <- incomeGridPointsHH1 + incomeGridPointsHH2
aggregateIncomeGridPoints
<- matrix(1 / 3, nrow = 3, ncol = 3) incomeTransitionMatrix
<- solveLCRiskSharing(
LCRiskSharingNoStorageResult
delta,
sigma,
punishment,
incomeGridPointsHH1,
incomeGridPointsHH2,
incomeTransitionMatrix,
incomeGridPointsMatrix,
numIncomeStates,
numHouseholds,numRelativeParetoWeights = 10000,
iterationLimit = 1000,
diffLimit = 1e-8
)
set.seed(123)
<- 5000
simSize <- sample(seq(1, numIncomeStates), size = simSize, replace = TRUE)
incomeSeq
<- c(1)
relativeParetoWeightSeqNoStorage <- c()
consHH1SeqNoStorage
for (i in seq(1, simSize)) {
<- c(
relativeParetoWeightSeqNoStorage
relativeParetoWeightSeqNoStorage,%>%
relativeParetoWeightSeqNoStorage[i] pmax(
$relativeParetoWeightBounds[
LCRiskSharingNoStorageResult
incomeSeq[i],1
]%>%
) pmin(
$relativeParetoWeightBounds[
LCRiskSharingNoStorageResult
incomeSeq[i],2
]
)
)
<- c(
consHH1SeqNoStorage
consHH1SeqNoStorage,calculateHH1Consumption(
(aggregateIncomeGridPoints[incomeSeq[i]]),+ 1],
relativeParetoWeightSeqNoStorage[i
sigma
)
)
}
<- tibble(
simulationResultNoStorage period = seq(1, simSize),
income = incomeSeq,
consHH1 = consHH1SeqNoStorage
%>%
) mutate(
previousIncome = lag(income),
changeIncome = paste(as.character(previousIncome), '->', as.character(income))
)
4.2 Simulation result
The figure shows the consumption evolution over time. Unlike the model with storage, the model without storage results in a consumption pattern which takes only a finite number of values in the steady state.
%>%
simulationResultNoStorage filter(period >= 100) %>%
ggplot() +
geom_point(aes(x = period, y = consHH1), size = 0.3) +
xlab('Time period') +
ylab('HH1 consumption') +
theme_classic()
4.3 Comparison with the case with storage
The table below compares the consumption means, standard deviations, and the average utilities over time. Due to the storage with positive return on saving, the mean consumption is higher in the model with storage. However, due to the existence of storage, the autarky values are higher, and hence the risk-sharing functionality is degraded. This results in more variable consumption (second row) and hence, lower welfare (third row) in the model with storage. Notice that, this kind of reduced welfare is achieved only for medium-level returns on saving. If the return is too low, saving is not used and there is no welfare impact, and if the return is sufficiently high, the benefit from intertemporal consumption smoothing dominates and hence there is a welfare improvement.
<- readRDS(
simulationResult file.path('IntermediateData/simulationResult.rds')
)
<- cbind(
summaryTable c(
%>% filter(period >= 100) %>% .$consHH1) %>% mean,
(simulationResult %>% filter(period >= 100) %>% .$consHH1) %>% sd,
(simulationResult log(simulationResult %>% filter(period >= 100) %>% .$consHH1) %>% mean
),c(
%>% filter(period >= 100) %>% .$consHH1) %>% mean,
(simulationResultNoStorage %>% filter(period >= 100) %>% .$consHH1) %>% sd,
(simulationResultNoStorage log(simulationResultNoStorage %>% filter(period >= 100) %>% .$consHH1) %>% mean
)
)
rownames(summaryTable) <- c("Mean Cons.", "SD Cons", "Mean Util.")
colnames(summaryTable) <- c("With storage", "Without storage")
%>%
summaryTable kbl(digits = 4) %>%
kable_classic()
With storage | Without storage | |
---|---|---|
Mean Cons. | 0.5009 | 0.4992 |
SD Cons | 0.0863 | 0.0717 |
Mean Util. | -0.7067 | -0.7053 |