2

The problem is to write the python that calculates the interest due on a loan and prints a payment schedule. The interest due on a loan can be calculated according to the simple formula:

I = P × R × T

where I is the interest paid, P is the amount borrowed (principal), R is the interest rate, and T is the length of the loan.

Finally it needs to displayed as follows:

The program will print the amount borrowed, total interest paid, the amount of the monthly payment, and a payment schedule.

Sample session

Loan calculator

Amount borrowed: 100
Interest rate: 6
Term (years): 1

Amount borrowed:    $100.00
Total interest paid:  $6.00

           Amount     Remaining
Pymt#       Paid       Balance
-----      -------    ---------
  0        $ 0.00      $106.00
  1        $ 8.84      $ 97.16
  2        $ 8.84      $ 88.32
  3        $ 8.84      $ 79.48
  4        $ 8.84      $ 70.64
  5        $ 8.84      $ 61.80
  6        $ 8.84      $ 52.96
  7        $ 8.84      $ 44.12
  8        $ 8.84      $ 35.28
  9        $ 8.84      $ 26.44
 10        $ 8.84      $ 17.60
 11        $ 8.84      $  8.76
 12        $ 8.76      $  0.00

The complete problem description is here: http://openbookproject.net/pybiblio/practice/wilson/loan.php To accomplish this I have written the code which is as follows:

import decimal
from decimal import *
class loan_calc:

 def __init__(self):
  decimal.getcontext().prec = 3
  p = Decimal(input('Please enter your loan amount:'))
  r = Decimal(input('Please enter the rate of interest:'))
  t = Decimal(input('Please enter loan period:'))
  r_e = r/100
  i = p*r_e*t 
  term = t*12
  r_a = p+i
  amnt = p/term
  count = 0
  b_r = r_a
  print "Payment\t\tAmount Paid\t\tBal.Rem."
  while count <= term:
   if count == 0:
    print count,"\t\t"'0.00'"\t\t\t",b_r
    count += 1
    b_r -= amnt
    continue

   if term - count == 1:
    amnt = b_r
    print count,"\t\t",amnt,"\t\t\t",b_r
    count += 1
    b_r -= amnt
    continue

   else: 
    print count,"\t\t",amnt,"\t\t\t",b_r
    b_r -= amnt
    count += 1
    continue



loan = loan_calc()
halfer
  • 19,824
  • 17
  • 99
  • 186
hsinxh
  • 1,865
  • 6
  • 21
  • 25
  • 3
    Is there something that is not working? Also, I don't think you need a `class` for this. – user225312 Dec 23 '10 at 16:50
  • @sukhbir is right, the class serves no purpose. You could just get rid of the class and rename `__init__` to main or something. – Rafe Kettler Dec 23 '10 at 16:54
  • Ok I will remove the class. What else is wrong in the code ? Thanks for the answers. – hsinxh Dec 23 '10 at 16:56
  • @Harbhag I ran the program with amount borrowed 100, interest 6, and term 1 and I didn't get the output you posted. – Rafe Kettler Dec 23 '10 at 16:58
  • 2
    @Harbhag: Since there are no errors, does it work as you expect it to? If yes, then it's fine. Just get rid of the `class`, it serves no purpose. Also, `import decimal` is how you should do it, don't do a `from decimal import *`. And change `input` to `raw_input` as pointed out by jleedev in the answer. – user225312 Dec 23 '10 at 16:59
  • @Rafe Yes I am also confused about this, I dont know what is wrong.@sukhbir It actually work but not exactly how I want it to i.e its not giving output exactly same as in the problem. – hsinxh Dec 23 '10 at 17:03
  • As far as the errors in the output are concerned, go through each line and see for yourself where the problem is. That is for you to do it! – user225312 Dec 23 '10 at 17:16
  • I'm voting to close this question as off-topic because Stack Overflow isn't a free code-testing service. Please see: [Why is “Is this correct?” an off topic question, and what should I ask instead?](https://meta.stackoverflow.com/questions/359466/why-is-is-this-correct-an-off-topic-question-and-what-should-i-ask-instead) – EJoshuaS - Stand with Ukraine Aug 27 '18 at 13:26

5 Answers5

2

The use of Decimal(input()) is wrong:

>>> decimal.getcontext().prec=3
>>> decimal.Decimal(input('enter the number: '))
enter the number: 0.1
Decimal('0.1000000000000000055511151231257827021181583404541015625')

Using input causes Python to evaluate the input, which creates a floating-point value. Fix this by using raw_input and passing the string directly to Decimal:

>>> decimal.Decimal(raw_input('enter the number: '))
enter the number: 0.1
Decimal('0.1')

Indent the code by 4 spaces, follow PEP 8, and avoid single-character variable names.

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
  • 1
    I have changed the code and is now using raw_input. I did not know about PEP8, I will read about it now. You guys are really helpful. Thanks – hsinxh Dec 23 '10 at 17:06
  • @katriel: It can't be Python 3. Notice it is the `print` *statement*, not the *function* ;) – user225312 Dec 23 '10 at 17:13
2

To start with, I recommend against doing both import decimal and from decimal import *. Pick one and use what you need from there. Generally, I do import whatever and then use whatever.what_is_needed to keep the namespace cleaner.

As commenters have already noted there's no need to create a class for something this simple (unless this is homework and your instructor requires it). Delete the class declaration, change your def __init__(self) into a def main(), and call main where you currently instantiate loan_class. For more about main functions, see Guido's classic post about them.

The input values should be checked. A simple way to do this would be with a try-except block as they are converted to Decimal. The code could look something like:

prin_str = raw_input('Please enter your loan amount: ')
try:
    principal = decimal.Decimal(prin_str)
except decimal.InvalidOperation:
    print "Encountered error parsing the loan amount you entered."
    sys.exit(42)

To get this to work, you'd have to import sys sometime before the sys.exit() call. I usually put my imports at the start of the file.

Since all your inputs are to be of the same type, you could easily make this a general use function and then call that function for each input.

There does appear to be some sort of bug in the calculations. Resolving that is left as an exercise for the reader. ;-)

