cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              @import url(https://fonts.googleapis.com/css?family=Ubuntu);
@import url(https://fonts.googleapis.com/css?family=Roboto);

html,
body {
  overflow: hidden;
  margin: 0;
  padding: 0;
  height: 100%
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  background: #111;
}

svg {
  // outline: 1px dashed white;
}
            
          
!
            
              (function () {
  var
  utils    = window.utilities,
  identity = function(v){return v;};

  var
  PROP_EL     = '_el',
  PROP_SVG    = '_svg',
  PROP_DATA   = '_data',
  PROP_WIDTH  = '_width',
  PROP_HEIGHT = '_height',
  PROP_BAR_SPACING   = '_barSpacing',
  PROP_DPROP_LOW     = '_propertyLow',
  PROP_DPROP_HIGH    = '_propertyHigh',
  PROP_DPROP_VOL     = '_propertyVolume',
  PROP_DPROP_DATE    = '_propertyDate',
  PROP_DPROP_CLOSE   = '_propertyClose',
  PROP_DPROP_OPEN    = '_propertyOpen',
  PROP_MAR_CANDLE    = '_marginCandle',
  PROP_MAR_VOLUME    = '_marginVolume',
  PROP_COL_CANDBG    = '_colorCandleBackground',
  PROP_COL_BODYDN    = '_colorBodyDown',
  PROP_COL_BODYUP    = '_colorBodyUp',
  PROP_COL_STEMDN    = '_colorStemDown',
  PROP_COL_STEMUP    = '_colorStemUp',
  PROP_COL_VOLBG     = '_colorVolBackground',
  PROP_COL_VOLDN     = '_colorVolSell',
  PROP_COL_VOLUP     = '_colorVolBuy',
  PROP_COL_VOLDNBORD = '_colorVolSellBorder',
  PROP_COL_VOLUPBORD = '_colorVolBuyBorder',
  PROP_COL_LABTIMEBG = '_colorLabelTimeBackground',
  PROP_COL_LABTIMEFG = '_colorLabelTimeForeground',
  PROP_COL_LABCANBG  = '_colorLabelCandleBackground',
  PROP_COL_LABCANFG  = '_colorLabelCandleForeground',
  PROP_COL_LABVOLBG  = '_colorLabelVolumeBackground',
  PROP_COL_LABVOLFG  = '_colorLabelVolumeForeground',
  PROP_LAB_FAM       = '_labelFontFamily',
  PROP_LAB_SIZE      = '_labelFontSize';

  function D3CandleStickChart (el, props) {
    this[PROP_WIDTH]        = 320;
    this[PROP_HEIGHT]       = 240;
    this[PROP_DATA]          = [];

    if(window.d3 === undefined) {
      throw new Error('Unable to locate the d3 library');
    }

    props = utils.isPlainObject(props) ? props : {};

    // load any data in the props
    this.loadData(props.data);

    this.width  = props.width;
    this.height = props.height;
    this.parent = el || document.body;
    
    this[PROP_DPROP_LOW]     = props.propertyLow                || 'low';
    this[PROP_DPROP_HIGH]    = props.propertyHigh               || 'high';
    this[PROP_DPROP_VOL]     = props.propertyVolume             || 'volume';
    this[PROP_DPROP_DATE]    = props.propertyDate               || 'date';
    this[PROP_DPROP_CLOSE]   = props.propertyClose              || 'close';
    this[PROP_DPROP_OPEN]    = props.propertyOpen               || 'open';
    this[PROP_BAR_SPACING]   = props.barSpacing                 || 0.35;
    this[PROP_MAR_CANDLE]    = props.marginCandle               || 15;
    this[PROP_MAR_VOLUME]    = props.marginVolume               || 5;
    this[PROP_COL_CANDBG]    = props.colorCandleBackground      || '#111';
    this[PROP_COL_BODYDN]    = props.colorCandleBodyDown        || '#FF2121';
    this[PROP_COL_BODYUP]    = props.colorCandleBodyUp          || this[PROP_COL_CANDBG];
    this[PROP_COL_STEMDN]    = props.colorCandleStemDown        || this[PROP_COL_BODYDN];
    this[PROP_COL_STEMUP]    = props.colorCandleStemUp          || '#1BD31B';
    this[PROP_COL_VOLBG]     = props.colorVolumeSell            || '#222';
    this[PROP_COL_VOLDN]     = props.colorVolumeBuy             || this[PROP_COL_BODYDN];
    this[PROP_COL_VOLUP]     = props.colorVolumeSellBorder      || this[PROP_COL_VOLBG];
    this[PROP_COL_VOLDNBORD] = props.colorVolumeBuyBorder       || this[PROP_COL_BODYDN];
    this[PROP_COL_VOLUPBORD] = props.colorVolumeBackground      || this[PROP_COL_STEMUP];
    this[PROP_COL_LABTIMEBG] = props.colorLabelTimeBackground   || '#333';
    this[PROP_COL_LABTIMEFG] = props.colorLabelTimeForeground   || '#aaa';
    this[PROP_COL_LABCANBG]  = props.colorLabelCandleBackground || '#333';
    this[PROP_COL_LABCANFG]  = props.colorLabelCandleForeground || '#aaa';
    this[PROP_COL_LABVOLBG]  = props.colorLabelVolumeBackground || '#444';
    this[PROP_COL_LABVOLFG]  = props.colorLabelVolumeForeground || '#bbb';
    this[PROP_LAB_FAM]       = props.labelFontFamily            || 'Ubuntu';
    this[PROP_LAB_SIZE]      = props.labelFontSize              || 10;
  }

  function redrawIdent () {
    return function (val) {
      this.redraw(this.svg, this.data);
      return val;
    };
  }

  function redrawGetSetter (prop, allowNull) {
    return {
      get: utils.getter(prop),
      set: utils.setter(prop, allowNull, redrawIdent()),
    };
  }

  Object.defineProperties(D3CandleStickChart.prototype, {
    parent: { // auto-install if this gets changed.
      get: utils.getter(PROP_EL),
      set: utils.setter(PROP_EL, false, function (parent) {
        if (parent) {
          if (this.parent) { // un-install any previous charts in parent
            this.uninstall(this.parent);
          }
          return this.install(parent);
        }

        return this.parent;
      })
    },
    svg: {
      get: utils.getter(PROP_SVG)
    },
    width: {
      get: utils.getter(PROP_WIDTH),
      set: utils.setterInt(PROP_WIDTH, 0)
    },
    height: {
      get: utils.getter(PROP_HEIGHT),
      set: utils.setterInt(PROP_HEIGHT, 0)
    },
    aspectRatio: {
      get: function () {
        return this.height / this.width;
      }
    },
    data: {
      get: utils.getter(PROP_DATA)
    },

    propertyLow:                 redrawGetSetter(PROP_DPROP_LOW),
    propertyHigh:                redrawGetSetter(PROP_DPROP_HIGH),
    propertyVolume:              redrawGetSetter(PROP_DPROP_VOL),
    propertyDate:                redrawGetSetter(PROP_DPROP_DATE),
    propertyClose:               redrawGetSetter(PROP_DPROP_CLOSE),
    propertyOpen:                redrawGetSetter(PROP_DPROP_OPEN),
    barSpacing:                  redrawGetSetter(PROP_BAR_SPACING),
    marginCandle:                redrawGetSetter(PROP_MAR_CANDLE),
    marginVolume:                redrawGetSetter(PROP_MAR_VOLUME),
    colorCandleBackground:       redrawGetSetter(PROP_COL_CANDBG),
    colorCandleBodyDown:         redrawGetSetter(PROP_COL_BODYDN),
    colorCandleBodyUp:           redrawGetSetter(PROP_COL_BODYUP),
    colorCandleStemDown:         redrawGetSetter(PROP_COL_STEMDN),
    colorCandleStemUp:           redrawGetSetter(PROP_COL_STEMUP),
    colorVolumeSell:             redrawGetSetter(PROP_COL_VOLDN),
    colorVolumeBuy:              redrawGetSetter(PROP_COL_VOLUP),
    colorVolumeSellBorder:       redrawGetSetter(PROP_COL_VOLDNBORD),
    colorVolumeBuyBorder:        redrawGetSetter(PROP_COL_VOLUPBORD),
    colorVolumeBackground:       redrawGetSetter(PROP_COL_VOLBG),
    colorLabelTimeBackground:    redrawGetSetter(PROP_COL_LABTIMEBG),
    colorLabelTimeForeground:    redrawGetSetter(PROP_COL_LABTIMEFG),
    colorLabelCandleBackground:  redrawGetSetter(PROP_COL_LABCANBG),
    colorLabelCandleForeground:  redrawGetSetter(PROP_COL_LABCANFG),
    colorLabelVolumeBackground:  redrawGetSetter(PROP_COL_LABVOLBG),
    colorLabelVolumeForeground:  redrawGetSetter(PROP_COL_LABVOLFG),
    labelFontFamily:             redrawGetSetter(PROP_LAB_FAM),
    labelFontSize:               redrawGetSetter(PROP_LAB_SIZE)
  });

  D3CandleStickChart.low = function (prev, next) {
    if(prev === null) return next;
    return (next===null) ? prev : Math.min(prev, next);
  };

  D3CandleStickChart.high = function (prev, next) {
    if(prev === null) return next;
    return (next===null) ? prev : Math.max(prev, next);
  };

  D3CandleStickChart.dateMS = function (v) {
    if(v instanceof Date) return v.getTime();
    if(typeof v === 'string') {
      var ms = Date.parse(v);
      if(isNaN(ms)) return null;
      return ms;
    }
    if(typeof v === 'number'&&!isNaN(v)) return v;
    return null;
  };

  D3CandleStickChart.lowDate = function (prev, next) {
    return D3CandleStickChart.low(prev, D3CandleStickChart.dateMS(next));
  };

  D3CandleStickChart.highDate = function (prev, next) {
    return D3CandleStickChart.high(prev, D3CandleStickChart.dateMS(next));
  };

  D3CandleStickChart.dateSeriesMinutes = function (ms) {
    return moment(ms).format('mm:ss');
  };

  D3CandleStickChart.dateSeriesHours = function (ms) {
    return moment(ms).format('HH:mm');
  };

  D3CandleStickChart.dateSeriesDays = function (ms) {
    return moment(ms).format('MMM, DD');
  };

  D3CandleStickChart.dateSeriesMonths = function (ms) {
    return moment(ms).format('MMM, YYYY');
  };

  D3CandleStickChart.dateSeriesYears = function (ms) {
    return moment(ms).format('MMM, YYYY');
  };

  D3CandleStickChart.dateSeriesFunctionFromDiff = function (start, end) {
    var diff = Math.abs(D3CandleStickChart.dateMS(start) - D3CandleStickChart.dateMS(end));
    return (diff >= 3.1104e+10
      ? D3CandleStickChart.dateSeriesYears : (diff >= 2.592e+9
      ? D3CandleStickChart.dateSeriesMonths : (diff >= 1.728e+8
      ? D3CandleStickChart.dateSeriesDays : (diff >= 3.6e+6
      ? D3CandleStickChart.dateSeriesHours : D3CandleStickChart.dateSeriesMinutes
    ))));
  };

  D3CandleStickChart.getScales = function (data, spec) {
    spec = spec || {};
    var
    propLow  = spec.propertyLow    ||'low',
    propHigh = spec.propertyHigh   ||'high',
    propVol  = spec.propertyVolume ||'volume',
    propDate = spec.propertyDate   ||'date';

    return data.reduce(function (p, c) { // find all domains using a single data iteration (optimal)
      p.lowestLow   = D3CandleStickChart.low(p.lowestLow,     c[propLow]);
      p.highestHigh = D3CandleStickChart.high(p.highestHigh,  c[propHigh]);
      p.volumeLow   = D3CandleStickChart.low(p.volumeLow,     c[propVol]);
      p.volumeHigh  = D3CandleStickChart.high(p.volumeHigh,   c[propVol]);
      p.dateLow     = D3CandleStickChart.lowDate(p.dateLow,   c[propDate]);
      p.dateHigh    = D3CandleStickChart.highDate(p.dateHigh, c[propDate]);
      return p;
    }, { // seed the unprocessed domains
      lowestLow:   null,
      highestHigh: null,
      volumeLow:   null,
      volumeHigh:  null,
      dateLow:     null,
      dateHigh:    null
    });
  };

  D3CandleStickChart.prototype.resize = function (width, height, svg) {
    svg    = svg || this.svg;
    width  = width || this.width;
    height = height || this.height;

    svg.attr('width', width);
    svg.attr('height', height);

    if(width !== this.width) {
      this.width = width;
    }
    if(height !== this.height) {
      this.height = height;
    }

    return this;
  };

  D3CandleStickChart.prototype.clearData = function () {
    this.data.splice(0, this.data.length);
    this.reset();
    return true;
  };

  D3CandleStickChart.prototype.loadData = function (data) {
    if(!utils.isArray(data)) return false;
    Array.prototype.push.apply(this.data, data);
    if(this.parent) this.redraw();
    return true;
  };

  D3CandleStickChart.prototype.reset = function (svg) {
    svg = svg || this.svg;
    this.resetCandles(svg);
    this.resetVolume(svg);
    this.resetLabels(svg);
  };

  D3CandleStickChart.prototype.getScales = function (data) {
    return D3CandleStickChart.getScales(data || this.data, {
      propertyLow:    this.propertyLow,
      propertyHigh:   this.propertyHigh,
      propertyVolume: this.propertyVolume,
      propertyDate:   this.propertyDate
    });
  };

  D3CandleStickChart.prototype.coordCandles = function () {
    var
    volcoord = this.coordVolume();

    return {
      x: volcoord.x,
      y: 0,
      h: volcoord.y,
      w: volcoord.w
    };
  };

  D3CandleStickChart.prototype.resetCandles = function (svg) {
    svg.selectAll('g.candles').remove();
    return this;
  };

  D3CandleStickChart.prototype.redrawCandles = function (svg, scales) {
    var
    propLow       = this.propertyLow,
    propHigh      = this.propertyHigh,
    propVol       = this.propertyVolume,
    propDate      = this.propertyDate,
    propClose     = this.propertyClose,
    propOpen      = this.propertyOpen,
    colorBG       = this.colorCandleBackground,
    colorBodyDown = this.colorCandleBodyDown,
    colorBodyUp   = this.colorCandleBodyUp,
    colorStemDown = this.colorCandleStemDown,
    colorStemUp   = this.colorCandleStemUp,
    margin        = this.marginCandle || 0,
    coord = this.coordCandles(),
    approxW  = (coord.w / this.data.length),
    barSpace = approxW * this.barSpacing,
    barWidth = approxW - barSpace,
    stemWidth = barWidth * 0.15,
    group = svg.append('g')
      .attr('class', 'candles')
      .attr('transform', 'translate('+[coord.x, coord.y].join(' ') +')'),
    calcDateX = d3.scale.linear()
      .domain([scales.dateLow, scales.dateHigh])
        .range([0, coord.w - barWidth]),
    calcRY = d3.scale.linear()
      .domain([scales.lowestLow, scales.highestHigh])
        .range([coord.h - margin, margin]);

    if(stemWidth < 1)  {
      stemWidth = 1;
    }
    if(barWidth < 1)  {
      barWidth = 1;
    }

    group.append('rect')
      .attr('class', 'background')
      .attr('width', coord.w)
      .attr('height', coord.h)
      .attr('fill', colorBG);

    var
    dOpenCloseMin = function (d) { return Math.min(d[propClose], d[propOpen]); },
    dOpenCloseMax = function (d) { return Math.max(d[propClose], d[propOpen]); },
    dIsDown       = function (d) { return d[propClose] < d[propOpen]; },
    dStemColor    = function (d) { return dIsDown(d) ? colorStemDown : colorStemUp; },
    dBodyColor    = function (d) { return dIsDown(d) ? colorBodyDown : colorBodyUp; };

    if(stemWidth > 0) {
      group.selectAll('rect.candle-stem')
        .data(this.data)
        .enter().append('rect')
        .attr('class', 'candle-stem')
        .attr('width', stemWidth)
        .attr('x', function (d) { return calcDateX(d[propDate]) + ((barWidth/2) - (stemWidth/2)); })
        .attr('y', function (d) {
          return calcRY(d[propHigh]);
        })
        .attr('height', function (d) {
          return calcRY(d[propLow]) - calcRY(d[propHigh]);
        })
        .attr('fill', dStemColor);
    }

    group.selectAll('rect.candle-body')
      .data(this.data)
      .enter().append('rect')
      .attr('class', 'candle-body')
      .attr('width', barWidth)
      .attr('vmin', dOpenCloseMin)
      .attr('vmax', dOpenCloseMax)
      .attr('x', function (d) { return calcDateX(d[propDate]); })
      .attr('y', function (d) { return calcRY(this.getAttribute('vmax')); })
      .attr('height', function (d) {
        return calcRY(this.getAttribute('vmin'))-calcRY(this.getAttribute('vmax'));
      })
      .attr('stroke-width', 0.5)
      .attr('stroke-linecap', 'butt')
      .attr('stroke-rendering', 'crispEdges')
      .attr('stroke', dStemColor)
      .attr('fill', dBodyColor);

    return svg;
  };

  D3CandleStickChart.prototype.coordVolume = function () {
    var
    volheight = 100,
    coordlabels = this.coordLabels(),
    coordSX = coordlabels.seriesX;

    if(volheight / this.height > 0.25) {
      volheight = 50;
    }

    return {
      x: coordSX.x,
      y: coordSX.y - volheight,
      w: coordSX.w,
      h: volheight
    };
  };

  D3CandleStickChart.prototype.resetVolume = function (svg) {
    svg.selectAll('g.volume').remove();
    return this;
  };

  D3CandleStickChart.prototype.redrawVolume = function (svg, scales) {
    var
    coord              = this.coordVolume(),
    propVol            = this.propertyVolume,
    propDate           = this.propertyDate,
    propClose          = this.propertyClose,
    propOpen           = this.propertyOpen,
    colorVolSell       = this.colorVolumeSell,
    colorVolBuy        = this.colorVolumeBuy,
    colorVolSellBorder = this.colorVolumeSellBorder,
    colorVolBuyBorder  = this.colorVolumeBuyBorder,
    colorBG            = this.colorVolumeBackground,
    approxW            = (coord.w / this.data.length),
    barSpace           = approxW * this.barSpacing,
    barWidth           = approxW - barSpace,
    margin             = this.marginVolume || 0;

    if(barWidth < 1)  {
      barWidth = 1;
    }

    var
    group = svg.append('g')
      .attr('class', 'volume')
      .attr('transform', 'translate('+[coord.x, coord.y].join(' ') +')'),
    calcDateX = d3.scale.linear()
      .domain([scales.dateLow, scales.dateHigh])
        .range([0, coord.w - barWidth]),
    calcVolHeight = d3.scale.linear()
      .domain([scales.volumeLow, scales.volumeHigh])
        .range([2, coord.h - margin]),
    dIsDown        = function (d) { return d[propClose] < d[propOpen]; },
    dColorBars     = function (d) { return dIsDown(d) ? colorVolSell : colorVolBuy; },
    dColorBarsBord = function (d) { return dIsDown(d) ? colorVolSellBorder : colorVolBuyBorder; };

    group.append('rect')
      .attr('class', 'background')
      .attr('width', coord.w)
      .attr('height', coord.h)
      .attr('fill', colorBG);

    group.selectAll('rect.volume-bar')
      .data(this.data)
      .enter().append('rect')
      .attr('class', 'volume-bar')
      .attr('fill', dColorBars)
      .attr('stroke', dColorBarsBord)
      .attr('width', barWidth)
      .attr('stroke-width', 0.5)
      .attr('stroke-linecap', 'butt')
      .attr('stroke-rendering', 'crispEdges')
      .attr('height', function (d) {
        return calcVolHeight(d[propVol]);
      })
      .attr('x', function (d) {
        return calcDateX(d[propDate]);
      })
      .attr('y', function (d) {
        return (coord.h - this.getAttribute('height'));
      });

    return svg;
  };

  D3CandleStickChart.prototype.coordLabels = function () {
    var
    xheight = 20,
    ywidth  = 75;

    return {
      seriesX: {
        x: 0,
        y: this.height - xheight,
        w: this.width - ywidth,
        h: xheight
      },
      seriesY: {
        x: this.width - ywidth,
        y: 0,
        w: ywidth,
        h: this.height
      }
    };
  };

  D3CandleStickChart.prototype.resetLabels = function (svg) {
    svg.selectAll('g.labels').remove();
    return this;
  };

  D3CandleStickChart.prototype.redrawLabels = function (svg, scales) {
    var
    coord         = this.coordLabels(),
    coordvol      = this.coordVolume(),
    coordcand     = this.coordCandles(),
    fontFamily    = this.labelFontFamily,
    fontSize      = this.labelFontSize,
    colorTimeBG   = this.colorLabelTimeBackground,
    colorTimeFG   = this.colorLabelTimeForeground,
    colorCandleBG = this.colorLabelCandleBackground,
    colorCandleFG = this.colorLabelCandleForeground,
    colorVolumeBG = this.colorLabelVolumeBackground,
    colorVolumeFG = this.colorLabelVolumeForeground,
    marginCandle  = this.marginCandle || 0,
    marginVolume  = this.marginVolume || 0,
    cSX = coord.seriesX,
    cSY = coord.seriesY,
    groupY = svg.append('g')
      .attr('class', 'labels label-y')
      .attr('transform', 'translate('+[cSY.x, cSY.y].join(' ') +')'),
    groupX = svg.append('g')
      .attr('class', 'labels label-x')
      .attr('transform', 'translate('+[cSX.x, cSX.y].join(' ') +')'),

    // time series data & functions:
    timeticks = 10,
    dateX = d3.scale.linear()
      .domain([scales.dateLow, scales.dateHigh])
        .range([cSX.x, cSX.x + cSX.w]),

    // candle data & functions:
    candticks = 6,
    candY = d3.scale.linear()
      .domain([scales.lowestLow, scales.highestHigh])
        .range([coordcand.y + coordcand.h - marginCandle, coordcand.y + marginCandle]),

    // volume data & functions:
    volticks = 5,
    volY = d3.scale.linear()
      .domain([scales.volumeLow, scales.volumeHigh])
        .range([coordvol.y + coordvol.h, coordvol.y + fontSize + marginVolume]);

    groupX.append('rect') // placeholder for time-series labels
      .attr('width', cSX.w)
      .attr('height', cSX.h)
      .attr('fill', colorTimeBG);

    groupY.append('rect') // placeholder for X labels (end-cap)
     .attr('y', cSX.y)
     .attr('width', cSY.w)
     .attr('height', cSX.h)
     .attr('fill', colorTimeBG);

    groupX.selectAll('text.series-x')
      .data(dateX.ticks(timeticks))
      .enter().append('svg:text')
      .attr('class', 'series-x')
      .attr('x', dateX)
      .attr('y', 0)
      .attr('dy', (cSX.h/2)+(fontSize/2))
      .attr('font-family', fontFamily)
      .attr('font-size', fontSize)
      .attr('text-anchor', 'middle')
      .attr('fill', colorTimeFG)
      .text(D3CandleStickChart.dateSeriesFunctionFromDiff(scales.dateLow, scales.dateHigh));

    // candle labels
    groupY.append('rect') // background
      .attr('y', coordcand.y)
      .attr('width', cSY.w)
      .attr('height', coordcand.h)
      .attr('fill', colorCandleBG);

    groupY.selectAll('text.candle-label') // ticks
      .data(candY.ticks(candticks))
      .enter().append('text')
      .attr('class', 'candle-label')
      .attr('x', 0)
      .attr('y', candY)
      .attr('dx', cSY.w/2)
      // .attr('dy', fontSize/2)
      .attr('font-family', fontFamily)
      .attr('font-size', fontSize)
      .attr('text-anchor', 'middle')
      .attr('fill', colorCandleFG)
      .text(function (d) {
        return utils.isNumber(d) ? ( d < 1 ? d.toFixed(4) : d) : d;
      });

    // volume labels:
    groupY.append('rect') // background
      .attr('y', coordvol.y)
      .attr('width', cSY.w)
      .attr('height', coordvol.h)
      .attr('fill', colorVolumeBG);

    groupY.selectAll('text.vol-label') // ticks
      .data(volY.ticks(volticks))
      .enter().append('text')
      .attr('class', 'vol-label')
      .attr('x', 0)
      .attr('y', volY)
      .attr('dx', cSY.w/2)
      .attr('dy', 0)
      .attr('fill', colorVolumeFG)
      .attr('font-family', fontFamily)
      .attr('font-size', fontSize)
      .attr('text-anchor', 'middle')
      .text(String);

    return svg;
  };

  D3CandleStickChart.prototype.redraw = function (svg, data) {
    svg  = svg  || this.svg;
    data = data || this.data;

    // clear existing charted data
    this.reset(svg);

    if(!data.length) { // bail out if no data
      return svg;
    }

    // resize SVG to outer limits
    svg.attr('width', this.width);
    svg.attr('height', this.height);
    svg.attr('viewBox', '0 0 '+ this.width + ' ' + this.height);

    var
    /***** scales:
    * lowestLow
    * highestHigh
    * volumeLow
    * volumeHigh
    * dateLow
    * dateHigh
    *****/
    scales = this.getScales(data);

    this.redrawCandles(svg, scales);
    this.redrawVolume(svg, scales);
    this.redrawLabels(svg, scales);
  };

  D3CandleStickChart.prototype.initView = function (svg) {
    this.resize(this.width, this.height, svg);
    this.redraw(svg, this.data);
    return svg;
  };

  D3CandleStickChart.prototype.install = function (parent) {
    parent = parent || this.parent;
    if(!parent) {
      return false;
    }

    // set the parent's svg context
    this[PROP_SVG] = this.initView(d3.select(parent).append('svg:svg'));

    return parent;
  };

  D3CandleStickChart.prototype.uninstall = function (parent) {
    d3.select(parent||this.parent)
      .select('svg')
      .remove();

    return parent;
  };

  this.D3CandleStickChart = D3CandleStickChart;

}).call(this);

//
// Test code
//

(function (el) {
  if(!el) throw new Error('Unable to find main canvas.');

  var
  chart = new window.D3CandleStickChart(el, {}),

  // data loading stuff
  msPerSec  = 1000,
  pair      = 'BTC_BTS',
  period    = 14400,
  num       = 100,
  epocEnd   = Math.round(Date.now()/msPerSec),
  epocStart = Math.round(epocEnd - (period * num));

  function reloadData() {
    $.ajax({ // fetch some chart data
      url: 'https://poloniex.com/public?command=returnChartData&currencyPair='+pair+'&start='+epocStart+'&end='+epocEnd+'&period='+period,
      json: true,
      success: function (data) {
        chart.clearData();
        chart.loadData(data.map(function (record) {
          record.date *= msPerSec;
          return record;
        }));
      }
    });
  }

  reloadData();
  
  setInterval(reloadData, Math.round((period * msPerSec)/2));

  (window.onresize = function () {
    chart.resize(window.innerWidth, window.innerHeight);
    chart.redraw();
  })();

})(document.getElementById('main'))
            
          
!
999px
Loading ..................

Console