Modelling fake social security benefits with C and gnuplot



DISCLAIMER BEGINS

In this article we consider a hypothetical social security system which could exist in some imaginary country. Though I used some scraps of knowledge about the real social security system as an inspiration to illustrate this post - this article, the code snippets in it and conclusions should never be used as a source of information about the real social security benefits. Please use official websites to get the actual information on this topic or about any similar programs in your country.

DISCLAIMER ENDS

Today, I would like to show how using a classic command-line plotting tool gnuplot and a little bit of programming can help to explore and better understand various aspects of personal finances. For this demonstration we will use a simplistic model of social security benefits inspired by the social security in the US in 2022, and an imaginary situation where a high wage-earning person wants to understand how the early retirement or a relocation to a different country (and the subsequent cessation of the contributions) can affect the future benefits of the said person. We will generate a dataset with the projections of the payments with respect to the number of working years and will use gnuplot to examine the data and see patterns.

First, let’s write a simple program to generate the dataset. This of course can be done in any programming language, and I don’t see any reason why good old C wouldn’t be a good choice for it.

/*
 * Generating dataset for a FAKE model of social security
 * Prints to stdout lines with 3 comma-separated numbers: 
 * number of working years, expected monthly pay, delayed ROI
 *
 */

#include <stdio.h>

#define YEARLY_WAGE_INCOME 999999999990.0
#define SS_YEARLY_CAP 147000.0
#define SS_TAX_RATE 0.062
#define MIN_YEARS 10
#define MAX_YEARS 35
#define BENDPOINT_90 926.0
#define BENDPOINT_32 (5583.0-BENDPOINT_90)

#define MIN(A, B) (((A) < (B)) ? (A) : (B))

#define SS_INCOME MIN(YEARLY_WAGE_INCOME, SS_YEARLY_CAP)
#define SS_YEARLY_CONTRIB (SS_INCOME * SS_TAX_RATE)

int main() {
  for (int years=1; years<MIN_YEARS; years++) {
      printf("%d,0,0\n", years);
  }
  for (int years=MIN_YEARS; years<=MAX_YEARS; years++) {
    double lifetime_avg = (1.0 * SS_INCOME * years) / MAX_YEARS;
    double remain = lifetime_avg / 12;
    double part_90 = MIN(remain, BENDPOINT_90);
    remain = remain - part_90;
    double part_32 = MIN(remain, BENDPOINT_32);
    remain = remain - part_32;
    double pia = part_90 * 0.9 + part_32 * 0.32 + remain * 0.15;
    // Number of yeas, monthly payment, rate of delayed returns on investments
    printf("%d,%f,%f\n", years, pia, pia*12.0/(SS_YEARLY_CONTRIB * years));
  }
}

As the comment says - we print to the standard output lines with 3 numbers:

  1. Number of working years. In our model we expect the person to work some number of years and fully stop at some point, wait until the retirement age and then start collecting the benefits without any new contributions to the system.

  2. Expected monthly payment after retirement for the given number of the career years. In our model we don’t take into account the inflation but expect program administrators to conduct indexation of all values to keep up with the inflation. So our results are expressed in the purchasing power of the currency in the first year.

  3. Delayed ROI - this is the yearly payouts after the retirement divided by the total contributions to the system made throughout the career. This is different from the normal Returns on Investments, because the person will not start receiving the benefits until the retirement age (and waiting time for the retirement age is not part of our model). Thus this value can not be directly compared to the ROI of common financial instruments. (Profitability of the social security is lower due to the time waited for the retirement age).

The formula depends on averaged contributions in the range of 35 years. We expect our model worker to earn above the CAP and thus be taxed/make contributions up to the maximum possible amount each working year. The model takes into account the corrections to the payments made by the program administrators. (The monthly averaged value of the capped earnings are divided into 3 parts and only certain percentage from each part is paid to the retiree). In our model we don’t have any changes to the program rules and the used constants during the whole period of the observation (which is a major difference from the real world).

Now let’s write a simple Makefile to compile the program, generate the data and launch gnuplot script.

all: generate data.csv

generate: generate.c
	cc -o generate generate.c
data.csv: generate
	./generate > data.csv
run: generate data.csv
	gnuplot -p plot.gp

Each time when we run make run command, a gnuplot window should pop up, and if the data generating code was changed, Make will recompile the program and regenerate the data for us before launching gnuplot.

Finally let’s write a missing piece - gnuplot script (plot.gp) to visualise the generated data:

#!/usr/bin/gnuplot

set title "Fake modeling of social security payments"
set datafile separator ','           # Comma separated values
set xlabel 'Number of working years'
set ylabel 'Monthly pay'
set y2label 'Delayed ROI'
set y2tics                           # Add tics to the second (right) Y axis.
set format y "%g$"                   # Format of the left axis is "value$"
set format y2 "%g%%"                 # Format of the right axis is "value%"
set ytics nomirror                   # Do not mirror tics from left to right
set style circle radius 0.2          # Make circles smaller
set key left                         # Put legend to the left


# Plot column 2 as Y, with column 1 as X using circles.
# Also plot (column 3 * 100) as Y2, with column 1 as X, using fillsteps style
plot 'data.csv' using 1:2 title "Monthly pay $" with circles, \
'' using 1:($3*100) title "Delayed ROI %" with fillsteps axis x1y2

Now make run command should produce the following image:

image plot

On the picture we can notice that the payment trajectory bends around year 16 and the growth of the expected payment becomes slower. Also our scenario and our fake model with a high-earning person shows that after the minimum number of the working years which are required to be eligible for the benefits (10 in our case) - the more years the participant works - the lower their projected rate of the delayed returns.

I will left the conclusions about the early retirement/relocation to our imaginary person who lives in a fictional world. And for you, dear reader, I hope that the example above can work as a template for the exploration of the real world with the help of easily accessible free software.