DATA Step, Macro, Functions and more

Passing list of arguments to Finance(XIRR) function

Reply
Contributor
Posts: 36

Passing list of arguments to Finance(XIRR) function

Hi there.

I've got a project where i need to calculate  the Internal Rate of Return for non periodic payments.

I'm using the XIRR Finance function to do it but i can't seem to find how to use it with variable input, or even if it's possible.

I've got a dataset with lots of rows and i need to calculate the XIRR for each row.

The problem is that every row as a different number of periods and the function doesn't seem to accept arguments like amt(1)-amt(n), neither i can use syntax like amt1-amtn as "n" is different in each row.

Consider the following example:

data a;

input amt1-amt5 per1-per5;

cards;

-100 101 102 103 . 1 2 3 4 .

-100 101 102 103 105 1 2 3 4 5

run;

For the first row i need to call the function for the first 4 amounts and periods:

IRR=FINANCE('XIRR',amt1,amt2,amt3,amt4,per1,per2,per3,per4);

For the second row i need to call the function for the first 5 amounts and periods:

IRR=FINANCE('XIRR',amt1,amt2,amt3,amt4,amt5,per1,per2,per3,per4,per5);

Now imagine this with a lot of input rows, each one with different amounts and periods.

At the moment i'm using a giant if trying the value for "n", generated by macro code based on the max periods i have in the input data which, as you can imagine, is heavy dutty! Smiley Happy

Any ideas?

Regards,

PA.

Super User
Posts: 10,549

Re: Passing variable arguments to Finance(XIRR) function

What is the range of your N?

It may be possible to test for the number of nonmissing arguments, for your example : test= 5 - nmiss(amt1-amt5)

and then use a select (test) to branch to the appropriate calculation.

Select(test);

     when (2) irr=finance(amt1,amt2,per1,per2);

     when (3) irr=finance(amt1,amt2,amt3,per1,per2,per3);

/*to your max n*/

    otherwise;

end;

admittedly if n gets large this is a tad clumsy but if you write the longest one first then copy paste in the editor and remove items doesn't take that long.

Contributor
Posts: 36

Re: Passing variable arguments to Finance(XIRR) function

The range on "N" is variable. It can be 1 as it can be 5000. N is calculated through two variables (date1 and date2) in my input data like N=date2-date1, which is the number of times that i have to calculate IRR for that row.

For each row the calculation goes like this:

1- Calculate "N" for that row

2- Calculate IRR for the row "N" times like:

N=1

IRR=FINANCE('XIRR',amt1,amt2,per1,per2);

N=2

IRR=FINANCE('XIRR',amt1,amt2,amt3,per1,per2,per3);

.

.

.

N=N

IRR=FINANCE('XIRR',amt1,amt2,...,amtN+1,per1,per2,...,perN+1);

If i understood your suggestion, what i have at the moment is like what you suggested.

I'm doing a first iteration of the input data to calculate max(N) so that in a second iteration i generate the select clause with macro coding. The problem here is the complexity of the iteration. I have: N rows * date2-date1 cycles * MAX(N) if tryes.

Has you can imagine, even with a small subset of data, it takes a while to compute this. The first two components of the formula are impossible to remove but the MAX(N) if tryes could be removed if there was a way to pass to the function a list of values, which i feel there isn't.

Respected Advisor
Posts: 3,777

Re: Passing variable arguments to Finance(XIRR) function

Try this using the already mentioned conversion of missing to zersos.

data a;

   input amt1-amt5 per1-per5;

   cards;

-100 101 102 103 . 1 2 3 4 .

-100 101 102 103 105 1 2 3 4 5

run;

proc stdize reponly method=sum missing=0 out=a2;

   var _all_;

   run;

data a2;

   set a2;

   IRR=FINANCE('XIRR',of amt: perSmiley Happy;

   run;

proc print;

   run;

Respected Advisor
Posts: 3,124

Re: Passing variable arguments to Finance(XIRR) function

Hi,

First of all, I know close to nothing about this finance function, so I am not surprised that there are better solutions. It seems to me that:

1. Finance function does not take missing value as mean, max or other math functions, so when there is a missing value, the result will be missing.

2. Finance function does not take variable list very well, so you need to spell out all of the variable names explicitly.

3. In 'xirr' scenario, missing value can be replaced by zero????( not sure of this part, just a wild guess from comparing results).

So given above, you can try the following, first you need to replace all of the missings with zero, then just use one universal function.

data a;

input amt1-amt5 per1-per5;

array ir _numeric_;

do over ir;

ir+0;

end;

IRR=FINANCE('XIRR',amt1,amt2,amt3,amt4,amt5,per1,per2,per3,per4,per5);

cards;

-100 101 102 103 . 1 2 3 4 .

-100 101 102 103 105 1 2 3 4 5

run;

Good Luck,

Haikuo

Contributor
Posts: 36

Re: Passing variable arguments to Finance(XIRR) function

I'm not sure there is another way of implementing calculation that needs XIRR then implementing the algorithm behind it.

Since the original algorithm has a summatory i believe it wont much better or even better than using the finance function.

What you stated about the function is all true and as given me a real pain.

But i'm preatty sure your sugestion takes some complexity out of the algorithm i currently have.

To give an idea of what i'm currently facing here is a sample of the code (The code is simplified, there are some hash and key searches in the middle of this):

DO I=DATE1+1 TO DATE2;

   PERIOD+1;

   PER(PERIOD)=I;

   CASHFLOWS(PERIOD)=TEQUIV/100/365*1*-CASHFLOWS(1);

   AMT(PERIOD)=ABS(AMT(PERIOD-1))+CASHFLOWS(PERIOD);

      SELECT (PERIOD);

      %DO I=2 %TO &MAXP;

      WHEN (&I) TIR=FINANCE('XIRR',

           %DO J=1 %TO &I;

           AMT(&J),

           %END;

           %DO J=1 %TO &I;

           PER(&J),

           %END;

           .05)*100;

      %END;

       OTHERWISE;

       END;

END;

Each of the three arrays (cashflows, amt, per) are set to missing at the beggining of each iteration (call missing).

Setting them to zero at the beggining of each iteration, instead of missing, and calling the function with &maxp arguments instead of using the select clause might make this go faster.

I'll just have to evaluate if having zeros gives me the same result.

It was a nice tip, thk you.

Super User
Posts: 5,097

Re: Passing variable arguments to Finance(XIRR) function

How about expanding the original suggestion of using zeros?

Use just one formula, for the maximum time period.  Run through a DO loop, each time setting one more value to 0 (starting at the end of the time periods and working back toward the beginning).  You'll be setting actual non-zero values to 0 along the way.  The question is, would the function generate the proper answer?

Good luck.

Super User
Posts: 5,097

Re: Passing variable arguments to Finance(XIRR) function

Last idea for me ...

I imagine that the formula for an internal rate of return is not that complicated.  You already have arrays set up, with a DO loop, to populate CASHFLOWS and AMT for each period.  Why not just set up a few more arrays (and possibly a variable or two holding cumulative numbers across all prior periods), and let your current DO loop calculate the internal rate of return without using a function?

Good luck.

Super User
Posts: 9,691

Re: Passing list of arguments to Finance(XIRR) function

OR You can use lots of  Macro variable . But I am sure it isn't suited for large table.

data a;
input amt1-amt5 per1-per5;
cards;
-100 101 102 103 . 1 2 3 4 .
-100 101 102 103 105 1 2 3 4 5
;
run;
options missing=' ';
proc sql noprint;
 select count(*) into : n from a;
 select catx(",","IRR=FINANCE('XIRR'",amt1,amt2,amt3,amt4,amt5,per1,per2,per3,per4,per5)||");output;"
   into : list1 - : list%left(&n) 
    from a;
quit;

%macro a;
data want;
%do i=1 %to &n ;
 &&list&i
%end;
run;
%mend a;
%a 
 


Ksharp

Ask a Question
Discussion stats
  • 8 replies
  • 1365 views
  • 3 likes
  • 6 in conversation