Prepared by: Hexin Xu, Research Assistant
Sargent Institute of Quantitative Economics and Finance
Peking Univeristy HSBC Business School
*This tutorial assumes that you have basic knowledge of Python. If you are new to Python, don't worry! Check out our tutorial:
Aging is a process that everyone will encounter. Retirees don't have salary income anymore, but still maintains consumptions for basic living necessities. They are also pursuing high-quality life and enjoying colorful activities as society advances, which all need financial support. According to statistics, Chinese people have an average life expectancy of 76.4 years. Cost on just food for a Chinese couple each year is about:
¥25/person 2 people 3meals/day * 365days/year = $¥54,750$.
Whereas in China, the basic social welfare is on average $¥1200$ per month, or $¥14,400$ per year. Far below the basic requirement, let alone additional consumptions.
Therefore, as an important way to guarantee regular amount of income for retirees, endowment insurance has gained popularity in recent years. Insurance can be classified into three categories according to its ways to pay back: saving model, dividend model and investment model. Various kinds of insurance for different purposes are developed by insurance companies and with the smooth tongues of salesman, many people are easily persuaded into buying insurance with no idea of what profit or return it may bring (in some cases they may lose money). In this article, we'll use Python to calculate the value of a regular endowment insurance and show how it changes over time. We will also plot the value to make it clear with matplotlib tools.
Suppose you want to buy an endowment insurance, and here's the details of a (real) insurance --"Enjoy Happiness Insurance" with 20 years' payback.
The first 20 years after purchase is the “Paying Period”, during which you have to pay ¥22260 each year. Then from the 21st year on, you can receive payback ¥37026 each year for at least 20 years (let's call it “Payback Period”), and the payback will continue indefinitely, so long as you are alive. There are two ways to withdraw from the insurance: voluntary or accident resulting in death (or serious injury). Voluntary withdraw can only happen during the Paying Period when you stopped submitting the payment each year. When voluntarily withdraw, you receive a refund from the insurance company that is equal to the "Cash Value" of your previous payments, which is decided by the insurance company and shown to you at purchase. And one would receive max{Cash Value, Accident Insurance} if he or she unluckily had an accident during Paying Period. The only way to withdraw the insurance during Payback Period is accident and you would receive all the rest of the payback in 20 years.
Let's start calculating and plotting with two commonly-used libs: numpy and matplotlib.
import numpy as np
import matplotlib.pyplot as plt
Firstly, we need information of payments, Cash Value, Accident Insurance of each year for Paying Period and payback of each year for Payback Period. However, we do not know the Cash Value(CV) and Accident Insurance(AI) yet from the previous example so let's estimate it ourselves based on economic knowledge of insurance.(Actually insurance company only shows you the amount of these two series without telling you the formula with which they are calculated.)
Cash Value refers to the current value of your payments left after deduction of management fee, commission and other cost of insurance company. Same payments at different years have different current value because of duration and accumulated interest. It is determined recursively by the formula:
CV(t) = CV(t-1) * one-year saving rate + payment(t) - cost(t).
To explain the formula, the CV of time t equals the CV of time t-1 saved for another year, plus this year's payment, and minus the insurance company's cost. Then since the management fee of insurance company drops as time passes by (so that your loss of withdrawing at early stage will be very high), we assume it to be an inverse function of year and calibrate it to the cash value information provided by the insurance. It turns out that this method produces estimates that are close to the actual numbers provided by the insurance company. We can define a function for this:
def cost(year):
return 17000 / (year ** 1.5)
The one-year saving rate of Chinese banks at 2018 was 1.85% on average, and for simplicity, let's assume it is constant during the span of the insurance. So, we can calculate the Cash Value of time t:
rate = 1.0185 # gross interest rate = 1 + 1.85%
def cash(year):
if year == 1: #Note that there's no t-1 money left for time 1 so it is set to be 0.
return payment_y - cost(1)
return cash(year - 1) * rate + payment_y - cost(year)
As mentioned before, the payment upon accident during Paying Period is max{Cash Value, Accident Insurance}, and Accident Insurance is simply the year multiples of a basement--¥19920. So, we can calculate the Cash Value and accident payment at the same time:
acc_base = 19920 # base payment upon accident
Define variables and array first.
n = 20 # 20 years of Paying period
m = 30 # our analysis covers 30 years of Payback period
payment_y = 22600 # annual payment to the insurance company during Paying Period
accident = [] # Payment upon accident during Paying Period
cash_list = [] # Cash Value
profit = [] # actual profit
profit_acdt = [] # profit upon accident
How do we evaluate the insurance as a mean for saving? In order to answer this question, we can't simply compare the monetary costs in the first 20 years with the payments after 20 years. The reason is that there is an opportunity cost of purchasing the insurance. Opportunity cost is defined as the payoff that you forgo by choosing the insurance. By definition, it is the payoff from highest-valued alternative option. For simplicity, we will choose an obvious alternative: saving the same exact string of cash with a bank.
When you save in a bank, you get interest payment on your existing balance. Note that long-term deposit interest rate should be used here since we are talking about a long-term deposit, and the interest rate on such deposit is usually higher than the short-term one. For example, the current long-term deposit rate is 2.75%. Your bank balance can be calculated as:
rate_long = 1.0275 ## long-term deposite interest rate
bmk = []
bmk.append(payment_y * rate_long) ## Return at the end of first year
for i in range(n - 1): ## payment period
updated = (bmk[i] + payment_y) * rate_long ## The payment is added to deposit each year, and together earns a compound interest
bmk.append(updated)
for j in range(m): ## after payment period
updated = bmk[19 + j] * rate_long ## No extra money added to deposit each year
bmk.append(updated)
How about your wealth from purchasing the insurance?
In the first 20 years, you can get the cash value of the insurance when you withdraw, so CV would be your wealth.
for i in range(n): # note i ranges from 0 to n-1
cash_list.append(int(cash(i+1))) ## calculate CV each year
profit.append(cash_list[i]) ## Payoff from voluntary withdraw = cash value
Starting from the 21st year, you start to receive yearly insurance payment, but loss the option to withdraw and get the cash value. Thus your wealth level is reset to zero initially, but it will start to increase rapidly. Note that after the first 20 years, your bank balance still receives interest payments. To make the appropriate comparison between the two saving options, we will assume that you also put the insurance payment into a bank account. Thus your wealth from the insurance into the paying period is given by:
payback_y = 37026
payback_updated = []
payback_updated.append(payback_y * rate_long)
for i in range(m - 1): ## payback of the insurance if put into bank
payback_updated.append((payback_updated[-1] + payback_y) * rate_long) ## same as benchmark
for j in range(m): ## profit of payback period
profit.append(payback_updated[j])
Note that the insurance do also serves as an insurance. That is to say, you will receive a lump-sum payment upon an accident. Payoff from that scenario is harder to calculate, because it is unclear whether and when you will have an accident. As an illustration, we consider the case that an accident insurance is paid right at the end of the 20-year paying period. We also assume that the payment is saved in a bank account. Numpy.cumsum can help us calculate the cumulative sum of paybacks quickly. In that case, your wealth from the insurance will be given by:
for i in range(n): ## paying period: note i ranges from 0 to n-1
accident.append(max(cash_list[i],acc_base * (i+1))) ## payment upon accident = max(CV,accident insurance)
profit_acdt.append(accident[i]) ## Payoff upon accident = accident insurance payment
paybacks = np.cumsum(payback_y * np.ones(m)) ## use cumsum to quickly get cumulative payback
profit_acdt.append(paybacks[-1] * rate_long) ## payback at 20th-year
for i in range(m):
profit_acdt.append(profit_acdt[-1] * rate_long)
print(profit)
print(profit_acdt)
If you happen to have an insurance contract, you can adjust the parameters to make it close to the real value. Print out the result and it is not hard to find that losses of withdrawing at early age are large indeed.
Till now, we have successfully calculated one's profit series with respect to year. Let's plot it with matplotlib.
You can add other illustration items to the plot, such as title, axis, colors and legends.
plt.plot(bmk) ## blue line
plt.plot(profit) ## yellow line
plt.plot(profit_acdt) ## gree line
plt.legend(("benchmark","insurance_saving","insurance_accident"))
plt.show()
Insurance companies often advocate buying their product as a way to save for retirement. But as you can see, your wealth from buying the insurance will not exceed the option of saving directly in a bank account until 40 years after purchasing the insurance! You'd better wish that you live very long! Also note that saving in a bank is a safe and low-return option over the long run. Your opportunity cost may as well be even higher than the example here. For example, investing in bonds or stocks usually offers higher return than saving in a bank.
On the other hand, the insurance does provide an accident insurance. If you did get unlucky, the higher payoff upon accident may provide you a little comfort by paying you more than you could get from saving in a bank. Given that function, you may value the insurance somewhat more than the value reflected in the yellow line in the graph. How much more should you add to the yellow line for the insurance function? The value of that option can be relatively easily found: You can check the insurance premium you would have to pay to get the accident insurance alone!
All in all, the take-away seems to be: Next time when you get a call from an insurance sales person, it is better to do a little bit of calculation before agreeing to buy the insurance!
Suppose we already know the Cash Value and Accident Insurance for any year. We can import the data directly in the form of a CSV file (which is a commonly used data type for python) instead of typing the numbers manually. After all, we cannot do all things manually with a computer: what if the data size is very large and involves 20*12 months' data? What about in days?
import csv
with open("insurance_data.csv", newline = '') as f: ## open the csv file--replace the thing between "" with your local path
data = csv.reader(f) ## use csv reader
field_names = next(data) ## get the first line data; in most cases, it is the column titles
for row in data: ## read the data row by row
cash_list.append(int(row[0]))
accident.append(int(row[1]))
f.close() ## always remember to close the file you opened in code
Create a data file under the same file director with your code and take a try. Test if the results are the same with previous one. Be careful not to re-generate "cash_list" and "accident" arrays this time.
If you want to write the results, for example the "profit" and "profit_acdt" arrays that we calculated during the program, try below:
with open("write_data.csv",'w') as wf:
writer = csv.writer(wf)
writer.writerow(["profit","profit_acdt"])
for i in range(n + m):
writer.writerow([profit[i],profit_acdt[i]]) #write the date by row
wf.close()
See if you can output useful results with programming now!