Subscribe for automatic updates: RSS icon RSS

Login icon Sign in for full access | Help icon Help
Advanced search

Pages: [1]
  Reply  |  Print  
Author Topic: Financial functions  (Read 13567 times)
David T.
Posts: 2


« on: May 21, 2017, 12:54:36 pm »

Hi,

I've put in a request for a full set of Financial functions along the lines of the util.Math class. I don't expect a speedy implementation, if at all !

Can anybody help with the following in 4GL:
My immediate need is for a function xirr() which would calculate the internal rate of return for a series of variable cash flows over variable dates.

A popular and apparently robust function XIRR is implemented in MS Excel and two possible examples of relevant code can be found at http://stackoverflow.com/questions/5179866/xirr-calculation

To be most useful, the method would return a single DECIMAL(16) variable after having read its data from a 1-dimensional dynamic array a bit like:

FUNCTION xirr (x)
  DEFINE
    xirr_rate DECIMAL(16),
    x DYNAMIC ARRAY OF RECORD              # the cash flows, populated by the calling program and called by reference
      dd DATE,
      mm MONEY
  END RECORD

  # do it

  RETURN xirr_rate
END FUNCTION
David T.
Posts: 2


« Reply #1 on: June 27, 2017, 10:17:11 pm »

OK, no replies to this, so I worked up the following which works for me:

FUNCTION xirr (initial_guess, tolerance)
{
OBJECTIVE AND APPROACH:
Microsoft Excel's XIRR function generates the Internal Rate of Return for a variable series of
cash flows of irregular timings. It calculates XIRR using Newtons Method. There is discussion about
whether the Bisection method generates better results, but for our purposes Newtons Method does the
job in a small number of iterations and that's good enough !

In our application we are interested in investment growth rates, so the NPV code segments use the
growth formulation rather than the discounting formulation. This makes no difference to the end
result because the payments are just a series of positive or negative cash flows and the big -ve
one can be either as the first investment payment or the last closing valuation.

This implementation is  based on C# code published at:
  https://stackoverflow.com/questions/5179866/xirr-calculation

It assumes the following global array has been populated:
  payments_schedule DYNAMIC ARRAY OF RECORD
    date DATE,
    value DECIMAL(24,2)
  END RECORD,

The C code for this is:
#        public static double Newtons_method(double guess, fx f, fx df) {
#            double x0 = guess;
#            double x1 = 0.0;
#            double err = 1e+100;
#
#            while (err > tol) {
#                x1 = x0 - f(x0)/df(x0);
#                err = Math.Abs(x1-x0);
#                x0 = x1;
#            }
#
#            return x0;
#        }

  DEFINE
    iterations SMALLINT,
    initial_guess,            # eg 0.1 = 10%; initial starting return value as a floating point not %; equivalent to "double" in C
    tolerance,                # eg 0.00001 = 0.001%
    current_error,          # current error
    current_best_estimate,  # current working return rate
    revised_rate FLOAT      # revised return rate

  # set up default values:
  IF initial_guess IS NULL OR NOT initial_guess THEN LET initial_guess = 0.10 END IF # 10%
  IF tolerance IS NULL OR NOT tolerance THEN LET tolerance = 0.00001 END IF          # 0.001%

  # set up initial values:
  LET current_best_estimate = initial_guess
  LET revised_rate = 0    # this is redundant ?
  LET current_error = 1000
  LET iterations = 0

  # this bit is the Newtons Method bit; we limit it to 100 iterations just in case:
  WHILE current_error > tolerance AND iterations < 100
    LET revised_rate = current_best_estimate -
        (xirr_total_npv(current_best_estimate) / xirr_total_first_derivative_npv(current_best_estimate))
    LET current_error = abs(revised_rate-current_best_estimate)    # abs is in library2; it returns a DECIMAL(24) value
    LET current_best_estimate = revised_rate
    LET iterations = iterations + 1
  END WHILE

  RETURN 100 * revised_rate   # this returns xirr as the % rate
END FUNCTION # xirr

FUNCTION xirr_total_npv (growth_rate)
{
This gets the aggregate of the elemental NPVs for all of the single payments. Normally the length
of the investment period would be (t1 - t0 + 1), we assume the cash flow is effective at the end of
the working day so we go along with the C code implementation of (t1 - t0). Also, the C code
reverses the dates to give the -ve exponent used for discounting; but we use the +ve variation.

The C code for this is:
#        public static fx total_f_xirr(double[] payments, double[] days) {
#            fx resf = (double x) => 0.0;
#
#            for (int i = 0; i < payments.Length; i++) {
#                resf = composeFunctions(resf,f_xirr(payments,days,days[0]));
#            }
#            return resf;
#        }
#       where f_xirr is given by:
#        public static fx f_xirr(double p, double dt, double dt0) {
#            return (double x) => p*Math.Pow((1.0+x),((dt0-dt)/365.0));
#        }
  DEFINE
    start_date, end_date DATE,
    growth_rate FLOAT,          # this is the current best estimate of the growth rate
    total_npv DECIMAL (24,3)    # this is the total FINAL value

  LET total_npv = 0
  LET start_date = payments_schedule[1].date
  LET end_date = payments_schedule[payments_schedule.getLength()].date

  FOR i = 1 TO payments_schedule.getLength()
    LET total_npv = total_npv +
        # this is based on the classic NPV formula, but used as a growth formula:
        payments_schedule.value * util.math.pow ( (1+growth_rate), (end_date-payments_schedule.date)/365 )
  END FOR

  RETURN total_npv
END FUNCTION # xirr_total_npv

FUNCTION xirr_total_first_derivative_npv (growth_rate)
{
This gets the first derivatives of the  aggregate of the elemental NPVs for all of the single payments
The C code for this is:
#        public static fx total_df_xirr(double[] payments, double[] days) {
#            fx resf = (double x) => 0.0;
#
#            for (int i = 0; i < payments.Length; i++) {
#                resf = composeFunctions(resf,df_xirr(payments,days,days[0]));
#            }
#
#            return resf;
#        }
#       where df_xirr is given by:
#        public static fx df_xirr(double p, double dt, double dt0) {
#            return (double x) => (1.0/365.0)*(dt0-dt)*p*Math.Pow((x+1.0),(((dt0-dt)/365.0)-1.0));
#        }
  DEFINE
    start_date, end_date DATE,
    growth_rate FLOAT,          # this is the current best estimate of the growth rate
    total_first_derivative_npv DECIMAL (24,3)

  LET total_first_derivative_npv = 0
  LET start_date = payments_schedule[1].date
  LET end_date = payments_schedule[payments_schedule.getLength()].date

  FOR i = 1 TO payments_schedule.getLength()
    LET total_first_derivative_npv = total_first_derivative_npv +
        # this is based on the classic NPV formula, but used as a growth formula:
        (1/365) * (end_date-payments_schedule.date) * payments_schedule.value *
          util.math.pow ( (1+growth_rate), (((end_date-payments_schedule.date)/365 ) - 1))
  END FOR

  RETURN total_first_derivative_npv
END FUNCTION # xirr_total_first_derivative_npv
Reuben B.
Four Js
Posts: 1119


« Reply #2 on: June 28, 2017, 02:39:43 am »

Feel free to publish to a GitHub repository so that others can find, download and use.

What I would suggest is coding so that it can be called from an IMPORT FGL, and rather than relying on a global array to be populated, define a public type and pass the cashflows array as a parameter.

Also don't rely on other libraries, I notice you had a reference to an abs() from your own library so until we persuade our head of development to add an abs() function or operator :-), you would need to include it in your repository so that it was self-contained. 

Reuben


Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Reuben B.
Four Js
Posts: 1119


« Reply #3 on: June 28, 2017, 02:57:57 am »

An alternative approach which I jus t thought of and tried is to use the formulas built-into the java Apache POI library

Using my Apache POI library available here ... https://github.com/FourjsGenero/fgl_apache_poi

The following code ...

Code
  1. IMPORT FGL fgl_excel
  2.  
  3. MAIN
  4. DEFINE workbook     fgl_excel.workbookType
  5. DEFINE sheet        fgl_excel.sheetType  
  6. DEFINE row          fgl_excel.rowType  
  7. DEFINE cell         fgl_excel.cellType
  8.  
  9.    -- create workbook
  10.    CALL fgl_excel.workbook_create() RETURNING workbook
  11.  
  12.    -- create a worksheet
  13.    CALL fgl_excel.workbook_createsheet(workbook) RETURNING sheet
  14.  
  15.    -- create row
  16.    CALL fgl_excel.sheet_createrow(sheet, 0) RETURNING row
  17.  
  18.    CALL fgl_excel.row_createcell(row, column2row("A")) RETURNING cell
  19.    CALL fgl_excel.cell_number_set(cell,-1000000 )
  20.  
  21.    CALL fgl_excel.row_createcell(row, column2row("B")) RETURNING cell
  22.    CALL fgl_excel.cell_number_set(cell,550000 )
  23.  
  24.    CALL fgl_excel.row_createcell(row, column2row("C")) RETURNING cell
  25.    CALL fgl_excel.cell_number_set(cell,550000 )
  26.  
  27.    CALL fgl_excel.row_createcell(row, column2row("D")) RETURNING cell
  28.    CALL fgl_excel.cell_formula_set(cell,"IRR(A1:C1,0.1)")  
  29.  
  30.    DISPLAY SFMT("Value in cell D1 = %1",  workbook.getCreationHelper().createFormulaEvaluator().evaluate(cell).getNumberValue())
  31.  
  32. END MAIN

outputs Value in cell D1 = 0.06596460097781877 which is same value as if I had plugged into Excel directly.

Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Sebastien F.
Four Js
Posts: 545


« Reply #4 on: June 28, 2017, 10:08:55 am »

Reuben,

While a built-in ABS(x) function or operator would make sense, for now one can use:

   IIF(x<0,-x,x)

To avoid to have a additional library for just for that function.

Seb
David H.
Posts: 158


« Reply #5 on: July 04, 2017, 03:58:22 pm »

On the subject of IIF(), before this was added to the language we had our own equivalent with an extra parameter...

IIF( bool-expr, true-expr, false-expr, bool-treat_null_as)

The programmer could then explicitly decide how they wanted a NULL condition to be treated.

Hopefully one day we will get also function overloading in Genero as well so IIF() could be called as:-

IIF(var == "A", "Accepted", "Rejected")
IIF(var == "A", "Accepted", "Rejected",TRUE) --Treat NULL as Accepted

David
Reuben B.
Four Js
Posts: 1119


« Reply #6 on: July 04, 2017, 11:55:55 pm »

Quote
On the subject of IIF(), before this was added to the language we had our own equivalent with an extra parameter...

IIF( bool-expr, true-expr, false-expr, bool-treat_null_as)

The programmer could then explicitly decide how they wanted a NULL condition to be treated.

Hopefully one day we will get also function overloading in Genero as well so IIF() could be called as:-

IIF(var == "A", "Accepted", "Rejected")
IIF(var == "A", "Accepted", "Rejected",TRUE) --Treat NULL as Accepted

David

Technically IIF is an operator, not a function.

What you want can be coded as

IIF(NVL(var,"A")=="A", "Accepted","Rejected")

IIF(NVL(var=="A",TRUE),"Accepted","Rejected")

IIF(var!="A","Rejected","Accepted")

IIF(NOT(var=="A"),"Rejected","Accepted")


I'd be very surprised if we overloaded as per your suggestion, you could also implement yourself with the pre-processor

&define IIFN(p1,p2,p3,p4) IIF(NVL(p1,p4),p2,p3)

(and replace your existing IIF with IIFN  s/IIF/IIFN/g)

I suspect if you had raised that during the 2.40 EAP when IIF was introduced that was what would be suggested.  Someone who had an existing nvl function that operated slightly different to the nvl operator we introduced had to do something similar.

Reuben





Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines