LinReg Channel
Linear regression line + std-deviation envelopes.
Draws the best-fit straight line through recent prices, then puts dotted lines a few standard deviations above and below. Touching the top line is overextended — sell. Touching the bottom is washed-out — buy.
Draws the best-fit line through recent prices, then puts dotted lines a few standard deviations above and below. Touching the top is overextended; the bottom is washed out. The trend slope of the line itself is its own signal — positive slope = uptrend, negative = down.
y = β·x + α; bands = y ± k·σ(residuals)
Real LinReg Channel 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 linregBot: BotDef = {
id: "linreg",
name: "LinReg Channel",
category: "stats",
glyph: "λ",
tagline: "Linear regression line + std-deviation envelopes.",
formula: "y = β·x + α; bands = y ± k·σ(residuals)",
params: [
{ key: "period", label: "Period", kind: "number", default: 60, min: 20, max: 200, step: 1 },
{ key: "bands", label: "Std bands", kind: "number", default: 2, min: 1, max: 3, step: 0.1 },
],
run: (ctx, p): BotResult => {
const period = Math.round(num(p, "period", 60));
const k = num(p, "bands", 2);
const px = closes(ctx.candles);
const r = linregChannel(px, period, k);
const signals: Signal[] = [];
const sigArr: (1 | -1 | 0)[] = px.map(() => 0);
for (let i = 1; i < px.length; i++) {
const u = r.upper[i], l = r.lower[i];
if (u == null || l == null) continue;
if (px[i - 1] < (r.lower[i - 1] ?? -Infinity) && px[i] >= l) {
signals.push({ i, kind: "buy", price: px[i] });
sigArr[i] = 1;
} else if (px[i - 1] > (r.upper[i - 1] ?? Infinity) && px[i] <= u) {
signals.push({ i, kind: "sell", price: px[i] });
sigArr[i] = -1;
}
}
const bt = backtestLongOnly(px, sigArr);
const last = px[px.length - 1];
const lm = r.mid[r.mid.length - 1] ?? last;
const lu = r.upper[r.upper.length - 1] ?? last;
const ll = r.lower[r.lower.length - 1] ?? last;
return {
signals,
metrics: [
{ key: "fair", label: "Trend line", value: fmtNum(lm), tone: "info" },
{ key: "upper", label: "Upper", value: fmtNum(lu), tone: "bear" },
{ key: "lower", label: "Lower", value: fmtNum(ll), tone: "bull" },
{ key: "trades", label: "Trades", value: String(bt.trades) },
],
overlay: [
{ values: r.mid, color: "var(--fg-dim)", label: "LinReg" },
{ values: r.upper, color: "var(--bear-dim)", label: "Upper", dashed: true },
{ values: r.lower, color: "var(--bull-dim)", label: "Lower", dashed: true },
],
summary: `Trend ${lm.toFixed(2)}, channel ${ll.toFixed(2)}–${lu.toFixed(2)}.`,
beginner:
"Draws the best-fit line through recent prices, then puts dotted lines a few standard deviations above and below. Touching the top is overextended; the bottom is washed out.",
verdict: {
side: last < ll ? "buy" : last > lu ? "sell" : "hold",
text: last < ll ? "Below lower channel." : last > lu ? "Above upper channel." : "Inside the channel.",
confidence: Math.min(1, Math.abs(last - lm) / (lu - lm || 1)),
},
equity: bt.equity,
};
},
};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.
- ·Visualising trend strength. Slope and channel width together summarise the regime.
- ·Setting stops. Lower channel for long stops, upper channel for short stops.
- ·Combining with momentum. Trade reversions inside the channel; trend-follow when price pierces the band.
- ·Sharp regime changes. The regression refits; the channel snaps to the new trend, briefly leaving recent price action outside the band.
- ·Curved trends (parabolic moves). Linear fits a curved trajectory poorly.
- ·Window too short. <30 bars gives unstable slope estimates.
BUY when price reclaims the lower band from below. SELL when it rejects the upper band. Confidence scales with distance from the mid-line. Slope of the regression line is published in the metrics — sustained positive slope means the trend bot interpretation is the safer one.
How is this different from Bollinger?+
What's the right window?+
Volatility envelopes around a moving average.
Adaptive fair-value tracker. Smooth and self-correcting.
The Turtle trade — buy n-day highs, sell n-day lows.
Buy when fast moving average crosses above slow.
Bet on snap-back when the price wanders too far.
Is this market trending, mean-reverting, or random?