class FortuneTeller::Utils::SocialSecurity

Calculates adjusted benefit from PIA Based on www.ssa.gov/oact/quickcalc/early_late.html 11/16/2017

Constants

AWI_1951_START
COLA_1975_START
DELAY_RATES
TRANSITION_YEARS

Attributes

pia[RW]

Public Class Methods

new(dob:, start_month:) click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 9
def initialize(dob:, start_month:)
  @dob = dob
  @adjusted_dob = (dob.day == 1 ? dob.yesterday : dob)
  @start_month = start_month.at_beginning_of_month
end

Private Class Methods

awi_projector() click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 225
def self.awi_projector
  projected = []
  projected_increases = {
    # From https://www.ssa.gov/oact/TR/TRassum.html
    2017 => 3.9,
    2018 => 4.8,
    2019 => 4.5,
    2020 => 4.3,
    2021 => 4.2,
    2022 => 3.9,
    2023 => 3.7,
    2024 => 3.8,
    2025 => 3.8,
    2026 => 3.8
  }
  last_awi = AWI_1951_START[2016-1951]
  (2017..2070).each do |year|
    increase = projected_increases[[2026, year].min]
    last_awi = (last_awi*((100+increase)/100.0)).floor
    projected << last_awi
  end
  projected
end

Public Instance Methods

calculate_benefit() click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 64
def calculate_benefit
  frm = full_retirement_month
  puts "FRM: #{frm}"
  return @pia if @start_month == frm

  if(@start_month < frm)
    min_rm = min_retirement_month
    raise bounds_error(start: @start_month, min: min_rm) if @start_month < min_rm
    early_benefit( months: month_diff(@start_month, frm) )
  else
    max_rm = max_retirement_month
    raise bounds_error(start: @start_month, max: max_rm) if @start_month > max_rm
    late_benefit( months: month_diff(frm, @start_month) )
  end
end
estimate_pia(current_salary:, annual_raise:) click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 15
def estimate_pia(current_salary:, annual_raise:)
  year = Date.today.year
  last_year = @start_month.year
  salary_history = {year => current_salary}
  ((@dob.year+18)..(year-1)).reverse_each do |y|
    salary_history[y] = (salary_history[y+1]/annual_raise).floor
  end
  if(last_year > year)
    ((year+1)..last_year).each do |y|
      salary_history[y] = (salary_history[y-1]*annual_raise).floor
    end
  end
  salaries = salary_history.map{|y, s| s*indexing_factors[y]}
  aime = (salaries.sort.last(35).reduce(:+)/(35.0*12)).floor
  puts "AIME #{aime}"

  if aime > bend_points[1]
    pia_62 = (0.9*bend_points[0]+0.32*(bend_points[1]-bend_points[0])+0.15*(aime-bend_points[1])).floor
  elsif aime > bend_points[0]
    pia_62 = (0.9*bend_points[0]+0.32*(aime-bend_points[0])).floor
  else
    pia_62 = (0.9*aime).floor
  end

  pia_adjusted = pia_62
  ((@dob.year+63)..@start_month.year).each do |y|
    pia_adjusted = (pia_adjusted*(100+COLA_1975_START[y-1-1975])/100.0).floor
  end

  @pia = pia_adjusted
end
fra_pia=(fra_pia) click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 47
def fra_pia=(fra_pia)
  pia_adjusted = fra_pia
  if @start_month.year < full_retirement_month.year
    (@start_month.year..(full_retirement_month.year-1)).each do |y|
      pia_adjusted = (pia_adjusted/((100+COLA_1975_START[y-1975])/100.0)).floor
    end
  elsif @start_month.year > full_retirement_month.year
    puts "START GREATER"
    ((full_retirement_month.year+1)..@start_month.year).each do |y|
      puts "START: #{@start_month.year}, YEAR: #{y}"
      puts "PIA ADJ = #{pia_adjusted}"
      pia_adjusted = (pia_adjusted*(100+COLA_1975_START[y-1-1975])/100.0).floor
    end
  end
  @pia = pia_adjusted
end

Private Instance Methods

bend_points() click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 134
def bend_points
  return @bend_points unless @bend_points.nil?

  age_62_year = @dob.year+62
  age_62_index = AWI_1951_START[(age_62_year-1951)]
  year_1979_index = AWI_1951_START[(1979-1951)]
  year_1979_bends = [18000, 108500]
  @bend_points = [
    (year_1979_bends[0].to_f*(age_62_index/year_1979_index)).floor,
    (year_1979_bends[1].to_f*(age_62_index/year_1979_index)).floor,
  ]
end
bounds_error(start:, min: nil, max: nil) click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 221
def bounds_error(start:, min: nil, max: nil)
  self.class::StartDateBounds.new(start: start, min: min, max: max)
end
early_benefit(months:) click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 193
def early_benefit(months:)
  if months <= 36
    multiplier = 100.0 - ((5.0 * months) / 9.0)
  else
    multiplier = 100.0 - 20.0 - ((5.0 * months) / 12.0)
  end
  puts "EARLY PIA: #{(pia*multiplier/100.0).floor}"
  (@pia*multiplier/100.0).floor
end
full_retirement_month() click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 164
def full_retirement_month
  return @frm unless @frm.nil?

  year = @adjusted_dob.year
  frm = @adjusted_dob.at_beginning_of_month
  if year <= 1938
    @frm = frm.years_since(65)
  elsif (year >= 1943 and year <= 1954)
    @frm = frm.years_since(66)
  elsif year >= 1960
    @frm = frm.years_since(67)
  else
    t = TRANSITION_YEARS[year][0]
    @frm = frm.years_since(t[0]).years_since(t[1])             
  end     
end
indexing_factors() click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 147
def indexing_factors
  return @indexing_factors unless @indexing_factors.nil?

  age_60_year = @dob.year+60
  age_60_index = AWI_1951_START[(age_60_year-1951)]
  @indexing_factors = {}
  (18..60).each do |age|
    year = @dob.year+age
    @indexing_factors[year] = age_60_index.to_f/AWI_1951_START[(year-1951)]
  end
  (61..70).each do |age|
    year = @dob.year+age
    @indexing_factors[year] = 1.0          
  end
  @indexing_factors
end
late_benefit(months:) click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 203
def late_benefit(months:)
  year = @adjusted_dob.year
  if year <= 1924
    monthly = 6/24.0
  elsif year <= 1942
    monthly = DELAY_RATES[year]
  else
    monthly = 16.0/24.0
  end
  multiplier = 100.0 + (monthly*months)
  puts "LATE PIA: #{(pia*multiplier/100.0).floor}"
  (@pia*multiplier/100.0).floor
end
max_retirement_month() click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 181
def max_retirement_month
  @adjusted_dob.at_beginning_of_month.years_since(70)
end
min_retirement_month() click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 185
def min_retirement_month
  if @adjusted_dob.day == 1
    @adjusted_dob.years_since(62)
  else
    @adjusted_dob.at_beginning_of_month.years_since(62).months_since(1)
  end
end
month_diff(a, b) click to toggle source
# File lib/fortuneteller/utils/social_security.rb, line 217
def month_diff(a, b)
  (b.year * 12 + b.month) - (a.year * 12 + a.month)
end