doughnut 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7bc1546c0df232365b38159c778448b343458f94
4
- data.tar.gz: 19f2d1ef046b25fd11e9835f059ad87c6e5f06a1
3
+ metadata.gz: e0dcdc95522dc60f4014b3d243542e9312b09521
4
+ data.tar.gz: 5b25a23ebadd05e66344b8d1ed110d62ba15a87b
5
5
  SHA512:
6
- metadata.gz: 35a7b3618af9f8358f79fbb2e5a400fb447a94a07521b2a15124818d548538e333c685a0a98694b17506b5b3cd52a380d4b87be89ddc83c02e629cbf8e4e3786
7
- data.tar.gz: 3b10b380cda929553691e32e602928107cc2a51f97916b2da4bd863175b46f65d3861b3491d0d8ff62236abe435296182cd09d22be9234e79d4e8d938ceeeb5e
6
+ metadata.gz: 23b34df6870d38f36362481b8543b6773700c2dd1710dcef05f73d29c8db10944d0474db4efc2356a6f3004cbd997736084b80735a807c63d25261467c289116
7
+ data.tar.gz: d80e2c1cff77910bca4634d8d5e5979fdfb2fdd73b3bcb75fe24f4272f02fa4b2eaf9186357e83a09038eca7e455ce573d088fb6e1bf81f0decaf650b8d70535
data/Rakefile CHANGED
@@ -1,2 +1,3 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+ import "./lib/doughnut/tasks/retirement.rake"
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec"
24
24
  spec.add_runtime_dependency "descriptive_statistics"
25
+ spec.add_runtime_dependency "rubystats"
25
26
  end
@@ -1,6 +1,11 @@
1
- require "doughnut/expenses"
2
- require "doughnut/income"
3
- require "doughnut/retirement_calculator"
1
+ require "doughnut/monte_carlo/population_factory"
2
+ require "doughnut/monte_carlo/population_tester"
3
+ require "doughnut/monte_carlo/predicted_return"
4
+
5
+ require "doughnut/retirement_calculator/expenses"
6
+ require "doughnut/retirement_calculator/income"
7
+ require "doughnut/retirement_calculator/retirement_calculator"
8
+
4
9
  require "doughnut/version"
5
10
 
6
11
  module Doughnut
@@ -0,0 +1,31 @@
1
+ module Doughnut
2
+
3
+ class PopulationFactory
4
+
5
+ def initialize(list_of_assets)
6
+ @num_genomes = 10
7
+ @list_of_assets = list_of_assets
8
+ end
9
+
10
+ def build_random_population
11
+ Array.new(@num_genomes, random_genome)
12
+ end
13
+
14
+ private
15
+
16
+ def random_genome
17
+ output = []
18
+ @list_of_assets.each_with_index do |asset, indx|
19
+ output << asset.merge!({fraction: gene_fractions[indx]})
20
+ end
21
+ end
22
+
23
+ def gene_fractions
24
+ fracs = Array.new(@list_of_assets.length, rand)
25
+ s = fracs.sum
26
+ fracs.map { |x| x/s }
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,19 @@
1
+ require "rubystats"
2
+
3
+ module Doughnut
4
+
5
+ class PredictedReturn
6
+
7
+ def initialize(avg_return = 0, standard_deviation = 0)
8
+ @avg_return = avg_return
9
+ @standard_deviation = standard_deviation
10
+ end
11
+
12
+ def return(num_data_points = 1)
13
+ return @avg_return if @standard_deviation == 0
14
+ Rubystats::NormalDistribution.new(@avg_return, @standard_deviation).rng
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -9,14 +9,6 @@ module Doughnut
9
9
  @inflation_rate = 0.0322
10
10
  end
11
11
 
12
- def future_expenses(death_date, discount_rate)
13
- output = []
14
- (Date.today..death_date).each do |mydate|
15
- output << present_value(mydate, discount_rate) if is_last_day(mydate)
16
- end
17
- output
18
- end
19
-
20
12
  def total_expenses(death_date, discount_rate)
21
13
  output = 0
22
14
  future_expenses(death_date, discount_rate).each do |h|
@@ -25,9 +17,17 @@ module Doughnut
25
17
  output
