Vaughan

Present value in code

Finance

To try show some of the financial concepts in practice I\'ve create a library on github to track it - FinanceLibrary. I\'m not going to include the tests below but I added tests to each class as I created it. Have a look at the project for a more complete view. The first calculation I want to show is Present Value. To do this I need some initial setup code. I\'m going to start by declaring that all instruments have cashflows.


public class CashFlow
{
    public string Currency { get; set; }
    public decimal Amount { get; set; }
    public DateTime Date { get; set; }
}
public interface IInstrument
{
    IEnumerable<CashFlow> GetCashflows();
}

I\'m only going to create one example instrument, a bond.

public class Bond : IInstrument
{
    ICouponCalculationService _couponCalculationService;
    DateTime _startDate;
    DateTime _maturityDate;
    decimal _notional;
    CouponType _couponType;
    string _currency;
    decimal _interestRate;

    public Bond(ICouponCalculationService couponCalculationService, DateTime startDate, int termYears, 
        CouponType couponType, decimal notional, string currency, decimal interestRate)
    {
        _couponCalculationService = couponCalculationService;
        _startDate = startDate;
        _notional = notional;
        _maturityDate = _startDate.AddYears(termYears);
        _couponType = couponType;
        _currency = currency;
        _interestRate = interestRate;
    }

    public IEnumerable<CashFlow> GetCashflows()
    {
        TimeSpan couponIncrement = _couponCalculationService.GetCouponIncrement(_startDate, _couponType);

        DateTime cashflowDate = _startDate.Add(couponIncrement);
        decimal couponAmount = _couponCalculationService.GetInterestAmount(_notional, _interestRate, _couponType);

        while (cashflowDate < _maturityDate)
        {
            yield return new CashFlow
            {
                Amount = couponAmount,
                Currency = _currency,
                Date = cashflowDate
            };

            cashflowDate = cashflowDate.Add(couponIncrement);
        }

        yield return new CashFlow
        {
            Amount = _notional + couponAmount,
            Currency = _currency,
            Date = _maturityDate
        };
    }
}

public enum CouponType
{
    SemiAnnual,
    Annual,
}

I seperated the code to calculate the Coupon increment to another class to help with testing.


public class CouponCalculationService : ICouponCalculationService
{
    public TimeSpan GetCouponIncrement(DateTime startDate, CouponType couponType)
    {
        switch (couponType)
        {
            case CouponType.Annual:
                return startDate.AddYears(1) - startDate;

            case CouponType.SemiAnnual:
                return startDate.AddMonths(6) - startDate;

            default:
                throw new UnsupportedCouponTypeException(couponType);
        }
    }

    public decimal GetInterestAmount(decimal notional, decimal interestRate, CouponType couponType)
    {
        decimal couponFactor;
        switch (couponType)
        {
            case CouponType.Annual:
                couponFactor = 1m;
                break;

            case CouponType.SemiAnnual:
                couponFactor = 0.5m;
                break;

            default:
                throw new UnsupportedCouponTypeException(couponType);
        }

        return notional * interestRate * couponFactor;
    }
}

Once I have an example instrument I can instantiate I created a service that handle discounting the cashflows. You will see that it has a curvesource dependency. Forward curves can be quite complicated to calculate based off various indices and even this has changed quite substantially since 2008 with banks moving to OIS. Generally they are stored as a set of dates and discount factors. I\'m going to just use that and hard-code the values for all currencies.


public class DiscountingService : IDiscountingService
{
    ICurveSource _curveSource;

    public DiscountingService(ICurveSource curveSource)
    {
        _curveSource = curveSource;
    }

    public decimal Discount(DateTime today, CashFlow cashflow)
    {
        var discounts = _curveSource.Get(today, cashflow.Currency);

        //No interpolation intially. Assume the date is exactly there
        var discount = discounts.Single(d=>d.Date == cashflow.Date);

        return discount.Discount * cashflow.Amount;
    }
}

public class CurveSource : ICurveSource
{
    public DiscountFactor[] Get(DateTime today, string currency)
    {
        //Harcode intially
        return GetSampleDiscountFactors(today);
    }

    private DiscountFactor[] GetSampleDiscountFactors(DateTime today)
    {
        return new DiscountFactor[]
        {
            new DiscountFactor {Date = today, Discount = 1 },
            new DiscountFactor {Date = today.AddMonths(1), Discount = 0.99m },
            new DiscountFactor {Date = today.AddMonths(2), Discount = 0.98m },
            new DiscountFactor {Date = today.AddMonths(3), Discount = 0.95m },
            new DiscountFactor {Date = today.AddMonths(6), Discount = 0.90m },
        };
    }
}

With these classes my PresentValue calculation class is actually quite simple.


public class PresentValue
{
    IDiscountingService _discountingService;

    public PresentValue(IDiscountingService discountingService)
    {
        _discountingService = discountingService;
    }

    public ScalarValue RunCalculation(DateTime today, IEnumerable<IInstrument> instruments)
    {
        decimal total = 0;
        //Assuming all instruments are the same currency
        string firstCurrency = ;

        foreach(var instrument in instruments)
        {
            foreach(var cf in instrument.GetCashflows())
            {
                if (string.IsNullOrWhiteSpace(firstCurrency))
                {
                    firstCurrency = cf.Currency;
                }
                total += _discountingService.Discount(today, cf);
            }
        }
        return new ScalarValue
        {
            Value = total,
            Currency = firstCurrency,
        };
    }
}

So there are a ton of simplifications that I made but this is the gist of the calculation.

Related Posts

BMC logoBuy me a coffee