MMavenFinDocs · v4.19
Not authorized
Walkthroughs

Tutorials

Three full workflows that combine multiple endpoints into useful answers. Each one starts from a working bearer token (see Quickstart §1) and ends with shippable data.

Compare Roth conversion strategies

You have a household with $1.8M of pre-tax traditional IRA, a 65-year-old client who'll be RMD-bound in eight years, and a current marginal rate of 12% (low — they just retired). You want to know how much lifetime tax savings each Roth strategy delivers and which beats the no-conversion baseline.

The recipe: run the optimizer three times — once per strategy — and diff the lifetime-savings totals.

const baseUrl = 'https://legacy.mavenfin.tech/isrestapi19/api/v1';
const auth    = { Authorization: 'Bearer ' + token };

async function optimize(strategy, targetBracket) {
  const res = await fetch(`${baseUrl}/households/1/plans/1/roth-optimize`, {
    method: 'POST',
    headers: { ...auth, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      strategy, targetBracket,
      startYear: 2026, endYear: 2034,
    }),
  });
  return res.json();
}

const [thr, fill, dyn] = await Promise.all([
  optimize('thresholdRoth', 22),
  optimize('bracketFill',   22),
  optimize('dynamic',       24),
]);

console.table([
  { strategy: 'thresholdRoth', savings: thr.totalLifetimeTaxSavings  },
  { strategy: 'bracketFill',   savings: fill.totalLifetimeTaxSavings },
  { strategy: 'dynamic',       savings: dyn.totalLifetimeTaxSavings  },
]);
import requests
from concurrent.futures import ThreadPoolExecutor

base = 'https://legacy.mavenfin.tech/isrestapi19/api/v1'
hdr  = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}

def optimize(strategy, target):
    return requests.post(
        f'{base}/households/1/plans/1/roth-optimize',
        json={'strategy': strategy, 'targetBracket': target,
              'startYear': 2026, 'endYear': 2034},
        headers=hdr, timeout=60,
    ).json()

with ThreadPoolExecutor(3) as ex:
    thr, fill, dyn = ex.map(
        lambda a: optimize(*a),
        [('thresholdRoth', 22), ('bracketFill', 22), ('dynamic', 24)],
    )

for name, r in [('threshold', thr), ('bracketFill', fill), ('dynamic', dyn)]:
    print(f'{name:>14}: ${r["totalLifetimeTaxSavings"]:>12,.0f}')

For most retirees we see bracketFill at 22% deliver 60–80% of the absolute savings of dynamic, in a small fraction of the cognitive overhead. The dynamic optimizer wins on edge cases — IRMAA-cliff households, large-pension households, or anyone with material wage income past 65.

Build a longevity-risk chart

Longevity risk is the probability of outliving your portfolio. With Monte Carlo, it's a clean computation: run the simulation, then bucket the terminal portfolio values to draw a probability curve.

The shortcut is to make a single call with a high runs count, request the percentile fan, and chart the response. P10 tells you the catastrophic case; P50 the median; P90 the windfall.

const res = await fetch(
  'https://legacy.mavenfin.tech/isrestapi19/api/v1/households/1/plans/1/montecarlo',
  {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' },
    body: JSON.stringify({ runs: 5000, seed: 7, equityReturn: 0.07, equityVol: 0.18 }),
  },
);
const mc = await res.json();

// Feed straight into Chart.js / D3 / Plotly as the y-axis values
const data = [
  { label: 'P10', y: mc.p10 },
  { label: 'P25', y: mc.p25 },
  { label: 'P50', y: mc.p50 },
  { label: 'P75', y: mc.p75 },
  { label: 'P90', y: mc.p90 },
];
console.log(`Success rate: ${(mc.successRate * 100).toFixed(1)}%`);
import requests, json

mc = requests.post(
    'https://legacy.mavenfin.tech/isrestapi19/api/v1/households/1/plans/1/montecarlo',
    headers={'Authorization': f'Bearer {token}',
             'Content-Type':  'application/json'},
    json={'runs': 5000, 'seed': 7,
          'equityReturn': 0.07, 'equityVol': 0.18},
    timeout=120,
).json()

print(f"success: {mc['successRate']*100:.1f}%")
for k in ('p10','p25','p50','p75','p90'):
    print(f"  {k.upper()}: ${mc[k]:>12,.0f}")
Pro tip. Use the same seed across UI sessions so users see stable numbers between page loads. Bump the seed (or remove it) only when explicitly exploring variability.

Calibrate Monte Carlo to a household

The default 7%/18% equity, 3%/5% bond assumptions are conservative-modern. For households with material international tilt, factor exposure, or alternative allocations, you'll want to override them. The deterministic projection's asset-allocation strategy is your input; the historical realized return is your calibration target.

The recipe: read the plan's effective allocation, look up corresponding mean/vol pairs from your own capital-market-assumptions table, then pass those into Monte Carlo.

import requests

base = 'https://legacy.mavenfin.tech/isrestapi19/api/v1'
hdr  = {'Authorization': f'Bearer {token}'}

# Your house view of capital-market assumptions
CMA = {
    'us_lg_cap':   { 'r': 0.068, 'v': 0.175 },
    'us_sm_cap':   { 'r': 0.078, 'v': 0.235 },
    'intl_dev':    { 'r': 0.071, 'v': 0.195 },
    'em':          { 'r': 0.085, 'v': 0.270 },
    'us_agg_bond': { 'r': 0.034, 'v': 0.055 },
    'cash':        { 'r': 0.020, 'v': 0.010 },
}

# Inspect the plan's effective allocation
inputs = requests.get(f'{base}/households/1/inputs', headers=hdr, timeout=30).json()
# (Pretend) extract the planned equity/bond split from inputs.aas[0].targetWeights
eq_weight   = 0.60
bond_weight = 0.40

# Weighted blend
eq_r = (0.55*CMA['us_lg_cap']['r']
      + 0.15*CMA['us_sm_cap']['r']
      + 0.20*CMA['intl_dev']['r']
      + 0.10*CMA['em']['r'])
eq_v = 0.185  # use a portfolio-vol estimator in production

mc = requests.post(
    f'{base}/households/1/plans/1/montecarlo',
    headers={**hdr, 'Content-Type': 'application/json'},
    json={
        'runs': 2500, 'seed': 1,
        'equityReturn': round(eq_r, 4),
        'equityVol':    round(eq_v, 4),
        'bondReturn':   CMA['us_agg_bond']['r'],
        'bondVol':      CMA['us_agg_bond']['v'],
    },
    timeout=120,
).json()
print(f"calibrated success: {mc['successRate']*100:.1f}%")

For production, the equity-vol blend needs the full asset-asset covariance matrix, not just a marginal-vol average. The simple weighted version above is fine for a first cut and to show users that custom CMAs are being respected.