26
18
  end
27
19
 
20
+ def future_expenses(death_date, discount_rate)
21
+ output = []
22
+ (Date.today..death_date).each do |mydate|
23
+ output << present_value(mydate, discount_rate) if is_last_day_of_month(mydate)
24
+ end
25
+ output
26
+ end
27
+
28
28
  private
29
29
 
30
- def is_last_day(mydate)
30
+ def is_last_day_of_month(mydate)
31
31
  mydate.month != mydate.next_day.month
32
32
  end
33
33
 
@@ -12,22 +12,14 @@ module Doughnut
12
12
  def future_income(death_date, discount_rate)
13
13
  output = []
14
14
  (Date.today..death_date).each do |mydate|
15
- output << present_value(mydate, discount_rate) if is_last_day(mydate)
16
- end
17
- output
18
- end
19
-
20
- def total_income(death_date, discount_rate)
21
- output = 0
22
- future_expenses(death_date, discount_rate).each do |h|
23
- output += h[:income]
15
+ output << present_value(mydate, discount_rate) if is_last_day_of_month(mydate)
24
16
  end
25
17
  output
26
18
  end
27
19
 
28
20
  private
29
21
 
30
- def is_last_day(mydate)
22
+ def is_last_day_of_month(mydate)
31
23
  mydate.month != mydate.next_day.month
32
24
  end
33
25
 
@@ -3,15 +3,19 @@ module Doughnut
3
3
  class RetirementCalculator
4
4
 
5
5
  attr_accessor :death_date, :portfolio_return
6
+ attr_accessor :current_net_worth
6
7
 
7
- def initialize
8
+ def initialize(net_worth = 207000)
8
9
  @death_date = Date.new(2067,7,19)
10
+ @current_net_worth = net_worth
9
11
  @portfolio_return = 0.1
10
12
  end
11
13
 
12
14
  def retirement_date(total_expenses, monthly_incomes)
13
15
  return Date.today if total_expenses == 0
16
+ return Date.today if total_expenses < @current_net_worth
14
17
  return @death_date if monthly_incomes.length == 0
18
+ monthly_incomes.insert( 0, {date: Date.today, income: @current_net_worth} )
15
19
  running_income = 0
16
20
  monthly_incomes.each do |h|
17
21
  running_income += h[:income]
@@ -0,0 +1,14 @@
1
+ require "doughnut/retirement_calculator/expenses"
2
+ require "doughnut/retirement_calculator/income"
3
+ require "doughnut/retirement_calculator/retirement_calculator"
4
+
5
+ task :calculate_retirement do
6
+ e = Expenses.new
7
+ i = Income.new
8
+ r = RetirementCalculator.new
9
+ death_date = r.death_date
10
+ discount_rate = r.portfolio_return
11
+ e_tot = e.total_expenses(death_date, discount_rate)
12
+ i_tot = i.future_income(death_date, discount_rate)
13
+ puts "#{r.retirement_date(e_tot, i_tot)}"
14
+ end
@@ -1,3 +1,3 @@
1
1
  module Doughnut
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -0,0 +1,121 @@
1
+ require "spec_helper"
2
+
3
+ module Doughnut
4
+
5
+ describe PopulationFactory do
6
+
7
+ describe "building a population of random genomes" do
8
+
9
+ context "one input" do
10
+
11
+ before(:each) do
12
+ @population = PopulationFactory.new( [{name: "X", avg_return: 1, standard_deviation: 5}] ).build_random_population
13
+ end
14
+
15
+ it "returns ten genomes" do
16
+ expect(@population.length).to eq 10
17
+ end
18
+
19
+ it "each genome has a length equal to one" do
20
+ @population.each do |genome|
21
+ expect(genome.length).to eq 1
22
+ end
23
+ end
24
+
25
+ it "each gene in a given genome has a name" do
26
+ @population.each do |genome|
27
+ genome.each do |gene|
28
+ expect(gene[:name]).not_to be nil
29
+ end
30
+ end
31
+ end
32
+
33
+ it "each gene in a given genome has an average return" do
34
+ @population.each do |genome|
35
+ genome.each do |gene|
36
+ expect(gene[:avg_return]).to eq 1
37
+ end
38
+ end
39
+ end
40
+
41
+ it "each gene in a given genome has a standard deviation" do
42
+ @population.each do |genome|
43
+ genome.each do |gene|
44
+ expect(gene[:standard_deviation]).to eq 5
45
+ end
46
+ end
47
+ end
48
+
49
+ it "each gene in a given genome has a fraction of one" do
50
+ @population.each do |genome|
51
+ genome.each do |gene|
52
+ expect(gene[:fraction]).to eq 1
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ context "multiple inputs" do
60
+
61
+ before(:each) do
62
+ @population = PopulationFactory.new( [{name: "X", avg_return: 1, standard_deviation: 5},{name: "Y", avg_return: 3, standard_deviation: 7}] ).build_random_population
63
+ end
64
+
65
+ it "returns ten genomes" do
66
+ expect(@population.length).to eq 10
67
+ end
68
+
69
+ it "each genome has a length greater than one" do
70
+ @population.each do |genome|
71
+ expect(genome.length).to eq 2
72
+ end
73
+ end
74
+
75
+ it "each gene in a given genome has a name" do
76
+ @population.each do |genome|
77
+ genome.each do |gene|
78
+ expect(gene[:name]).not_to be nil
79
+ end
80
+ end
81
+ end
82
+
83
+ it "each gene in a given genome has an average return" do
84
+ @population.each do |genome|
85
+ genome.each do |gene|
86
+ expect(gene[:avg_return] == 1 || gene[:avg_return] == 3).to be true
87
+ end
88
+ end
89
+ end
90
+
91
+ it "each gene in a given genome has a standard deviation" do
92
+ @population.each do |genome|
93
+ genome.each do |gene|
94
+ expect(gene[:standard_deviation] == 5 || gene[:standard_deviation] == 7).to be true
95
+ end
96
+ end
97
+ end
98
+
99
+ it "each fraction is NOT equal to one" do
100
+ @population.each do |genome|
101
+ genome.each do |gene|
102
+ expect(gene[:fraction]).not_to eq 1
103
+ end
104
+ end
105
+ end
106
+
107
+ it "the sum of all fractions equals one" do
108
+ @population.each do |genome|
109
+ sum = 0
110
+ genome.each { |gene| sum += gene[:fraction] }
111
+ expect(sum).to eq 1
112
+ end
113
+ end
114
+
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+
3
+ module Doughnut
4
+
5
+ describe PopulationTester do
6
+
7
+ it "returns no population if it is given no input" do
8
+ t = PopulationTester.new
9
+ expect(t.compete).to eq []
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,78 @@
1
+ require "spec_helper"
2
+ require "descriptive_statistics"
3
+
4
+ module Doughnut
5
+
6
+ describe PredictedReturn do
7
+
8
+ describe "#return" do
9
+
10
+ context "boundary conditions" do
11
+
12
+ it "gives zero predicted return if there are no inputs" do
13
+ expect(PredictedReturn.new.return).to eq 0
14
+ end
15
+
16
+ it "gives zero predicted return if both average return and standard deviation are zero" do
17
+ expect(PredictedReturn.new(0,0).return).to eq 0
18
+ end
19
+
20
+ it "gives the average return if standard deviation is zero" do
21
+ expect(PredictedReturn.new(0.5,0).return).to eq 0.5
22
+ end
23
+
24
+ it "gives a range of returns if the standard deviation is zero" do
25
+ expect(PredictedReturn.new(0,0.0001).return).to be > -1
26
+ expect(PredictedReturn.new(0,0.0001).return).to be < 1
27
+ end
28
+
29
+ end
30
+
31
+ context "Gaussian outputs" do
32
+
33
+ before(:each) do
34
+ @simulation = PredictedReturn.new(0.4, 0.1)
35
+ @thin_sim = PredictedReturn.new(0.8, 0.00000000001)
36
+ end
37
+
38
+ it "the data points should be unique" do
39
+ output = []
40
+ 50.times { output << @simulation.return }
41
+ expect(output.uniq.length).to eq 50
42
+ end
43
+
44
+ it "the data points should have the given mean" do
45
+ output = []
46
+ 50.times { output << @simulation.return }
47
+ expect(output.mean).to be > 0.25
48
+ expect(output.mean).to be < 0.45
49
+ end
50
+
51
+ it "the data points should have the given standard deviation" do
52
+ output = []
53
+ 50.times { output << @simulation.return }
54
+ expect(output.standard_deviation).to be > 0.0
55
+ expect(output.standard_deviation).to be < 0.2
56
+ end
57
+
58
+ it "creates data points near the average return if the standard deviation is small" do
59
+ output = []
60
+ 50.times { output << @thin_sim.return }
61
+ expect(output.mean).to be > 0.7999
62
+ expect(output.mean).to be < 0.8001
63
+ end
64
+
65
+ it "creates closely clustered data points if the standard deviation is small" do
66
+ output = []
67
+ 50.times { output << @thin_sim.return }
68
+ expect(output.standard_deviation).to be > 0.0000
69
+ expect(output.standard_deviation).to be < 0.0002
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,142 @@
1
+ require "spec_helper"
2
+
3
+ module Doughnut
4
+
5
+ describe RetirementCalculator do
6
+
7
+ describe "parameters" do
8
+
9
+ before(:each) do
10
+ @retire = RetirementCalculator.new
11
+ end
12
+
13
+ context "defaults" do
14
+
15
+ it "returns a death date of 80 years old" do
16
+ expect(@retire.death_date).to eq Date.new(2067,7,19)
17
+ end
18
+
19
+ it "returns average portfolio return of 10%" do
20
+ expect(@retire.portfolio_return).to eq 0.1
21
+ end
22
+
23
+ it "returns a current net worth of 207000" do
24
+ expect(@retire.current_net_worth).to eq 207000
25
+ end
26
+
27
+ it "updates the death date" do
28
+ @retire.death_date = Date.new(2001,3,7)
29
+ expect(@retire.death_date).to eq Date.new(2001,3,7)
30
+ end
31
+
32
+ it "updates the average portfolio return" do
33
+ @retire.portfolio_return = 0.99
34
+ expect(@retire.portfolio_return).to eq 0.99
35
+ end
36
+
37
+ it "updates the current net worth" do
38
+ @retire.current_net_worth = 100
39
+ expect(@retire.current_net_worth).to eq 100
40
+ end
41
+
42
+ it "sets the net worth on initialization" do
43
+ worth = RetirementCalculator.new(200).current_net_worth
44
+ expect(worth).to eq 200
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ describe "retirement date with no initial net worth" do
52
+
53
+ before(:each) do
54
+ @retire = RetirementCalculator.new(0)
55
+ end
56
+
57
+ it "returns today's date if there are no monthly expenses or incomes" do
58
+ expect(@retire.retirement_date(0,[])).to eq Date.today
59
+ end
60
+
61
+ it "returns today's date if there are no monthly expenses but positive incomes" do
62
+ expect(@retire.retirement_date(0, [{date: Date.new(2012,1,2), income: 100}])).to eq Date.today
63
+ end
64
+
65
+ it "returns the death date if there is no monthly income" do
66
+ expect(@retire.retirement_date(1,[])).to eq Date.new(2067,7,19)
67
+ end
68
+
69
+ it "returns the date of earned income if PV(income) > PV(expenses)" do
70
+ params = [{:date => Date.new(2040,1,2), :income => 2000}]
71
+ expect(@retire.retirement_date(1000, params)).to eq Date.new(2040,1,2)
72
+ end
73
+
74
+ it "returns the date of earned income if PV(income) = PV(expenses)" do
75
+ params = [{:date => Date.new(2041,2,8), :income => 1000}]
76
+ expect(@retire.retirement_date(1000, params)).to eq Date.new(2041,2,8)
77
+ end
78
+
79
+ it "returns the date of earned income if PV(income) > PV(expenses) for multiple incomes" do
80
+ params = [{:date => Date.new(2040,1,2), :income => 999}, {:date => Date.new(2049,7,8), :income => 1000}]
81
+ expect(@retire.retirement_date(1000, params)).to eq Date.new(2049,7,8)
82
+ end
83
+
84
+ it "returns the date of earned income if PV(income) = PV(expenses) for multiple incomes" do
85
+ params = [{:date => Date.new(2040,1,2), :income => 999}, {:date => Date.new(2049,7,8), :income => 1}]
86
+ expect(@retire.retirement_date(1000, params)).to eq Date.new(2049,7,8)
87
+ end
88
+
89
+ end
90
+
91
+ describe "retirement date with non-zero initial net worth" do
92
+
93
+ before(:each) do
94
+ @retire = RetirementCalculator.new(1000)
95
+ end
96
+
97
+ it "returns today's date if there are no monthly expenses or incomes" do
98
+ expect(@retire.retirement_date(0,[])).to eq Date.today
99
+ end
100
+
101
+ it "returns today's date if there are no monthly expenses but positive incomes" do
102
+ expect(@retire.retirement_date(0, [{date: Date.new(2011,1,2), income: 100}])).to eq Date.today
103
+ end
104
+
105
+ it "returns today's if there is no monthly income, but net worth exceeds expenses" do
106
+ expect(@retire.retirement_date(800,[])).to eq Date.today
107
+ end
108
+
109
+ it "returns the death date if there is no monthly income and net worth is less than expenses" do
110
+ expect(@retire.retirement_date(2000,[])).to eq Date.new(2067,7,19)
111
+ end
112
+
113
+ it "returns the date of earned income if current_net_worth + PV(income) > PV(expenses) (test 1)" do
114
+ params = [{:date => Date.new(2040,1,2), :income => 2000}]
115
+ expect(@retire.retirement_date(1100, params)).to eq Date.new(2040,1,2)
116
+ end
117
+
118
+ it "returns the date of earned income if current_net_worth + PV(income) > PV(expenses) (test 2)" do
119
+ params = [{:date => Date.new(2032,1,5), :income => 980}]
120
+ expect(@retire.retirement_date(1500, params)).to eq Date.new(2032,1,5)
121
+ end
122
+
123
+ it "returns the date of earned income if current_net_worth + PV(income) = PV(expenses)" do
124
+ params = [{:date => Date.new(2021,8,8), :income => 1000}]
125
+ expect(@retire.retirement_date(2000, params)).to eq Date.new(2021,8,8)
126
+ end
127
+
128
+ it "returns the date of earned income if current_net_worth + PV(income) > PV(expenses) for multiple incomes" do
129
+ params = [{:date => Date.new(2040,1,2), :income => 999}, {:date => Date.new(2049,7,8), :income => 1000}]
130
+ expect(@retire.retirement_date(2500, params)).to eq Date.new(2049,7,8)
131
+ end
132
+
133
+ it "returns the date of earned income if current_net_worth + PV(income) = PV(expenses) for multiple incomes" do
134
+ params = [{:date => Date.new(2040,1,2), :income => 999}, {:date => Date.new(2049,7,8), :income => 1}]
135
+ expect(@retire.retirement_date(2000, params)).to eq Date.new(2049,7,8)
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+
142
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doughnut
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyrus Vandrevala
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-21 00:00:00.000000000 Z
11
+ date: 2015-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubystats
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  description: ''
70
84
  email:
