Расчет амортизированной стоимости с использованием VBA (Bond) - PullRequest
1 голос
/ 01 апреля 2019

Я пытаюсь сделать «учетную» модель для амортизированной стоимости.Я сделаю один массив с фактическими датами оплаты, один массив с «амортизированной стоимостью» и другой массив, показывающий значения в отчетный день (например, 31.12).Я уже сделал это вручную, но хочу, чтобы эти действия выполнялись «одним щелчком», просто меняя входные данные.Я довольно новичок в VBA (всего пару дней) и до сих пор борюсь с массивом "дата платежа", показывающим денежный поток по облигации.

Пока у меня есть следующий код

Sub LoanAmortization()

'----------------------------------------------------------------------------------------------------------------------------------------------
'1)Define the arrays and variables that will be used along the process
'----------------------------------------------------------------------------------------------------------------------------------------------

'Dim Trends As Workbook                         'Variable to refer to the workbook

    Dim initLoanBal As Double         'Initial bond amount
    Dim DayCountBasis As Double       'Day count convention
    Dim BegDate As Date               'Date of bond repayment
    Dim MaturityDate As Date          'Date of bond repayment
    Dim TransCost As Double           'Transactioncosts on bonds
    Dim PayFreq As Double             'Frequency of coupon payments on bond (e.g. quarterly)
    Dim initRate As Double            'Interest rate on bond
    Dim CashFlowArray() As Integer    'Array of Cash flows on bond
    Dim CouponFreqString As String
    Dim NomRate As Double             'Rate used for cash flow calculation

    Dim i As Long
''----------------------------------------------------------------------------------------------------------------------------------------------
''2)Set variables for the calculation
''----------------------------------------------------------------------------------------------------------------------------------------------

    initLoanBal = ThisWorkbook.Worksheets("Amortisering").Range("D3").Value
    TransCost = Worksheets("Amortisering").Range("D4").Value
    initRate = Worksheets("Amortisering").Range("D5").Value
    Spread = Worksheets("Amortisering").Range("D6").Value
    DayCountBasis = Worksheets("Amortisering").Range("D7").Value
    CouponFreq = Worksheets("Amortisering").Range("E8").Value
    CouponFreqString = Worksheets("Amortisering").Range("D8").Value
    BegDate = Worksheets("Amortisering").Range("D9").Value
    MaturityDate = Worksheets("Amortisering").Range("D10").Value
    NomRate = initRate + Spread   

    '----------------------------------
    'Format variables for the calculation
    '----------------------------------
    Cells(5, 4).Select
    Selection.Value = initRate
    Selection.NumberFormat = "0.00%"


    Cells(6, 4).Select
    Selection.NumberFormat = "0.00%"


'-----------------------------------------------------------
'Set cash flows dates
'-----------------------------------------------------------
NoPeriods = DateDiff(CouponFreqString, BegDate, MaturityDate, vbMonday) 
' Number of periods ("payments") on the bond
    Range("G29") = BegDate
    Range("F31") = BegDate
    Range("G31").NumberFormat = "_(* #,##0_);_(* (#,##0);_(* ""-""??_);_(@_)"

                For i = 1 To NoPeriods
                    Cells(29, 7 + i) = DateAdd(CouponFreqString, i, BegDate)
                    Cells(31 + i, 6) = DateAdd(CouponFreqString, i, BegDate)
                Next i
'----------------------------------------------
'Set number of days dager
'----------------------------------------------

    For i = 1 To NoPeriods  ' No. days between payments (daycount convention)
           Cells(30, 7 + i) = WorksheetFunction.YearFrac(Cells(29, 6 + i), Cells(29, 7 + i), DayCountBasis)
    Next i
'----------------------------------------------
'Cash flow array
'----------------------------------------------
    For c = 1 To NoPeriods
        For i = 1 To NoPeriods
                Cells(30 + i, 7 + c) = initLoanBal * NomRate * Cells(30, 7 + c)
               Next i
    Next c


Range("G31") = -initLoanBal + TransCost

