Skip to content

Commit

Permalink
skeleton for ui
Browse files Browse the repository at this point in the history
  • Loading branch information
mdlacasse committed Dec 14, 2024
1 parent 37dac35 commit 6871621
Show file tree
Hide file tree
Showing 14 changed files with 406 additions and 9 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/github-actions-runtests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.12"]

steps:
- uses: actions/checkout@v4
Expand All @@ -29,7 +29,7 @@ jobs:
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=25 --per-file-ignores="__init__.py:F401" --max-line-length=127 --statistics
flake8 . --count --exit-zero --max-complexity=30 --per-file-ignores="__init__.py:F401" --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest
4 changes: 4 additions & 0 deletions interface/Asset_Allocations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import streamlit as st
import key as k

st.write('# Asset Allocations')
41 changes: 41 additions & 0 deletions interface/Assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import streamlit as st
import key as k


st.write('## Assets')

st.write('## Account Balances')
accounts = {'txbl': 'taxable', 'txDef': 'tax-deferred', 'txFree': 'tax-exempt'}
col1, col2 = st.columns(2, gap='small', vertical_alignment='top')#, border=False)
with col1:
iname0 = st.session_state.iname0
for key in accounts:
nkey = key+str(0)
k.init(nkey, 0)
ret = k.getNum('%s %s account ($k)' % (iname0, accounts[key]), nkey)

with col2:
if st.session_state.status == 'married':
iname1 = st.session_state.iname1
for key in accounts:
nkey = key+str(1)
k.init(nkey, 0)
ret = k.getNum('%s %s account ($k)' % (iname1, accounts[key]), nkey)

if st.session_state.status == 'married':
st.write('#### Beneficiary fractions')
col1, col2, col3 = st.columns(3, gap='small', vertical_alignment='top')#, border=False)
with col1:
nkey = 'benf'+str(0)
k.init(nkey, 1)
ret = k.getNum('Beneficiary fraction (%s)' % accounts['txbl'], nkey)

with col2:
nkey = 'benf'+str(1)
k.init(nkey, 1)
ret = k.getNum('Beneficiary fraction (%s)' % accounts['txDef'], nkey)

with col3:
nkey = 'benf'+str(2)
k.init(nkey, 1)
ret = k.getNum('Beneficiary fraction (%s)' % accounts['txFree'], nkey)
90 changes: 90 additions & 0 deletions interface/Basic_Information.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from datetime import date
import streamlit as st

import key as k
import owlplanner as owl


def checkStartDate(key):
mydate = st.session_state['_'+key]
mydatelist = mydate.split('/')
if (len(mydatelist) != 2 or mydatelist[0] > 12 or mydatelist[1] > 31):
st.info('Invalid date.')
return False
k.push(key)
return True


def checkAllOK():
ss = st.session_state
return (ss.name == '' or ss.iname0 == ''
or (ss.status == 'married' and ss.iname1 == ''))


def genPlan():
ss = st.session_state
inames = [ss.iname0]
yobs = [ss.yob0]
life = [ss.life0]
if ss.status == 'married':
inames.append(ss.iname1)
yobs.append(ss.yob1)
life.append(ss.life1)

try:
print(inames, yobs, life, ss.name, ss.startDate)
plan = owl.Plan(inames, yobs, life, ss.name, ss.startDate)
except:
st.info('Failed plan creation.')
return
ss.plan = plan

st.write('## Basic Information')

choices = ['single', 'married']
k.init('status', choices[0])
st.radio('Marital status', choices,
index=choices.index(st.session_state['status']), key='_status',
on_change=k.push, args=['status'], horizontal=True)

col1, col2 = st.columns(2, gap='small', vertical_alignment='top')
with col1:
k.init('iname0', '')
if st.session_state.iname0 == '':
st.info('Fist name must be provided.')

iname0 = k.getText('Your first name', 'iname0')

k.init('yob0', 1965)
ret = k.getNum("%s's birth year"%iname0, 'yob0')

k.init('life0', 80)
ret = k.getNum("%s's expected longevity"%iname0, 'life0')

today = date.today()
todaysDate = '%d/%d' % (today.month, today.day)
k.init('startDate', todaysDate)
ret = k.getText("Plan's starting date on first year (MM/DD)", 'startDate',
callback=checkStartDate)

k.init('name', '')
if st.session_state.name == '':
st.info('A name for the plan must be provided.')
k.getText("Plan's name", 'name')

with col2:
if st.session_state['status'] == 'married':
k.init('iname1', '')
if st.session_state.iname1 == '':
st.info('Fist name must be provided.')

iname1 = k.getText("Your spouse's first name", 'iname1')

k.init('yob1', 1965)
ret = k.getNum("%s's birth year"%iname1, 'yob1')

k.init('life1', 80)
ret = k.getNum("%s's expected longevity"%iname1, 'life1')

st.button('Initialize plan', on_click=genPlan, disabled=checkAllOK())

40 changes: 40 additions & 0 deletions interface/Fixed_Income.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import streamlit as st
import key as k


def getInput(i, key, text, defval=0):
nkey = key+str(i)
k.init(nkey, defval)
iname = st.session_state['iname'+str(i)]
ret = st.number_input("%s's %s"%(iname, text), min_value=0,
value=st.session_state[nkey],
on_change=k.push, args=[nkey], key='_'+nkey)

if st.session_state.iname0 == '':
st.info('Basic information must be filled before filling this page.')
else:
st.write('# Fixed Income')

st.write('### Social Security')
col1, col2 = st.columns(2, gap='small', vertical_alignment='top')#, border=False)
with col1:
getInput(0, 'ssAge', 'social security age', 67)
getInput(0, 'ssAmt', 'social security amount (k$)')

with col2:
if st.session_state.status == 'married':
getInput(1, 'ssAge', 'social security age', 67)
getInput(1, 'ssAmt', 'social security amount (k$)')

st.write('### Pension')
col1, col2 = st.columns(2, gap='small', vertical_alignment='top')#, border=False)
with col1:
getInput(0, 'pAge', 'pension age', 65)
getInput(0, 'pAmt', 'social security amount (k$)')

with col2:
if st.session_state.status == 'married':
getInput(1, 'pAge', 'pension age', 65)
getInput(1, 'pAmt', 'social security amount (k$)')


4 changes: 4 additions & 0 deletions interface/Introduction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import streamlit as st

st.write('## Owl Retirement Planner')

37 changes: 37 additions & 0 deletions interface/Optimization_Parameters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import streamlit as st
import key as k


st.write('# Optimization Parameters')

col1, col2 = st.columns(2, gap='small', vertical_alignment='top')#, border=False)
with col1:
iname = st.session_state.iname0
k.init('maxX0', 1000)
ret = k.getNum("%s's maximum Roth Conversion ($k)"%iname, 'maxX0')

with col2:
if st.session_state.status == 'married':
iname = st.session_state.iname1
k.init('maxX1', 1000)
ret = k.getNum("%s's maximum Roth Conversion ($k)"%iname, 'maxX1')

k.init('med', True)
ret = k.getToggle('Medicare and IRMAA calculations', 'med')

choices = ['flat', 'smile']
k.init('profile', choices[1])
ret = k.getRadio("Spending profile", choices, 'profile')

choices = ['Net spending', 'Bequest']
k.init('objective', choices[0])
ret = k.getRadio("Maximize", choices, 'objective')

if st.session_state.objective == 'Net spending':
k.init('bequest', 0)
ret = k.getNum("Desire bequest ($k)", 'bequest')

else:
k.init('spending', 0)
ret = k.getNum("Desired annual net spending ($k)", 'spending')

91 changes: 91 additions & 0 deletions interface/Rate_Selection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import streamlit as st
import key as k

FXRATES = {
'conservative': [8, 5, 4, 3],
'realistic': [11, 6, 5, 3],
'historical average': [0, 0, 0, 0],
'user': [0, 0, 0, 0]
}


def update_rates(key):
# print('updating rates', key)
k.push(key)
fxType = st.session_state[key]
rates = FXRATES[fxType]
for j in range(4):
k.store('fxRate'+str(j), rates[j])


st.write('# Rate Selection')

choices1 = ['fixed', 'varying']
k.init('rateType', choices1[0])
ret = k.getRadio('## Rate type', choices1, 'rateType')

if st.session_state.rateType == 'fixed':
choices2 = ['conservative', 'realistic', 'historical average', 'user']
k.init('fixedType', choices2[0])
ret = k.getRadio('Select fixed rates', choices2, 'fixedType', update_rates)

st.write('#### Fixed rate values (%)')
for j in range(4):
rates = FXRATES[ret]
k.init('fxRate'+str(j), rates[j])

ro = ret != 'user'
col1, col2, col3, col4 = st.columns(4, gap='small', vertical_alignment='top')
with col1:
ret = k.getNum('S&P 500', 'fxRate0', ro)

with col2:
ret = k.getNum('Corporate Bonds Baa', 'fxRate1', ro)

with col3:
ret = k.getNum('10-y Treasury Notes', 'fxRate2', ro)

with col4:
ret = k.getNum('Common Assets / Inflation', 'fxRate3', ro)


elif st.session_state.rateType == 'varying':
choices3 = ['historical', 'histochastic', 'stochastic']
k.init('varyingType', choices3[0])
ret = k.getRadio('Select varying rates', choices3, 'varyingType')

else:
st.info('Logic error')

if ((st.session_state.rateType == 'fixed' and 'hist' in st.session_state.fixedType)
or (st.session_state.rateType == 'varying' and 'hist' in st.session_state.varyingType)):
k.init('yfrm', 1922)
k.init('yto', 2023)

col1, col2 = st.columns(2, gap='small', vertical_alignment='top')
with col1:
ret = st.number_input('Starting year', min_value=1922,
max_value=st.session_state['yto'],
value=st.session_state['yfrm'],
on_change=k.push, args=['yfrm'], key='_yfrm')

with col2:
ret = st.number_input('Ending year', max_value=2023,
min_value=st.session_state['yfrm'],
value=st.session_state['yto'],
on_change=k.push, args=['yto'], key='_yto')

st.write('### Other rates')

k.init('divRate', 2)
ret = k.getNum('Dividends return rate (%)', 'divRate')

st.write('### Income taxes')

k.init('gainTx', 15)
ret = k.getNum('Long-term capital gain income tax rate (%)', 'gainTx')

k.init('heirsTx', 30)
ret = k.getNum('Heirs income tax rate (%)', 'heirsTx')


8 changes: 8 additions & 0 deletions interface/Summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import streamlit as st
import key as k

st.write('# Summary')

k.init('summary', '')

st.write(st.session_state.summary)
21 changes: 21 additions & 0 deletions interface/Wages_And_Contributions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import streamlit as st
import pandas as pd
import key as k


# k.dump()
st.write('## Wages and Contributions')

if st.session_state.iname0 == '':
st.info('Basic Information must be filled before loading file.')
else:
timeList = st.file_uploader('Upload contribution file')
if timeList:
df0 = pd.read_excel(timeList, sheet_name=st.session_state['iname0'])
st.write(st.session_state['iname0'])
st.write(df0)
if st.session_state['status'] == 'married':
df1 = pd.read_excel(timeList, sheet_name=st.session_state['iname1'])
st.write(st.session_state['iname1'])
st.write(df1)

Loading

0 comments on commit 6871621

Please sign in to comment.