uruba / FinanCalc

:moneybag: A lightweight, simple and easy PHP library for calculating annuities (e.g., mortgages) and other financial instruments according to various input data
The Unlicense
30 stars 6 forks source link
annuity bond bond-yield debt dividend maturity mortgage mortgage-calculator payment timespan

FinanCalc

A lightweight, simple and easy PHP library for calculating annuities (e.g., mortgages) and other financial instruments.

Composer package Build Status Code Climate Scrutinizer Code Quality codecov.io

Requirements

Optional:

Features

Much more to come – including calculators for discount securities (NOTE: can now be calculated with help of the simple discount calculator), bond valuation, duration, stock pricing... Also looking into other optimizations and improvements. Current hot ideas:

Please bear in mind that this is an ALPHA version containing incomplete features. The codebase is prone to drastic changes during its way out of the alpha stage.

Learning the ropes

Place the library files into your directory structure

Just copy all the files somewhere appropriate, like a dedicated "vendor" or "lib" directory (so that it doesn't make a mess out of your directory hierarchy). Nothing more is needed.

Alternatively, you can obtain the library as a package via Composer. It's hosted on Packagist

Include it in your project

The initialization is dead simple. Just include the init.php file in the main directory and you are good to go!

// replace the example Composer-bound path with yours
require_once 'vendor/uruba/financalc/init.php';

Or, if you are using Composer, you can use its autoload.php file instead.

require_once 'vendor/autoload.php';

Instantiation

You have two choices as to how to instantiate the appropriate class to get your results:

Factory methods

Since the library automatically keeps track of pre-defined factory methods (contained in the classes which are members of the namespace FinanCalc\Calculators\Factories), it's very easy and straightforward to utilize them.

From the main FinanCalc object (whose instance you get by calling its static method getInstance()) you have to call the getFactory() method, which takes in the name of the factory class as a parameter of type string (you can find all the included factory classes in the src/calculators/factories directory).

This method yields you the final calculator object on which you can call the appropriate methods to retrieve your results (as described in the next chapter) or change the input parameters via its setters.

use FinanCalc\FinanCalc;

...

$annuityCalculatorFactory = FinanCalc
    ::getInstance()
    ->getFactory('DebtAmortizatorFactory')
    ->newYearlyDebtAmortization(
        40000,
        6,
        0.12);

Direct instantiation

The second option is to instantiate the calculator class of your choice directly by calling its constructor with appropriate parameters (you can find all the included calculator classes in the src/calculators directory).

use FinanCalc\Calculators\DebtAmortizator;
use FinanCalc\Utils\Time\TimeUtils;

...

$annuityCalculatorDirect = new DebtAmortizator(
                                       40000,
                                       6,
                                       TimeSpan::asDuration(1),
                                       0.12);

Getting results

There are three ways to retrieve raw results of your calculations:

Directly accessible result object

It's very simple to retrieve the results just by calling the appropriate getter methods. Every calculator class implementing the CalculatorAbstract has a getter method getResult(), which enables you to get an appropriate object representing the result of the calculation according to the data passed earlier to the constructor/factory method of a given calculator class (this intermediate step is unneeded since the version 0.3.0, please update your code by removing the calls of the getResult() method if you're upgrading from any of the earlier versions).

We'll demonstrate the process on our AnnuityCalculator – step by step, day by day:

  1. step is to instantiate the appropriate calculator class, either by constructor or by a factory method (refer to the previous chapter for more information)

    use FinanCalc\FinanCalc;
    
    ...
    
    // Instantiation by a factory method 
    // – 
    // in our case we calculate a yearly-compounded annuity
    // with a duration of 5 periods (here years),
    // 100000 money units paid out per period
    // and a compounding interest rate of 0.15 (i.e., 15%)
    $annuityCalculatorObject = FinanCalc
                                    ::getInstance()
                                    ->getFactory('AnnuityCalculatorFactory')
                                    ->newYearlyAnnuity(
                                        100000, 
                                        5, 
                                        0.15);
  2. step is to get the desired value by exploiting the appropriate getter methods (for a detailed list of available gettter methods please refer to the Reference chapter)

    // get the present value of the annuity in arrears
    // (as a string)
    $PV = $annuityCalculatorObject->getPresentValue(
                        new AnnuityPaymentTypes(AnnuityPaymentTypes::IN_ARREARS)
                    );
    // get the future value of the annuity in arrears
    // (as a string)
    $FV = $annuityCalculatorObject->getFutureValue(
                        new AnnuityPaymentTypes(AnnuityPaymentTypes::IN_ARREARS)
                    );

