Exemple #1
0
        public static InstallmentPlanRow FromInstallmentPlanCS(InstallmentPlanRowCS installmentPlanRowCS)
        {
            if (!Enum.TryParse(installmentPlanRowCS.ActivityKind, true, out ActivityKind activityKind))
            {
                activityKind = ActivityKind.Repayment;
            }
            var planRow = new InstallmentPlanRow
            {
                ActivityKind                 = activityKind,
                Annuity                      = (decimal)(installmentPlanRowCS.Annuity ?? 0),
                CashCollateral               = (decimal)(installmentPlanRowCS.CashCollateral ?? 0),
                Date                         = installmentPlanRowCS.Date ?? DateTime.Today,
                DeferredPayment              = (decimal)(installmentPlanRowCS.DeferredPayment ?? 0),
                DeferredPaymentInstallment   = (decimal)(installmentPlanRowCS.DeferredPaymentInstallment ?? 0),
                Description                  = installmentPlanRowCS.Description,
                Disbursement                 = (decimal)(installmentPlanRowCS.Disbursement ?? 0),
                DiscountedCashCollateralFlow = (decimal)(installmentPlanRowCS.DiscountedCashCollateralFlow ?? 0),
                DiscountedDisbursement       = (decimal)(installmentPlanRowCS.DiscountedDisbursement ?? 0),
                DiscountedNetCashFlow        = (decimal)(installmentPlanRowCS.DiscountedNetCashFlow ?? 0),
                Fee = (decimal)(installmentPlanRowCS.Fee ?? 0),
                InterestRepayment  = (decimal)(installmentPlanRowCS.InterestRepayment ?? 0),
                NetCashFlow        = (decimal)(installmentPlanRowCS.NetCashFlow ?? 0),
                Ordinal            = installmentPlanRowCS.Ordinal ?? 0,
                OtherExpenses      = (decimal)(installmentPlanRowCS.OtherExpenses ?? 0),
                OutstandingBalance = (decimal)(installmentPlanRowCS.OutstandingBalance ?? 0),
                PrincipalRepayment = (decimal)(installmentPlanRowCS.PrincipalRepayment ?? 0),
                StartingBalance    = (decimal)(installmentPlanRowCS.StartingBalance ?? 0),
                YearFrac           = 0
            };

            return(planRow);
        }