71
85
  - cyrus.vandrevala@gmail.com
@@ -80,13 +94,20 @@ files:
80
94
  - Rakefile
81
95
  - doughnut.gemspec
82
96
  - lib/doughnut.rb
83
- - lib/doughnut/expenses.rb
84
- - lib/doughnut/income.rb
85
- - lib/doughnut/retirement_calculator.rb
97
+ - lib/doughnut/monte_carlo/population_factory.rb
98
+ - lib/doughnut/monte_carlo/population_tester.rb
99
+ - lib/doughnut/monte_carlo/predicted_return.rb
100
+ - lib/doughnut/retirement_calculator/expenses.rb
101
+ - lib/doughnut/retirement_calculator/income.rb
102
+ - lib/doughnut/retirement_calculator/retirement_calculator.rb
103
+ - lib/doughnut/tasks/retirement.rake
86
104
  - lib/doughnut/version.rb
87
- - spec/expenses_spec.rb
88
- - spec/income_spec.rb
89
- - spec/retirement_calculator_spec.rb
105
+ - spec/monte_carlo/population_factory_spec.rb
106
+ - spec/monte_carlo/population_tester.rb
107
+ - spec/monte_carlo/predicted_return_spec.rb
108
+ - spec/retirement_calculator/expenses_spec.rb
109
+ - spec/retirement_calculator/income_spec.rb
110
+ - spec/retirement_calculator/retirement_calculator_spec.rb
90
111
  - spec/spec_helper.rb
