Rebalancing without the tax bill

/v1/portfolio/rebalance computes the trades that restore target weights — and, with tax_aware, the realized gain those sells would trigger plus a contributions-first plan that steers new cash to the underweights instead. Where do the targets come from? An age-based /v1/portfolio/glidepath feeds straight in.

1 · Make the call

A $100,000 four-fund portfolio, 5% drift bands, $10,000 of new cash to invest.

curl -X POST https://api.numeratica.com/v1/portfolio/rebalance \
  -H "Authorization: Bearer nmr_sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "holdings": [
      {"label": "us_stocks",   "value": 45000, "target_weight": 0.40, "cost_basis": 30000},
      {"label": "intl_stocks", "value": 20000, "target_weight": 0.20, "cost_basis": 15000},
      {"label": "bonds",       "value": 25000, "target_weight": 0.30, "cost_basis": 24000},
      {"label": "reits",       "value": 10000, "target_weight": 0.10, "cost_basis": 8000}
    ],
    "new_contribution": 10000,
    "band": 0.05,
    "tax_aware": true
  }'

2 · The response

{
  "result_id": "rebalance_3e03c2cec19f1743",
  "engine_version": "rebalance-1.0.0",
  "result": {
    "total_current": 100000,
    "investable_total": 110000,          /* holdings + the new contribution */
    "trades": [
      { "label": "us_stocks", "current_weight": 0.45, "target_weight": 0.40,
        "drift": 0.05, "trade": -1000, "action": "sell" },
      { "label": "intl_stocks", "drift": 0, "trade": 2000, "action": "buy",
        "contribution": 1818.18 },
      { "label": "bonds", "drift": -0.05, "trade": 8000, "action": "buy",
        "contribution": 7272.73 },
      { "label": "reits", "drift": 0, "trade": 1000, "action": "buy",
        "contribution": 909.09 }
    ],
    "max_drift": 0.05,
    "needs_rebalance": false,            /* nothing outside the 5% band — yet */
    "estimated_realized_gain": 333.33,   /* if the us_stocks sell executes */
    "contributions_first": {
      "contribution_allocated": 10000,
      "max_drift_after": 0.0091,         /* 5% drift → 0.9% with zero selling */
      "fully_rebalanced": false
    }
  },
  "disclaimer": "Calculation engine output for informational purposes only; not financial or investment advice."
}

A full rebalance would sell $1,000 of US stocks and realize a $333 gain. Contributions-first allocates the $10,000 to the underweights instead — max drift falls from 5% to 0.9% with zero selling and zero realized gain. In taxable accounts, that's the default move; sell only what new cash can't fix.

3 · Where the targets come from

A 40-year-old on a linear target-date glide from 80/20 to 40/60 at retirement (65), continuing "through" to 30/70 at 80: /v1/portfolio/glidepath returns the year-by-year path (result_id glidepath_dbd27f8f170dac0d) — this year's row is exactly the target_weight input above. Pair with /v1/capital-market-assumptions to price the blended return/volatility of any point on the path.

4 · Render it

Before/after drift bars — the case for contributions-first in one glance.

Rendered output
<div style="font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#1a2230;max-width:600px">
  <div style="font-size:14px;color:#5b6675;margin-bottom:8px">$100k portfolio + $10k new cash · drift from target, before → after contributions-first</div>
  <div style="display:grid;grid-template-columns:90px 1fr 1fr;gap:6px;font-size:13px;align-items:center">
    <div></div><div style="color:#5b6675;font-size:12px">Before</div><div style="color:#5b6675;font-size:12px">After (no sells)</div>
    <div>US stocks</div>
    <div style="background:#e8edf8;border-radius:4px"><div style="width:100%;background:#b45309;height:14px;border-radius:4px"></div></div>
    <div style="background:#e8edf8;border-radius:4px"><div style="width:18%;background:#16a34a;height:14px;border-radius:4px"></div></div>
    <div>Bonds</div>
    <div style="background:#e8edf8;border-radius:4px"><div style="width:100%;background:#b45309;height:14px;border-radius:4px"></div></div>
    <div style="background:#e8edf8;border-radius:4px"><div style="width:18%;background:#16a34a;height:14px;border-radius:4px"></div></div>
    <div>Intl / REITs</div>
    <div style="background:#e8edf8;border-radius:4px"><div style="width:4%;background:#16a34a;height:14px;border-radius:4px"></div></div>
    <div style="background:#e8edf8;border-radius:4px"><div style="width:4%;background:#16a34a;height:14px;border-radius:4px"></div></div>
  </div>
  <div style="margin-top:12px;display:grid;grid-template-columns:1fr 1fr;gap:10px">
    <div style="border:1px solid #e6e9ef;border-radius:10px;padding:12px">
      <div style="font-size:12px;color:#5b6675">Full rebalance</div>
      <div style="font-size:18px;font-weight:700">$333 realized gain</div>
    </div>
    <div style="border:2px solid #16a34a;border-radius:10px;padding:12px">
      <div style="font-size:12px;color:#16a34a;font-weight:600">Contributions-first</div>
      <div style="font-size:18px;font-weight:700">$0 sold · drift 5% → 0.9%</div>
    </div>
  </div>
  <div style="color:#5b6675;font-size:12px;margin-top:6px">result_id rebalance_3e03c2cec19f1743 · not investment advice</div>
</div>

The taxable-account companions: gain & loss harvesting for what to sell when you must, and /v1/tax/asset-location for which account each asset belongs in to begin with.

← All guides