<jupyter_start><jupyter_text>Example financial decision:> **Whether to commit to owning a home & what does this entail** Stakeholders - K&J Question space> What is the wealth outcome over time?  > What is the cashflow commitment?  > What do the numbers mean with the QE shenanigans in play?  > What scenarios are we looking at?  > How does letting affect the calculation?  > By when are we in the money, in real terms?  > What do exit options look like over the years?<jupyter_code>import pandas as pd
import numpy as np
import datetime

pd.set_option('plotting.backend', 'pandas_bokeh')
pd.plotting.output_notebook()

from bokeh.models.formatters import NumeralTickFormatter, PrintfTickFormatter
from bokeh.plotting import show<jupyter_output><empty_output><jupyter_text>Example listing details<jupyter_code>price, property_name, location, photo = (
    250_000, 
    'Pretty home', 
    'Richmond, Surrey', 
    'https://unsplash.com/home.png')<jupyter_output><empty_output><jupyter_text>Mortgage calculation<jupyter_code># mortgage calculation
# (price, downpayment, rate, term, interest-only) -> time series of debt balance, time series of accumulated payments

def mortgage(price:int, downpayment:int, rate:float, term_years:int, interest_only=False) -> pd.DataFrame:
    
    principal = price - downpayment
    rate = rate / 12
    term = term_years * 12
    periods = np.arange(term) + 1
    
    if not interest_only:
        full_payments = np.pmt(rate, term, principal) * np.ones_like(periods)
        principal_payments = np.ppmt(rate, periods, term, principal)
        interest_payments = np.ipmt(rate, periods, term, principal)
    else:
        full_payments = (principal * rate) * -np.ones_like(periods)
        principal_payments = np.zeros_like(periods)
        interest_payments = full_payments
        
    debt_balance = (principal + principal_payments.cumsum()).round(2)
    
    return pd.DataFrame(
        data=np.array([debt_balance, full_payments, principal_payments, interest_payments]).T,
        columns='debt_balance, full_payments, principal_payments, interest_payments'.split(', '),
        index=pd.date_range(start=datetime.date.today(), periods=term, freq='M')
    )

mortgage(price, 0, 0.04, 30)<jupyter_output><empty_output><jupyter_text>Annualized mortgage cash flow<jupyter_code>mortgage(price, 0, 0.04, 25).resample('Y')['full_payments'].sum()<jupyter_output><empty_output><jupyter_text>Home value appreciation<jupyter_code>def home_value(price:int, growth_rate:float, term_years:int) -> pd.Series:
    periods = np.arange(term_years)
    value = np.fv(growth_rate, periods, 0, -price)
    return pd.Series(
        name='home_value',
        data=value,
        index=pd.date_range(start=datetime.date.today(), periods=term_years, freq='Y')
    )
# optional letting cash flow -> time series of cash payments
def letting_cash_flow(monthly_rate:int, term_years:int, growth_rate:float, vacancy_rate:float=2/12) -> pd.Series:
    periods = np.arange(term_years)
    value = np.fv(growth_rate, periods, 0, -1) * monthly_rate * (1-vacancy_rate)
    return pd.Series(
        name='rental_income',
        data=value,
        index=pd.date_range(start=datetime.date.today(), periods=term_years, freq='Y')
    )
home_value(price, 0.01, 15)
letting_cash_flow(1250, 25, .02)<jupyter_output><empty_output><jupyter_text>Step 1: Scenario - investment outcomes<jupyter_code># Parameters:
transaction_cost = 0.05 * price
downpayment = 0.05 * price 
years = 25
inflation = .018
inflation_houseprice = 0.2
rate_mortgage = 0.135
rental_rate = 0
interest_only = False

print('## Scenario')
print('House price: £', price)
print('transaction_cost: £', transaction_cost)
print('downpayment: £', downpayment)
print('term (years)', years)
print(f'inflation {100*inflation:.2f}%')
print(f'house price appreciation: {100*inflation_houseprice:.2f}%')
print(f'rate_mortgage: {100*rate_mortgage:.2f}%')
print('monthly rental income:', 0)
print('interest-only:', interest_only)
# Schedule and outcomes over time

loan_schedule = mortgage(price, downpayment, rate_mortgage, years, interest_only=interest_only)
debt_balance = loan_schedule.debt_balance.resample('Y').last()
interest_paid = loan_schedule.interest_payments.resample('Y').sum().cumsum()

# Home value and letting income
home = home_value(price, inflation_houseprice, years)
letting_income = letting_cash_flow(rental_rate, years, inflation).cumsum()

