In [ ]:
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import dash_bootstrap_components as dbc
from datetime import datetime
import pytz
# Define constants
HECM_LIMIT = 1089300
HECM_MIP = 0.02
HECM_ANNUAL_MIP = 0.005
HECM_INTEREST_RATE = 6.75
HOMESAFE_INTEREST_RATE = 9.69
INVESTMENT_RATE = 8.0
HOMESAFE_OTHER_FEES = 4500
HOMESAFE_ORIG_FEE_PERCENT = 0.10
# Define PLF charts
plf_chart = {
60: 0.295, 61: 0.302, 62: 0.317, 63: 0.324, 64: 0.331, 65: 0.338, 66: 0.346, 67: 0.354, 68: 0.362, 69: 0.370,
70: 0.375, 71: 0.375, 72: 0.377, 73: 0.386, 74: 0.395, 75: 0.405, 76: 0.411, 77: 0.422, 78: 0.433, 79: 0.439,
80: 0.451, 81: 0.463, 82: 0.476, 83: 0.489, 84: 0.502, 85: 0.516, 86: 0.530, 87: 0.545, 88: 0.558, 89: 0.573,
90: 0.590, 91: 0.607, 92: 0.624, 93: 0.642, 94: 0.661, 95: 0.679, 96: 0.689, 97: 0.696, 98: 0.696, 99: 0.696,
100: 0.696
}
plf_chart_homesafe = {age: plf + 0.018 if age <= 79 else plf_chart[80] + 0.018 for age, plf in plf_chart.items()}
def future_value(principal, rate, years):
return principal * ((1 + rate / 100) ** years)
def future_value_of_monthly_contributions(payment, rate, months):
monthly_rate = rate / 12 / 100
return payment * (((1 + monthly_rate) ** months - 1) / monthly_rate) * (1 + monthly_rate)
def calculate_hecm_loan(home_value, age, existing_mortgage_balance, birthday_soon, other_fees=2500):
if home_value < 300000:
raise ValueError("Home value must be at least $300000 for HECM.")
if birthday_soon:
age += 1
plf = plf_chart.get(age, plf_chart[100])
principal_limit = home_value * plf
upfront_mip = home_value * HECM_MIP
origination_fee = 6000 # Max origination fee
mandatory_obligations = existing_mortgage_balance + upfront_mip + origination_fee + other_fees
initial_disbursement_limit = min(max(0.6 * principal_limit, mandatory_obligations + 0.1 * principal_limit), principal_limit)
remaining_draw = principal_limit - initial_disbursement_limit
available_now = initial_disbursement_limit - mandatory_obligations
return available_now, remaining_draw, principal_limit
def calculate_homesafe_loan(home_value, age, existing_mortgage_balance):
plf = plf_chart_homesafe.get(age, plf_chart_homesafe[99])
principal_limit = home_value * plf
initial_loan_balance = principal_limit - existing_mortgage_balance
origination_fee = initial_loan_balance * HOMESAFE_ORIG_FEE_PERCENT
mandatory_obligations = existing_mortgage_balance + origination_fee + HOMESAFE_OTHER_FEES
available_loan = principal_limit - mandatory_obligations
return available_loan, principal_limit
def plot_payments(home_value, age, existing_mortgage_balance, current_payment, projection_years, starting_savings, monthly_bills, birthday_soon):
try:
if home_value <= HECM_LIMIT:
if age < 62:
if home_value >= 300000:
available_now, principal_limit = calculate_homesafe_loan(home_value, age, existing_mortgage_balance)
available_later = 0
loan_type = "HomeSafe"
else:
return f"Error: Age must be 62 or older for HECM and home value must be at least $300000."
else:
available_now, available_later, principal_limit = calculate_hecm_loan(home_value, age, existing_mortgage_balance, birthday_soon)
loan_type = "HECM"
else:
if age < 60:
raise ValueError("Age must be 60 or older for HomeSafe.")
available_now, principal_limit = calculate_homesafe_loan(home_value, age, existing_mortgage_balance)
available_later = 0
loan_type = "HomeSafe"
cashout_future_value = future_value(available_now, INVESTMENT_RATE, projection_years)
reverse_growth = starting_savings + cashout_future_value
if loan_type == "HECM" and available_later > 0:
reverse_growth += future_value(available_later, INVESTMENT_RATE, projection_years - 1)
no_reverse_savings = starting_savings
for month in range(projection_years * 12):
no_reverse_savings -= monthly_bills
no_reverse_savings *= (1 + INVESTMENT_RATE / 100 / 12)
reverse_growth += future_value_of_monthly_contributions(current_payment, INVESTMENT_RATE, projection_years * 12)
fig = go.Figure(data=[
go.Bar(name='Reverse Mortgage', x=['Reverse Mortgage'], y=[reverse_growth]),
go.Bar(name='No Reverse Mortgage', x=['No Reverse Mortgage'], y=[no_reverse_savings])
])
fig.update_layout(
title=f'Projected Savings: Reverse Mortgage vs. No Reverse Mortgage ({projection_years} Years)',
xaxis_title='Options',
yaxis_title=f'Total Savings After {projection_years} Years ($)',
barmode='group'
)
cash_options = f"Reverse Mortgage Type: **{loan_type}**\nCASH Available at Closing: **${available_now:,.2f}**"
if loan_type == "HECM" and available_later > 0:
cash_options += f"\nCASH Available After 1 Year: **${available_later:,.2f}**"
disclosure = ("THIS IS NOT A COMMITMENT TO LEND. THIS IS NOT A PREAPPROVAL. "
"THIS TOOL IS FOR ILLUSTRATIVE PURPOSES ONLY. PLEASE CONTACT "
"MATTHEW SOLUM DIRECTLY AT (949) 270-1475 TO RECEIVE YOUR FREE 'UNLOCK DREAMS' REPORT. "
"A PERSONALIZED WEALTH NAVIGATOR REPORT WITH 7 OPTIONS ANALYZED INCLUDING REVERSE MORTGAGE OPTIONS. "
"UNLOCK YOUR EQUITY AND LIVE YOUR GOLDEN YEARS.\n"
"**RESULTS WILL VARY**. NMLS 304938 with Nations Reverse headquartered in Henderson, NV. "
"I only originate and close loans in Washington and Oregon. "
"My focus is providing FHA HECM Reverse Mortgages for Refinance and Purchase, and also Jumbo Reverse Mortgages "
"(i.e., FAR’s HomeSafe Reverse Mortgage). "
"See full disclosure on this website: seniorwealthnavigator.com")
# Adjusting the timestamp to PST
pst = pytz.timezone('America/Los_Angeles')
timestamp = datetime.now(pst).strftime("%m-%d-%Y-%I:%M %p PST")
simulation_variables = (f"Date/Time: {timestamp}\nHome Value: ${home_value:,}\nAge: {age}\nExisting Mortgage Balance: ${existing_mortgage_balance:,}\n"
f"Current Payment: ${current_payment:,}\nProjection Years: {projection_years}\n"
f"Starting Savings: ${starting_savings:,}\nMonthly Bills Paid from Savings: ${monthly_bills:,}")
return fig, cash_options, disclosure, simulation_variables, ""
except ValueError as e:
return {}, "", "", "", str(e)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container([
html.H1("Senior Wealth Navigator Tool"),
html.Pre(id='simulation-variables', style={'margin-top': '20px', 'background-color': '#f4f4f4', 'padding': '10px'}),
html.Div(id='cash-options', style={'font-weight': 'bold', 'margin-top': '20px'}),
dbc.Row([
dbc.Col([
html.Label("Home Value (Estimated):"),
dcc.Input(id='home-value-input', type='number', value=500000, min=300000, max=5000000),
html.Small("Enter a value between $300,000 and $5,000,000")
], width=12, style={'margin-bottom': '20px'}),
dbc.Col([
html.Label("Age of Youngest Borrower:"),
dcc.Input(id='age-input', type='number', value=70, min=60, max=100),
html.Small("Enter a value between 60 and 100")
], width=12, style={'margin-bottom': '20px'}),
dbc.Col([
html.Label("Will you be turning a year older within the next 6 months?"),
dbc.RadioItems(
id='birthday-soon-input',
options=[
{'label': 'Yes', 'value': True},
{'label': 'No', 'value': False}
],
value=False,
inline=True
)
], width=12, style={'margin-bottom': '20px'}),
dbc.Col([
html.Label("Existing Mortgage Balance (1st, 2nd, 3rd, other liens):"),
dcc.Input(id='existing-mortgage-balance-input', type='number', value=100000, min=0, max=2500000),
html.Small("Enter a value between $0 and $2,500,000")
], width=12, style={'margin-bottom': '20px'}),
dbc.Col([
html.Label("Current Monthly Mortgage P&I Payments (1st, 2nd, 3rd, other liens):"),
dcc.Input(id='current-payment-input', type='number', value=900, min=0, max=7000),
html.Small("Enter a value between $0 and $7,000")
], width=12, style={'margin-bottom': '20px'}),
dbc.Col([
html.Label("Projection Years:"),
dcc.Input(id='projection-years-input', type='number', value=5, min=1, max=30),
html.Small("Enter a value between 1 and 30")
], width=12, style={'margin-bottom': '20px'}),
dbc.Col([
html.Label("Starting Savings:"),
dcc.Input(id='starting-savings-input', type='number', value=70000, min=0, max=10000000),
html.Small("Enter a value between $0 and $10,000,000")
], width=12, style={'margin-bottom': '20px'}),
dbc.Col([
html.Label("Monthly Bills Paid from Savings:"),
dcc.Input(id='monthly-bills-input', type='number', value=500, min=0, max=10000),
html.Small("Enter a value between $0 and $10,000")
], width=12, style={'margin-bottom': '20px'})
]),
dbc.Button("Push for Cashout and Peace of Mind", id='run-button', color='primary', style={'font-size': '1.25em', 'font-weight': 'bold'}),
html.Div(id='error-message', style={'color': 'red', 'margin-top': '10px'}),
dcc.Graph(id='output-graph'),
html.Div(id='disclosure', style={'margin-top': '10px', 'font-style': 'italic', 'white-space': 'pre-wrap'})
], fluid=True)
@app.callback(
[Output('output-graph', 'figure'),
Output('cash-options', 'children'),
Output('disclosure', 'children'),
Output('simulation-variables', 'children'),
Output('error-message', 'children')],
[Input('home-value-input', 'value'),
Input('age-input', 'value'),
Input('birthday-soon-input', 'value'),
Input('existing-mortgage-balance-input', 'value'),
Input('current-payment-input', 'value'),
Input('projection-years-input', 'value'),
Input('starting-savings-input', 'value'),
Input('monthly-bills-input', 'value'),
Input('run-button', 'n_clicks')]
)
def update_output(home_value, age, birthday_soon, existing_mortgage_balance, current_payment, projection_years, starting_savings, monthly_bills, n_clicks):
if n_clicks is None:
raise dash.exceptions.PreventUpdate
# Validate inputs
if not (300000 <= home_value <= 5000000):
return {}, "", "", "", "Home Value must be between $300,000 and $5,000,000"
if not (60 <= age <= 100):
return {}, "", "", "", "Age must be between 60 and 100"
if not (0 <= existing_mortgage_balance <= 2500000):
return {}, "", "", "", "Existing Mortgage Balance must be between $0 and $2,500,000"
if not (0 <= current_payment <= 7000):
return {}, "", "", "", "Current Payment must be between $0 and $7,000"
if not (1 <= projection_years <= 30):
return {}, "", "", "", "Projection Years must be between 1 and 30"
if not (0 <= starting_savings <= 10000000):
return {}, "", "", "", "Starting Savings must be between $0 and $10,000,000"
if not (0 <= monthly_bills <= 10000):
return {}, "", "", "", "Monthly Bills Paid from Savings must be between $0 and $10,000"
result = plot_payments(home_value, age, existing_mortgage_balance, current_payment, projection_years, starting_savings, monthly_bills, birthday_soon)
if isinstance(result, tuple):
fig, cash_options, disclosure, simulation_variables, error_message = result
return fig, cash_options, disclosure, simulation_variables, error_message
return {}, result, "", "", ""
if __name__ == '__main__':
app.run_server(debug=False, port=8052)