91
112
  homepage: ''
92
113
  licenses:
@@ -113,7 +134,10 @@ signing_key:
113
134
  specification_version: 4
114
135
  summary: Generate statistics for a personal portfolio.
115
136
  test_files:
116
- - spec/expenses_spec.rb
117
- - spec/income_spec.rb
118
- - spec/retirement_calculator_spec.rb
137
+ - spec/monte_carlo/population_factory_spec.rb
138
+ - spec/monte_carlo/population_tester.rb
139
+ - spec/monte_carlo/predicted_return_spec.rb
140
+ - spec/retirement_calculator/expenses_spec.rb
141
+ - spec/retirement_calculator/income_spec.rb
142
+ - spec/retirement_calculator/retirement_calculator_spec.rb
119
143
  - spec/spec_helper.rb
@@ -1,75 +0,0 @@
1
- require "spec_helper"
2
-
3
- module Doughnut
4
-
5
- describe RetirementCalculator do
6
-
7
- before(:each) do
8
- @retire = RetirementCalculator.new
9
- end
10
-
11
- describe "parameters" do
12
-
13
- context "defaults" do
14
-
15
- it "returns a death date of 80 years old" do
16
- expect(@retire.death_date).to eq Date.new(2067,7,19)
17
- end
18
-
19
- it "returns average portfolio return of 10%" do
20
- expect(@retire.portfolio_return).to eq 0.1
21
- end
22
-
23
- it "updates the death date" do
24
- @retire.death_date = Date.new(2001,3,7)
25
- expect(@retire.death_date).to eq Date.new(2001,3,7)
26
- end
27
-
28
- it "updates the average portfolio return" do
29
- @retire.portfolio_return = 0.99
30
- expect(@retire.portfolio_return).to eq 0.99
31
- end
32
-
33
- end
34
-
35
- end
36
-
37
- describe "retirement date" do
38
-
39
- it "returns today's date if there are no monthly expenses or incomes" do
40
- expect(@retire.retirement_date(0,[])).to eq Date.today
41
- end
42
-
43
- it "returns today's date if there are no monthly expenses but positive incomes" do
44
- expect(@retire.retirement_date(0, [{date: Date.new(2012,1,2), income: 100}])).to eq Date.today
45
- end
46
-
47
- it "returns the death date if there is no monthly income" do
48
- expect(@retire.retirement_date(1,[])).to eq Date.new(2067,7,19)
49
- end
50
-
51
- it "returns the date of earned income if PV(income) > PV(expenses)" do
52
- params = [{:date => Date.new(2040,1,2), :income => 2000}]
53
- expect(@retire.retirement_date(1000, params)).to eq Date.new(2040,1,2)
54
- end
55
-
56
- it "returns the date of earned income if PV(income) = PV(expenses)" do
57
- params = [{:date => Date.new(2041,2,8), :income => 1000}]
58
- expect(@retire.retirement_date(1000, params)).to eq Date.new(2041,2,8)
59
- end
60
-
61
- it "returns the date of earned income if PV(income) > PV(expenses) for multiple incomes" do
62
- params = [{:date => Date.new(2040,1,2), :income => 999}, {:date => Date.new(2049,7,8), :income => 1000}]
63
- expect(@retire.retirement_date(1000, params)).to eq Date.new(2049,7,8)
64
- end
65
-
66
- it "returns the date of earned income if PV(income) = PV(expenses) for multiple incomes" do
67
- params = [{:date => Date.new(2040,1,2), :income => 999}, {:date => Date.new(2049,7,8), :income => 1}]
68
- expect(@retire.retirement_date(1000, params)).to eq Date.new(2049,7,8)
69
- end
70
-
71
- end
72
-
73
- end
74
-
75
- end