Bollinger Bands Calculator

Calculate Bollinger Bands for trading analysis with our interactive and accessible online tool.

Inputs

How to Use This Calculator

Paste or type your historical closing prices separated by commas. Set the lookback period and the multiplier you prefer for the standard deviation band. Hit Calculate or wait for the inputs to settle after editing; results refresh automatically without producing NaN or Infinity.

Methodology

The calculator finds the simple moving average (SMA) over the selected period, computes the standard deviation of those same closing prices, and then plots the upper and lower bands by adding and subtracting the multiplier times the standard deviation from the SMA. That gives you a volatility envelope aligned with how technical analysts expect Bollinger Bands to behave.

  • Middle band equals the SMA of the latest data points.
  • Upper and lower bands stretch equally above and below the SMA according to the std dev multiplier.
  • All input parsing is deterministic so that the calculator never surfaces NaN or Infinity.
Results are estimates for informational purposes only. Always confirm with live market data or a licensed advisor before trading decisions.

Full original guide (expanded)

Data Source and Methodology

All calculations are based on the widely accepted Bollinger Bands formula. For more details on the application of Bollinger Bands, please visit iForex Education Center. All calculations adhere strictly to the methodologies outlined therein.

The Formula Explained

Bollinger Bands: Middle Band = SMA(N), Upper Band = SMA(N) + (K × StdDev), Lower Band = SMA(N) − (K × StdDev)

Glossary of Variables

  • Closing Prices: The end-of-day prices for a given period.
  • Period: Number of days over which to calculate the moving average.
  • Standard Deviation Multiplier: The multiplier applied to the standard deviation to calculate the bands.

Frequently Asked Questions (FAQ)

How are Bollinger Bands calculated?
Bollinger Bands are calculated using a simple moving average and the standard deviation of the asset's price over a specified period.

What do Bollinger Bands indicate?
They indicate market volatility and can signal potential overbought or oversold conditions.

Formulas

Core formula:

Middle Band = SMA(N)

Upper Band = SMA(N) + (K × StdDev)

Lower Band = SMA(N) − (K × StdDev)

  • SMA(N): Simple moving average over the selected period.
  • StdDev: Standard deviation of the same set of closing prices.
  • K: Multiplier (default 2) that widens or narrows the volatility envelope.
Citations
Changelog
Version: 0.1.0-draft
Last code update: 2026-01-19
  • 0.1.0-draft — 2026-01-19: Initial audit spec draft generated from HTML extraction (review required).
  • Verify formulas match the calculator engine and convert any text-only formulas to LaTeX.
  • Confirm sources are authoritative and relevant to the calculator methodology.
