Candlestick chart

캔들스틱 차트

Compresses four numbers per period — open, high, low, close — into one body-and-wick mark, the standard for financial time series.

Also known as: OHLC chart봉 차트
···
html
<div class="viz">
  <svg id="svg"></svg>
</div>
css
body{display:block}
.viz{position:relative;width:100%;height:100%}
svg{display:block;width:100%;height:100%;overflow:visible}
.axis{stroke:var(--line);stroke-width:1}
.tick{fill:var(--muted);font-weight:600}
.minor{display:none}
@media (min-width:460px){.minor{display:inline}}
.wick{stroke-width:1.2}
.up{fill:var(--surface);stroke:var(--accent);stroke-width:1.6}
.down{fill:var(--accent-2);stroke:var(--accent-2)}
js
var svg = document.getElementById('svg');
var NS = 'http://www.w3.org/2000/svg';
function se(t){ return document.createElementNS(NS,t); }
function sa(e,o){ for (var k in o) e.setAttribute(k,o[k]); }
function R(a,b){ return a + Math.random()*(b-a); }

var N = 12;
function genSeries(){
  var out = [], price = R(40,60);
  for (var i=0;i<N;i++){
    var open = price;
    var close = Math.max(6, Math.min(94, open + R(-9,9)));
    var high = Math.max(open,close) + R(0,4);
    var low = Math.min(open,close) - R(0,4);
    out.push({ o:open, h:Math.min(99,high), l:Math.max(1,low), c:close });
    price = close;
  }
  return out;
}
var cur = genSeries();
var W=300, H=200;

function measure(){
  var r = svg.getBoundingClientRect();
  W = Math.max(r.width,10); H = Math.max(r.height,10);
  sa(svg,{ viewBox: '0 0 ' + W + ' ' + H });
}

function draw(){
  svg.innerHTML = '';
  var padL=24, padR=10, padT=12, padB=14;
  var pw=W-padL-padR, ph=H-padT-padB;
  function Y(v){ return padT+ph-(v/100)*ph; }
  var slot = pw/N;
  var bw = Math.min(16, slot*0.55);

  [0,100].forEach(function(v){
    var g = se('line');
    sa(g,{x1:padL,y1:Y(v),x2:W-padR,y2:Y(v),class:'axis' + (v===100?' minor':'')});
    svg.appendChild(g);
  });

  cur.forEach(function(k,i){
    var cx = padL+slot*i+slot/2;
    var up = k.c >= k.o;
    var wick = se('line');
    sa(wick,{x1:cx,y1:Y(k.h),x2:cx,y2:Y(k.l),class:'wick',stroke:up?'var(--accent)':'var(--accent-2)'});
    svg.appendChild(wick);
    var top = Y(Math.max(k.o,k.c)), bot = Y(Math.min(k.o,k.c));
    var body = se('rect');
    sa(body,{x:cx-bw/2,y:top,width:bw,height:Math.max(bot-top,1.4),rx:1.4,class:up?'up':'down'});
    svg.appendChild(body);
  });
}

function animate(target){
  var from = cur;
  var t0 = performance.now();
  function step(t){
    var p = Math.min(1,(t-t0)/750);
    var e = 1-Math.pow(1-p,3);
    cur = from.map(function(k,i){
      var to = target[i];
      return { o:k.o+(to.o-k.o)*e, h:k.h+(to.h-k.h)*e, l:k.l+(to.l-k.l)*e, c:k.c+(to.c-k.c)*e };
    });
    draw();
    if (p<1) requestAnimationFrame(step); else cur = target;
  }
  requestAnimationFrame(step);
}

new ResizeObserver(function(){ measure(); draw(); }).observe(svg);
measure();
draw();
setInterval(function(){ animate(genSeries()); }, 3800);

The body (thick part) spans open to close; the wick (thin line) spans the period's high to low. A line chart only shows the closing value — a candlestick also shows how much it swung along the way.

Color convention (often green-up/red-down) is deeply ingrained, but color alone erases the distinction for colorblind readers. This demo pairs color with shape too — an up candle is hollow (outline only), a down candle is solid.

Once there are hundreds of candles, they can't all fit the screen width without blurring together — widen the period each candle represents (daily → weekly), or add zoom/scroll.

When to use

Use it for financial or price-like time series where open, high, low, and close all carry meaning per period.