Monte Carlo VaR
Worst loss you'd expect 95% of the time.
Run thousands of simulated 'next 10 days' for the stock. The 5th-worst outcome out of 100 is your Value-at-Risk — the bad day you should plan to survive. Helps you size positions so a normal bad day doesn't blow up your account.
Run thousands of simulated 'next 10 days'. The 5th-worst outcome out of 100 is your Value-at-Risk — the bad day you should plan to survive. CVaR is the average of the days worse than that. The most rigorous way to translate 'how risky is this position' into a dollar number.
VaR = quantile of simulated horizon returns
Real Monte Carlo VaR bot, running on real Yahoo data when the symbol is available. Drag the params — the bot re-runs instantly.
This is the actual code the bot runs — not a re-explanation, not a simplified version. Whatever ships here is what executes when you press Run All in the workbench. Read it, copy it, fork it, build a better one.
const varBot: BotDef = {
id: "mc-var",
name: "Monte Carlo VaR",
category: "risk",
glyph: "Σ",
tagline: "Worst loss you'd expect 95% of the time.",
formula: "VaR = quantile of simulated horizon returns",
params: [
{ key: "sims", label: "Simulations", kind: "number", default: 4000, min: 1000, max: 20000, step: 500 },
{ key: "horizon", label: "Horizon (days)", kind: "number", default: 10, min: 1, max: 90, step: 1 },
{ key: "confidence", label: "Confidence %", kind: "number", default: 95, min: 90, max: 99, step: 0.5 },
{ key: "notional", label: "Notional $", kind: "number", default: 100000, min: 1000, max: 10000000, step: 1000 },
],
run: (ctx, p): BotResult => {
const sims = Math.round(num(p, "sims", 4000));
const horizon = Math.round(num(p, "horizon", 10));
const conf = num(p, "confidence", 95) / 100;
const notional = num(p, "notional", 100000);
const px = closes(ctx.candles);
const rets = returns(px);
const mean = rets.reduce((a, b) => a + b, 0) / Math.max(1, rets.length);
const sd = Math.sqrt(rets.reduce((a, b) => a + (b - mean) ** 2, 0) / Math.max(1, rets.length - 1));
const norm = makeNorm(31);
const finals: number[] = [];
for (let i = 0; i < sims; i++) {
let r = 1;
for (let d = 0; d < horizon; d++) r *= 1 + (mean + sd * norm());
finals.push(r - 1);
}
finals.sort((a, b) => a - b);
const idx = Math.floor((1 - conf) * sims);
const varRet = finals[idx];
const cvar = finals.slice(0, Math.max(1, idx)).reduce((a, b) => a + b, 0) / Math.max(1, idx);
const expRet = finals.reduce((a, b) => a + b, 0) / sims;
return {
signals: [],
metrics: [
{ key: "var", label: `VaR ${(conf * 100).toFixed(0)}%`, value: fmtMoney(varRet * notional), tone: "bear" },
{ key: "cvar", label: "CVaR (tail avg)", value: fmtMoney(cvar * notional), tone: "bear" },
{ key: "exp", label: `${horizon}d expected`, value: fmtMoney(expRet * notional), tone: expRet > 0 ? "bull" : "bear" },
{ key: "vol", label: "Daily vol", value: `${(sd * 100).toFixed(2)}%`, tone: "info" },
],
summary: `${(conf * 100).toFixed(0)}% confident the ${horizon}-day loss won't exceed ${fmtMoney(varRet * notional)} on ${fmtMoney(notional)} notional.`,
beginner:
"Run thousands of simulated 'next 10 days'. The 5th-worst outcome out of 100 is your Value-at-Risk — the bad day you should plan to survive. CVaR is the average of the days worse than that.",
verdict: {
side: "warn",
text: `Plan for losses up to ${fmtMoney(Math.abs(varRet * notional))} this ${horizon}-day window.`,
confidence: 0.85,
},
};
},
};id— unique key the workbench uses to find the bot.params— the sliders + inputs you see on the cell.run(ctx, p)— the function that gets called with candles + your params and returns the verdict.verdict— the BUY / SELL / HOLD pill at the top of the cell.metrics— the small stat boxes shown in the cell body.
- Copy the whole block above.
- On /quant, click + Import your bot in the bot library.
- Paste, hit save. It hot-loads into your workspace.
- Edit any param defaults or logic to your taste — it's now yours.
- ·Risk budgeting at the portfolio level. Aggregate VaR across positions for a single 95% number.
- ·Margin sizing. Brokers margin you on rough VaR; computing your own gives you an early warning.
- ·Sanity-check stop-losses. If your stop is tighter than 95% VaR, expect to be stopped out frequently by random drift.
- ·Tail-risk underestimation. MC under GBM (which we use) misses fat-tails — real 99% VaR is worse than this simulation suggests.
- ·Stationarity assumption. The bot uses recent realised vol; in regime shifts (2020 March), VaR was wrong by orders of magnitude before adapting.
- ·Path-dependence. VaR ignores how the loss happened — slow drift to -10% vs gap to -10% have very different psychology.
Always WARN — VaR doesn't trade, it sizes risk. The headline number is 'plan for losses up to $X this 10-day window'. CVaR is shown alongside as the average of the bad-tail outcomes; that's what you actually lose when VaR breaches.