1+ # // Copyright (c) 2012 Sutoiku, Inc. (MIT License)
2+
3+ # // Some algorithms have been ported from Apache OpenOffice:
4+
5+ # /**************************************************************
6+ # *
7+ # * Licensed to the Apache Software Foundation (ASF) under one
8+ # * or more contributor license agreements. See the NOTICE file
9+ # * distributed with this work for additional information
10+ # * regarding copyright ownership. The ASF licenses this file
11+ # * to you under the Apache License, Version 2.0 (the
12+ # * "License"); you may not use this file except in compliance
13+ # * with the License. You may obtain a copy of the License at
14+ # *
15+ # * http://www.apache.org/licenses/LICENSE-2.0
16+ # *
17+ # * Unless required by applicable law or agreed to in writing,
18+ # * software distributed under the License is distributed on an
19+ # * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20+ # * KIND, either express or implied. See the License for the
21+ # * specific language governing permissions and limitations
22+ # * under the License.
23+ # *
24+ # *************************************************************/
25+
26+
27+ def years_between_dates (date1 , date2 ):
28+ delta = date2 - date1
29+ return (delta .days / 365 )
30+
31+
32+ # // Credits: algorithm inspired by Apache OpenOffice
33+ # // Calculates the resulting amount
34+ def irrResult (values , dates , rate ):
35+ r = rate + 1
36+ result = values [0 ]
37+ for i in range (1 , len (values )):
38+ result = result + values [i ] / pow (r , years_between_dates (dates [0 ], dates [i ]))
39+ i = i + 1
40+ return result
41+
42+
43+ # // Calculates the first derivation
44+ def irrResultDeriv (values , dates , rate ):
45+ r = rate + 1
46+ result = 0
47+ for i in range (1 , len (values )):
48+ frac = years_between_dates (dates [0 ], dates [i ])
49+ result = result - frac * values [i ] / pow (r , frac + 1 )
50+ i = i + 1
51+ return result
52+
53+
54+ def xirr (values , dates ):
55+ # // Check that values contains at least one positive value and one negative value
56+ positive = False
57+ negative = False
58+ for v in values :
59+ if v > 0 :
60+ positive = True
61+ if v < 0 :
62+ negative = True
63+
64+ # // Return error if values does not contain at least one positive value and one negative value
65+ if not (positive and negative ):
66+ return 'Error'
67+ # // Initialize guess and resultRate
68+ guess = 0.1
69+ resultRate = guess
70+
71+ # // Set maximum epsilon for end of iteration
72+ epsMax = 1e-10
73+
74+ # // Set maximum number of iterations
75+ iterMax = 20
76+
77+ # // Implement Newton's method
78+ iteration = 0
79+ contLoop = True
80+ while contLoop and (iteration < iterMax ):
81+ resultValue = irrResult (values , dates , resultRate )
82+ newRate = resultRate - (resultValue / irrResultDeriv (values , dates , resultRate ))
83+ epsRate = abs (newRate - resultRate )
84+ resultRate = newRate
85+ if resultRate < - 1 :
86+ resultRate = - 0.999999999
87+ contLoop = (epsRate > epsMax ) and (abs (resultValue ) > epsMax )
88+ iteration = iteration + 1
89+ if contLoop :
90+ return epsRate > epsMax , epsRate , 'iterMax'
91+ return resultRate
0 commit comments