This package uses Monte Carlo simulation to estimate the fair market value of a large portfolio of synthetic variable annuities. The portfolio of variable annuities under consideration is generated based on realistic features of common types of guarantee riders in North America. The Monte Carlo simulation engine generates sample paths of asset prices based on Black-Scholes model. In this vignette, we will demonstrate the functionalities provided in this package.
For illustrative purposes, we will use few scenarios to valuate a pool of two variable annuities. Users may obtain a more robust valuation result by increasing the amount of risk-neutral scenarios.
In this step, we exploit Newton’s method to calculate discount factors and forward rates at different tenor based on given swap rates using buildCurve().
# Initialize required inputs to boostrap a curve
swap <- c(0.69, 0.77, 0.88, 1.01, 1.14, 1.38, 1.66, 2.15)*0.01
tenor <- c(1, 2, 3, 4, 5, 7, 10, 30)
fixFreq <- 6
fixDCC <- "Thirty360"
fltFreq <- 6
fltDCC <- "ACT360"
calendar <- "NY"
bdc <- "Modified_Foll"
curveDate <- "2016-02-08"
numSetDay <- 2
yieldCurveDCC <- "Thirty360"
# Bootstrap a forward curve
buildCurve(swap, tenor, fixFreq, fixDCC, fltFreq, fltDCC, calendar, bdc,
curveDate, numSetDay, yieldCurveDCC)
#> obsDate discountFac zeroRate forwardCurve dayCount
#> 1 2016-02-08 1.0000000 0.000000000 0.006912035 0.000000
#> 2 2017-02-10 0.9930975 0.006888125 0.008520036 1.005556
#> 3 2018-02-10 0.9847078 0.007683825 0.011060713 2.005556
#> 4 2019-02-10 0.9739354 0.008787170 0.014146403 3.005556
#> 5 2020-02-10 0.9603499 0.010100373 0.016846310 4.005556
#> 6 2021-02-10 0.9444396 0.011420029 0.020451281 5.005556
#> 7 2023-02-10 0.9073275 0.013882093 0.024514485 7.005556
#> 8 2026-02-10 0.8451708 0.016812319 0.032098279 10.005556
#> 9 2046-02-10 0.5147311 0.022132923 0.032098279 30.005556
In the following example, we first simulate the index movements using genIndexScen(). Three of the inputs to genIndexScen() are stored as default data under variable names “mCov”, “indexNames”, and “cForwardCurve” respectively. For illustration purposes, we will simulate 100 scenarios for 360 steps with a step length dT = 1/12 and seed = 1.
The underlying model utilizes the multivariate Black-Scholes model. All the simulated index movements are stored in a 3D-array with dimensions [number of Scenarios, number of Steps, number of Indices]
US | SMALL | INT | FIXED | MONEY |
---|---|---|---|---|
0.15 | 0.0 | 0.00 | 0.000 | 0.0000 |
0.00 | 0.2 | 0.00 | 0.000 | 0.0000 |
0.00 | 0.0 | 0.17 | 0.000 | 0.0000 |
0.00 | 0.0 | 0.00 | 0.029 | 0.0000 |
0.00 | 0.0 | 0.00 | 0.000 | 0.0061 |
Index Names |
---|
US |
SMALL |
INT |
FIXED |
MONEY |
# We will show the index simulated path for five months of the first scenario
indexScen <- genIndexScen(mCov, 100, 360, indexNames, 1 / 12, cForwardCurve, 1)
indexScen[1, 1:5, ]
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.9280933 0.7108209 1.0542468 1.0391611 1.0050411
#> [2,] 1.0160765 1.1770017 0.9065351 0.9935556 0.9305910
#> [3,] 0.9066403 0.9151064 1.2239807 1.0199796 0.9939919
#> [4,] 1.1897872 0.9397168 0.9853616 1.0115470 1.0117871
#> [5,] 1.0327826 0.9718774 0.8855638 1.0016170 0.9723979
Then we use genFundScen() to map the index movements to funds according to different allocations of capital using a fund map (stored as default data under variable “fundMap”). The fund movements are also stored in a 3D-array with dimension [number of Scenarios, number of Steps, number of Funds]
# Again, we show the fund simulated path for five months of the first scenario
fundScen <- genFundScen(fundMap, indexScen)
fundScen[1, 1:5, ]
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7]
#> [1,] 0.9280933 0.7108209 1.0542468 1.0391611 1.0050411 0.8411844 0.9911700
#> [2,] 1.0160765 1.1770017 0.9065351 0.9935556 0.9305910 1.0804466 0.9613058
#> [3,] 0.9066403 0.9151064 1.2239807 1.0199796 0.9939919 0.9100268 1.0653105
#> [4,] 1.1897872 0.9397168 0.9853616 1.0115470 1.0117871 1.0897591 1.0875744
#> [5,] 1.0327826 0.9718774 0.8855638 1.0016170 0.9723979 1.0084206 0.9591732
#> [,8] [,9] [,10]
#> [1,] 0.9836272 0.9512190 0.9474726
#> [2,] 1.0048160 0.9876751 1.0047520
#> [3,] 0.9633100 1.1313184 1.0119398
#> [4,] 1.1006671 0.9716682 1.0276399
#> [5,] 1.0171998 0.9114579 0.9728478
Perhaps the most value-added step in this package is the generation of synthetic portfolio of variable annuities that has realistic charateristic features. Using the fuction genPortInception(), users can generate a synthetic variable annuity portfolio of desirable size. The function genPortInception() has certain predetermined default values based on the research in the package reference. We recommend users to change these default values, such as maturity and issue range, to match their portfolio characteristics. In the current version, there are a few constraints for the portfolio being generated: The issue range must be later than the first date of historical scenario; The maturity range should also be set after the valuation date to be meaningful.
# For illustration purposes, we will only simulate one guarantee contract for each of the 19 guarantee types. Please note that due to randomness the generated portfolio under this code block may not align with the default VAPort under lazy data.
genPortInception(issueRng = c("2001-08-01", "2014-01-01"), numPolicy = 1)
#> recordID survivorShip gender productType issueDate matDate
#> DBRP 1 1 F DBRP 2013-04-01 2043-04-01
#> DBRU 2 1 F DBRU 2002-02-01 2018-02-01
#> DBSU 3 1 F DBSU 2005-09-01 2021-09-01
#> ABRP 4 1 M ABRP 2002-03-01 2030-03-01
#> ABRU 5 1 M ABRU 2009-01-01 2038-01-01
#> ABSU 6 1 M ABSU 2012-11-01 2035-11-01
#> IBRP 7 1 F IBRP 2012-03-01 2033-03-01
#> IBRU 8 1 F IBRU 2009-10-01 2039-10-01
#> IBSU 9 1 M IBSU 2002-04-01 2024-04-01
#> MBRP 10 1 M MBRP 2006-04-01 2036-04-01
#> MBRU 11 1 M MBRU 2006-04-01 2028-04-01
#> MBSU 12 1 M MBSU 2011-09-01 2041-09-01
#> WBRP 13 1 F WBRP 2003-09-01 2024-09-01
#> WBRU 14 1 F WBRU 2010-08-01 2029-08-01
#> WBSU 15 1 M WBSU 2010-03-01 2035-03-01
#> DBAB 16 1 F DBAB 2002-09-01 2031-09-01
#> DBIB 17 1 F DBIB 2004-05-01 2023-05-01
#> DBMB 18 1 F DBMB 2002-02-01 2031-02-01
#> DBWB 19 1 F DBWB 2012-03-01 2027-03-01
#> birthDate currentDate baseFee riderFee rollUpRate gbAmt gmwbBalance
#> DBRP 1978-10-01 2013-04-01 0.02 0.0025 5e-04 0 0
#> DBRU 1952-06-01 2002-02-01 0.02 0.0035 5e-04 0 0
#> DBSU 1976-09-01 2005-09-01 0.02 0.0035 5e-04 0 0
#> ABRP 1963-03-01 2002-03-01 0.02 0.0050 5e-04 0 0
#> ABRU 1956-07-01 2009-01-01 0.02 0.0060 5e-04 0 0
#> ABSU 1976-10-01 2012-11-01 0.02 0.0060 5e-04 0 0
#> IBRP 1958-03-01 2012-03-01 0.02 0.0060 5e-04 0 0
#> IBRU 1975-02-01 2009-10-01 0.02 0.0070 5e-04 0 0
#> IBSU 1961-08-01 2002-04-01 0.02 0.0070 5e-04 0 0
#> MBRP 1974-06-01 2006-04-01 0.02 0.0050 5e-04 0 0
#> MBRU 1979-12-01 2006-04-01 0.02 0.0060 5e-04 0 0
#> MBSU 1963-01-01 2011-09-01 0.02 0.0060 5e-04 0 0
#> WBRP 1958-08-01 2003-09-01 0.02 0.0065 5e-04 0 0
#> WBRU 1975-09-01 2010-08-01 0.02 0.0075 5e-04 0 0
#> WBSU 1972-07-01 2010-03-01 0.02 0.0075 5e-04 0 0
#> DBAB 1968-12-01 2002-09-01 0.02 0.0075 5e-04 0 0
#> DBIB 1963-04-01 2004-05-01 0.02 0.0085 5e-04 0 0
#> DBMB 1973-02-01 2002-02-01 0.02 0.0075 5e-04 0 0
#> DBWB 1966-03-01 2012-03-01 0.02 0.0090 5e-04 0 0
#> wbWithdrawalRate withdrawal fundNum1 fundNum2 fundNum3 fundNum4
#> DBRP 5e-04 0 1 2 3 4
#> DBRU 5e-04 0 1 2 3 4
#> DBSU 5e-04 0 1 2 3 4
#> ABRP 5e-04 0 1 2 3 4
#> ABRU 5e-04 0 1 2 3 4
#> ABSU 5e-04 0 1 2 3 4
#> IBRP 5e-04 0 1 2 3 4
#> IBRU 5e-04 0 1 2 3 4
#> IBSU 5e-04 0 1 2 3 4
#> MBRP 5e-04 0 1 2 3 4
#> MBRU 5e-04 0 1 2 3 4
#> MBSU 5e-04 0 1 2 3 4
#> WBRP 5e-04 0 1 2 3 4
#> WBRU 5e-04 0 1 2 3 4
#> WBSU 5e-04 0 1 2 3 4
#> DBAB 5e-04 0 1 2 3 4
#> DBIB 5e-04 0 1 2 3 4
#> DBMB 5e-04 0 1 2 3 4
#> DBWB 5e-04 0 1 2 3 4
#> fundNum5 fundNum6 fundNum7 fundNum8 fundNum9 fundNum10 fundValue1
#> DBRP 5 6 7 8 9 10 0.00
#> DBRU 5 6 7 8 9 10 0.00
#> DBSU 5 6 7 8 9 10 0.00
#> ABRP 5 6 7 8 9 10 0.00
#> ABRU 5 6 7 8 9 10 0.00
#> ABSU 5 6 7 8 9 10 53946.32
#> IBRP 5 6 7 8 9 10 15294.43
#> IBRU 5 6 7 8 9 10 26752.47
#> IBSU 5 6 7 8 9 10 0.00
#> MBRP 5 6 7 8 9 10 0.00
#> MBRU 5 6 7 8 9 10 48316.53
#> MBSU 5 6 7 8 9 10 39784.12
#> WBRP 5 6 7 8 9 10 19981.46
#> WBRU 5 6 7 8 9 10 67683.52
#> WBSU 5 6 7 8 9 10 21382.17
#> DBAB 5 6 7 8 9 10 0.00
#> DBIB 5 6 7 8 9 10 26108.78
#> DBMB 5 6 7 8 9 10 144417.30
#> DBWB 5 6 7 8 9 10 64632.67
#> fundValue2 fundValue3 fundValue4 fundValue5 fundValue6 fundValue7
#> DBRP 248464.93 0.00 0.00 0.00 0.00 0.00
#> DBRU 15226.72 15226.72 15226.72 0.00 15226.72 15226.72
#> DBSU 363370.26 0.00 0.00 0.00 0.00 0.00
#> ABRP 137737.66 0.00 0.00 0.00 0.00 0.00
#> ABRU 0.00 0.00 0.00 61639.49 0.00 61639.49
#> ABSU 53946.32 0.00 53946.32 53946.32 0.00 53946.32
#> IBRP 0.00 0.00 15294.43 0.00 15294.43 0.00
#> IBRU 26752.47 26752.47 26752.47 26752.47 26752.47 26752.47
#> IBSU 50889.49 0.00 50889.49 0.00 0.00 50889.49
#> MBRP 116634.59 0.00 0.00 0.00 0.00 0.00
#> MBRU 48316.53 48316.53 48316.53 48316.53 48316.53 48316.53
#> MBSU 39784.12 39784.12 0.00 0.00 0.00 39784.12
#> WBRP 19981.46 19981.46 19981.46 19981.46 19981.46 19981.46
#> WBRU 0.00 67683.52 0.00 67683.52 0.00 67683.52
#> WBSU 21382.17 21382.17 21382.17 21382.17 0.00 21382.17
#> DBAB 0.00 332019.56 0.00 0.00 0.00 0.00
#> DBIB 26108.78 0.00 26108.78 26108.78 26108.78 26108.78
#> DBMB 0.00 0.00 0.00 0.00 0.00 0.00
#> DBWB 0.00 64632.67 64632.67 64632.67 64632.67 0.00
#> fundValue8 fundValue9 fundValue10 fundFee1 fundFee2 fundFee3 fundFee4
#> DBRP 248464.93 0.00 0.00 0.003 0.005 0.006 0.008
#> DBRU 15226.72 15226.72 15226.72 0.003 0.005 0.006 0.008
#> DBSU 0.00 0.00 0.00 0.003 0.005 0.006 0.008
#> ABRP 0.00 0.00 0.00 0.003 0.005 0.006 0.008
#> ABRU 61639.49 0.00 61639.49 0.003 0.005 0.006 0.008
#> ABSU 0.00 0.00 53946.32 0.003 0.005 0.006 0.008
#> IBRP 15294.43 15294.43 0.00 0.003 0.005 0.006 0.008
#> IBRU 26752.47 26752.47 0.00 0.003 0.005 0.006 0.008
#> IBSU 0.00 0.00 50889.49 0.003 0.005 0.006 0.008
#> MBRP 116634.59 0.00 0.00 0.003 0.005 0.006 0.008
#> MBRU 0.00 48316.53 48316.53 0.003 0.005 0.006 0.008
#> MBSU 0.00 39784.12 0.00 0.003 0.005 0.006 0.008
#> WBRP 19981.46 19981.46 19981.46 0.003 0.005 0.006 0.008
#> WBRU 0.00 0.00 67683.52 0.003 0.005 0.006 0.008
#> WBSU 21382.17 0.00 21382.17 0.003 0.005 0.006 0.008
#> DBAB 0.00 0.00 0.00 0.003 0.005 0.006 0.008
#> DBIB 0.00 26108.78 26108.78 0.003 0.005 0.006 0.008
#> DBMB 0.00 0.00 0.00 0.003 0.005 0.006 0.008
#> DBWB 0.00 0.00 64632.67 0.003 0.005 0.006 0.008
#> fundFee5 fundFee6 fundFee7 fundFee8 fundFee9 fundFee10
#> DBRP 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> DBRU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> DBSU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> ABRP 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> ABRU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> ABSU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> IBRP 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> IBRU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> IBSU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> MBRP 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> MBRU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> MBSU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> WBRP 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> WBRU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> WBSU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> DBAB 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> DBIB 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> DBMB 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> DBWB 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
After generating the above required elements for Monte Carlo valuation, we can now proceed to calculate the fair market price of the portfolio by calling the function valuatePortfolio(). Under the current version of the package, all the annuity contracts in the portfolio are assumed to be valuated on the same date, i.e. the first date of our simulated fund scenario. Users can either use the default mortality table by calling “mortTable”, or input a mortality table to project liability cash flows.
# In this vignette, we will arbitrarily use the first two scenarios from fundSen to valuate a portfolio of two guarantees to speed up the execution of the example.
# The input cForwardCurve is a vector of 0.02 with dimension 360. It could also be a forward curve calculated using the buildCurve() function.
valuatePortfolio(VAPort[1:2, ], mortTable, fundScen[1, , ], 1 / 12, cForwardCurve)
#> $portVal
#> [1] 0
#>
#> $portRC
#> [1] 597.3302
#>
#> $vecVal
#> [1] 0 0
#>
#> $vecRC
#> [1] 347.5886 249.7416
Note that users can also “age” the portfolio, calling the function agePortfolio(), to a particular valuatioon date by incorporating the historical fund movements prior to that date.
# Again, we will arbitrarily age a portfolio of two guarantees to speed up the execution.
targetDate <- "2016-01-01"
# Here we generate historical fund scenarios using default index data stored under "histIdxScen"
histFundScen <- genFundScen(fundMap, histIdxScen)
# Perform aging
agePortfolio(VAPort[1:2, ], mortTable, histFundScen, histDates, dT = 1 / 12, targetDate, cForwardCurve)
#> recordID survivorShip gender productType issueDate matDate
#> DBRP 1 1 M DBRP 2010-11-01 2036-11-01
#> DBRU 2 1 M DBRU 2013-02-01 2034-02-01
#> birthDate currentDate baseFee riderFee rollUpRate gbAmt gmwbBalance
#> DBRP 1965-07-01 2016-01-01 0.02 0.0025 5e-04 0 0
#> DBRU 1969-11-01 2016-01-01 0.02 0.0035 5e-04 0 0
#> wbWithdrawalRate withdrawal fundNum1 fundNum2 fundNum3 fundNum4
#> DBRP 5e-04 0 1 2 3 4
#> DBRU 5e-04 0 1 2 3 4
#> fundNum5 fundNum6 fundNum7 fundNum8 fundNum9 fundNum10 fundValue1
#> DBRP 5 6 7 8 9 10 120788.3
#> DBRU 5 6 7 8 9 10 0.0
#> fundValue2 fundValue3 fundValue4 fundValue5 fundValue6 fundValue7
#> DBRP 135115.39 90082.35 56505.24 47943.95 126814.8 0.00
#> DBRU 54975.59 0.00 44099.64 42005.60 0.0 51996.72
#> fundValue8 fundValue9 fundValue10 fundFee1 fundFee2 fundFee3 fundFee4
#> DBRP 0.00 103083.3 85152.89 0.003 0.005 0.006 0.008
#> DBRU 51055.69 0.0 0.00 0.003 0.005 0.006 0.008
#> fundFee5 fundFee6 fundFee7 fundFee8 fundFee9 fundFee10
#> DBRP 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
#> DBRU 0.001 0.0038 0.0045 0.0055 0.0047 0.0046
Though the primary purpose of this package is to valuate a portfolio of variable annuities, users can also use the valuateOnePolicy() and the ageOnePolicy() functions to perform fair market valuation on a single variable annuity as demonstrated below.
exPolicy <- VAPort[1, ]
valuateOnePolicy(exPolicy, mortTable, fundScen[1:2, , ], 1 / 12, cForwardCurve)
#> $policyValue
#> [1] 0
#>
#> $riskCharge
#> [1] 454.1419
# Similarly, users can age this single policy before pricing it. We use the same target date and historical fund scenario as generated before
exPolicy <- VAPort[1, ]
ageOnePolicy(exPolicy, mortTable, histFundScen, histDates, dT = 1 / 12, targetDate, cForwardCurve)
#> recordID survivorShip gender productType issueDate matDate
#> DBRP 1 1 M DBRP 2010-11-01 2036-11-01
#> birthDate currentDate baseFee riderFee rollUpRate gbAmt gmwbBalance
#> DBRP 1965-07-01 2016-01-01 0.02 0.0025 5e-04 0 0
#> wbWithdrawalRate withdrawal fundNum1 fundNum2 fundNum3 fundNum4
#> DBRP 5e-04 0 1 2 3 4
#> fundNum5 fundNum6 fundNum7 fundNum8 fundNum9 fundNum10 fundValue1
#> DBRP 5 6 7 8 9 10 120788.3
#> fundValue2 fundValue3 fundValue4 fundValue5 fundValue6 fundValue7
#> DBRP 135115.4 90082.35 56505.24 47943.95 126814.8 0
#> fundValue8 fundValue9 fundValue10 fundFee1 fundFee2 fundFee3 fundFee4
#> DBRP 0 103083.3 85152.89 0.003 0.005 0.006 0.008
#> fundFee5 fundFee6 fundFee7 fundFee8 fundFee9 fundFee10
#> DBRP 0.001 0.0038 0.0045 0.0055 0.0047 0.0046