iosloopsswiftamortization

Create Loop for Amortization Schedule in Swift


I'm looking to figure out a simple loop in order to calculate an amortization schedule in Swift.

So far, here is my setup on Playground:

let loanAmount: Double = 250000.00
let intRate: Double = 4.0
let years: Double = 30.0

var r: Double = intRate / 1200
var n: Double = years * 12
var rPower: Double = pow(1 + r, n)

var monthlyPayment: Double = loanAmount * r * rPower / (rPower - 1)
var annualPayment: Double = monthlyPayment * 12

For the actual loop, I'm unsure how to fix the code below.

for i in 0...360 {

  var interestPayment: Double = loanAmount * r
  var principalPayment: Double = monthlyPayment - interestPayment
  var balance: Double; -= principalPayment
}

Looking to generate a monthly schedule. Thanks in advance for any tip.


Solution

  • I'm guessing you mean to declare the balance variable outside the loop, and to decrement it inside the loop:

    // stylistically, in Swift it's usual to leave
    // off the types like Double unless you have a
    // reason to be explicit
    let loanAmount = 250_000.00
    let intRate = 4.0
    let years = 30.0
    
    // since these are one-off calculations, you
    // should use let for them, too.  let doesn't
    // just have to be for constant numbers, it just
    // means the number can't change once calculated.
    let r = intRate / 1200
    let n = years * 12
    let rPower = pow(1 + r, n)
    
    // like above, these aren't changing.  always prefer let
    // over var unless you really need to vary the value
    let monthlyPayment = loanAmount * r * rPower / (rPower - 1)
    let annualPayment = monthlyPayment * 12
    
    // this is the only variable you intend to "vary"
    // so does need to be a var
    var balance = loanAmount
    
    // start counting from 1 not 0 if you want to use an open
    // (i.e. including 360) range, or you'll perform 361 calculations:
    for i in 1...360 {
        // you probably want to calculate interest 
        // from balance rather than initial principal
        let interestPayment = balance * r
        let principalPayment = monthlyPayment - interestPayment
    
        balance -= principalPayment
        println(balance)
    }
    

    This should print out the correct balances going down to zero for the final balance (well actually 9.73727765085641e-09 – but that's a whole other question).

    If you wanted to create a monthly balance, say in an array, you could add an additional array variable to store that in:

    var balance = loanAmount
    //array of monthly balances, with the initial loan amount to start with:
    var monthlyBalances = [balance]
    for i in 1...360 {
        let interestPayment = balance * r
        let principalPayment = monthlyPayment - interestPayment
    
        balance -= principalPayment
        monthlyBalances.append(balance)
    }
    

    Advanced version for anyone who's interested

    You might wonder if there's a way to declare monthlyBalances with let rather than var. And there is! You could use reduce:

    let monthlyBalances = reduce(1...360, [loanAmount]) { 
      payments, _ in
        let balance = payments.last!
        let interestPayment = balance * r
        let principalPayment = monthlyPayment - interestPayment
    
        return payments + [balance - principalPayment]
    }
    

    However this is a bit nasty for a couple of reasons. It would much much nicer if the Swift standard library had a slightly different version of reduce called accumulate that generated an array out of a running total, like this:

    let monthlyBalances = accumulate(1...360, loanAmount) { 
      balance, _ in
        let interestPayment = balance * r
        let principalPayment = monthlyPayment - interestPayment
    
        return balance - principalPayment
    }
    

    And here's a definition of accumulate:

    func accumulate<S: SequenceType, U>
      (source: S, var initial: U, combine: (U, S.Generator.Element) -> U)
       -> [U] {
            var result: [U] = []
            result.append(initial)
            for x in source {
                initial = combine(initial, x)
                result.append(initial)
            }
            return result
    }