feature
Moving Average
Public library indicator Moving Average (ma). Same source as the Chartnaut-owned row in the live indicator table.
What it plots
A single overlay line on price: SMA, EMA, WMA, or RMA of close over period.
Key decisions
Piece | Why it matters |
|---|---|
| Draws on the price pane. |
| One script exposes four MA formulas without separate indicators. |
| Rolling close history; seed EMA/RMA from an SMA when the buffer first fills. |
Early | Avoid plotting half-warmed averages. |
Full script
meta({
shortName: "MA",
kind: "overlay",
dataSource: "candles",
category: "custom",
});
dialog({
title: "Moving Average Configuration",
description: "Configure Moving Average settings",
width: 400,
height: 350,
resizable: false,
showEnabledCheckbox: true,
resetToDefaults: {
enabled: true,
confirmationMessage: "Are you sure you want to reset all Moving Average settings to their default values?",
},
});
const period = input.number({
id: "period",
label: "Period",
default: 20,
min: 1,
step: 1,
});
const maType = input.select({
id: "maType",
label: "Type",
default: "SMA",
options: [
{ value: "SMA", label: "SMA" },
{ value: "EMA", label: "EMA" },
{ value: "WMA", label: "WMA" },
{ value: "RMA", label: "RMA" },
],
});
const lineColor = input.color({
id: "lineColor",
label: "Line Color",
default: "#2962FF",
allowAlpha: true,
});
const lineStyle = input.lineStyle({
id: "lineStyle",
label: "Line Style",
default: LineStyle.Solid,
});
const labelsEnabled = input.boolean({
id: "labelsEnabled",
label: "Show Labels",
default: true,
});
layout(
tab({ id: "general", title: "General" },
section({ id: "parameters", title: "Parameters" }, period, maType),
section({ id: "display", title: "Display Options" }, labelsEnabled),
),
tab({ id: "appearance", title: "Appearance" },
section({ id: "line", title: "Line", columns: 2 }, lineColor, lineStyle),
),
);
output.line({
id: "ma",
lineWidth: 2,
colorFrom: lineColor,
lineStyleFrom: lineStyle,
labelFrom: labelsEnabled,
titleWhenLabeled: "MA",
});
/** @param {ScriptedCtx} ctx */
export function onBar(ctx) {
var p = ctx.params.period || 20;
var typ = ctx.params.maType || "SMA";
var closeBuf = ctx.accum("closeBuf", [], function (prev) {
prev.push(ctx.close);
return prev;
});
if (closeBuf.length < p) return;
var val;
if (typ === "SMA") {
var sum = 0;
for (var i = closeBuf.length - p; i < closeBuf.length; i++) sum += closeBuf[i];
val = sum / p;
} else if (typ === "EMA") {
var k = 2 / (p + 1);
var prevEma = ctx.accum("ema", 0, function (prev) { return prev; });
if (closeBuf.length === p) {
var smaSum = 0;
for (var j = 0; j < p; j++) smaSum += closeBuf[j];
val = smaSum / p;
} else {
val = ctx.close * k + prevEma * (1 - k);
}
ctx.accum("ema", 0, function () { return val; });
} else if (typ === "WMA") {
var wSum = 0;
var wDenom = p * (p + 1) / 2;
for (var w = 0; w < p; w++) {
wSum += closeBuf[closeBuf.length - p + w] * (w + 1);
}
val = wSum / wDenom;
} else {
var alpha = 1 / p;
var prevRma = ctx.accum("rma", 0, function (prev) { return prev; });
if (closeBuf.length === p) {
var rmaSum = 0;
for (var r = 0; r < p; r++) rmaSum += closeBuf[r];
val = rmaSum / p;
} else {
val = ctx.close * alpha + prevRma * (1 - alpha);
}
ctx.accum("rma", 0, function () { return val; });
}
ctx.plot("ma", val);
}