Exemple #2
0
        public override void CalculateOffer(PriceCalculationParameters calculationParameters, OfferPriceCalculation priceCalculator, string feeCurrencyConversionMethod)
        {
            var ignore = priceCalculator.CalculatePrice(this, calculationParameters).Result;

            // isCompound i calendarBasis izvuci tokom CalculatePrice da dodatno opisu Napr
            bool isCompound = false;
            CalendarBasisKind calendarBasis = CalendarBasisKind.CalendarActActISDA;
            double            yearlyRate    = Convert.ToDouble(Napr) / 100;

            var ir = Conditions.InterestRates.FirstOrDefault(it => it.Kind == InterestRateKinds.RegularInterest);

            if (ir != null)
            {
                isCompound    = ir.IsCompound;
                calendarBasis = ir.CalendarBasis;
                yearlyRate    = Convert.ToDouble(ir.CalculatedRate);
                Napr          = Convert.ToDecimal(yearlyRate);
            }


            var startDate = calculationParameters.RequestDate ?? DateTime.Today;

            List <InstallmentPlanRow> rows       = new List <InstallmentPlanRow>();
            List <FeeCondition>       rowFeeList = new List <FeeCondition>();
            List <FeeCondition>       fees       = Conditions.Fees;

            fees = FeeCalculation.PrepareFees(fees, Currency, feeCurrencyConversionMethod);

            decimal  runningAmount = Math.Round(Amount, 2);
            DateTime runningDate   = startDate;

            if (CalculationDate > DateTime.Today)
            {
                CalculationDate = runningDate;
            }
            else
            {
                runningDate = CalculationDate ?? DateTime.Today;
            }

            // SLOBA: Fee scheduling, and scheduling in general has to be improved in advanced calculator.
            decimal totalFeeAmount = 0;

            if (fees != null)
            {
                List <FeeCondition> feeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.EventTriggered && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                totalFeeAmount = Math.Round(FeeCalculation.CalculateFee(Amount, feeList), 4);
            }
            NumberOfInstallments = 1;

            /* Disbursment */
            InstallmentPlanRow rowDisbursement = new InstallmentPlanRow();

            rowDisbursement.Date               = runningDate;
            rowDisbursement.Ordinal            = 0;
            rowDisbursement.ActivityKind       = ActivityKind.Disbursement;
            rowDisbursement.Description        = "Loan disbursement";
            rowDisbursement.Disbursement       = Amount;
            rowDisbursement.StartingBalance    = 0;
            rowDisbursement.OutstandingBalance = Amount;
            rowDisbursement.PrincipalRepayment = 0;
            rowDisbursement.InterestRepayment  = 0;
            rowDisbursement.NetCashFlow        = Amount - totalFeeAmount;
            rowDisbursement.Fee      = totalFeeAmount;
            rowDisbursement.YearFrac = 0;
            rows.Add(rowDisbursement);
            int i = -1;

            InstallmentPlanRow rowInterest;
            var intrStartDate = startDate;

            runningDate = runningDate.AddMonths(1);
            while (runningDate <= MaturityDate.Value)
            {
                i++;


                var interest = Math.Round(InterestCalculation.CalculateInterest(intrStartDate, runningDate, runningDate, Convert.ToDouble(yearlyRate), 'Y', isCompound, calendarBasis, Convert.ToDouble(runningAmount))
                                          .Sum(it => it.Interest), 2);

                rowInterest = new InstallmentPlanRow();

                rowInterest.Ordinal            = i + 1;
                rowInterest.Date               = runningDate;
                rowInterest.ActivityKind       = ActivityKind.Repayment;
                rowInterest.Description        = "Interest and fee repayment";
                rowInterest.InterestRepayment  = Convert.ToDecimal(interest);
                rowInterest.PrincipalRepayment = 0;
                rowInterest.Annuity            = rowInterest.InterestRepayment;
                rowInterest.StartingBalance    = Math.Round(runningAmount, 2);
                rowInterest.OutstandingBalance = Math.Round(runningAmount, 2);

                #region Fee calculation
                if (fees != null)
                {
                    decimal totalRowFee = 0;
                    rowFeeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.Monthly && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                    if (rowFeeList.Count > 0)
                    {
                        totalRowFee += FeeCalculation.CalculateFee(runningAmount, rowFeeList);
                    }
                    if ((i + 1) % 4 == 0)
                    {
                        rowFeeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.Quarterly && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                        if (rowFeeList.Count > 0)
                        {
                            totalRowFee += FeeCalculation.CalculateFee(runningAmount, rowFeeList);
                        }
                    }
                    if ((i + 1) % 6 == 0)
                    {
                        rowFeeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.Semiyearly && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                        if (rowFeeList.Count > 0)
                        {
                            totalRowFee += FeeCalculation.CalculateFee(runningAmount, rowFeeList);
                        }
                    }
                    if ((i + 1) % 12 == 0)
                    {
                        rowFeeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.Yearly && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                        if (rowFeeList.Count > 0)
                        {
                            totalRowFee += FeeCalculation.CalculateFee(runningAmount, rowFeeList);
                        }
                    }
                    rowInterest.Fee = Math.Round(totalRowFee, 4);
                }
                #endregion
                rowInterest.NetCashFlow = rowInterest.Disbursement - rowInterest.PrincipalRepayment - rowInterest.InterestRepayment - rowInterest.Fee - rowInterest.OtherExpenses;
                rowInterest.YearFrac    = Convert.ToDecimal(InterestCalculation.YearFrac(startDate, runningDate)); //, calendarBasis);
                rows.Add(rowInterest);
                intrStartDate = runningDate;
                runningDate   = runningDate.AddMonths(1);
            }

            i++;

            rowInterest = new InstallmentPlanRow();

            rowInterest.Ordinal            = i + 1;
            rowInterest.Date               = runningDate;
            rowInterest.ActivityKind       = ActivityKind.Repayment;
            rowInterest.Description        = "Principal repayment";
            rowInterest.InterestRepayment  = 0;
            rowInterest.PrincipalRepayment = Amount;
            rowInterest.Annuity            = Amount;
            rowInterest.StartingBalance    = Amount;
            rowInterest.OutstandingBalance = 0;

            #region Fee calculation
            rowInterest.Fee = 0;
            #endregion
            rowInterest.NetCashFlow = rowInterest.Disbursement - rowInterest.PrincipalRepayment - rowInterest.InterestRepayment - rowInterest.Fee - rowInterest.OtherExpenses;
            rowInterest.YearFrac    = Convert.ToDecimal(InterestCalculation.YearFrac(startDate, runningDate)); //, calendarBasis);
            rows.Add(rowInterest);
            NumberOfInstallments = 1;

            // arrangementRequest.InstallmentPlan = rows;
            CalculateAPR(rows);
        }
        // SLOBA: term and fees parameters are inconsistently extracted from arrangementRequest
        public void CalculateInstallmentPlan(string feeCurrencyConversionMethod)
        {
            int term = Utility.GetMonthsFromPeriod(Term);
            List <InstallmentPlanRow> rows       = new List <InstallmentPlanRow>();
            List <FeeCondition>       rowFeeList = new List <FeeCondition>();
            List <FeeCondition>       fees       = Conditions.Fees;

            fees = FeeCalculation.PrepareFees(fees, Currency, feeCurrencyConversionMethod);

            decimal  runningAmount = Math.Round(Amount, 2);
            DateTime runningDate   = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 0, 0, 0, 0);

            if (CalculationDate > DateTime.Today)
            {
                CalculationDate = runningDate;
            }
            else
            {
                runningDate = CalculationDate ?? DateTime.Today;
            }
            DateTime startDate = runningDate;

            // SLOBA: Fee scheduling, and scheduling in general has to be improved in advanced calculator.
            decimal totalFeeAmount = 0;

            if (fees != null)
            {
                List <FeeCondition> feeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.EventTriggered && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                totalFeeAmount = Math.Round(FeeCalculation.CalculateFee(Amount, feeList), 4);
            }
            NumberOfInstallments = 1;

            bool isCompound = false;
            CalendarBasisKind calendarBasis = CalendarBasisKind.CalendarActActISDA;
            double            yearlyRate    = 0;
            var ir = Conditions.InterestRates.FirstOrDefault(it => it.Kind == InterestRateKinds.RegularInterest);

            if (ir != null)
            {
                isCompound    = ir.IsCompound;
                calendarBasis = ir.CalendarBasis;
                yearlyRate    = Convert.ToDouble(ir.CalculatedRate / 100);
            }

            // this is huge simplification. not good
            double monthlyRate = yearlyRate / 12;

            if (isCompound)
            {
                monthlyRate = Math.Pow(1 + yearlyRate, 1.0000000 / 12.0000000) - 1;
            }

            // SLOBA: Logic for calculating cash flow is done in manner that is later hard to extend. Also columns are hardcoded.
            // SLOBA: This is totally ok for basic calculator though

            /* Disbursment */
            InstallmentPlanRow rowDisbursement = new InstallmentPlanRow();

            rowDisbursement.Date               = runningDate;
            rowDisbursement.Ordinal            = 0;
            rowDisbursement.ActivityKind       = ActivityKind.Disbursement;
            rowDisbursement.Description        = "Loan disbursement";
            rowDisbursement.Disbursement       = Amount;
            rowDisbursement.StartingBalance    = 0;
            rowDisbursement.OutstandingBalance = Amount;
            rowDisbursement.PrincipalRepayment = 0;
            rowDisbursement.InterestRepayment  = 0;
            rowDisbursement.NetCashFlow        = Amount - totalFeeAmount;
            rowDisbursement.Fee      = totalFeeAmount;
            rowDisbursement.YearFrac = 0;
            rows.Add(rowDisbursement);

            for (var i = 0; i < term; i++)
            {
                decimal interest  = Math.Round(runningAmount * Convert.ToDecimal(monthlyRate), 2);
                decimal principal = Math.Round(Annuity - interest, 2);

                if (i == term - 1)
                { // last ... corection for decimal places
                    principal = Math.Round(runningAmount, 2);
                    interest  = Math.Round(Annuity - principal, 2);
                }

                InstallmentPlanRow rowInterest = new InstallmentPlanRow();

                rowInterest.Ordinal            = i + 1;
                runningDate                    = runningDate.AddMonths(1);
                rowInterest.Date               = runningDate;
                rowInterest.ActivityKind       = ActivityKind.Repayment;
                rowInterest.Description        = "Anuity repayment";
                rowInterest.InterestRepayment  = interest;
                rowInterest.PrincipalRepayment = principal;
                rowInterest.Annuity            = Annuity;
                rowInterest.StartingBalance    = Math.Round(runningAmount, 2);
                runningAmount                  = (runningAmount - principal);
                rowInterest.OutstandingBalance = Math.Round(runningAmount, 2);

                #region Fee calculation
                if (fees != null)
                {
                    decimal totalRowFee = 0;
                    rowFeeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.Monthly && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                    if (rowFeeList.Count > 0)
                    {
                        totalRowFee += FeeCalculation.CalculateFee(runningAmount, rowFeeList);
                    }
                    if ((i + 1) % 4 == 0)
                    {
                        rowFeeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.Quarterly && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                        if (rowFeeList.Count > 0)
                        {
                            totalRowFee += FeeCalculation.CalculateFee(runningAmount, rowFeeList);
                        }
                    }
                    if ((i + 1) % 6 == 0)
                    {
                        rowFeeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.Semiyearly && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                        if (rowFeeList.Count > 0)
                        {
                            totalRowFee += FeeCalculation.CalculateFee(runningAmount, rowFeeList);
                        }
                    }
                    if ((i + 1) % 12 == 0)
                    {
                        rowFeeList = fees.FindAll(f => f.Frequency == FeeConditionFrequency.Yearly && f.EffectiveDate <= DateTime.Today && f.Currencies.Contains(Currency));
                        if (rowFeeList.Count > 0)
                        {
                            totalRowFee += FeeCalculation.CalculateFee(runningAmount, rowFeeList);
                        }
                    }
                    rowInterest.Fee = Math.Round(totalRowFee, 4);
                }
                #endregion
                rowInterest.NetCashFlow = rowInterest.Disbursement - rowInterest.PrincipalRepayment - rowInterest.InterestRepayment - rowInterest.Fee - rowInterest.OtherExpenses;
                rowInterest.YearFrac    = Yearfrac(startDate, runningDate, calendarBasis);
                rows.Add(rowInterest);
                NumberOfInstallments += 1;
            }
            // arrangementRequest.InstallmentPlan = rows;
            CalculateAPR(rows);
        }