Finance::Performance::Calc - Perl extension to calculate linked performance numbers.
use 5.006001; use Finance::Performance::Calc qw (:all); print ROR(bmv => 20_000, emv => 21_567.87) # or, for convenience print ROR(20_000, 21_567.87); print ROR(bmv => 20_000, emv => 21_567.87, flows => [ {mvpcf => 20_345, cf => 1000}, {mvpcf => 23_500, cf => -3000} ]); print link_ROR(0.017,0.234,'-3.4%'); print ized_ROR(0.07,5); Finance::Performance::Calc::return_percentages(1); print ROR(20_000,21_567.87); print ROR(bmv => 20_000, emv => 21_567.87, flows => [{mvpcf => 20_345, cf => 1000}, {mvpcf => 23_500, cf => -3000} ]); print link_ROR(0.017,0.234,'-3.4%'); print ized_ROR(0.07,5);
This module allows you to calculate performance for number of situations:
Single period performance, given a beginning market value (BMV) and ending market value (EMV) and optional cash flows in between.
Linked periodic performance; i.e, given three consecutive monthly returns, calculate the performance over the quarter.
"ized" performance. Given a rate of return that covers multiple periods, calculate the per-period return.
The formulae are taken from the book "Measuring Investment Performance". Author: David Spaulding. ISBN: 0-7863-1177-0.
NOTE Before using in a production environment, you should independently verify that the results obtained match what you expect. There may be unintentionally made assumptions in this code about timing and precision and what-not that do not match your assumptions with respect to the calcualtions.
my $ror = ROR($bmv, $emv, @flows);
Rate Of Return. Given a beginning market value ($bmv) and an ending market value ($emv), the rate of return is calculated as
($emv - $bmv)/$bmv
If there are intervening cash flows between the beginning market value and the ending market value, they are each represented by a hash ref containing the keys 'mvpcf' (market value prior to cash flow) and 'cf' (cash flow). The return is caculated by determining the rate of return between each cash flow and then linking the returns together. In this case, the beginning market value and the ending market value are treated as zero cash flow events; that is:
EMV is treated as {mvpcf => EMV, cf => 0} BMV is treated as {mvpcf => BMV, cf => 0}
The rate of return is in decimal form (0.02) for a return of two percent (2.0%).
my $ror = linked_ROR(0.02, -0.02, '3.5%');
Given previously calculated multiple rates of return, calculate the overall rate of return. The rates are linked using the algorithm:
o Convert any percentages to decimal (/100).
o Add 1.00 to each rate
o Multiply all the rates together
o Subtract 1.00 from the result
o Convert to percentage (* 100)
The function properly interprets strings with percents signs by dividing by 100 before using the value in any calculation.
The rate of return is in decimal form (0.02 for a return of two percent).
my $ror = ('7.0%',12);
Given a rate of return, and a number of periods, calculate the rate of return for each period. In our example, the 7.0% return is annual. We want to find the monthly return (12 months in one year). The calculation is:
((ROR + 1.0) ** (1.0/numPeriods)) - 1.00;
The rate of return is in decimal form (0.02 for a return of two percent).
If this function is called with an argument of 1, all of the rates of return that are returned from the functions will be strings in percentage form; i.e. '7.45%' instead of 0.0745. Setting to 0 turns this off. This eliminates the need for doing the math and adding the percent yourself in a known display circumstance.
If called with arguments, the prior value is returned.
If called with no arguments, the current value is returned.
If this function is called with an argument of 1, the steps in ROR and link_ROR will be printed as they are executed. This does impose a speed penalty. Setting to 0 turns this off.
If called with arguments, the prior value is returned.
If called with no arguments, the current value is returned.
The module does not do any extra special handling with regards to precision. The first example above
print ROR(20_000,21_567.87);happily returns a value of
0.0783934999999999on my Linux box. In matters of precision, you have two choices:
1
Use regular Perl scalar floats as arguments and round the final result.
2
Use numeric objects whose behavior can be controlled as arguments. The only requirements for such an object are that addition, subtraction, multiplication and division ae overloaed for the object. For example:
use Math::FixedPrecision; print ROR(new Math::FixedPrecision(20_000), new Math::FixedPrecision(21_567.87));results in
0.08Two (and only two) decimal places are returned because the most precise term is the 21_567.87 value, two decimal places.
If you want more precision, ask for it:
use Math::FixedPrecision; print ROR(new Math::FixedPrecision(20_000), new Math::FixedPrecision(21_567.87,4));resulting in
0.0784Be forewarned that using numeric objects as opposed to native Perl numeric data types can result in loss of speed (see the example script eg/eg.pl in the distribution). YMMV. Test before using in a production scenario.
perl.
Matthew O. Persico, <persicom@cpan.org>
Copyright (C) 2004 by Matthew O. Persico
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.6.1 or, at your option, any later version of Perl 5 you may have available.
Before using in a production environment, you should independently verify that the results obtained match what you expect. I have to the best of my ability checked the results of this module, but I do not present any warrantee or guarrantee that the module is free from error.