Therewith the process is concluded and you can now use the obtained results in any way you see fit.

Serialized output

If you want to get the marshaled object representation of the result, you can utilize the built-in method getSerializedResult(SerializerInterface $serializer) which is implemented in the base abstract class, from which every calculator class inherits. You just have to pass a serializer object (i.e., any object of a class which implements the SerializerInterface interface) to it.

We'll again demonstrate the process on our venerable AnnuityCalculator using the XMLSerializer:

  1. step is the same – instantiate the appropriate calculator class, either through the constructor or via a factory method (refer to the previous chapter for more information)

    use FinanCalc\FinanCalc;
    use FinanCalc\Utils\Serializers\XMLSerializer;
    
    ...
    
    // Instantiation by a factory method
    // –
    // in our case we calculate a yearly-compounded annuity
    // with a duration of 5 periods (here years),
    // 100000 money units paid out per period
    // and a compounding interest rate of 0.15 (i.e., 15%)
    $annuityCalculatorObject = FinanCalc
                                    ::getInstance()
                                    ->getFactory('AnnuityCalculatorFactory')
                                    ->newYearlyAnnuity(
                                        100000,
                                        5,
                                        0.15);
  2. step is to get the serialized result object

    $result = $annuityCalculatorObject->getSerializedResult(new XMLSerializer());
  3. now we have a comprehensive representation of the result object in the target format. In our example it looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
      <annuitySinglePaymentAmount>100000</annuitySinglePaymentAmount>
      <annuityNoOfCompoundingPeriods>5</annuityNoOfCompoundingPeriods>
      <annuityInterest>0.15</annuityInterest>
      <annuityPeriodLength>
        <years>1.00000000</years>
        <months>12.00000000</months>
        <days>360.00000000</days>
      </annuityPeriodLength>
      <annuityPresentValue>
        <in_advance>385497.83</in_advance>
        <in_arrears>335215.53</in_arrears>
      </annuityPresentValue>
      <annuityFutureValue>
        <in_advance>775373.79</in_advance>
        <in_arrears>674238.12</in_arrears>
      </annuityFutureValue>
    </root>

NOTE: The name of the "root" element in the XML output can be customized by the config property serializers_root_elem_name. In the future, it will be automatically assigned according to the type of the result object.

You can easily create your own serializer classes by implementing the SerializerInterface. A minimal example of a serializer (in this particular demonstrative case with YAML-formatted output) is here – FinanCalc-YAMLSerializer.

Array

You can also get a result's representation as an array. This representation is primarily used to pass the calculator object's properties to a serializer. The output should therefore be equivalent except for the semantic representation. It also enables you to easily implement your own serializer classes, which are then passed the array as their parameter.

Let's demonstrate the process for the last time on our AnnuityCalculator:

  1. step is the same – instantiate the appropriate calculator class, either through the constructor or via a factory method (refer to the previous chapter for more information)

    use FinanCalc\FinanCalc;
    
    ...
    
    // Instantiation by a factory method
    // –
    // in our case we calculate a yearly-compounded annuity
    // with a duration of 5 periods (here years),
    // 100000 money units paid out per period
    // and a compounding interest rate of 0.15 (i.e., 15%)
    $annuityCalculatorObject = FinanCalc
                                    ::getInstance()
                                    ->getFactory('AnnuityCalculatorFactory')
                                    ->newYearlyAnnuity(
                                        100000,
                                        5,
                                        0.15);
  2. step is to get the result array

    $result = $annuityCalculatorObject->getResultAsArray();
  3. the result array will look like this (the "var_export" representation):

    array (
      'annuitySinglePaymentAmount' => '100000',
      'annuityNoOfCompoundingPeriods' => '5',
      'annuityInterest' => '0.15',
      'annuityPeriodLength' =>
      array (
        'years' => '1.00000000',
        'months' => '12.00000000',
        'days' => '360.00000000',
      ),
      'annuityPresentValue' =>
      array (
        'in_advance' => '385497.83',
        'in_arrears' => '335215.53',
      ),
      'annuityFutureValue' =>
      array (
        'in_advance' => '775373.79',
        'in_arrears' => '674238.12',
      ),
    )

Configuration