End Sub

ЦЕЛЬ

Таким образом, проблема появляется в части «массив денежных потоков».1. Конечной целью является использование XIRR для расчета эффективной процентной ставки для каждого периода на основе NomRate.

Я хочу, чтобы NomRate менялся для каждого периода, поскольку происходит изменение плавающей ставки.

Я хочу, чтобы окончательный платеж в каждой строке был равенВыплата процентов и погашение кредита (т.е. initLoanBal).

Я хочу, чтобы первый денежный поток был равен расчетному амортизированному расходу за предыдущий период.

Я хочу, чтобы массив уменьшался на 1 за каждую итерацию.

Пожалуйста, смотрите изображение для иллюстрации (зеленые значения - это "значения амортизированной стоимости" из следующего массива, который является амортизированной стоимостьюзначения) как я хочу, чтобы это выглядело

enter image description here

Ответы [ 2 ]

0 голосов
/ 17 апреля 2019

В мои первые дни в VBA я разработал калькулятор / планировщик повторных платежей по кредитам в качестве учебного проекта. Программа берет входные параметры из Userform и рассчитывает график погашения кредита. Я приложу файл ниже, чтобы вы могли на него взглянуть. Основным алгоритмом расчета графика погашения кредита является алгоритм Bisection. Это тот же самый файл, который используется в Excel Goal Seek.

Примечание: Код немного элементарен, потому что, как я упоминал ранее, я только начинал, поэтому я не знал, что могу передавать параметры в Subs вместо того, чтобы делать переменные общедоступными, моя переменная Называние было ужасным, и так далее. При этом у меня нет времени, чтобы вернуться и сделать его надежным, однако процедуры тщательно прокомментированы, поэтому я все еще думаю, что вы могли бы поучиться на этом.

Файл: Калькулятор погашения кредита

Редактировать: Финансовое моделирование с использованием Excel и VBA от Chandan Sengupta также является отличным ресурсом. Я использовал несколько идей для создания своего калькулятора погашения кредита.

Ниже приведен основной код для расчетов. Обратите внимание, что каждая из следующих переменных задается в пользовательской форме, но вместо этого они могут быть установлены в ячейки: IntsRate, loanLife, PrcplBal, ymtFrqy, CompFrqy, IntvlLng, VariDateIntvl, UserDate