# Actual cash flow
cash_flow = (
    loan_schedule.full_payments.resample('Y').sum()
    + letting_income
)
cash_flow.iloc[0] -= transaction_cost
cash_flow.iloc[0] -= downpayment
cash_flow = cash_flow.cumsum()

# Real situation
wealth = home + letting_income - downpayment - debt_balance - transaction_cost
wealth
cash_flow
# discounted value at time T - discounting by inflation

df = pd.DataFrame(
    data = [debt_balance, interest_paid, cash_flow, wealth],
    index = 'debt_balance, interest_payments, cash_flow, wealth'.split(', ')
).T.dropna()

discounting_factor = np.pv(inflation, np.arange(len(df)), 0, fv=-1)
df = df.mul(discounting_factor, axis=0)<jupyter_output><empty_output><jupyter_text>Outcomes chart<jupyter_code>p = df.plot(kind='line', 
            show_figure=False,
            toolbar_location=None,
            panning=False,
            zooming=False,
            plot_data_points=True,
            plot_data_points_size=5
           )
p.hover.tooltips = [('year', '$index')] + list(
    (c, f'£@{c}'+'{0,0}')
for c in df.columns)
p.hover.mode='mouse'
p.yaxis[0].formatter = NumeralTickFormatter(format='£0,0')
p.xaxis.major_label_orientation = 3.14/5
p.legend.location = "top_left"
# p.yaxis[0].formatter.use_scientific = False
show(p)<jupyter_output><empty_output><jupyter_text>Wealth chart<jupyter_code>p = (df.wealth + df.cash_flow).rename('Net wealth').plot.line(
    show_figure=False,
    toolbar_location=None,
    hovertool_string=r"""<h4> Net wealth: </h4> £@{Net wealth}{0,0}""",
    panning=False,
    zooming=False
)
p.yaxis[0].formatter = NumeralTickFormatter(format='$0,0')
p.xaxis.major_label_orientation = 3.14/5
p.legend.location = "top_left"
show(p)<jupyter_output><empty_output><jupyter_text>Outcomes chart in `pygal`<jupyter_code>from IPython.display import HTML
import pygal
line_chart = pygal.Line(dynamic_print_values=True, value_formatter=lambda x: f'£{x:,.0f}')
line_chart.title = 'Investment Outcomes:'
line_chart.x_labels = map(str, range(0, years+1))

line_chart = pygal.Line(dynamic_print_values=True, value_formatter=lambda x: f'£{x:,.0f}')
for c in df.columns:
    line_chart.add(c, df[c])

pygal_script = '<script type="text/javascript" src="http://kozea.github.com/pygal.js/latest/pygal-tooltips.min.js"></script>'
HTML(line_chart.render(is_unicode=True)+pygal_script)<jupyter_output><empty_output><jupyter_text>Outcomes chart in `altair`<jupyter_code>import altair as alt
alt.renderers.enable('notebook')
altair_format_df = (
    df.reset_index()
    .melt('index', var_name='Factor', value_name='Amount')
    .rename(columns={'index':'Year'}))
alt.Chart(altair_format_df).mark_line(size=7, point=True).encode(
    x='Year:T',
    y='Amount:Q',
    color='Factor', 
    tooltip=[
        'Year:T', 'Factor:N', 
        alt.Tooltip('Amount:Q', format='$,.2f')]
).interactive()

alt.Chart(altair_format_df.query('Factor == "wealth"')).mark_area(opacity=0.4).encode(
    x='Year:T',
    y=alt.Y('Amount:Q', stack=None),
    color='Factor'
)<jupyter_output><empty_output><jupyter_text>Step 2: Turn it into a callableOur intention is to examine multiple scenarios!<jupyter_code>from download_listing import get_listing
from mortgage import mortgage
from home_value import home_value, letting_cash_flow

from charts import altair_outcomes_chart, pygal_outcomes_chart, altair_wealth_chart, altair_format
def scenario(
        price=250_000,
        transaction_cost = 0.05,
        downpayment = 0.05,
        years = 25,
        inflation = .018,
        inflation_houseprice = 0.045,
        rate_mortgage = 0.035,
        rental_rate = 0,
        interest_only = False
        ):
    # ...
    return "result"
scenario(price=1_000_000, years=20, inflation=.1)<jupyter_output><empty_output><jupyter_text>Step 3: Make it interactive<jupyter_code>from ipywidgets import interact
interact(scenario)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13005780346820808, 'fraction_numerical': 0.01794315992292871, 'mean_word_length': 4.9791216702663785, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 66, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '665398', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 3004, 'n_tokens_neox': 2831, 'n_words': 561, 'path': '1 Typical workflow.ipynb', 'repo_name': 'MichaMucha/odsc2019-voila-jupyter-web-app', 'size': '8304'}