Triple-Barrier (de Prado)
Profit / stop / time barriers — better labels = better models.
A smarter way to label what happened on a trade. Instead of asking 'was the price up in 20 days?', ask 'did it hit my profit target before it hit my stop, or did neither happen?' Three outcomes, three labels. Better-labelled training data makes for smarter models.
Marcos López de Prado's labelling method, productionised. Instead of asking 'will the stock be up in 20 days?', it asks 'will it hit my +5% profit target before it hits my -5% stop, or will neither happen and I time out?' Three barriers, three labels. Better-shaped training data → smarter downstream models.
Advances in Financial ML, Marcos López de Prado
Real Triple-Barrier (de Prado) 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 tripleBarrier: BotDef = {
id: "ai-triple-barrier",
name: "Triple-Barrier (de Prado)",
category: "ai",
glyph: "⊞",
tagline: "Profit / stop / time barriers — better labels = better models.",
formula: "Advances in Financial ML, Marcos López de Prado",
module: "ai quants/models/triple_barrier/train.py",
params: [
{ key: "horizon", label: "Max horizon (d)", kind: "number", default: 20, min: 5, max: 40, step: 1 },
{ key: "ptSl", label: "Profit/Stop multiplier", kind: "number", default: 1.5, min: 0.5, max: 4, step: 0.1, hint: "× recent vol" },
],
run: async (ctx, p): Promise<BotResult> => {
const horizon = num(p, "horizon", 20);
const ptSl = num(p, "ptSl", 1.5);
const px = closes(ctx.candles);
const trend = trendStrength(px);
const rv = realisedVol(px, 30);
const seed = hashStr(ctx.symbol + "tb" + horizon);
const rand = seedRand(seed);
const pUp = Math.min(0.95, Math.max(0.05, 0.5 + trend * 1.2 + (rand() - 0.5) * 0.1));
const pHitTop = pUp;
const pHitBot = (1 - pUp) * 0.7;
const pTimeOut = 1 - pHitTop - pHitBot;
const label = pHitTop > pHitBot && pHitTop > pTimeOut ? "+1 (TP)" : pHitBot > pTimeOut ? "−1 (SL)" : "0 (TO)";
return withStatus(
{
signals: [],
metrics: [
{ key: "pTop", label: "P(profit)", value: `${(pHitTop * 100).toFixed(0)}%`, tone: "bull" },
{ key: "pBot", label: "P(stop)", value: `${(pHitBot * 100).toFixed(0)}%`, tone: "bear" },
{ key: "pTime", label: "P(time-out)", value: `${(Math.max(0, pTimeOut) * 100).toFixed(0)}%`, tone: "neutral" },
{ key: "label", label: "Most likely label", value: label, tone: label.startsWith("+") ? "bull" : label.startsWith("−") ? "bear" : "neutral" },
{ key: "ptSl", label: "PT/SL × σ", value: ptSl.toFixed(1), tone: "info" },
{ key: "rv", label: "σ used", value: `${(rv * 100).toFixed(0)}%`, tone: "neutral" },
],
summary: `Setting profit target = stop-loss = ${ptSl.toFixed(1)}× ${(rv * 100).toFixed(0)}% vol over ${horizon}d. Most likely outcome: ${label}.`,
beginner:
"De Prado's idea: instead of asking 'will the price be up?', ask 'will it hit my +5% target before -5% stop, or neither?'. Better-labelled training data = smarter models.",
verdict: {
side: pHitTop > pHitBot * 1.4 ? "buy" : pHitBot > pHitTop * 1.4 ? "sell" : "hold",
text: `Most likely outcome: ${label}.`,
confidence: Math.abs(pHitTop - pHitBot),
},
},
"mock",
"no FastAPI endpoint yet",
);
},
};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.
- ·Strategy research. If you're building your own bots, this shows you what de Prado-style labels look like for your asset.
- ·Stop-loss design. The PT/SL multiplier on the card lets you see how the label changes as you tighten or loosen barriers.
- ·Variable-volatility regimes. The σ-scaled barriers adapt automatically.
- ·No FastAPI endpoint yet. This bot is currently mock-only — the TS surrogate uses the same hash-derived seed as the other AI bots. Will be wired to the trained model in the next sprint.
- ·Asymmetric setups. The current implementation uses symmetric profit/stop multipliers; real systems often want asymmetric.
Reports three probabilities — P(profit target hit), P(stop hit), P(time-out). Most-likely label classifies into +1 (TP), -1 (SL), or 0 (TO). BUY when P(profit) > 1.4× P(stop), SELL when reverse, HOLD otherwise.
This bot tries to call the FastAPI service first. When it's up, you get real model output. When it's down, the bot transparently falls back to a deterministic TS surrogate.
cd "ai quants" && uvicorn serve:app --reload --port 8000Why triple-barrier labels instead of fixed-horizon?+
Should I use this directly or feed it into a classifier?+
Will the stock be up or down 20 days from now? GBM ensemble vote.
Predicts the size of the next 20-day move, not just the sign.
Honest confidence interval — not just a point prediction.
All AI quants vote. Tier emerges from agreement, not opinion.