Savings Calculator ð°
inputs âïļ
Code
viewof duration_in = Inputs.range([0,15],{step:1,value:5,label:'Duration (Yrs)'})
viewof interest_rate = Inputs.range([0,2],{step:0.5,value:2,label:'Interest Rate (%)'})
viewof one_off_payment_in = Inputs.range([0,10000],{step:100,value:0,label:'One off Payment'})
viewof annual_payment_in = Inputs.range([0,3000],{step:100,value:1000,label:'Annual Payment'})
workings ð
Code
width = 600
input_cursors = [{duration_in,interest_rate_in:interest_rate/100,one_off_payment_in,annual_payment_in}];
viewof grid = embed(
calcuvizspec({
models: [savings],
input_cursors,
mark: {type: 'text', tooltip: false, size:16, fontWeight: 'bold'},
encodings: {
x: {name: 'year_in', type: 'nominal', domain: _.range(0,duration_in+0+.1)},
text: {name: 'value', type: 'quantitative', format:',.2f'},
y: {name: 'formula', type:'nominal', domain: savings_formulae_not_inputs},
color: {name: 'formula', type:'nominal', domain: savings_formulae_not_inputs, legend: false},
},
width, height:150,
spec_post_process: spec => {
spec.encoding.x.axis = {labelFontSize:20, titleFontSize:20, labelAngle:0, orient:'top'};
spec.encoding.y.axis = {labelFontSize:20, titleFontSize:20 };
return spec
}
}), {renderer: 'svg'})
Code
viewof bars = embed(
calcuvizspec({
models: [savings],
input_cursors,
mark: 'bar',
encodings: {
x: {name: 'year_in', type: 'nominal', domain: _.range(0,duration_in+0+.1)},
y: {name: 'value', type: 'quantitative', independent:true},
row: {name: 'formula', type:'nominal', domain: savings_formulae_not_inputs},
color: {name: 'formula', type:'nominal', domain: savings_formulae_not_inputs,legend:false},
},
width, height:40,
spec_post_process: spec => {
spec.encoding.x.axis = {labelFontSize:20, titleFontSize:20, labelAngle:0, orient:'top'};
spec.encoding.y.axis = {title:null, labelFontSize:20};
spec.encoding.row.header.labelFontSize=14;
return spec
}
}), {renderer: 'svg'})
Code
viewof lines = embed(
calcuvizspec({
models: [savings],
input_cursors,
mark: {type: 'line', point: true },
encodings: {
x: {name: 'year_in', type: 'nominal', domain: _.range(0,duration_in+0+.1)},
y: {name: 'value', type: 'quantitative', independent:true},
row: {name: 'formula', type:'nominal', domain: savings_formulae_not_inputs},
color: {name: 'formula', type:'nominal', domain: savings_formulae_not_inputs, legend: false},
},
width, height:40,
spec_post_process: spec => {
spec.encoding.x.axis = {labelFontSize:20, titleFontSize:20, labelAngle:0, orient:'top'};
spec.encoding.y.axis = {title:null, labelFontSize:20};
spec.encoding.row.header.labelFontSize=14;
return spec
}
}), {renderer: 'svg'})
calculang formulae ðŠ behind the workings
Code
code_viewer = async (entrypoint, formula) => {
const cul_fetch = await fetch(`https://calcy-quarty-vizys-online.pages.dev/models/savings/savings${1 ? '-nomemo_esm' : '_esm'}/cul_scope_${0}.${code_opt_fv.indexOf('source') != -1 ? 'cul.js' : 'mjs'}`)
const cul = await cul_fetch.text()
return md`
~~~js
${
formulae_objs.filter(f => f.name == formula_select).map(f => cul.split('\n').filter((d,i) => i >= f.loc.start.line-1 && i < f.loc.end.line).join('\n').slice(13)).join('\n\n')
}
~~~
`
}
code_viewer('',formula_select)
ð§ and the values used: behind the formulae ð§
Code
appendix
Code
formulae_objs = Object.values(savings_introspection_nomemo.cul_functions).filter(d => d.reason == 'definition' && savings_inputs.indexOf(d.name+'_in') == -1)
viewof code_opt_fv = Inputs.select(["ð calculang source ðŽ", "âĻ calculang output âĻ for ðĨïļ"], {label: "ð§ via ðēs", value: "ð calculang source ðŽ"})
viewof formula_select = Inputs.select(/*Object.entries(introspection_nomemo.cul_functions).filter(([a,b]) => b.reason == 'definition' && b.cul_source_scope_id==0).map(([a,b]) => b.name)*/savings_formulae_not_inputs, {label: "formula"})
Code
{grid.addEventListener('mousemove', (event, item) => {
viewof formula_select.value = item.datum.formula;
viewof year_in.value = item.datum.year_in;
viewof year_in.dispatchEvent(new CustomEvent("input"))
viewof formula_select.dispatchEvent(new CustomEvent("input"))
})
bars.addEventListener('mousemove', (event, item) => {
viewof formula_select.value = item.datum.formula;
viewof year_in.value = item.datum.year_in;
viewof year_in.dispatchEvent(new CustomEvent("input"))
viewof formula_select.dispatchEvent(new CustomEvent("input"))
})
lines.addEventListener('mousemove', (event, item) => {
viewof formula_select.value = item.datum.formula;
viewof year_in.value = item.datum.year_in;
viewof year_in.dispatchEvent(new CustomEvent("input"))
viewof formula_select.dispatchEvent(new CustomEvent("input"))
})
}
Code
Code
viewof year_in = Inputs.input(5)//range([-1,7],{step:1,value:5,label:'year_in'})
nodes = savings_fondued.__tracer.nodes().filter(d => d.type == 'function' && d.name != 'get')
nodes2 = savings_fondued.__tracer.nodes().filter(d => (d.type == 'function' || d.type == 'callsite') && d.name != 'get') // for mapping to callsites
invocs = { return savings_fondued.__tracer.logDelta(savings_fondued.__tracer.trackLogs({ ids: nodes.map(d => d.id)}), 200000 ) ; year_in }
invocs2 = { return savings_fondued.__tracer.logDelta(savings_fondued.__tracer.trackLogs({ ids: nodes2.map(d => d.id)}), 200000 ) ; year_in }
// https://observablehq.com/d/8a3610a164139cef
// lots of hardcodes, replaced with year_in
relationships = [...invocs.filter(d => d.parents).map(d => ({d, parent:invocs.filter(e => e.invocationId==d.parents[0].invocationId)[0]})),
...invocs.filter(d => !d.parents).map(d => ({d, parent:'no parent'}))]
relationships2 = relationships.map(d => ({...d.d, year_in: d.d.arguments[0].value.ownProperties.year_in ? d.d.arguments[0].value.ownProperties.year_in.value : undefined, parent_formula: d.parent.formula, parent_year_in:d.parent.arguments ? d.parent.arguments[0].value.ownProperties.year_in.value : undefined, parent_value: d.parent != 'no parent' ? d.parent.returnValue.value : undefined, parent:d.parent}))
relationships3 = _.uniqBy(relationships2, d => d.parent_formula+d.parent_year_in+d.formula+d.arguments)
WORKINGS_NEW3 = relationships3.filter(d => d.parent != 'no parent').filter(d => d.parent.type='callsite').filter(d => d.parent.formula == formula_select && d.parent.arguments[0].value.ownProperties.year_in.value == year_in)
w3 = WORKINGS_NEW3.map(d => d.returnValue.value)
Code
savings = require('https://calcy-quarty-vizys-online.pages.dev/models/savings/savings.js')
savings_fondued = require('https://calcy-quarty-vizys-online.pages.dev/models/savings/savings-nomemo_esm/dist/cul_scope_0-babeled-fondued.bundle.js')
savings_introspection_nomemo_fetch = await fetch('https://calcy-quarty-vizys-online.pages.dev/models/savings/savings-nomemo.introspection.json')
savings_introspection_nomemo = savings_introspection_nomemo_fetch.json({typed:true})
savings_formulae = Object.values(savings_introspection_nomemo.cul_functions).filter(d => d.reason == 'definition').map(d => d.name)
//savings_inputs = Object.values(savings_introspection_nomemo.cul_functions).filter(d => d.reason == 'input definition').map(d => d.name).sort()
savings_inputs = Object.values(savings_introspection_nomemo.cul_functions).filter(d => d.reason == 'input definition').map(d => d.name).sort()
savings_formulae_not_inputs = Object.values(savings_introspection_nomemo.cul_functions).filter(d => d.reason == 'definition' && savings_inputs.indexOf(d.name+'_in') == -1).map(d => d.name)