Public IntsRate As Double, loanLife As String, PrcplBal As Double, PymtFrqy As String, CompFrqy As String, _
IntvlLng As Integer, VariDateIntvl As Integer
Public UserDate As Date
Option Explicit
Option Private Module
Public Sub LoanTableCalculations()

 Dim LR As Long, numOfIterations As Long, iCol As Long, pCol As Long, rNum As Long, outrow As Long
 Dim balTolerance As Double
 Dim yrBegBal() As Double, yrEndBal() As Double, ipPay() As Double, finalBal As Double 
 Dim annualPmnt As Double, aPmtOld As Double

    Application.ScreenUpdating = False

    '************************************************************
    ' User inputs
    '************************************************************
     'Read the date entered by user on the userform
    UserDate = LoanUserform.txtPymtBegn.Value 'start of payments

    'Conditionally set date interval and row headers _
    based on user input
    If PymtFrqy = "Annually" Then
       VariDateIntvl = 12
            Cells(8, 4).Value2 = "Year"
            Cells(8, 5).Value2 = "Year Beg-Balance"
            Cells(8, 6).Value2 = "Annual Payment"
            Cells(8, 9).Value2 = "Year End-Balance"

      ElseIf PymtFrqy = "Semi-Annually" Then
       VariDateIntvl = 6
            Cells(8, 4).Value2 = "Semi-Annual Periods"
            Cells(8, 5).Value2 = "Semi-Annual Beg-Balance"
            Cells(8, 6).Value2 = "Semi-Annual Payment"
            Cells(8, 9).Value2 = "Semi-Annual End-Balance"

      ElseIf PymtFrqy = "Quarterly" Then
       VariDateIntvl = 4
            Cells(8, 4).Value2 = "Quarters"
            Cells(8, 5).Value2 = "Quarter Beg-Balance"
            Cells(8, 6).Value2 = "Quarterly Payment"
            Cells(8, 9).Value2 = "Quarter End-Balance"

      ElseIf PymtFrqy = "Monthly" Then
       VariDateIntvl = 1
            Cells(8, 4).Value2 = "Month"
            Cells(8, 5).Value2 = "Month Beg-Balance"
            Cells(8, 6).Value2 = "Monthly Payment"
            Cells(8, 9).Value2 = "Month End-Balance"

    End If

    '************************************************************
    'My inputs
    '************************************************************
     balTolerance = 0.5 'Specifies desired accuracy
     iCol = 1
     pCol = 2
     outrow = 8 'sets row where data will be output to

      'finds last row of data in column 3
      LR = Worksheets("Loan Amortization").Cells(Rows.Count, 3).End(xlUp).Row

     'Clear previous data and format
     '*****************************
      'Data
      Rows(outrow + 1 & ":" & (outrow + LR + 6)).ClearContents
      'Table Borders
      Rows(outrow + 1 & ":" & (outrow + LR + 6)). _
      Borders.LineStyle = xlNone

          'Redimension the arrays
          ReDim yrBegBal(1 To IntvlLng + 1)
          ReDim ipPay(1 To IntvlLng + 1, 1 To 2)
          ReDim yrEndBal(1 To IntvlLng)

        '************************************************************
        ' Computations and output; bisection algorithm
        '************************************************************
         annualPmnt = PrcplBal * IntsRate

             'This Do loop controls the iteration
             Do While finalBal > balTolerance Or finalBal = 0

                 'Initialize balance at the beginning of year 1
                 yrBegBal(1) = PrcplBal

                'Loop to calculate and store year-by-year data
                For rNum = 1 To IntvlLng
                 ipPay(rNum, iCol) = yrBegBal(rNum) * IntsRate
                 ipPay(rNum, pCol) = annualPmnt - ipPay(rNum, iCol)
                 yrEndBal(rNum) = yrBegBal(rNum) - ipPay(rNum, pCol)

                 yrBegBal(rNum + 1) = yrEndBal(rNum)

                Next rNum

                    finalBal = yrEndBal(IntvlLng)
                    aPmtOld = annualPmnt

                    'Calculate the next annual payment to try
                    annualPmnt = annualPmnt + (finalBal * (1 + IntsRate) ^ _
                    (-IntvlLng)) / IntvlLng

                    'Count # of iterations
                    numOfIterations = numOfIterations + 1

             Loop

        'Note these calculations could be placed in an array and then _ 
        be sent to a worksheet in all at once 
        '************************************************************
        ' Output data to worksheet
        '************************************************************
        Cells(outrow + 1, 3).Value = UserDate

         For rNum = 1 To IntvlLng
            Cells(outrow + rNum + 1, 3).Value = WorksheetFunction.EDate(Cells(outrow + rNum, 3).Value, VariDateIntvl)
            Cells(outrow + rNum, 4).Value = rNum 'Year number
            Cells(outrow + rNum, 5).Value = yrBegBal(rNum)
            Cells(outrow + rNum, 6).Value = annualPmnt
            Cells(outrow + rNum, 7).Value = ipPay(rNum, iCol)
            Cells(outrow + rNum, 8).Value = ipPay(rNum, pCol)
            Cells(outrow + rNum, 9).Value = yrEndBal(rNum)
         Next rNum


        '************************************************************
        ' Format data in table
        '************************************************************
         'format as dollars
         Range(Cells(outrow + 1, 5), Cells(outrow + IntvlLng, 9)). _
         NumberFormat = "$#,##0"


         'format as dates
         Range("C9" & ":" & "C" & (IntvlLng + 8)).NumberFormat = "m/d/yy"
         Cells(outrow + IntvlLng + 1, 3).ClearContents


         'Add Borders
          Range(Cells(outrow, 3), Cells(outrow + IntvlLng, 9)).Borders.LineStyle = xlContinuous

    'Clear Variables
    IntsRate = Empty
    loanLife = Empty
    PrcplBal = Empty
    PymtFrqy = Empty
    CompFrqy = Empty
    IntvlLng = Empty
    VariDateIntvl = Empty
    UserDate = Empty

    Application.ScreenUpdating = True