Verified by Ugo Candido Last Updated: 2026-01-19 Version 0.1.0-draft
Version 1.5.0
+ round2(safe).toFixed(2); }; const els = { closingPrices: document.getElementById('closingPrices'), period: document.getElementById('period'), stdDev: document.getElementById('stdDev'), middleBand: document.getElementById('middleBand'), upperBand: document.getElementById('upperBand'), lowerBand: document.getElementById('lowerBand'), stdDevValue: document.getElementById('stdDevValue'), dataPoints: document.getElementById('dataPoints'), errorBox: document.getElementById('errorBox'), scheduleWrap: document.getElementById('scheduleWrap'), scheduleBody: document.getElementById('scheduleBody'), toggleSchedule: document.getElementById('toggleSchedule'), downloadCsv: document.getElementById('downloadCsv'), calcBtn: document.getElementById('calcBtn'), resetBtn: document.getElementById('resetBtn') }; const DEFAULT_PRICES = '147.32,146.18,145.10,146.43,147.85,148.40,149.90,151.22,150.81,152.05,152.60,153.33,154.14,153.78,154.91,155.30,156.10,155.88,156.62,157.20,157.41,158.02,157.65,158.78,159.30'; const DEFAULT_PERIOD = '20'; const DEFAULT_STD_MULTIPLIER = '2'; let currentSchedule = []; function parseInputs() { const rawPrices = els.closingPrices.value.split(/[,\n]+/).map((item) => item.trim()).filter(Boolean); const prices = rawPrices.map((item) => Number(item)).filter(Number.isFinite); return { prices, period: parseInt(els.period.value, 10), stdDevMultiplier: parseFloat(els.stdDev.value) }; } function validate(inputs) { const errors = []; if (inputs.period < 1 || !Number.isFinite(inputs.period)) { errors.push('Period must be 1 or greater.'); } if (!Number.isFinite(inputs.stdDevMultiplier) || inputs.stdDevMultiplier < 0) { errors.push('Standard deviation multiplier must be 0 or greater.'); } if (inputs.prices.length < inputs.period) { errors.push('Provide at least as many closing prices as the selected period.'); } if (inputs.prices.length === 0) { errors.push('At least one closing price is required.'); } return { ok: errors.length === 0, errors }; } function compute(inputs) { const { prices, period, stdDevMultiplier } = inputs; const relevant = prices.slice(-period); if (relevant.length < period) return null; const sma = relevant.reduce((sum, price) => sum + price, 0) / period; const variance = relevant.reduce((acc, price) => acc + Math.pow(price - sma, 2), 0) / period; const deviation = Math.sqrt(variance); const upper = sma + (stdDevMultiplier * deviation); const lower = sma - (stdDevMultiplier * deviation); const schedule = relevant.map((price, index) => { const diff = price - sma; return { num: prices.length - relevant.length + index + 1, close: round2(price), deviation: round2(diff), upperGap: round2(upper - price), lowerGap: round2(price - lower) }; }); return { middleBand: round2(sma), upperBand: round2(upper), lowerBand: round2(lower), stdDev: round2(deviation), dataPoints: prices.length, schedule }; } function formatValues(results) { return { middleBand: fmtNumber(results.middleBand), upperBand: fmtNumber(results.upperBand), lowerBand: fmtNumber(results.lowerBand), stdDev: fmtNumber(results.stdDev), dataPoints: results.dataPoints.toString(), schedule: results.schedule }; } function render(formatted, errors) { if (errors.length > 0) { els.errorBox.style.display = 'block'; els.errorBox.textContent = errors.join(' '); els.toggleSchedule.disabled = true; els.downloadCsv.disabled = true; els.scheduleWrap.style.display = 'none'; els.toggleSchedule.textContent = 'Show Data Table'; currentSchedule = []; return; } els.errorBox.style.display = 'none'; els.errorBox.textContent = ''; els.middleBand.textContent = formatted.middleBand; els.upperBand.textContent = formatted.upperBand; els.lowerBand.textContent = formatted.lowerBand; els.stdDevValue.textContent = formatted.stdDev; els.dataPoints.textContent = formatted.dataPoints; const scheduleRows = formatted.schedule || []; currentSchedule = scheduleRows; els.toggleSchedule.disabled = currentSchedule.length === 0; els.downloadCsv.disabled = currentSchedule.length === 0; if (els.scheduleWrap.style.display === 'block' && currentSchedule.length > 0) { renderTable(); } } function renderTable() { els.scheduleBody.innerHTML = ''; const frag = document.createDocumentFragment(); for (const row of currentSchedule) { const tr = document.createElement('tr'); tr.innerHTML = ` ${row.num} ${fmtNumber(row.close)} ${fmtNumber(row.deviation)} ${fmtNumber(row.upperGap)} ${fmtNumber(row.lowerGap)} `; frag.appendChild(tr); } els.scheduleBody.appendChild(frag); } function update() { const inputs = parseInputs(); const validation = validate(inputs); if (!validation.ok) { render({}, validation.errors); return; } const results = compute(inputs); if (!results) { render({}, ['Unable to compute with the provided inputs.']); return; } const formatted = formatValues(results); render(formatted, []); } els.calcBtn.addEventListener('click', update); els.resetBtn.addEventListener('click', () => { els.closingPrices.value = DEFAULT_PRICES; els.period.value = DEFAULT_PERIOD; els.stdDev.value = DEFAULT_STD_MULTIPLIER; const debouncedUpdate = debounce(update, 100); document.querySelectorAll('#inputsCard input, #inputsCard select, #inputsCard textarea') .forEach((el) => { el.addEventListener('input', debouncedUpdate); el.addEventListener('change', debouncedUpdate); }); update(); }); els.toggleSchedule.addEventListener('click', () => { const isHidden = els.scheduleWrap.style.display !== 'block'; els.scheduleWrap.style.display = isHidden ? 'block' : 'none'; els.toggleSchedule.textContent = isHidden ? 'Hide Data Table' : 'Show Data Table'; if (isHidden && currentSchedule.length > 0) { renderTable(); } }); els.downloadCsv.addEventListener('click', () => { if (currentSchedule.length === 0) return; let csv = '#,Closing Price,Deviation,Upper Gap,Lower Gap\n'; for (const row of currentSchedule) { csv += `${row.num},${row.close},${row.deviation},${row.upperGap},${row.lowerGap}\n`; } const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'bollinger-data.csv'; a.click(); URL.revokeObjectURL(url); }); [els.closingPrices, els.period, els.stdDev].forEach((el) => { el.addEventListener('input', debouncedUpdate); el.addEventListener('change', debouncedUpdate); }); update(); })();