doughnut 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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