End Sub
0 голосов
/ 02 апреля 2019

Я предлагаю вам использовать функцию вместо макроса для этого.

Функция будет действовать как функция Excel. Например, если я выполняю функцию с именем TRIPLE, которая вычисляет 3 * x, где x - номер ячейки, я могу использовать TRIPLE (A1) в Excel для вычисления тройки в ячейке A1.

В вашем примере, с вашим пояснением, я попытался понять каждый шаг, чтобы сделать этот код для вас, однако сложность не помогла мне сделать это.

Но я кое-что начал. Идея этой функции заключается в том, чтобы вы указали все, что вам нужно (ячейки, начинающиеся с Hovedstol), даты, налоги и индекс результата. Если вам это нужно, вы можете добавить что угодно, используя мои инструкции. Результатом для функции является вычисление Формеля.

Пример: для вашего первого результата вы должны написать в Excel:

=LoanAmortization(B2,B3,B4,B5,B6,F2:F20,G2:G20,1)

Для вашего второго результата:

=LoanAmortization(B2,B3,B4,B5,B6,F2:F20,G2:G20,1)

Налоги организованы в G2: G20.

Итак, код должен иметь эти параметры в начале в vba, чтобы действовать как функция:

Function LoanAmortization(A As Double, B As Double, C As Double, D As Double, E As Double, ByRef DatesRange As Excel.Range, ByRef TaxesRange As Excel.Range, MIndex As Integer) As Double
End Function

Теперь вам нужно будет работать с массивами, чтобы делать все, что вы хотите, вам не нужно форматировать ячейки, вы сможете создавать свой лист сколько угодно много раз, и код все равно будет работать. Чтобы создать массив, вам нужно сначала указать количество элементов, в этом примере вы можете создать матрицу из 3 столбцов от 1 до 3 (начиная с номера 0, если не указано) и 2 строк, начиная с номера 1 (то же самое, если вы не укажете, номер 0 будет первым):

Dim ArrayExample(1 to 2, 1 to 3) As Double

Вы также можете Redim вашего массива, но вы потеряете ваши данные, даже если вы используете preserve, вы не можете изменить тип переменной. Если размер имеет значение из переменной, вам необходимо использовать ReDim:

ReDim ArrayExample(1 to 4, 0 to 3)

Чтобы преобразовать Excel.range в массив, просто используйте это после вашего объявления:

ArrayExample = ArrayRange.Value

Чтобы использовать матрицу, просто найдите нужную строку и столбец, примеры:

ArrayExample(3, 2) = 1
i = ArrayExample(1) 'Just one column (have to be specified in declaration)
ArrayExample(0, 0) = "test"

Чтобы использовать любую функцию Excel, например функцию CountA, просто используйте это:

Application.WorksheetFunction.CountA

Вот что я сделал:

Function LoanAmortization(A As Double, B As Double, C As Double, D As Double, E As Double, ByRef DatesRange As Excel.Range, ByRef TaxesRange As Excel.Range, MIndex As Integer) As Double

    Dim qtd As Integer
    Dim Dates(), Taxes(), DatesDifference() As Double 'If bug, use Variant variable type
    qtd = Application.WorksheetFunction.CountA(DatesRange)
    ReDim DatesRange(1 to qtd), Taxes(1 to qtd), DatesDifference(1 to qtd - 1)
    For 1 to qtd - 1
        DatesDifference(i) = DatesRange(i + 1) - DatesRange(i)
    Next

End Function

С этим вы сможете продолжить код, извините, чтобы больше не помогать. Если у вас есть какие-либо сомнения относительно того, как сделать что-то более конкретное, я постараюсь вам помочь.

...