diff --git a/CHANGE_LOG.md b/CHANGE_LOG.md index cd0c81d..7ba52e1 100644 --- a/CHANGE_LOG.md +++ b/CHANGE_LOG.md @@ -1,3 +1,10 @@ +## Version 0.2.4 + +* Cashflow Invalid Messages are now public. +* Cashflow Invalid? won't raise exception +* Running XIRR in an invalid cashflow will throw exception +* New Cashflow No Exception XIRR call. + ## Version 0.2.3 * Major fix to Bisection Engine. diff --git a/lib/xirr/cashflow.rb b/lib/xirr/cashflow.rb index 0199a1f..ba55371 100644 --- a/lib/xirr/cashflow.rb +++ b/lib/xirr/cashflow.rb @@ -19,11 +19,7 @@ def initialize(*args) # Check if Cashflow is invalid and raises ArgumentError # @return [Boolean] def invalid? - if positives.empty? || negatives.empty? - raise ArgumentError, invalid_message - else - false - end + positives.empty? || negatives.empty? end # Inverse of #invalid? @@ -47,7 +43,7 @@ def max_date # Calculates a simple IRR guess based on period of investment and multiples. # @return [Float] def irr_guess - ((multiple ** (1 / years_of_investment)) - 1).round(3) if valid? + valid? ? ((multiple ** (1 / years_of_investment)) - 1).round(3) : false end # @param guess [Float] @@ -55,25 +51,55 @@ def irr_guess # @return [Float] # Finds the XIRR according to the method provided. Default to Bisection def xirr(guess = nil, method = Xirr.config.default_method) - _method = case method - when :bisection - Bisection.new(self) - when :newton_method - NewtonMethod.new(self) - else - raise ArgumentError, "There is no #{method} method" - end - _method.send :xirr, guess if valid? + if valid? + choose_(method).send :xirr, guess + else + raise ArgumentError, invalid_message + end + end + + # Calls XIRR but throws no exception and returns with 0 + # @param guess [Float] + # @param method [Symbol] + # @return [Float] + def xirr_no_exception(guess = nil, method = Xirr.config.default_method) + if invalid? + BigDecimal.new(0, Xirr::PRECISION) + else + xirr(guess, method) + end end + # First investment date # @return [Time] def min_date @min_date ||= self.map(&:date).min end + # @return [String] + # Error message depending on the missing transaction + def invalid_message + return 'No positive transaction' if positives.empty? + return 'No negative transaction' if negatives.empty? + end + private + # @param method [Symbol] + # Choose a Method to call. + # @return [Class] + def choose_(method) + case method + when :bisection + Bisection.new(self) + when :newton_method + NewtonMethod.new(self) + else + raise ArgumentError, "There is no #{method} method" + end + end + # @api private # Sorts the {Cashflow} by date ascending # and finds the signal of the first transaction. @@ -129,13 +155,6 @@ def split_transactions @negatives, @positives = self.partition { |x| x.amount >= 0 } # Inverted as negative amount is good end - # @api private - # @return [String] - # Error message depending on the missing transaction - def invalid_message - return 'No positive transaction' if positives.empty? - return 'No negative transaction' if negatives.empty? - end end diff --git a/lib/xirr/version.rb b/lib/xirr/version.rb index c0b4e64..a6bf1b1 100644 --- a/lib/xirr/version.rb +++ b/lib/xirr/version.rb @@ -1,4 +1,4 @@ module Xirr # Version of the Gem - VERSION = "0.2.3" + VERSION = "0.2.4" end diff --git a/test/test_cashflow.rb b/test/test_cashflow.rb index 46043a1..012e563 100644 --- a/test/test_cashflow.rb +++ b/test/test_cashflow.rb @@ -89,9 +89,14 @@ end it 'is invalid' do - assert_raises(ArgumentError) { @cf.valid? } + assert true, !@cf.valid? end + it 'returns 0 instead of expection ' do + assert_equal BigDecimal.new(0, 6), @cf.xirr_no_exception + end + + it 'with a wrong method is invalid' do assert_raises(ArgumentError) { @cf.xirr(nil, :no_method) } end @@ -101,7 +106,7 @@ end it 'raises error when xirr is called' do - assert_raises(ArgumentError) { @cf.irr_guess } + assert true, !@cf.irr_guess end end @@ -114,7 +119,7 @@ end it 'is invalid' do - assert_raises(ArgumentError) { @cf.valid? } + assert true, !@cf.valid? end it 'raises error when xirr is called' do @@ -122,7 +127,7 @@ end it 'raises error when xirr is called' do - assert_raises(ArgumentError) { @cf.irr_guess } + assert true, !@cf.irr_guess end end