GreenMatt
  • 18,244
  • 7
  • 53
  • 79
1

Rewritten (in Python 3, sorry). I'm not sure I understand the algorithm but you can modify it. Maybe this will help?

import decimal

balance = decimal.Decimal(input("Amount borrowed: "))
rate = decimal.Decimal(input("Rate (%): ")) / 100
term = int(input("Term (years): "))

print("\t".join(s.rjust(15) for s in ("Payment", "Amount Paid", "Balance")))
print("-"*54)

balance *= (1 + rate * term)
payment = balance / (12 * term)
total = 0
for month in range(12 * term):
    if balance < payment:
        payment = balance
    print(("{: >15.2f}\t"*3)[:-1].format(payment, total, balance))
    total += payment
    balance -= payment

Notice the following:

  • No silly variable name. Everything is named how it should be.
  • String formatting to make the printing neater.
  • No nasty while loop with multiple cases. You're going to print one line per month in the term, so use a for loop.
Katriel
  • 120,462
  • 19
  • 136
  • 170
  • you code is elegant. I tried to run it with python3 but amount paid keeps increasing. – hsinxh Dec 23 '10 at 17:26
  • Ah, I misunderstood `amount paid` to mean `total amount paid, ever`. You should be able to rewrite the algorithm to print `amount paid this month` -- you'll need to delete a couple of lines and modify the `print` call. – Katriel Dec 23 '10 at 17:28
1

Here's an answer closely patterned to the way yours is written. Using the method I explained and suggested when you asked about how to round off a floating number in python, it uses the decimal module equivalent of the math module's ceil function to get the same answer as shown at the practice link (except for some minor output formatting). I also re-indented the code to the more commonly used 4-spaces, and renamed the variables to be a little more readable. Hope you learn something from it. Notice that I don't set decimal.getcontext().prec to 3 (I don't believe it does what you think).

import decimal

def main():
    principle = decimal.Decimal(raw_input('Please enter your loan amount:'))
    rate = decimal.Decimal(raw_input('Please enter rate of interest (percent):')) / 100
    term = decimal.Decimal(raw_input('Please enter loan period (years):')) * 12

    interest = (principle * rate).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_HALF_EVEN)
    balance = principle + interest
    payment = (balance / term).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_CEILING)
    print "Payment\t\tAmount Paid\t\tRem.Bal."
    for count in range(1+term):
        if count == 0:
            print count, "\t\t0.00\t\t\t", balance
        elif count == term: # last payment?
            payment = balance
            balance -= payment
            print count, "\t\t", payment, "\t\t\t", balance
        else:
            balance -= payment
            print count, "\t\t", payment, "\t\t\t", balance

main()

# > python loan_calc.py
# Please enter your loan amount:100
# Please enter rate of interest (percent):6
# Please enter loan period (years):1
# Payment         Amount Paid             Rem.Bal.
# 0               0.00                    106.00
# 1               8.84                    97.16
# 2               8.84                    88.32
# 3               8.84                    79.48
# 4               8.84                    70.64
# 5               8.84                    61.80
# 6               8.84                    52.96
# 7               8.84                    44.12
# 8               8.84                    35.28
# 9               8.84                    26.44
# 10              8.84                    17.60
# 11              8.84                    8.76
# 12              8.76                    0.00
Community
  • 1
  • 1
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Sure I learned a lot from your code. It worked correctly, thanks for answering my question. Respect – hsinxh Dec 23 '10 at 19:55
1

The logic is much the same as previous answers, but severely reformatted. Enjoy!

# Loan payment calculator
import decimal

def dollarAmt(amt):
    "Accept a decimal value and return it rounded to dollars and cents"
    # Thanks to @Martineau!
    # I found I had to use ROUND_UP to keep the final payment
    # from exceeding the standard monthly payments.
    return amt.quantize(decimal.Decimal('0.01'), rounding=decimal.ROUND_UP)

def getAmt(msg):
    "Get user input and return a decimal value"
    return decimal.Decimal(raw_input(msg))

class MonthlyFixedPaymentLoan(object):
    def __init__(self, principal, yearlyRate, months):
        self.principal  = principal
        self.yearlyRate = yearlyRate
        self.months     = months

        self.interest   = dollarAmt(principal * yearlyRate * (months/12))
        self.balance    = principal + self.interest
        self.payment    = dollarAmt(self.balance / months)

    def __str__(self):
        return ("Amount borrowed:     ${0:>10}\n" +
                "Total interest paid: ${1:>10}").format(dollarAmt(self.principal), dollarAmt(self.interest))

    def payments(self):
        # 'month 0'
        yield 0, decimal.Decimal('0.00'), self.balance

        pmt = self.payment
        bal = self.balance
        for mo in range(1,self.months):
            bal -= pmt
            yield mo, pmt, bal

        # final payment
        yield self.months, bal, decimal.Decimal('0.00')

def main():
    amt  = getAmt('Amount borrowed ($): ')
    rate = getAmt('Interest rate (%/yr): ')
    pd   = getAmt('Loan term (years): ')

    loan = MonthlyFixedPaymentLoan(amt, rate/100, pd*12)

    print('')
    print(loan)
    print('')
    print('Month     Payment       Balance')
    print('-----    --------    ----------')

    for mo,pay,rem in loan.payments():
        print('{0:>4}     ${1:>7}    ${2:>9}'.format(mo, pay, rem))

if __name__=="__main__":
    main()
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99