Initial commit. Things dont work yet.

--
user: JohnKent
branch 'default'
added greenfield_mortgage.txt
added mortgage.py
added mortgage_template.py
added statement.pdf.jinja
added statement.txt.jinja
This commit is contained in:
JohnKent
2018-12-19 23:09:04 -05:00
commit 53327a9e09
5 changed files with 576 additions and 0 deletions

25
greenfield_mortgage.txt Normal file
View File

@@ -0,0 +1,25 @@
{
"principal": 97750.00,
"interest rate": 5.5,
"periods per year": 12,
"periods": 182,
"start date": "2017-11-07",
"start_interest_date": "2017-11-11",
"first payment month": "2017-12-15",
"monthly_payment": 803.00,
"payment day of month": "15",
"payments": [
["2017-12-11", "803.00"],
["2018-01-23", "803.00"],
["2018-03-23", "803.00"],
["2018-04-18", "803.00"],
["2018-04-26", "803.00"],
["2018-05-15", "0.00"],
["2018-06-15", "0.00"],
["2018-07-12", "803.00"],
["2018-08-07", "803.00"],
["2018-09-06", "803.00"],
["2018-10-11", "803.00"],
["2018-11-13", "803.00"]
]
}

130
mortgage.py Normal file
View File

@@ -0,0 +1,130 @@
import json
from decimal import *
from datetime import *
# this program reads the json file describing a mortgage and calculate the actual and projected amortization
# the first payment in the file should be the interest paid at closing
# the interest at closing covers interest that would be incurred from the date of closing until the first day
# that will be covered by the first loan payment
# i.e., the first loan payment is expected a month and a half after closing, the first two weeks interest is due
# at closing. The first payment will incur interest from one month before the bill is due.
#read in the file
#filename = "./10Kloan.txt"
filename = "./dadmortgage.txt"
#filename = "./brendamortgage.txt"
#filename = "./greenfield_mortgage.txt"
if filename:
with open(filename, 'r') as f:
datastore = json.load(f)
#read in the loan profile information
annual_rate = Decimal(datastore['interest rate'])/100
daily_interest_rate = annual_rate/360
principal = Decimal(datastore["principal"]).quantize(Decimal("1.00"))
periods_per_year = Decimal(datastore["periods per year"])
total_periods = Decimal(datastore["periods"])
payment_day_of_month = int(datastore['payment day of month'])
if "monthly_payment" in datastore:
monthly_payment = Decimal(datastore["monthly_payment"]).quantize(Decimal("1.00"))
else:
#calculate expected monthly payment
periodic_rate = annual_rate/periods_per_year
discount_factor = (((1+periodic_rate)**total_periods)-1) / (periodic_rate * ((1+periodic_rate)**total_periods))
monthly_payment = (principal / discount_factor).quantize(Decimal("1.00"))
print "Principal: ", principal
print "Interest Rate: ", annual_rate
print "Payments Per Year: ", periods_per_year
print "Loan Term: ", total_periods
print "Monthly Payment", monthly_payment
print "Payments due day: ", payment_day_of_month
print
#loop over the payments and calculate the actual amortization
actual_payments = datastore["payments"]
remaining_principal = principal
annual_interest = 0
total_interest = 0
interest_paid_through_date = datetime.strptime(datastore["start_interest_date"], "%Y-%m-%d").date()
next_payment_date = datetime.strptime(datastore["first payment month"], '%Y-%m-%d').date()
next_bill_date = date(year=next_payment_date.year, month=next_payment_date.month, day=payment_day_of_month)
old_bill_date = next_bill_date
payment_number = 1
current_year = next_bill_date.year
print "Payment History:"
print "\tBill\t\t\tPayment\tDays of\tPayment\t\tRemaining\t\tPrincipal\tInterest\t\t\tNew"
print "#\tDate\t\t\tDate\tInterst\tAmount\t\tPrincipal\t\tPayment\t\tPayment\t\t\tBalance"
for payment in actual_payments:
payment_date = datetime.strptime((payment[0]), '%Y-%m-%d').date()
payment_amount = Decimal(payment[1]).quantize(Decimal("1.00"))
#print next_bill_date, "\t", next_payment_date, "\t",
days_since_last_payment = (payment_date-interest_paid_through_date).days
#print days_since_last_payment
new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00"))
# new_principal = monthly_payment - new_interest
new_principal = payment_amount - new_interest
print payment_number, " ", next_bill_date, "\t\t", payment_date, "\t", days_since_last_payment, "\t", payment_amount, "\t\t\t", remaining_principal, "\t\t", new_principal,\
"\t\t", new_interest,
interest_paid_through_date = payment_date
total_interest = total_interest + new_interest
annual_interest=annual_interest+new_interest
remaining_principal = remaining_principal - new_principal
print "\t\t\t", remaining_principal
payment_number=payment_number+1
old_bill_date = next_bill_date
if old_bill_date.month < 12:
next_bill_date = date(year=old_bill_date.year, month = old_bill_date.month + 1, day=payment_day_of_month)
print "Total interest paid to date for", old_bill_date.year, " is", annual_interest
else:
next_bill_date = date(year=old_bill_date.year+1, month = 1, day=payment_day_of_month)
print "Total interest for ", old_bill_date.year, " was ", annual_interest
annual_interest = 0
print "Total interest paid to date on this loan is", total_interest
#loop over remaining scheduled payments and present estimated amortization
print "\nEstimated future amortization"
while (payment_number <= total_periods) and (remaining_principal > 0):
print payment_number, "\t",
payment_number = payment_number+1
print next_bill_date,
days_since_last_payment = (next_bill_date-interest_paid_through_date).days
print days_since_last_payment, "\t",
new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00"))
#make sure the last payment isn't too much
if new_interest+remaining_principal < monthly_payment:
monthly_payment = new_interest + remaining_principal
new_principal = monthly_payment - new_interest
print monthly_payment, "\t\t\t", remaining_principal, "\t\t", new_principal,\
"\t\t", new_interest,
remaining_principal = remaining_principal - new_principal
print "\t", remaining_principal
interest_paid_through_date = next_bill_date
old_bill_date = next_bill_date
if old_bill_date.month < 12:
next_bill_date = date(year=old_bill_date.year, month = old_bill_date.month + 1, day=payment_day_of_month)
else:
next_bill_date = date(year=old_bill_date.year+1, month = 1, day=payment_day_of_month)
if remaining_principal > 0:
print 'Balloon payment due:', remaining_principal