The configuration capabilities are currently very limited so there's next to nothing to tinker with.

The default configuration values are currently to be found in the "constants/Defaults.php" file, but there will be a possibility to use an easily accessible JSON (or PHP) configuration file in the future.

Tests

The library includes a "test" subdirectory which contains all the basic tests. For your peace of mind, feel free to give them a run on your setup (provided that you have PHPUnit good and ready) and ensure that everything checks out.

The test-case examples are adapted directly from the lectures of a university course "4ST608 - Introduction to Financial and Insurance Mathematics" taught at the University of Economics in Prague by Prof. RNDr. Tomáš Cipra, DrSc., to whom goes all the credit.

Reference

Here you can find the documentation for each of the vanilla calculator types.

The implicit type of setters'/constructors' arguments as well as getters' returned values is String if not stated otherwise.

AnnuityCalculator

namespace FinanCalc\Calculators

Setters
Getters

AnnuityCalculatorFactory (AnnuityCalculator's factory object)

namespace FinanCalc\Calculators\Factories

AnnuityPaymentTypes

namespace FinanCalc\Constants

AnnuityValueTypes

namespace FinanCalc\Constants

DebtAmortizator

namespace FinanCalc\Calculators

Setters
Getters

DebtAmortizatorFactory (DebtAmortizator's factory object)

namespace FinanCalc\Calculators\Factories

RepaymentInstance

namespace FinanCalc\Calculators\DebtAmortizator

BondFairValueCalculator

namespace FinanCalc\Calculators

Setters
Getters

BondFairValueCalculatorFactory (BondFairValueCalculator's factory object)

namespace FinanCalc\Calculators\Factories

BondYTMCalculator

namespace FinanCalc\Calculators

Setters
Getters

BondYTMCalculatorFactory (BondYTMCalculator's factory object)

namespace FinanCalc\Calculators\Factories

BondDurationCalculator

namespace FinanCalc\Calculators

Setters
Getters

BondDurationCalculatorFactory (BondDurationCalculator's factory object)

namespace FinanCalc\Calculators\Factories

SimpleDiscountCalculator

namespace FinanCalc\Calculators

Setters
Getters

SimpleDiscountCalculatorFactory (SimpleDiscountCalculator's factory object)

namespace FinanCalc\Calculators\Factories

SimpleInterestCalculator

namespace FinanCalc\Calculators

Setters
Getters

SimpleInterestCalculatorFactory (SimpleInterestCalculator's factory object)

namespace FinanCalc\Calculators\Factories

StockDividendDiscountModelCalculator

namespace FinanCalc\Calculators

Setters

Getters

StockDividendDiscountModelCalculatorFactory (StockDividendDiscountModelCalculator's factory object)

namespace FinanCalc\Calculators\Factories

StockDDMTypes

namespace FinanCalc\Constants

StockInvestmentRatiosCalculator

namespace FinanCalc\Calculators

Setters

Getters

StockInvestmentRatiosCalculatorFactory (StockInvestmentRatiosCalculator's factory object)

namespace FinanCalc\Calculators\Factories

Time values are primarily represented by individual instances of TimeSpan class. Its concept is (roughly) similar to a structure of the same (unqualified) name in .NET Framework.

TimeSpan

namespace FinanCalc\Utils\Time

To initialize the class, you should use one of its three static factory methods (the default constructor is declared as private and it is not advised to circumvent this measure).

Setters

Getters

If you ever need a convenient way to convert time units, feel free to make use of static methods of the TimeUtils class which is built for this purpose (it's also used internally within the library). It also takes into account the day count convention that's currently set in the library's configuration (i.e., the number of days in a year).

TimeUtils

namespace FinanCalc\Utils\Time

Getters

DISCLAIMER

You are free to use/modify/extend the library as you please - for it to serve your purpose. As per the (un)license, the software is provided as is and the original author cannot be held liable for any losses/damages directly or indirectly resulting from using thereof. Attribution is welcome, but certainly not required.

NOTE The library is currently work-in-progress and it is certain that new features will be added in the process. Consider this, therefore, as a preview product prone to abrupt and extensive changes that may affect functionality of an external code adapted to a prior version(s) of the library. Always explore the provisional compatibility of the library with your project in case you upgrade to a new version of the library (by means of extensive testing of the code in which you are exerting the library's features).

Be everything as it may, thank you for checking out FinanCalc :bowtie: