Documentation
Compound Strategy Lab is an educational financial-systems simulator. This page explains what the product is designed to help you explore, how each piece works, the equations behind it, and — just as importantly — what the model leaves out.
Purpose
Compound Strategy Lab helps people visualize and test financial strategies. It represents income, accounts, investments, debts, expenses, and cash flows as one connected system so a user can change one part and observe how that decision affects everything linked to it.
The product is built for questions such as: What happens to the rest of the strategy if more cash goes toward debt? How does reinvesting income change the long-term result? When does internally generated income begin funding more of the system? How sensitive is the outcome to a different return, yield, fee, expense, or time horizon?
It does not solve a user's financial life, choose investments, or determine the correct strategy. It provides a controlled place to explore assumptions, compare alternatives, and understand compounding before making real-world decisions. Every result remains an estimate produced by the inputs and model described in these docs.
General overview
The site contains two complementary simulation tools:
- Compound Visualizer — an interactive comparison of compound and simple interest, drawn as stacks of $100 bills at real-world scale so the accelerating effect of compounding is easier to grasp.
- Strategy Lab — a visual editor where nodes represent financial positions, liabilities, income, or spending and arrows represent cash flows. The connected strategy is simulated month by month using assumed rates or supported historical data, with optional uncertainty analysis.
Use the Compound Visualizer to isolate the basic mechanism of compounding. Use the Strategy Lab to explore how that mechanism behaves inside a larger financial system where contributions, income, withdrawals, debts, expenses, and reinvestment affect one another.
Both tools run entirely in your browser. The Strategy Lab autosaves its diagram to this browser's local storage; the current product does not upload the strategy or connect to financial accounts.
Compound Visualizer
Compound interest
Interest is added to the balance, and then itself earns interest. With monthly compounding:
balance ×= (1 + rate/12) each monthDaily compounding uses (1 + rate/365)^365/12 per month. Annual compounding credits the full rate on last year's balance once a year, plus half a year's rate on that year's contributions (they were in the account for six months on average).
Simple interest
Interest accrues only on the money you put in — past interest never earns anything, so growth is a straight line:
interest each month = contributions so far × rate/12The money stacks
A US bill is about 0.10922 mm thick, so a strap of one hundred $100 bills ($10,000) stands about 1.09 cm tall:
stack height = (balance / $100) × 0.10922 mm$1M ≈ 1.09 m, $100M ≈ 109 m, $1B ≈ 1.09 km. The comparison objects (coffee cup through Mount Everest) are drawn at their true heights against the same scale.
Strategy Lab basics
The Lab simulates your diagram in monthly steps over the chosen horizon. Two kinds of money movement exist, and the distinction drives everything:
- Growth (price appreciation) compounds inside the node. You can't route a percentage of it — a percentage arrow only ever carries income — but you can still get at it by selling, which is what a fixed-dollar arrow does.
- Income (yield) — dividends, coupons, rent, interest, LP fees and staking rewards — is what a percentage arrow carries.
- A fixed-dollar arrow sells. Set an arrow to carry $X/month instead of a percentage and it draws that much out of the node's balance every month, whatever the node earned. That is what a retirement withdrawal, a debt payment or a bucket top-up actually is, and it is the only kind of arrow that can shrink the position it leaves. Nodes with no income at all can only use this kind.
Routing rules
- Whatever income isn't routed reinvests in place (like a DRIP).
- A node's outgoing arrows can never total more than 100% of its income — the editor caps each arrow at whatever share the node's other arrows have left, so the diagram always shows the shares that actually run. Strategies saved before this was enforced are scaled down proportionally on load, which preserves their simulated behavior exactly.
- Monthly Income and Pension nodes route fresh cash instead of yield. Whatever their arrows don't claim is pay you never allocated: it does not pile up in the node, and it is not counted in the "Contributed" line. An income node is a faucet, not an account — route the remainder to a Treasury if you mean to save it, or to an Expenses node to record it as spending.
- Nodes with no income can still fund others by selling down. A plain token hold, gold, a stock, a treasury, or any node whose yield is set to 0% has no income stream to route a percentage of — so an arrow out of it carries a fixed dollar amount per month, drawn from the node's balance. It's capped at what the node holds: once drawn to zero, the arrow carries nothing.
- A dormant target's share rolls onto the arrows still running. When a node switches off — a debt reaching zero, a spending sink outside its active years — the percentage that was feeding it is redistributed across that source's remaining live arrows, in proportion to their sizes. This is what makes a debt cascade work: as each loan clears, the payment servicing it lands on the next one automatically, which is exactly what the avalanche and snowball methods do by hand.
- Cycles are allowed — that's what makes a flywheel. Money is conserved: routing moves dollars, it never creates them.
- Routing is not free. Every arrow has a routing cost — the swap fees, gas and slippage that hop really costs, as a percent of each dollar moved. It is a leak, not a transfer: the cut simply never arrives. In a flywheel the same dollar can hop several times a year, so leaving every arrow at 0% quietly assumes a frictionless loop, and the report card flags it when you do. Roughly 0.1% for a plain transfer, 0.3%+ when the move involves a swap. Saved strategies made before this existed keep their zero costs so their numbers don't change under them.
- Each node can also take its own external monthly contribution, counted in the "Contributed" line.
Assets, debts and money spent
Not every node is something you own. Each one counts toward the headline figure in one of three ways, and the inspector says which:
- Assets add to it — the default.
- Liabilities (Mortgage, Loan / Credit Card) subtract from it. The balance shown is what you still owe: it grows every month at the interest rate, and arrows pointing into it are payments that shrink it. Nothing can flow out of a debt — drawing from a loan would be borrowing more, which this model doesn't do — so its outgoing arrows are ignored. When the balance reaches zero the node goes dormant: payments stop, and its share of the income re-routes as described above. Because a debt is on the canvas, the results bar relabels itself net worth, and it can legitimately go negative.
- Spending (Expenses / Spending) is excluded. Its balance is the running total of everything routed in, and that money has left the plan for good.
This changes what "Contributed" and "Return" mean, in three ways worth knowing:
- An opening debt counts negatively toward contributed, so a plan that starts with a $300k house and a $240k mortgage begins at $60k of equity and 0% return, rather than showing an instant paper loss.
- Pay routed straight to living costs is not "contributed" — it was never invested. Counting it would make every plan that funds its own expenses look like it lost money.
- Withdrawals you spent are added back into the profit. A retiree who draws $2M out of a portfolio over thirty years produced that money; if it were simply subtracted, every drawdown strategy would read as a failure.
Employer match is deliberately excluded from contributed as well — it is return, not savings. A 50% match is an immediate 50% gain on the money, and showing it that way is the honest presentation.
Switching things on and off
Monthly Income, Pension / Social Security and Expenses / Spending carry an Active from year / Active until year pair (0 means "from the start" and "never stops"). Outside that window the node produces nothing, and arrows pointing into it carry nothing. Everything else on the canvas is assumed active for the whole horizon.
That single control is what draws most retirement shapes: a paycheck that stops at year 12 is Coast FIRE; a benefit that starts at year 25 is Social Security; a spending sink that begins at retirement is a drawdown. Income nodes also take an annual raise, and spending sinks a cost inflation that grows any fixed-dollar arrow feeding them — leaving that at 0 models a retiree whose costs never rise, which is comfortable on the chart and wrong in life.
Because a node window lives on the node, two things that share a node but happen at different times need arrow timing instead. Every arrow has its own Active from year / Active until year pair, and the distinction between the two is worth holding onto:
- A node window says the thing does not exist — no job yet, pension not yet claimed.
- An arrow window says the flow is paused — the portfolio exists all along, you simply do not start drawing from it until 65.
The two behave differently on purpose. When a node goes dormant its share cascades to sibling arrows (the debt-payoff snowball). When an arrow is paused, its share does not cascade — it simply reinvests in place — so switching one arrow off is a clean way to stop a single transfer without disturbing the rest of the diagram. A retirement drawdown that begins at year 30 is an arrow with Active from year 30; a mortgage overpayment that you expect to stop once the loan clears takes care of itself, because the debt node goes dormant on its own.
Taxes and inflation
Two switches sit above the results chart. Both are display-only lenses — they re-skin the numbers you already simulated rather than re-running anything, and both are off by default so the raw projection is what you see first.
- Tax applies one blended rate to the strategy's gains — the growth above what you contributed. Principal is never taxed.
- Inflation restates every figure in today's dollars, discounting future values at the rate you set. A balance that doubles nominally may buy far less than double. Balances are discounted at their own month; Contributed, Spent and withdrawals are discounted dollar-by-dollar at the date each dollar actually moved — not by the end-of-horizon factor, which would treat a deposit made in year 1 as if it were made in year 30. Because both sides of the ledger are dated correctly, the Return figure under this lens is a genuine real return (and is labelled "Real return"): a plan that merely keeps pace with inflation reads roughly 0%.
They are intentionally crude, and the honest limitations matter:
- The tax rate treats every account identically. It cannot tell a Roth from a taxable brokerage, so with the toggle on, the tax-advantaged nodes lose the very advantage they exist to show. If you are comparing wrappers, read the pre-tax numbers.
- It taxes gains as if you liquidated today, ignoring the deferral and step-up that make buy-and-hold tax-efficient, and it does not tax money already withdrawn (only what is still invested). So a drawdown plan is taxed lightly here even though its withdrawals would be taxable in life.
- A single rate hides asset location and the different rates on ordinary income, qualified dividends and long-term gains — exactly the distinctions that make tax planning worth doing.
- Inflation is display-only, so if you already entered real (inflation-adjusted) return rates on your nodes, turning it on double-counts. Enter nominal rates and let the toggle do the deflating, or leave it off.
- Under the inflation lens, cash and bonds can show a negative real return. That is not a bug — it is the number the nominal view hides.
For a genuine tax projection you would need account-level treatment, realization on sale, and brackets — a larger system than a single rate. The toggle is here for a ballpark on the drag, not a tax return.
Contribution caps and the employer match
The Tax-Advantaged Account node wraps an investment in a 401(k), IRA, HSA or 529. Picking an account type fills in that wrapper's annual contribution limit and tax treatment, both of which stay editable. The cap applies to everything arriving in a plan year — the node's own monthly contribution and anything routed down its arrows — and contributions above it are turned away rather than carried over. The node summary reports how much was blocked; blocked money is not counted as contributed, so the return isn't penalised for a deposit that was never accepted.
Tax treatment is recorded but not yet simulated. Every projection on this site is pre-tax, so a Roth and a pre-tax account currently grow identically here. In reality a pre-tax dollar is worth roughly 1 − your retirement rate when it comes out. Read the field as a label on the diagram until tax drag ships.
The 401(k) + Employer Match node adds free money on top of every contribution it receives, up to a monthly cap. Employer matches are normally quoted against salary ("50% of the first 6%"), so convert: on a $75,000 salary that caps the match at 75,000 × 6% × 50% ÷ 12 ≈ $188/month.
Yields don't last forever
Some income-bearing nodes include a yield decay in %/yr: APR(t) = APR₀ · (1 − decay)^t. This is useful anywhere a headline yield is unlikely to last: bank rates change, business margins compress, and high-yield credit spreads mean-revert. It is especially important for pools, lending and staking, where fee APRs can compress as capital arrives and reward schedules dilute. At 15%/yr, a 40% APR is about 21% by year five. The field defaults to 0 so nothing changes silently; use it when a constant income assumption would flatter the plan.
Editor
- Click or drag palette items onto the canvas; drag from a node's edge dots to draw an arrow.
- Select a node or arrow to edit it in the right panel; typed values are clamped to each parameter's legal range.
- Each node's inspector has a collapsible Node summary with a month-by-month scrubber: drag it across the horizon to read that node's balance, monthly inflow/outflow, net change, contributed-so-far, earned ($ and %), realized yield (its effective APR after uptime and decay), and share of net worth — plus its role in the flywheel (how many nodes it feeds and is fed by). It's a snapshot of the last run, so it refreshes when you reselect the node.
- Income-producing asset nodes also show the share of their income that is implicitly reinvested in place. It is simply the income left after outgoing arrows; the same figure appears on mobile node cards.
- Delete/Backspace removes the selection. Cmd/Ctrl+Z undoes, Shift+Cmd+Z or Ctrl+Y redoes. Actions made within ~⅓ of a second coalesce into one undo step.
- Your diagram autosaves to this browser (one slot). "Clear" empties the canvas; templates replace it.
Reading the results
- White line — Expected. The mean outcome: every asset earns its assumed average. Liquidity pools (classic or concentrated) show their result relative to holding the two tokens: fees × your fee uptime, minus their expected impermanent loss. The expected line is a deterministic point estimate; the risk band around it comes from sampling how wrong that IL and uptime estimate could be, so an LP still contributes real spread (see concentrated liquidity). The stats bar shows the median next to the average: outcomes are lopsided, so the two differ, and the median is usually the better number to plan around.
- Violet band — Monte Carlo. The 10th–90th percentile of 200 randomized runs. 1-in-10 outcomes fall below it, 1-in-10 above.
- Dashed violet — median run. Usually sits below the white expected line. That's not a bug: returns are right-skewed, so rare big winners pull the average above the typical outcome.
- Dashed grey — contributed. Money put in: principals plus monthly cash, less any opening debt, and excluding both pay routed straight to living costs and employer match. The gap to the white line is what the strategy earned.
- Colored lines — each node's balance, matching its color in the diagram. Node cards show their final value; arrows show roughly how many dollars per month they carry by the end (pulse dots scale with it).
Flywheel momentum
The momentum strip turns “flywheel” into a measurable property rather than a name. It compares the capital you supplied with income the assets themselves generated over the trailing 12 months, then reports how much of that income acquired more productive assets. Routing costs are deducted. Money sent to spending, debt, or an idle treasury remains part of the strategy, but does not count as productive reinvestment.
- Push means external savings still do nearly all the work; Acquisition means internal income has begun buying assets; Compound means that reinvestment is material; Self-sustaining means internal reinvestment equals or exceeds external productive capital over a rolling year.
- Internally funded is internal reinvestment divided by all new productive capital. Momentum crossover is the first rolling year internal reinvestment matched the external push. Expense coverage compares asset income with spending represented in the diagram.
Templates
The template picker loads a worked example onto the canvas. Each one carries a plain-English brief — what it does, what it takes on faith, and where it breaks — shown in the report-card panel whenever nothing is selected. They are illustrative settings, not recommendations. The numbers are there to be changed; a template you don't edit is just someone else's guess.
Flywheels are reserved for systems where income-producing assets generate cash that acquires more productive assets and can be measured against the owner's continuing contributions. Other useful examples are labeled honestly as portfolios, foundations, debt systems, income systems, retirement plans, goals, or cautionary examples. A compounding cascade can still be effective; it simply is not presented as a closed, self-reinforcing flywheel.
The Rental Acquisition Flywheel uses continuously purchasable real-estate/REIT equity as a proxy. Real buildings are discrete purchases with financing, closing costs, and reserve thresholds; the current engine does not simulate that acquisition event.
One of them, Narrow Range Trap, is deliberately a losing strategy. A ±5% range on ETH, left un-managed, sits in range only about 12% of a five-year horizon, so its advertised 120% APR is collected barely a tenth of the time — nowhere near enough to cover a ~46% impermanent loss. It exists because a gallery where every example wins teaches the wrong lesson.
| Template | Type | Horizon | What it does | Main risk |
|---|
Node reference
Parameters, default equations, and per-node caveats. Generated from the same definitions the simulator runs, so this list can't drift out of date.
Advanced crypto & DeFi mechanics
These reference notes cover the optional cryptocurrency nodes. They are kept separate from the core Lab guide because the same routing, compounding, risk, and reporting rules apply to traditional-finance strategies without them.
Classic AMM math (Uniswap v2 style)
A 50/50 constant-product pool holds two tokens, A and B. As prices move, arbitrage keeps rebalancing the pool, so the position's value tracks the geometric mean of the two token price factors:
position value ×= √(fA · fB) each monthwhere fA, fB are the tokens' monthly price factors. Trading fees and incentives arrive separately as routable income (the LP APR).
Impermanent loss
Because the pool keeps selling the outperformer, an LP position is worth less than simply holding the two tokens whenever their price ratio moves. With r = the change in the A/B price ratio:
IL = 2·√r / (1 + r) − 1| Price ratio change | Impermanent loss vs HODL |
|---|
IL is symmetric in direction — a ratio of 2× and ½× lose the same amount — and it's "impermanent" only if the ratio returns to where you entered.
Volatility drag
Even with no net price trend, volatility alone bleeds an AMM position, because √(fA·fB) is concave. What matters is the volatility of the A/B ratio, which depends on how much the two tokens move together (their correlation ρ). The size of that bleed over a year is roughly:
drag ≈ (σA² + σB² − 2ρ·σA·σB) / 8 per yearAn ETH/stablecoin pool at 70% ETH volatility loses about 6.1%/yr to this — the fee APR has to clear that hurdle before the position beats holding. Correlation is why volatile/volatile pairs can still be gentle: ETH and BTC each swing hard, but at ρ ≈ +0.8 their ratio barely moves, so an ETH/BTC pool suffers a fraction of the IL that the individual volatilities suggest. This is the intuition behind the impermanent-loss figure you set on the node; when a new pool seeds its starting estimate, this is the calculation it uses, with ρ and the volatilities auto-filled from the last 90 days of real data.
Concentrated liquidity (Uniswap v3 style)
Instead of spreading liquidity over every price, you pack it into a range around your entry price. You set the range as absolute min/max prices (exactly as a pool UI shows them) or as a percent below/above the current price. The range can be asymmetric, so it's described by two bounds relative to the entry price P₀:
a = √(P_min / P₀), b = √(P_max / P₀)A full-range position is the limit a → 0, b → ∞; a symmetric geometric range is b = 1/a.
Capital efficiency
Inside the range, the same dollars provide more liquidity and earn proportionally more fees than a full-range position (Uniswap v3 whitepaper):
efficiency E = 2 / (2 − a − 1/b)| Range width | Fee efficiency vs full range |
|---|
The catch: impermanent loss is amplified by roughly the same factor, and outside the range you earn no fees at all.
Concentration multiplies your edge. Fees and impermanent-loss drag are both amplified by the same factor E, so a fair comparison must scale fees with concentration: if the full-range pool out-earns the volatility drag, a tighter range multiplies the profit — if it doesn't, it multiplies the loss (plus range-management work). The Lab asks for the total in-range APR of your actual position, as your pool dashboard shows it — so when you compare a concentrated range against a classic pool, remember the same pool's concentrated APR is roughly E× its full-range APR, and enter each number accordingly. Typing the same APR into both compares two different pools and makes concentration look unconditionally bad.
Position value and impermanent loss
With r = the A/B ratio change since entry, the position's value in token-B terms, relative to entry, follows:
in range: V(r) = (2√r − a − r/b) / (2 − a − 1/b)
above range: V = (b − a) / (2 − a − 1/b) (100% token B)
below range: V = r·(1/a − 1/b) / (2 − a − 1/b) (100% token A)Impermanent loss is that value divided by what the tokens you actually deposited would be worth at the same price — not by a presumed 50/50 hold. The deposit's own hold factor is:
H(r) = (r·(1 − 1/b) + (1 − a)) / (2 − a − 1/b)
IL = 1 − V(r) / H(r)A concentrated deposit is generally not 50/50. It is half-and-half only when the range is geometrically centred (b = 1/a, i.e. √(P_min·P_max) = P₀), where H collapses to (1+r)/2 and this reduces to the classic formula. A range entered as ±30% is arithmetically symmetric, not geometrically, and actually deposits about 43% of the volatile token — dividing by (1+r)/2 there, as many simplified calculators do, mis-states the loss. See the verification section for how this is checked against external references.
Range management
- Re-center monthly — an active LP moves the range to follow the price. Fees keep flowing, but every move locks in that month's impermanent loss permanently. Under high volatility this realized-IL bleed can overwhelm even large fee APRs.
- Set and forget — the range is fixed at entry. If the price drifts out, fees stop and the position sits 100% in one token until the price comes back. The inspector estimates when the assumed drift walks out of your range.
Exact-mechanics cross-check
The Lab's closed-form position curves are cross-validated in your browser, on every load, against a contract-exact port of Uniswap's whitepaper accounting — liquidity minting, token composition, and position value, as implemented by the uniswappy reference library (DeFiPy). The two agree to machine precision across symmetric, asymmetric, and razor-thin ranges; the same exact math powers the "exact split at entry" readout in the LP inspector.
How the expected line handles this
The pool projection is net of holding: token prices are held flat, so "holding" stays at your deposit, and the position ends at (1 − your expected impermanent loss) of that hold, plus fees earned at your APR × fee uptime. Impermanent loss and fee uptime are inputs you own (see below), so the expected line is a clean, deterministic point estimate rather than an opaque simulation. Range re-centering is charged as a real per-move cost, and for a trigger range the number of re-centers per year is derived from the pair's volatility and range width (a driftless walk crosses a log-distance d in expected time d²/σ², so crossings scale as σ²/d²).
The risk band is where uncertainty enters: each of the 200 Monte Carlo runs draws its own realized impermanent loss and fee uptime from a mean-preserving spread around your estimates, so the band brackets the expected line instead of collapsing onto it — and if you assert zero IL and perfect uptime, there is genuinely nothing left uncertain, the band collapses, and the report card says so rather than awarding a pass.
Monte Carlo
The band reruns your whole strategy 200 times with random monthly price shocks (geometric Brownian motion). Each asset's monthly factor is:
factor = exp( (μ − σ²/2)/12 + σ·√(1/12)·z ), z ~ N(0,1)where μ = ln(1 + growth) and σ is the node's volatility. This keeps the average yearly growth equal to your input while volatility spreads the outcomes. The runs use fixed random seeds, so the band is stable — it only changes when your inputs do.
- The shaded area spans the 10th to 90th percentile of total net worth each month.
- Yield percentages (APRs, coupons, dividends) are held constant — only prices are shocked.
- Important: every node's shocks are drawn independently. If two nodes both hold ETH, the model lets them move separately, which real markets don't — see caveats.
Backtesting & decision tools
Whole-strategy historical replay
Choose Historical backtest when you want to see how the strategy would have behaved through a real, continuous stretch of market history. The Lab starts at the beginning of the selected window and moves forward one month at a time until its last complete month. A five-year window is a five-year replay, not a five-year sample projected out to your usual horizon.
Every eligible asset follows the same calendar. If the window ends in July 2026, a five-year replay begins in July 2021: its first simulated month uses the real July-to-August 2021 return, then August-to-September 2021, and so on through July 2026. When stocks, gold, ETH, or BTC moved together in a particular historical month, the replay shows that same relationship. The risk band resamples blocks of those same shared months, preserving the market regimes and cross-asset correlation that actually occurred.
ETH and BTC use bundled CoinGecko history. Stock Index uses VTI or SPY, International Equity uses VXUS, and Gold uses GLD monthly adjusted-close total returns generated from Alpha Vantage. Adjusted close already includes splits and reinvested distributions, so the Lab ignores those nodes' entered growth and volatility in historical mode and does not add a second dividend.
Some nodes intentionally keep their assumptions. Dividend stocks, bonds, REITs, savings, and other income-routing nodes continue using the values you enter. Their income needs to travel through the arrows, while an adjusted-close return already includes distributions; combining both would count the same income twice. The inspector tells you whether each node is replaying history or using its assumption.
A historical replay is evidence, not a forecast. The available window is limited to dates shared by every historical asset on the canvas. Funds start at their real inception dates; the Lab does not invent earlier proxy history or fill missing months. If you want a 20-year forward-looking range, switch back to Assumed rates. A future feature may use historical periods as scenarios for longer projections, but that would be labeled a projection rather than presented as a literal backtest.
Fee-uptime backtest
The fee-uptime measurement lives inside the Edit Pool Simulator. It replays real bundled prices — CoinGecko daily closes for ETH and BTC, USDC pegged at $1 — against the exact range you set, then reports the share of days the price actually sat inside it. To reduce entry-timing luck it uses a rolling-anchor method: a fresh range of your width is planted on every day of the selected 30 / 90 / 180 / 365-day window, each is followed for a month, and the results are averaged. It is a useful input for your pool estimate, but it describes one recent market regime rather than your whole holding period; fee uptime therefore defaults to a figure you can set yourself.
Scenarios & comparison
Save stores the current diagram as a named scenario; Compare runs any set of them side by side — overlaid expected and 10th-percentile lines plus a metrics table (contributed, expected, p10/p90, safe-layer share, house-money year, underwater verdict). The HODL twin option auto-builds a benchmark where every LP is replaced by simply holding its two tokens 50/50 — same contributions, no fees, no impermanent loss. Export/Import moves strategies as JSON files.
The report card
When nothing is selected, the right panel grades the current strategy. Every test grades something the projection actually simulates — a check that reads a parameter the engine ignores would be certifying a result it never measured, so where a test can't apply it abstains (—) and says why instead of passing by default:
- Edge test — does each pool's fee income clear its impermanent loss? It compares the APR you actually collect (fees × uptime) against the APR needed to cover your expected IL over the horizon.
- Underwater test — is the 10th-percentile outcome still above total contributions? Abstains when nothing in the strategy models uncertainty, since the 10th percentile would then equal the expected line and the test would pass by construction.
- House-money milestone — when do the safe layers (treasury, savings, lending), even at their 10th percentile, cover everything you put in?
- Robustness — is the strategy still profitable at half the fees, and under rougher conditions? The rough sweep moves what the projection actually reads: doubled impermanent loss and 0.6× fee uptime on every pool.
- Drawdown drill — the typical and 1-in-10 worst peak-to-trough falls across the Monte Carlo runs, and how long recovery took. Strategies get abandoned mid-drawdown, not at the horizon. Abstains when every run is identical, rather than reporting a −0% worst case that is an artifact of the model.
- Routing modeled as free — flags a diagram whose arrows all cost 0%, since a frictionless loop is the flywheel's most flattering silent assumption.
Failing and abstaining rows carry a concrete next step. When the strategy contains a pool, the card says up front that token prices are held flat — so these grades are about fees versus impermanent loss, not about what happens if ETH halves.
Is the impermanent-loss math right?
Impermanent loss is defined here the standard way: the position's value divided by what the tokens you actually deposited would be worth, at the same price. It is checked four ways.
- Against the whitepaper, symbolically. Both the position-value factor and the hold factor are derived from the Uniswap v3 amounts
x = L(1/√P − 1/√pb),y = L(√P − √pa)and match the closed forms the code uses exactly. - Against contract-exact liquidity math, numerically. Build the position, price it at the exit price, divide by a plain hold — 40 range/move combinations agree to 1 part in 1013.
- Against the published amplification factor. For a geometric range [P/n, P·n], concentrated impermanent loss is the classic v2 loss multiplied by exactly
√n / (√n − 1), independent of how far the price moved — the factor quoted in the standard v3 analyses. A ±100% range (n = 2) amplifies IL 3.414×. - Against the v2 limit. Widen the range to (0, ∞) and the formula collapses algebraically onto
1 − 2√k/(1+k), which matches defipy/uniswappy'scalc_ilossbit-for-bit.
One subtlety worth knowing, because it is easy to get wrong: a concentrated deposit is not 50/50. It is only half-and-half when the range is geometrically centred, √(pa·pb) = P. A range entered as ±30% is arithmetically symmetric, not geometrically, and actually deposits about 43% of the volatile token — so dividing by (1+k)/2 to get the hold value, as many simplified derivations do, misprices it. Where this tool disagrees with another calculator, that assumption is the first thing to check; the second is the benchmark, since "IL" is variously quoted against a hold of the deposit, a 50/50 hold, or a v2 position.
References used to define and verify the impermanent-loss math:
- defipy-devs/uniswappy — reference Uniswap v2/v3 library (DeFiPy). Our v2 loss matches its
UniswapImpLoss.calc_ilossbit-for-bit, and the v3 position/liquidity mechanics are cross-validated against its accounting on every page load. Worked tutorials: v2 IL, v3 IL. - Jiahua Xu et al., “Impermanent Loss in Uniswap v3” (arXiv:2111.09192) — closed-form derivation of the concentrated-liquidity loss and its amplification over the v2 case.
- Peteris Erins (Auditless), “Impermanent Loss in Uniswap V3” — source of the √n/(√n−1) amplification factor we reproduce for a geometric range [P/n, P·n].
- Uniswap v3 whitepaper / book — the liquidity and token-amount identities
x = L(1/√P − 1/√pb),y = L(√P − √pa)our value and hold factors are derived from.
How a liquidity pool is modeled
Both pool types — classic (v2) and concentrated (v3) — run through one projection that answers a single honest question: did providing liquidity beat just holding the two tokens? Token prices are held flat, so "holding" stays at your deposit, and the position ends at (1 − your expected impermanent loss) of that hold, plus fees earned at your APR scaled by a fee uptime — the share of the horizon the position is in range and collecting (a full-range classic pool is always in range, so 100%). There is no separate "simple" and "advanced" toggle; the two inputs that used to distinguish them — impermanent loss and fee uptime — are just parameters you set.
- Impermanent loss is yours. Type it in, or compute it with the built-in IL calculator: give it each token's price at deposit and at exit and it returns the loss for exactly that move, from the exact v2/v3 formulas, measured against your range. Four prices is all IL depends on — only the ratio between the tokens matters, so the dollar levels cancel (both tokens doubling is no loss). A brand-new pool is seeded with an estimate from its pair's 90-day volatility, so a fresh node never starts out claiming zero downside — but nothing recomputes it behind you afterwards.
- Fee uptime is yours too, and defaults to a figure you set. A new pool is seeded with the share of recent real history the exact range actually held; you adjust from there. You can switch the source to the live backtest window, but it measures a 30-day regime, so over a multi-year horizon it describes one market rather than your whole holding period.
- The risk band carries the uncertainty. The expected line is a deterministic point estimate, but each of the 200 Monte Carlo runs draws its own realized IL and uptime from a mean-preserving spread around your figures — so the band brackets the estimate instead of collapsing onto it. Assert zero IL and perfect uptime and there is nothing left uncertain: the band collapses and the report card says so rather than awarding a pass.
- Range management is a cost, not a toggle. A concentrated position picks one of three styles — re-center monthly, re-center at trigger, or set and forget — each with a configurable cost per re-center. Re-centering realizes impermanent loss rather than avoiding it; what it buys you is fee uptime. For a trigger range the expected number of re-centers per year is derived from the pair's volatility and range width.
Every one of these settings lives in the Edit Pool Simulator panel; the node inspector shows them read-only, so what the node is simulating is always visible without there being two places to change it. A concentrated position is set up like a real pool position, modeled after defi-lab's Uniswap v3 simulator: min/max bounds as actual prices (or a percent of the current price) snapped to the pool's tick grid, an investment amount, and a fee tier. The panel is a full-screen dashboard — the asset-value-vs-price payoff curve with draggable range handles (against unbounded v2 and HODL 50/50), the token breakdown across prices, an impermanent-loss chart versus a selectable benchmark, and the fee-uptime backtest. Fee income can be estimated from pool stats (your share of daily fees is investment ÷ TVL, times volume × fee tier) or typed as a manual APR; either way it feeds the node's APR so the diagram and the panel always agree.
Caveats & limitations
Read this list before trusting any number on the site.
The verification page shows the browser-based calculation checks, with each test's executable assertion and runtime result. Schema, template, saved-data, and live-data safeguards still run in the background but are excluded because they do not validate a formula; the page is evidence that the implemented mechanics match its mathematical checks, not a promise that any projection will come true.
- The projection is pre-tax; the tax toggle is a blunt lens, not a tax engine. Dividends, coupons, LP rewards, staking income and lending interest are all typically taxable in the year received, even when reinvested — over decades this is often the single largest error in any projection. The tax toggle gives a ballpark by haircutting gains at one blended rate, but it cannot distinguish account types, realization timing, or income kinds, so it is a sanity check rather than a calculation.
- Trading costs are optional inputs, not automatic. Every arrow has a routing cost and every re-center has a cost, so gas/swap friction can be modeled — but both default such that older diagrams stay free, and slippage and bid/ask spreads beyond the numbers you enter are not modeled.
- Rates are constant. Real LP APRs decay as pools attract capital; interest rates, dividend yields, and staking yields all move. A 24% APR held for 15 years is an assumption, not a forecast.
- No correlation between nodes in Assumed-rates mode. Within an LP node the two tokens are correlated via the ρ input, but each node's shocks are still independent of every other node's, so GBM risk bands are likely narrower than the truth when the same asset appears in several nodes. Historical backtest mode fixes this for ETH/BTC — bootstrapped blocks keep their real correlation.
- Backtest fees are constant; real fees aren't. A backtest replays real prices but holds your APR fixed. In reality trading-fee APRs spike exactly when volatility spikes (volume explodes in crashes and rallies) — the same weeks impermanent loss does its damage. So a constant APR misweights the fee-vs-IL race in the most decisive periods, likely understating fees in chaotic stretches and overstating them in dead ones. There is no equally free daily pool-volume history to fix this, so read backtest LP results as "right prices, approximate fees."
- Backtests inherit survivorship bias. The bundled history covers two of the best-performing assets ever recorded. A strategy looking great on replay says as much about ETH's decade as about the strategy — always compare against the HODL twin.
- Impermanent loss and fee uptime are your estimates. The projection takes them as inputs rather than deriving them from a price-path model, so the pool result is only as good as those two numbers. The fee-uptime backtest measures them from one recent 30-day window; a new pool's IL is seeded from 90-day volatility. Both are starting points you are meant to overwrite with your own view.
- Debt is serviced, never taken on. Mortgages and loans accrue interest and are paid down, and a debt lowers net worth — but nothing on the canvas can borrow. There is no cash-out refinance, no margin, no HELOC, and no liquidation. The lending node only supplies. Real leveraged flywheels borrow against collateral, and that is where both the extra yield and the blow-up risk live.
- The simulation is nominal; the inflation toggle only re-skins the display. Every figure is computed in nominal dollars — a balance that doubles over 25 years has not doubled in what it can buy, and at 3% inflation a dollar loses about half its value over that span. The inflation toggle discounts the displayed numbers into today's dollars, but it does not change the underlying rates: if you entered real returns on your nodes, turning it on double-counts. This matters most for cash and bonds, whose whole apparent safety is nominal, and it is why the Waiting for a Dip template understates its own cost with the toggle off.
- Contribution caps are enforced; the tax benefit is not. A tax-advantaged wrapper blocks contributions above its annual limit, but a Roth, a pre-tax 401(k) and a taxable account all grow identically here. Comparing wrappers on this page compares their limits, not their after-tax outcomes.
- Monthly resolution. Prices move once per month in the model. Intra-month crashes, liquidation wicks, and depeg events don't exist here.
- Lognormal returns. No fat tails, jumps, or black swans. Real crypto drawdowns are worse than a lognormal model suggests.
- Your work lives in this browser. One autosave slot in local storage; clearing site data erases it.
- Education only. Nothing here is financial advice.
Glossary
- APR
- Annual percentage rate — a yearly rate quoted without compounding. 12% APR paid monthly is 1% per month.
- APY
- Annual percentage yield — the yearly rate after compounding. 12% APR compounded monthly ≈ 12.68% APY.
- AMM
- Automated market maker — a smart contract that prices trades from a formula (like constant-product x·y=k) instead of an order book.
- Compounding
- Earning returns on past returns. The engine behind every curve on this site.
- Concentrated liquidity
- Providing AMM liquidity only within a chosen price range (Uniswap v3) — more fees per dollar in range, none outside, amplified impermanent loss.
- DCA
- Dollar-cost averaging — investing a fixed amount on a schedule regardless of price.
- Dividend yield
- Yearly cash dividends as a percentage of the position's value.
- Drift
- The average direction prices trend over time, separate from the random wiggle (volatility) around it.
- GBM
- Geometric Brownian motion — the standard "random walk with drift" model of prices used by the Monte Carlo runs.
- Impermanent loss (IL)
- How much less an AMM liquidity position is worth compared to simply holding its tokens, caused by the pool rebalancing as the price ratio moves.
- Lending / supply APR
- Interest earned for depositing assets into a money market where others borrow them.
- Liquidity pool (LP)
- A pot of two tokens that traders swap against; providers own a share and earn the trading fees.
- Monte Carlo
- Answering "what's the range of outcomes?" by simulating many randomized futures and reading percentiles off the results.
- Percentile
- The 10th percentile is the value that 10% of outcomes fall below. The band spans the 10th–90th.
- Principal
- The money you start a position with, before any growth.
- Staking
- Locking a token (e.g. ETH) to help secure its network in exchange for protocol rewards.
- Treasury
- In this Lab: a sink node that accumulates whatever flows into it without investing it.
- Volatility (σ)
- The standard deviation of yearly returns — how wildly the price swings around its average path. Stocks ≈ 15%, ETH ≈ 60–90%.