290
mortgage_template.py Normal file
View File

@@ -0,0 +1,290 @@
import json
from decimal import *
from datetime import *
from jinja2 import Environment, FileSystemLoader
from fpdf import FPDF, HTMLMixin
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
from email import Encoders
def loadLoanInformation(filename):
datastore = getDataStore(filename)
loanModel['loan'] = getLoan(dataStore)
loanModel['lender'] = getLender(datastore)
loanModel['borrower'] = getBorrower(datastore)
loanModel['paymentHistory'] = getPaymentHistory(datastore, loan, asOfDate)
loanModel['futureAmortization'] = getAmortization(datastore, loan, paymentHistory)
return loanModel
def getLoan(datastore):
# read in the loan profile information
annual_rate = Decimal(datastore['loan.interest rate']) / 100
daily_interest_rate = annual_rate / 360
principal = Decimal(datastore["loan.principal"]).quantize(Decimal("1.00"))
periods_per_year = Decimal(datastore["loan.periods per year"])
total_periods = Decimal(datastore["loan.periods"])
payment_day_of_month = int(datastore['loan.payment day of month'])
if "monthly_payment" in datastore:
monthly_payment = Decimal(datastore["monthly_payment"]).quantize(Decimal("1.00"))
else:
# calculate expected monthly payment
periodic_rate = annual_rate / periods_per_year
discount_factor = (((1 + periodic_rate) ** total_periods) - 1) / (
periodic_rate * ((1 + periodic_rate) ** total_periods))
monthly_payment = (principal / discount_factor).quantize(Decimal("1.00"))
loan['loan.account_number'] = '123456789'
loan['principal'] = principal
loan['term'] = total_periods
loan['annual_rate'] = annual_rate
loan['daily_interest_rate'] = daily_interest_rate
loan['rate'] = '' + (annual_rate * 100).__str__() + '%'
loan['next_payment_amt'] = 0
loan['next_payment_date'] = '12/12/12'
return loan
def getLender(datastore):
#to be replaced with real loading code
lender['name'] = 'Rivanna Graphite Investments, LLC'
lender['phone'] = '703.343.0782'
lender['address'] = '743 Madison St NW'
lender['city'] = 'Washington'
lender['state'] = 'DC'
lender['zip'] = '20011'
return getLender
def getBorrower(datastore):
#to be replaced with real loading code
borrower['name'] = 'Bear Houses, LLC'
borrower['address'] = '123 Any Street'
borrower['city'] = 'Alltown'
borrower['state'] = 'VA'
borrower['zip'] = '11111'
return borrower
def getDataStore(filename=None):
try:
if filename:
with open(filename, 'r') as f:
datastore = json.load(f)
except Exception as e:
print "An error occurred opening your loan file '#s'. " % filename
print "The Exception:"
print e.__repr__()
quit()
return datastore
def calculateLoanAmortization(loanModel):
return loanModel
def transformTemplate(template_fileName, loanModel):
# template_filename = "statement.txt.jinja"
# setup jinja for creating the statement
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template(template_fileName)
report = template.render(original_principal_balance=principal, future_payments=future_payments, \
past_payments=past_payments, balloon_payment=remaining_principal, \
total_interest_paid_to_date=total_interest, statement=statement)
return report
def generatePDFStatement():
template_filename = "statement.pdf.jinja"
pass
def generateHTMLStatement():
pass
def generateTextStatement():
pass
def generateEmail(from_address, to_address, subject, body, attachment):
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = from_address
msg['To'] = to_address
msg.attach(MIMEText(body))
part = MIMEBase('application', "octet-stream")
part.set_payload(attachment)
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="statement.pdf"')
msg.attach(part)
return msg
def main():
# this program reads the json file describing a mortgage and calculate the actual and projected amortization
# the first payment in the file should be the interest paid at closing
# the interest at closing covers interest that would be incurred from the date of closing until the first day
# that will be covered by the first loan payment
# i.e., the first loan payment is expected a month and a half after closing, the first two weeks interest is due
# at closing. The first payment will incur interest from one month before the bill is due.
from_address = 'jkent3rd@gmail.com'
passwd = "pvyrbcnzrjoizprn"
subject = "Mortgage or Loan Statement"
to_address = 'jkent3rd@gmail.com'
body = 'This email contains a PDF of your mortgage statement.'
# read in the file
# filename = "./10Kloan.txt"
# filename = "./10Kloan.test.txt"
# filename = "./dadmortgage.txt"
# filename = "./brendamortgage.txt"
filename = "./greenfield_mortgage.txt"
template_filename = "statement.pdf.jinja"
loanInformation = loadLoanInformation(filename)
loanModel = calculateLoanAmortization(loanInformation)
# read in the statement information
statement, lender, borrower, loan = {}, {}, {}, {}
lender = loanModel.lender
borrower = loanModel.borrower
past_payments = loadModel.past_payments
future_payments = loanModel.future_payments
statement['title'] = "Mortgage Statement - 185 James River Rd"
statement['date'] = "Today"
statement['lender'] = lender
statement['borrower'] = borrower
statement['loan'] = loan
# loop over the payments and calculate the actual amortization
actual_payments = loanModel["payments"]
remaining_principal = principal
annual_interest = 0
total_interest = 0
interest_paid_through_date = datetime.strptime(loanModel["start_interest_date"], "%Y-%m-%d").date()
next_payment_date = datetime.strptime(loanModel["first payment month"], '%Y-%m-%d').date()
next_bill_date = date(year=next_payment_date.year, month=next_payment_date.month, day=payment_day_of_month)
old_bill_date = next_bill_date
payment_number = 1
current_year = next_bill_date.year
for payment in actual_payments:
payment_date = datetime.strptime((payment[0]), '%Y-%m-%d').date()
payment_amount = Decimal(payment[1]).quantize(Decimal("1.00"))
days_since_last_payment = (payment_date - interest_paid_through_date).days
new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00"))
new_principal = payment_amount - new_interest
interest_paid_through_date = payment_date
total_interest = total_interest + new_interest
annual_interest = annual_interest + new_interest
remaining_principal = remaining_principal - new_principal
# create the payment record for the template to render
payment_record = {}
payment_record['payment_number'] = payment_number
payment_record['bill_date'] = next_bill_date
payment_record['payment_date'] = payment_date
payment_record['days_of_interest'] = days_since_last_payment
payment_record['payment_amount'] = payment_amount
payment_record['principal_payment'] = payment_amount - new_interest
payment_record['interest_payment'] = new_interest
payment_record['new_balance'] = remaining_principal
payment_record['interest_to_date'] = total_interest
payment_record['annual_interest_to_date'] = annual_interest
past_payments.append(payment_record)
payment_number = payment_number + 1
old_bill_date = next_bill_date
if old_bill_date.month < 12:
next_bill_date = date(year=old_bill_date.year, month=old_bill_date.month + 1, day=payment_day_of_month)
else:
annual_interest_record['year'] = old_bill_date.year
annual_interest_record['interest'] = annual_interest
past_interest.append(annual_interest_record)
next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month)
payment_record['print_interest_total'] = True
payment_record[
'interest_total_message'] = "Total interest for " + old_bill_date.year.__str__() + " was $" + annual_interest.__str__()
annual_interest = 0
if old_bill_date.month < 12:
payment_record['print_interest_total'] = True
payment_record[
'interest_total_message'] = "Total interest to date for " + old_bill_date.year.__str__() + " is $" + annual_interest.__str__()
# loop over remaining scheduled payments and present estimated amortization
while (payment_number <= total_periods) and (remaining_principal > 0):
days_since_last_payment = (next_bill_date - interest_paid_through_date).days
new_interest = (days_since_last_payment * remaining_principal * daily_interest_rate).quantize(Decimal("0.00"))
# make sure the last payment isn't too much
if new_interest + remaining_principal < monthly_payment:
monthly_payment = new_interest + remaining_principal
new_principal = monthly_payment - new_interest
remaining_principal = remaining_principal - new_principal
interest_paid_through_date = next_bill_date
# complete the future payment amortization record
future_payment_record = {}
future_payment_record['payment_number'] = payment_number
future_payment_record['payment_date'] = next_bill_date
future_payment_record['days_of_interest'] = days_since_last_payment
future_payment_record['payment_amount'] = monthly_payment
future_payment_record['principal_payment'] = new_interest
future_payment_record['interest_payment'] = new_interest
future_payment_record['new_balance'] = remaining_principal
future_payments.append(future_payment_record)
payment_number = payment_number + 1
old_bill_date = next_bill_date
if old_bill_date.month < 12:
next_bill_date = date(year=old_bill_date.year, month=old_bill_date.month + 1, day=payment_day_of_month)
else:
next_bill_date = date(year=old_bill_date.year + 1, month=1, day=payment_day_of_month)
report = transformTemplate('',loanModel)
# create pdf
class MyFPDF(FPDF, HTMLMixin):
pass
pdf = MyFPDF()
pdf.set_font(family='Arial', size=12)
pdf.add_page()
pdf.write_html(report)
# pdf.output(name='test.pdf', dest='F')
attachment = pdf.output(dest='S')
# send email
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(from_address, passwd)
email = generateEmail(from_address, to_address, subject, body, attachment)
server.sendmail(from_address, to_address, email.as_string())
server.close()
except:
print "Couldn't send email, dumping statement to file."
pdf.output(name='test.pdf', dest='F')
quit()
if __name__ == '__main__':
main()

