xirr 0.7.1 → 1.0.0
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 +4 -4
- data/.github/workflows/ci.yml +21 -0
- data/.gitignore +0 -0
- data/CHANGE_LOG.md +34 -5
- data/CLAUDE.md +82 -0
- data/README.md +133 -27
- data/Rakefile +10 -0
- data/benchmark/solvers.rb +59 -0
- data/ext/xirr/extconf.rb +23 -0
- data/ext/xirr/xirr_native.c +125 -0
- data/lib/xirr/base.rb +28 -13
- data/lib/xirr/bisection.rb +16 -12
- data/lib/xirr/bonds.rb +120 -0
- data/lib/xirr/brent.rb +108 -0
- data/lib/xirr/cashflow.rb +109 -23
- data/lib/xirr/config.rb +8 -5
- data/lib/xirr/depreciation.rb +97 -0
- data/lib/xirr/newton_method.rb +24 -50
- data/lib/xirr/periodic.rb +84 -0
- data/lib/xirr/rates.rb +39 -0
- data/lib/xirr/returns.rb +130 -0
- data/lib/xirr/rtsafe.rb +146 -0
- data/lib/xirr/rtsafe_c.rb +32 -0
- data/lib/xirr/transaction.rb +4 -9
- data/lib/xirr/tvm.rb +191 -0
- data/lib/xirr/version.rb +1 -1
- data/lib/xirr.rb +9 -2
- data/test/test_bonds.rb +34 -0
- data/test/test_brent.rb +48 -0
- data/test/test_cashflow.rb +61 -23
- data/test/test_depreciation.rb +26 -0
- data/test/test_helper.rb +9 -3
- data/test/test_periodic.rb +45 -0
- data/test/test_rates.rb +20 -0
- data/test/test_returns.rb +35 -0
- data/test/test_solver_properties.rb +135 -0
- data/test/test_transactions.rb +2 -2
- data/test/test_tvm.rb +69 -0
- data/xirr.gemspec +6 -4
- metadata +39 -42
- data/.coveralls.yml +0 -2
- data/.travis.yml +0 -8
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
require_relative 'test_helper'
|
|
2
|
+
|
|
3
|
+
# Property and stress tests for the rtsafe solver. These are the tests that would
|
|
4
|
+
# catch a bracketing or overflow regression: they assert the invariant a solver
|
|
5
|
+
# must satisfy — the returned rate sits within one precision-step of a genuine
|
|
6
|
+
# sign change of the net present value — rather than a hand-computed number.
|
|
7
|
+
describe 'rtsafe solver properties' do
|
|
8
|
+
# The rounding granularity of a converged result.
|
|
9
|
+
H = 10.0**(-Xirr.config.precision)
|
|
10
|
+
|
|
11
|
+
# The returned rate is a real root to the configured precision when the NPV
|
|
12
|
+
# changes sign across [rate - H, rate + H] (or is already ~0 there). This holds
|
|
13
|
+
# regardless of the flow's magnitude or maturity, so it doubles as the property
|
|
14
|
+
# assertion and the stress-case assertion.
|
|
15
|
+
def assert_root(cf, rate)
|
|
16
|
+
here = cf.xnpv(rate)
|
|
17
|
+
lo = cf.xnpv(rate - H)
|
|
18
|
+
hi = cf.xnpv(rate + H)
|
|
19
|
+
straddles = (lo <= 0 && hi >= 0) || (lo >= 0 && hi <= 0)
|
|
20
|
+
assert straddles || here.abs < 1e-6,
|
|
21
|
+
"rate #{rate} is not a root to precision #{Xirr.config.precision}: " \
|
|
22
|
+
"xnpv(r-H)=#{lo}, xnpv(r)=#{here}, xnpv(r+H)=#{hi}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# A valid, single-sign-change cashflow: an initial outlay followed by returns.
|
|
26
|
+
def random_cashflow(rng)
|
|
27
|
+
cf = Cashflow.new
|
|
28
|
+
base = Date.new(2000, 1, 1)
|
|
29
|
+
cf << Transaction.new(-(1_000 + rng.rand(9_000)), date: base)
|
|
30
|
+
n = 1 + rng.rand(10)
|
|
31
|
+
(1..n).each do |i|
|
|
32
|
+
days = i * (30 + rng.rand(400))
|
|
33
|
+
cf << Transaction.new(rng.rand(3_500) - 800, date: base + days)
|
|
34
|
+
end
|
|
35
|
+
# Guarantee at least one inflow so the series changes sign.
|
|
36
|
+
cf << Transaction.new(500 + rng.rand(6_000), date: base + (n + 1) * 400)
|
|
37
|
+
cf
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'returns a genuine root for random cashflows' do
|
|
41
|
+
rng = Random.new(0xC0FFEE)
|
|
42
|
+
checked = 0
|
|
43
|
+
300.times do
|
|
44
|
+
cf = random_cashflow(rng)
|
|
45
|
+
next unless cf.valid?
|
|
46
|
+
|
|
47
|
+
rate =
|
|
48
|
+
begin
|
|
49
|
+
cf.xirr!(method: :rtsafe)
|
|
50
|
+
rescue ArgumentError
|
|
51
|
+
next # genuine non-convergence, nothing to assert about
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
assert_root(cf, rate)
|
|
55
|
+
checked += 1
|
|
56
|
+
end
|
|
57
|
+
assert checked > 100, "property test was nearly vacuous: only #{checked} cases checked"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'agrees with bisection and newton on a well-behaved flow' do
|
|
61
|
+
cf = Cashflow.new
|
|
62
|
+
cf << Transaction.new(1000, date: '1985-01-01'.to_date)
|
|
63
|
+
cf << Transaction.new(-600, date: '1990-01-01'.to_date)
|
|
64
|
+
cf << Transaction.new(-6000, date: '1995-01-01'.to_date)
|
|
65
|
+
assert_in_delta cf.xirr(method: :bisection).to_f, cf.xirr(method: :rtsafe).to_f, 1e-5
|
|
66
|
+
assert_in_delta cf.xirr(method: :newton_method).to_f, cf.xirr(method: :rtsafe).to_f, 1e-5
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe 'stress cases' do
|
|
70
|
+
it 'handles a 30-year monthly schedule (long maturity, low per-period rate)' do
|
|
71
|
+
cf = Cashflow.new
|
|
72
|
+
base = Date.new(1990, 1, 1)
|
|
73
|
+
cf << Transaction.new(-100_000, date: base)
|
|
74
|
+
360.times { |m| cf << Transaction.new(650, date: base >> (m + 1)) }
|
|
75
|
+
rate = cf.xirr!(method: :rtsafe)
|
|
76
|
+
assert rate > 0, "expected a positive rate, got #{rate}"
|
|
77
|
+
assert_root(cf, rate)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'handles a near -100% return without overflowing' do
|
|
81
|
+
cf = Cashflow.new
|
|
82
|
+
cf << Transaction.new(-10_000, date: '2014-04-15'.to_date)
|
|
83
|
+
cf << Transaction.new(305.6, date: '2014-05-15'.to_date)
|
|
84
|
+
cf << Transaction.new(500, date: '2014-10-19'.to_date)
|
|
85
|
+
rate = cf.xirr!(method: :rtsafe)
|
|
86
|
+
assert rate < -0.9, "expected a rate near -1, got #{rate}"
|
|
87
|
+
assert rate > -1.0
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'handles a very long, low-rate horizon' do
|
|
91
|
+
cf = Cashflow.new flow: [
|
|
92
|
+
Transaction.new(-1000, date: Date.new(1957, 1, 1)),
|
|
93
|
+
Transaction.new(390_000, date: Date.new(2013, 1, 1))
|
|
94
|
+
]
|
|
95
|
+
assert_in_delta 0.112339, cf.xirr!(method: :rtsafe).to_f, 1e-5
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'reports non-convergence rather than crashing on an impossible bracket' do
|
|
99
|
+
# One day apart with an enormous return: the root is astronomically high and
|
|
100
|
+
# lies outside any sane bracket, so rtsafe gives up cleanly.
|
|
101
|
+
cf = Cashflow.new
|
|
102
|
+
cf << Transaction.new(1000, date: '1985-01-01'.to_date)
|
|
103
|
+
cf << Transaction.new(-6000, date: '1985-01-02'.to_date)
|
|
104
|
+
assert_raises(ArgumentError) { cf.xirr!(method: :rtsafe) }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'the native rtsafe_c matches the pure-Ruby rtsafe' do
|
|
108
|
+
skip 'native extension not compiled' unless Xirr::NATIVE
|
|
109
|
+
rng = Random.new(0xBEEF)
|
|
110
|
+
50.times do
|
|
111
|
+
cf = random_cashflow(rng)
|
|
112
|
+
next unless cf.valid?
|
|
113
|
+
assert_equal cf.xirr(method: :rtsafe), cf.xirr(method: :rtsafe_c)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'raises a clear error when rtsafe_c is chosen without the extension' do
|
|
118
|
+
skip 'extension is compiled here' if Xirr::NATIVE
|
|
119
|
+
cf = Cashflow.new
|
|
120
|
+
cf << Transaction.new(-1000, date: '2014-01-01'.to_date)
|
|
121
|
+
cf << Transaction.new(1100, date: '2015-01-01'.to_date)
|
|
122
|
+
assert_raises(ArgumentError) { cf.xirr(method: :rtsafe_c) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'newton does not crash when a guess overshoots below -100%' do
|
|
126
|
+
# A Newton step from this guess drives the rate below -1, where the
|
|
127
|
+
# discount base goes negative; the solver must bail, not raise.
|
|
128
|
+
cf = Cashflow.new
|
|
129
|
+
cf << Transaction.new(-10_000, date: '2014-04-15'.to_date)
|
|
130
|
+
cf << Transaction.new(305.6, date: '2014-05-15'.to_date)
|
|
131
|
+
cf << Transaction.new(500, date: '2014-10-19'.to_date)
|
|
132
|
+
assert_equal Xirr::REPLACE_FOR_NIL, cf.xirr(method: :newton_method, guess: 0.1)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
data/test/test_transactions.rb
CHANGED
|
@@ -6,10 +6,10 @@ describe 'Transaction' do
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
it 'converts amount to float' do
|
|
9
|
-
|
|
9
|
+
assert_kind_of Float, @t.amount
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
it '
|
|
12
|
+
it 'retrieves the date' do
|
|
13
13
|
assert_equal Date.today, @t.date
|
|
14
14
|
end
|
|
15
15
|
|
data/test/test_tvm.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require_relative 'test_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Xirr::TVM' do
|
|
4
|
+
it 'fv — future value' do
|
|
5
|
+
assert_in_delta 2886.68, Xirr::TVM.fv(0.05, 10, -100, -1000).round(2), 1e-2
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'pv — present value' do
|
|
9
|
+
assert_in_delta 1386.09, Xirr::TVM.pv(0.05, 10, -100, -1000).round(2), 1e-2
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'pmt — level payment' do
|
|
13
|
+
assert_in_delta(-162.75, Xirr::TVM.pmt(0.10, 10, 1000).round(2), 1e-2)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'ipmt — interest portion' do
|
|
17
|
+
assert_in_delta(-8.333333, Xirr::TVM.ipmt(0.10 / 12, 1, 12, 1000), 1e-6)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'ppmt — principal portion' do
|
|
21
|
+
assert_in_delta(-79.582554, Xirr::TVM.ppmt(0.10 / 12, 1, 12, 1000), 1e-6)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'ipmt + ppmt equals pmt for every period' do
|
|
25
|
+
payment = Xirr::TVM.pmt(0.10 / 12, 12, 1000)
|
|
26
|
+
(1..12).each do |per|
|
|
27
|
+
split = Xirr::TVM.ipmt(0.10 / 12, per, 12, 1000) + Xirr::TVM.ppmt(0.10 / 12, per, 12, 1000)
|
|
28
|
+
assert_in_delta payment, split, 1e-9
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'nper — number of periods' do
|
|
33
|
+
assert_in_delta 14.21, Xirr::TVM.nper(0.05, -100, 1000).round(2), 1e-2
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'rate — solves for the periodic rate' do
|
|
37
|
+
assert_equal 0.0, Xirr::TVM.rate(10, -100, 1000)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'validates the type argument' do
|
|
41
|
+
assert_raises(ArgumentError) { Xirr::TVM.fv(0.05, 10, -100, -1000, 2) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'handles annuity due (type 1)' do
|
|
45
|
+
# A due payment is the ordinary payment discounted one period earlier.
|
|
46
|
+
ordinary = Xirr::TVM.pmt(0.10, 10, 1000, 0, 0)
|
|
47
|
+
due = Xirr::TVM.pmt(0.10, 10, 1000, 0, 1)
|
|
48
|
+
assert_in_delta ordinary / 1.10, due, 1e-9
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe 'amortization_schedule' do
|
|
52
|
+
before do
|
|
53
|
+
@schedule = Xirr::TVM.amortization_schedule(0.10 / 12, 12, 1000)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'has a correct first row' do
|
|
57
|
+
first = @schedule.first
|
|
58
|
+
assert_equal(-87.92, first[:payment])
|
|
59
|
+
assert_equal(-8.33, first[:interest])
|
|
60
|
+
assert_equal(-79.59, first[:principal])
|
|
61
|
+
assert_equal 920.41, first[:balance]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'pays the balance down to exactly zero' do
|
|
65
|
+
assert_equal 0.0, @schedule.last[:balance]
|
|
66
|
+
assert_equal 12, @schedule.length
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/xirr.gemspec
CHANGED
|
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.version = Xirr::VERSION
|
|
9
9
|
spec.authors = ['tubedude']
|
|
10
10
|
spec.email = ['beto@trevisan.me']
|
|
11
|
-
spec.summary = %q{
|
|
12
|
-
spec.description = %q{Calculates
|
|
11
|
+
spec.summary = %q{XIRR and a finance toolkit for Ruby, built on a safeguarded-Newton solver}
|
|
12
|
+
spec.description = %q{Calculates the XIRR of a cashflow (the internal rate of return for transactions on arbitrary dates, like Excel's XIRR) using a safeguarded-Newton solver, with an optional native C build. Also includes time-value-of-money, bond, depreciation, rate-conversion, and performance-metric functions.}
|
|
13
13
|
spec.homepage = 'https://github.com/tubedude/xirr'
|
|
14
14
|
spec.license = 'MIT'
|
|
15
15
|
|
|
@@ -18,13 +18,15 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ['lib']
|
|
20
20
|
|
|
21
|
+
# Optional native rtsafe solver. The build is best-effort (see extconf.rb); the
|
|
22
|
+
# gem falls back to the pure-Ruby solver when it isn't compiled.
|
|
23
|
+
spec.extensions = ['ext/xirr/extconf.rb']
|
|
24
|
+
|
|
21
25
|
spec.required_ruby_version = '>=3.1'
|
|
22
26
|
|
|
23
27
|
spec.add_dependency 'activesupport', '>= 6.1', '< 8'
|
|
24
28
|
|
|
25
|
-
spec.add_development_dependency 'activesupport', '>= 6.1', '< 8'
|
|
26
29
|
spec.add_development_dependency 'minitest', '~> 5.14'
|
|
27
|
-
spec.add_development_dependency 'coveralls', '~> 0.8'
|
|
28
30
|
spec.add_development_dependency 'bundler', '>= 2.2'
|
|
29
31
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
30
32
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xirr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- tubedude
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-07-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -30,26 +30,6 @@ dependencies:
|
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '8'
|
|
33
|
-
- !ruby/object:Gem::Dependency
|
|
34
|
-
name: activesupport
|
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - ">="
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '6.1'
|
|
40
|
-
- - "<"
|
|
41
|
-
- !ruby/object:Gem::Version
|
|
42
|
-
version: '8'
|
|
43
|
-
type: :development
|
|
44
|
-
prerelease: false
|
|
45
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
-
requirements:
|
|
47
|
-
- - ">="
|
|
48
|
-
- !ruby/object:Gem::Version
|
|
49
|
-
version: '6.1'
|
|
50
|
-
- - "<"
|
|
51
|
-
- !ruby/object:Gem::Version
|
|
52
|
-
version: '8'
|
|
53
33
|
- !ruby/object:Gem::Dependency
|
|
54
34
|
name: minitest
|
|
55
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -64,20 +44,6 @@ dependencies:
|
|
|
64
44
|
- - "~>"
|
|
65
45
|
- !ruby/object:Gem::Version
|
|
66
46
|
version: '5.14'
|
|
67
|
-
- !ruby/object:Gem::Dependency
|
|
68
|
-
name: coveralls
|
|
69
|
-
requirement: !ruby/object:Gem::Requirement
|
|
70
|
-
requirements:
|
|
71
|
-
- - "~>"
|
|
72
|
-
- !ruby/object:Gem::Version
|
|
73
|
-
version: '0.8'
|
|
74
|
-
type: :development
|
|
75
|
-
prerelease: false
|
|
76
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
77
|
-
requirements:
|
|
78
|
-
- - "~>"
|
|
79
|
-
- !ruby/object:Gem::Version
|
|
80
|
-
version: '0.8'
|
|
81
47
|
- !ruby/object:Gem::Dependency
|
|
82
48
|
name: bundler
|
|
83
49
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -106,35 +72,58 @@ dependencies:
|
|
|
106
72
|
- - "~>"
|
|
107
73
|
- !ruby/object:Gem::Version
|
|
108
74
|
version: '13.0'
|
|
109
|
-
description: Calculates
|
|
110
|
-
|
|
75
|
+
description: Calculates the XIRR of a cashflow (the internal rate of return for transactions
|
|
76
|
+
on arbitrary dates, like Excel's XIRR) using a safeguarded-Newton solver, with an
|
|
77
|
+
optional native C build. Also includes time-value-of-money, bond, depreciation,
|
|
78
|
+
rate-conversion, and performance-metric functions.
|
|
111
79
|
email:
|
|
112
80
|
- beto@trevisan.me
|
|
113
81
|
executables: []
|
|
114
|
-
extensions:
|
|
82
|
+
extensions:
|
|
83
|
+
- ext/xirr/extconf.rb
|
|
115
84
|
extra_rdoc_files: []
|
|
116
85
|
files:
|
|
117
|
-
- ".
|
|
86
|
+
- ".github/workflows/ci.yml"
|
|
118
87
|
- ".gitignore"
|
|
119
88
|
- ".ruby-gemset"
|
|
120
89
|
- ".ruby-version"
|
|
121
|
-
- ".travis.yml"
|
|
122
90
|
- CHANGE_LOG.md
|
|
91
|
+
- CLAUDE.md
|
|
123
92
|
- Gemfile
|
|
124
93
|
- LICENSE.txt
|
|
125
94
|
- README.md
|
|
126
95
|
- Rakefile
|
|
96
|
+
- benchmark/solvers.rb
|
|
97
|
+
- ext/xirr/extconf.rb
|
|
98
|
+
- ext/xirr/xirr_native.c
|
|
127
99
|
- lib/xirr.rb
|
|
128
100
|
- lib/xirr/base.rb
|
|
129
101
|
- lib/xirr/bisection.rb
|
|
102
|
+
- lib/xirr/bonds.rb
|
|
103
|
+
- lib/xirr/brent.rb
|
|
130
104
|
- lib/xirr/cashflow.rb
|
|
131
105
|
- lib/xirr/config.rb
|
|
106
|
+
- lib/xirr/depreciation.rb
|
|
132
107
|
- lib/xirr/newton_method.rb
|
|
108
|
+
- lib/xirr/periodic.rb
|
|
109
|
+
- lib/xirr/rates.rb
|
|
110
|
+
- lib/xirr/returns.rb
|
|
111
|
+
- lib/xirr/rtsafe.rb
|
|
112
|
+
- lib/xirr/rtsafe_c.rb
|
|
133
113
|
- lib/xirr/transaction.rb
|
|
114
|
+
- lib/xirr/tvm.rb
|
|
134
115
|
- lib/xirr/version.rb
|
|
116
|
+
- test/test_bonds.rb
|
|
117
|
+
- test/test_brent.rb
|
|
135
118
|
- test/test_cashflow.rb
|
|
119
|
+
- test/test_depreciation.rb
|
|
136
120
|
- test/test_helper.rb
|
|
121
|
+
- test/test_periodic.rb
|
|
122
|
+
- test/test_rates.rb
|
|
123
|
+
- test/test_returns.rb
|
|
124
|
+
- test/test_solver_properties.rb
|
|
137
125
|
- test/test_transactions.rb
|
|
126
|
+
- test/test_tvm.rb
|
|
138
127
|
- xirr.gemspec
|
|
139
128
|
homepage: https://github.com/tubedude/xirr
|
|
140
129
|
licenses:
|
|
@@ -158,8 +147,16 @@ requirements: []
|
|
|
158
147
|
rubygems_version: 3.5.23
|
|
159
148
|
signing_key:
|
|
160
149
|
specification_version: 4
|
|
161
|
-
summary:
|
|
150
|
+
summary: XIRR and a finance toolkit for Ruby, built on a safeguarded-Newton solver
|
|
162
151
|
test_files:
|
|
152
|
+
- test/test_bonds.rb
|
|
153
|
+
- test/test_brent.rb
|
|
163
154
|
- test/test_cashflow.rb
|
|
155
|
+
- test/test_depreciation.rb
|
|
164
156
|
- test/test_helper.rb
|
|
157
|
+
- test/test_periodic.rb
|
|
158
|
+
- test/test_rates.rb
|
|
159
|
+
- test/test_returns.rb
|
|
160
|
+
- test/test_solver_properties.rb
|
|
165
161
|
- test/test_transactions.rb
|
|
162
|
+
- test/test_tvm.rb
|
data/.coveralls.yml
DELETED