82
statement.pdf.jinja Normal file
View File

@@ -0,0 +1,82 @@
<html>
<body>
<font face='arial' size='14'><p align='center'>{{ statement.title }}</p></font>
<font face='arial' size='10'><p align='center'>{{ statement.lender.name }}</p></font>
<font face='arial' size='8'>
<p align='center'>{{ statement.lender.phone }} - {{ statement.lender.address }} -
{{ statement.lender.city }} {{statement.lender.state }} {{ statement.lender.zip }}</p>
<p align='right'>Statement Date: {{ statement.date }}</p>
<p/>
<table>
<thead><tr><th width='45%' align='left'>Loan Information</th><th width='45%'>&nbsp;</th></tr></thead>
<tbody>
<tr><td>Borrower: {{ statement.borrower.name }}&nbsp;</td> <td>Account Number: {{ statement.loan.account_number }}</td></tr>
<tr><td>{{ statement.borrower.address }}&nbsp;</td> <td>Origination Date: {{ statement.loan.origination_date }}</td></tr>
<tr><td>{{ statement.borrower.city }}, {{statement.borrower.state }} {{ statement.borrower.zip }}</td>
<td>Original Principal: {{ "$%.2f"|format(statement.loan.principal) }}</td></tr>
<tr><td>Rate: {{statement.loan.rate }} </td> <td>Term: {{statement.loan.term }} months </td></tr>
<tr><td>Next Payment Due Date: {{statement.loan.next_due_date}} </td> <td>Payment Due: {{ "$%.2f"|format(statement.loan.next_payment_amt) }} </td></tr>
</tbody>
</table>
<p/>
<p class='section_header' color='red'><font face='arial' size='14'>Payment History</font></p>
<table>
<thead><tr>
<th width='5%'>#</td>
<th width='10%'>Due Date</th>
<th width='10%'>Date Paid</th>
<th width='10%'>Days Interest</th>
<th width='15%' align='right'>Payment Amt</th>
<th width='15%' align='right'>Principal Pmt</th>
<th width='15%' align='right'>Interest Pmt</th>
<th width='20%' align='right'>New Balance</th>
</tr></thead>
<tbody>
{% for item in past_payments %}
<tr><td align='center'> {{ item.payment_number }} </td>
<td align='center'> {{ item.bill_date }} </td>
<td align='center'> {{ item.payment_date }} </td>
<td align='center'> {{ item.days_of_interest }} </td>
<td align='right'> {{ "$%.2f"|format(item.payment_amount) }} </td>
<td align='right'> {{ "$%.2f"|format(item.principal_payment) }} </td>
<td align='right'> {{ "$%.2f"|format(item.interest_payment) }} </td>
<td align='right'> {{ "$%.2f"|format(item.new_balance) }} </td>
</tr>
{% if item.print_interest_total %}
<tr><td colspan='8'"> {{ item.interest_total_message }} </td></tr>
{% endif %}
{% endfor %}
</tbody>
</table>
<p>Total interest paid to date is {{ "$%.2f"|format(total_interest_paid_to_date) }}.</p>
<p/> <p/>
<p class='section_header'><font face='arial' size='14'>Remaining Amortization</font></p>
<table>
<thead><tr>
<th width='8%'>#</th>
<th width='15%'>Due Date</th>
<th width='8%'>Days Interest</th>
<th width='15%' align='right'>Payment Amt</th>
<th width='15%' align='right'>Principal Pmt</th>
<th width='15%' align='right'>Interest Pmt</th>
<th width='20%' align='right'>Principal Balance</th>
</tr></thead>
<tbody>
{% for item in future_payments %}
<tr><td align='center'> {{ item.payment_number }} </td>
<td align='center'> {{ item.payment_date }} </td>
<td align='center'> {{ item.days_of_interest }} </td>
<td align='right'> {{ "$%.2f"|format(item.payment_amount) }} </td>
<td align='right'> {{ "$%.2f"|format(item.principal_payment) }} </td>
<td align='right'> {{ "$%.2f"|format(item.interest_payment) }} </td>
<td align='right'> {{ "$%.2f"|format(item.new_balance) }} </td>
</tr>
{% endfor %}
</tbody>
</table>
<p>Balloon Payment Due: {{ "$%.2f"|format(balloon_payment) }} </p>
</font>
</body>
</html>

49
statement.txt.jinja Normal file
View File

@@ -0,0 +1,49 @@
Original Principal Balance: {{ original_principal_balance }}
Payment History
#\tBill Date
Payment Date
Days of Interest
Payment Amount
Principal Payment
Interest Payment
New Balance
{% for item in past_payments %}
{{ item.payment_number }}
{{ item.bill_date }}
{{ item.payment_date }}
{{ item.days_of_interest }}
{{ item.payment_amount }}
{{ item.principal_payment }}
{{ item.interest_payment }}
{{ item.new_balance }}
{% if item.print_interest_total %}
{{ item.interest_total_message }}
{% endif %}
{% endfor %}
Total interest paid to date is {{ total_interest_paid_to_date }}.
Remaining Amortization
#
Days of Interest
Due Date
Payment Amount
Scheduled Principal Payment
Scheduled Interest Payment
Expected New Balance
{% for item in future_payments %}
{{ item.payment_number }}
{{ item.days_of_interest }}
{{ item.payment_date }}
{{ item.payment_amount }}
{{ item.principal_payment }}
{{ item.interest_payment }}
{{ item.new_balance }}
{% endfor %}
Balloon Payment Due: {{ balloon_payment }}