export default function() { var base = baseIndicator() .type(ALGORITHM_TYPE) .accessor(d => d.ema); var underlyingAlgorithm = ema(); var mergedAlgorithm = merge() .algorithm(underlyingAlgorithm) .merge((datum, indicator) => { datum.ema = indicator; }); var indicator = function(data) { if (!base.accessor()) throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); return mergedAlgorithm(data); }; base.tooltipLabel(() => `${ALGORITHM_TYPE}(${underlyingAlgorithm.windowSize()})`); indicator.undefinedLength = function() { return underlyingAlgorithm.windowSize(); }; rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type", "tooltipLabel"); rebind(indicator, underlyingAlgorithm, "windowSize", "sourcePath"); rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); return indicator; }
export default function() { var base = baseIndicator() .type(ALGORITHM_TYPE) .accessor(d => d.bollingerBand) .stroke(appearanceOptions.stroke) .fill(appearanceOptions.fill); var underlyingAlgorithm = bollingerband(); var mergedAlgorithm = merge() .algorithm(underlyingAlgorithm) .merge((datum, indicator) => { datum.bollingerBand = indicator; }); var indicator = function(data) { if (!base.accessor()) throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); var newData = mergedAlgorithm(data); return newData; }; base.tooltipLabel(() => `BB (${underlyingAlgorithm.windowSize()}, ${underlyingAlgorithm.multiplier()}` + `, ${underlyingAlgorithm.movingAverageType()}): `); rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type", "tooltipLabel"); rebind(indicator, underlyingAlgorithm, "windowSize", "movingAverageType", "multiplier", "sourcePath"); rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); return indicator; }
export default () => { const base = boxPlotBase(); const join = dataJoin('g', 'box-plot'); const pathGenerator = shapeBoxPlot() .value(0); const boxPlot = (selection) => { if (selection.selection) { join.transition(selection); } selection.each((data, index, group) => { const filteredData = data.filter(base.defined); const g = join(select(group[index]), filteredData); g.enter() .attr('stroke', colors.black) .attr('fill', colors.gray) .append('path'); const width = base.computeBarWidth(filteredData); pathGenerator.orient(base.orient()) .width(width); g.each((d, i, g) => { const values = base.values(d, i); pathGenerator.median(values.median) .upperQuartile(values.upperQuartile) .lowerQuartile(values.lowerQuartile) .high(values.high) .low(values.low); select(g[i]) .attr('transform', 'translate(' + values.origin[0] + ',' + values.origin[1] + ')') .select('path') .attr('d', pathGenerator([d])); }); base.decorate()(g, data, index); }); }; rebindAll(boxPlot, base); rebind(boxPlot, join, 'key'); rebind(boxPlot, pathGenerator, 'cap'); return boxPlot; };
export default function() { var overSold = 70, middle = 50, overBought = 30; var base = baseIndicator() .type(ALGORITHM_TYPE) .accessor(d => d.rsi); var underlyingAlgorithm = rsi(); var mergedAlgorithm = merge() .algorithm(underlyingAlgorithm) .merge((datum, indicator) => { datum.rsi = indicator; }); var indicator = function(data) { if (!base.accessor()) throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); return mergedAlgorithm(data); }; base.tooltipLabel(() => `${ALGORITHM_TYPE} (${underlyingAlgorithm.windowSize()}): `); base.domain([0, 100]); base.tickValues([overSold, middle, overBought]); indicator.overSold = function(x) { if (!arguments.length) return overSold; overSold = x; base.tickValues([overSold, middle, overBought]); return indicator; }; indicator.middle = function(x) { if (!arguments.length) return middle; middle = x; base.tickValues([overSold, middle, overBought]); return indicator; }; indicator.overBought = function(x) { if (!arguments.length) return overBought; overBought = x; base.tickValues([overSold, middle, overBought]); return indicator; }; rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type", "tooltipLabel", "domain", "tickValues"); rebind(indicator, underlyingAlgorithm, "sourcePath", "windowSize"); rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); return indicator; }
export default () => { const base = xyBase(); const areaData = areaShape() .defined(base.defined); const area = (data) => { const context = areaData.context(); const projectedData = data.map(base.values); areaData.x((_, i) => projectedData[i].transposedX) .y((_, i) => projectedData[i].transposedY); const valueComponent = base.orient() === 'vertical' ? 'y' : 'x'; areaData[valueComponent + '0']((_, i) => projectedData[i].y0); areaData[valueComponent + '1']((_, i) => projectedData[i].y); context.beginPath(); areaData(data); context.fillStyle = colors.gray; base.decorate()(context, data); context.fill(); context.closePath(); }; rebindAll(area, base, exclude('barWidth')); rebind(area, areaData, 'curve', 'context'); return area; };
export default () => { const base = errorBarBase(); const join = dataJoin('g', 'error-bar'); const pathGenerator = shapeErrorBar() .value(0); const propagateTransition = maybeTransition => selection => maybeTransition.selection ? selection.transition(maybeTransition) : selection; const containerTranslation = (values) => 'translate(' + values.origin[0] + ', ' + values.origin[1] + ')'; const errorBar = (selection) => { if (selection.selection) { join.transition(selection); } const transitionPropagator = propagateTransition(selection); selection.each((data, index, group) => { const filteredData = data.filter(base.defined); const projectedData = filteredData.map(base.values); const g = join(select(group[index]), filteredData); g.enter() .attr('stroke', colors.black) .attr('fill', colors.gray) .attr('transform', (d, i) => containerTranslation(base.values(d, i)) + ' scale(1e-6, 1)') .append('path'); pathGenerator.orient(base.orient()); g.each((d, i, g) => { const values = projectedData[i]; pathGenerator.high(values.high) .low(values.low) .width(values.width); transitionPropagator(select(g[i])) .attr('transform', containerTranslation(values) + ' scale(1)') .select('path') .attr('d', pathGenerator([d])); }); base.decorate()(g, data, index); }); }; rebindAll(errorBar, base); rebind(errorBar, join, 'key'); return errorBar; };
export default (pathGenerator, seriesName) => { const base = ohlcBase(); const join = dataJoin('g', seriesName); const containerTranslation = (values) => 'translate(' + values.cross + ', ' + values.high + ')'; const propagateTransition = maybeTransition => selection => maybeTransition.selection ? selection.transition(maybeTransition) : selection; const candlestick = (selection) => { if (selection.selection) { join.transition(selection); } const transitionPropagator = propagateTransition(selection); selection.each((data, index, group) => { const filteredData = data.filter(base.defined); const g = join(select(group[index]), filteredData); g.enter() .attr('transform', (d, i) => containerTranslation(base.values(d, i)) + ' scale(1e-6, 1)') .append('path'); g.each((d, i, g) => { const values = base.values(d, i); const color = values.direction === 'up' ? colors.green : colors.red; const singleCandlestick = transitionPropagator(select(g[i])) .attr('class', seriesName + ' ' + values.direction) .attr('stroke', color) .attr('fill', color) .attr('transform', () => containerTranslation(values) + ' scale(1)'); pathGenerator.x(0) .width(values.width) .open(() => values.open - values.high) .high(0) .low(() => values.low - values.high) .close(() => values.close - values.high); singleCandlestick.select('path') .attr('d', pathGenerator([d])); }); base.decorate()(g, data, index); }); }; rebind(candlestick, join, 'key'); rebindAll(candlestick, base); return candlestick; };
export default function() { var force = calculator(); var mergedAlgorithm = merge() .algorithm(force) .merge(function(datum, indicator) { datum.force = indicator; return datum; }); var forceIndex = function(data) { return mergedAlgorithm(data); }; rebind(forceIndex, mergedAlgorithm, 'merge'); rebind(forceIndex, force, 'period', 'volumeValue', 'closeValue'); return forceIndex; }
export default function() { var stoc = calculator(); var mergedAlgorithm = merge() .algorithm(stoc) .merge(function(datum, indicator) { datum.stochastic = indicator; return datum; }); var stochasticOscillator = function(data) { return mergedAlgorithm(data); }; rebind(stochasticOscillator, mergedAlgorithm, 'merge'); rebind(stochasticOscillator, stoc, 'kPeriod', 'dPeriod', 'lowValue', 'closeValue', 'highValue'); return stochasticOscillator; }
export default () => { const symbol = symbolShape(); const base = xyBase(); const join = dataJoin('g', 'point'); const containerTransform = (origin) => 'translate(' + origin[0] + ', ' + origin[1] + ')'; const point = (selection) => { if (selection.selection) { join.transition(selection); } selection.each((data, index, group) => { const filteredData = data.filter(base.defined); const g = join(select(group[index]), filteredData); g.enter() .attr('transform', (d, i) => containerTransform(base.values(d, i).origin)) .attr('fill', colors.gray) .attr('stroke', colors.black) .append('path'); g.attr('transform', (d, i) => containerTransform(base.values(d, i).origin)) .select('path') .attr('d', symbol); base.decorate()(g, data, index); }); }; rebindAll(point, base, exclude('baseValue', 'barWidth')); rebind(point, join, 'key'); rebind(point, symbol, 'type', 'size'); return point; };
export default () => { const base = xyBase(); const pathGenerator = shapeBar() .x(0) .y(0); const valueAxisDimension = (generator) => base.orient() === 'vertical' ? generator.height : generator.width; const crossAxisDimension = (generator) => base.orient() === 'vertical' ? generator.width : generator.height; const bar = (data) => { const context = pathGenerator.context(); const filteredData = data.filter(base.defined); const projectedData = filteredData.map(base.values); const width = base.barWidth()(projectedData.map(d => d.x)); crossAxisDimension(pathGenerator)(width); if (base.orient() === 'vertical') { pathGenerator.verticalAlign('top'); pathGenerator.horizontalAlign('center'); } else { pathGenerator.horizontalAlign('right'); pathGenerator.verticalAlign('center'); } projectedData.forEach((datum, i) => { context.save(); context.beginPath(); context.translate(datum.origin[0], datum.origin[1]); valueAxisDimension(pathGenerator)(-datum.height); pathGenerator([datum]); context.fillStyle = colors.darkGray; base.decorate()(context, datum.d, i); context.fill(); context.closePath(); context.restore(); }); }; rebindAll(bar, base); rebind(bar, pathGenerator, 'context'); return bar; };
export default function() { var base = baseIndicator() .type(ALGORITHM_TYPE) .accessor(d => d.forceIndex); var underlyingAlgorithm = forceIndex(); var mergedAlgorithm = merge() .algorithm(underlyingAlgorithm) .merge((datum, indicator) => { datum.forceIndex = indicator; }); var indicator = function(data) { if (!base.accessor()) throw new Error(`Set an accessor to ${ALGORITHM_TYPE} before calculating`); return mergedAlgorithm(data); }; rebind(indicator, base, "id", "accessor", "stroke", "fill", "echo", "type"); rebind(indicator, underlyingAlgorithm, "sourcePath"); rebind(indicator, mergedAlgorithm, "merge", "skipUndefined"); return indicator; }
export default function() { var underlyingAlgorithm = forceIndex(); var smoothing, smoothingType, smoothingWindow; var merge = zipper() .combine((force, smoothed) => { return { force, smoothed }; }); function calculator(data) { var force = underlyingAlgorithm(data); var ma = smoothingType === "ema" ? ema() : sma(); var forceMA = ma .windowSize(smoothingWindow) .sourcePath(null); var smoothed = forceMA(force); return merge(force, smoothed); } rebind(calculator, underlyingAlgorithm, "sourcePath", "volumePath"); calculator.undefinedLength = function() { return underlyingAlgorithm.undefinedLength() + smoothingWindow; }; calculator.smoothing = function(x) { if (!arguments.length) { return smoothing; } smoothing = x; return calculator; }; calculator.smoothingType = function(x) { if (!arguments.length) { return smoothingType; } smoothingType = x; return calculator; }; calculator.smoothingWindow = function(x) { if (!arguments.length) { return smoothingWindow; } smoothingWindow = x; return calculator; }; return calculator; }
export default function() { let closeValue = (d, i) => d.close; let highValue = (d, i) => d.high; let lowValue = (d, i) => d.low; const emaComputer = exponentialMovingAverage() .period(13); const elderRay = data => { emaComputer.value(closeValue); return zip(data, emaComputer(data)) .map(d => { const bullPower = convertNaN(highValue(d[0]) - d[1]); const bearPower = convertNaN(lowValue(d[0]) - d[1]); return { bullPower, bearPower }; }); }; elderRay.closeValue = (...args) => { if (!args.length) { return closeValue; } closeValue = args[0]; return elderRay; }; elderRay.highValue = (...args) => { if (!args.length) { return highValue; } highValue = args[0]; return elderRay; }; elderRay.lowValue = (...args) => { if (!args.length) { return lowValue; } lowValue = args[0]; return elderRay; }; rebind(elderRay, emaComputer, 'period'); return elderRay; }
export default function() { const slidingWindow = _slidingWindow() .period(14); const wildersSmoothing = (values, prevAvg) => prevAvg + ((values[values.length - 1] - prevAvg) / values.length); const downChange = ([prevClose, close]) => prevClose < close ? 0 : prevClose - close; const upChange = ([prevClose, close]) => prevClose > close ? 0 : close - prevClose; const updateAverage = (changes, prevAverage) => prevAverage !== undefined ? wildersSmoothing(changes, prevAverage) : mean(changes); const makeAccumulator = () => { let prevClose; let downChangesAvg; let upChangesAvg; return closes => { if (!closes) { if (prevClose !== undefined) { prevClose = NaN; } return undefined; } if (prevClose === undefined) { prevClose = closes[0]; return undefined; } const closePairs = pairs([prevClose, ...closes]); downChangesAvg = updateAverage(closePairs.map(downChange), downChangesAvg); upChangesAvg = updateAverage(closePairs.map(upChange), upChangesAvg); const rs = !isNaN(prevClose) ? (upChangesAvg / downChangesAvg) : NaN; return convertNaN(100 - (100 / (1 + rs))); }; }; var rsi = data => { const rsiAccumulator = makeAccumulator(); slidingWindow.accumulator(rsiAccumulator); return slidingWindow(data); }; rebind(rsi, slidingWindow, 'period', 'value'); return rsi; }
export default () => { const symbol = symbolShape(); const base = xyBase(); const point = (data) => { const filteredData = data.filter(base.defined); const context = symbol.context(); filteredData.forEach((d, i) => { context.save(); const values = base.values(d, i); context.translate(values.origin[0], values.origin[1]); context.beginPath(); symbol(data); context.strokeStyle = colors.black; context.fillStyle = colors.gray; base.decorate()(context, d, i); context.stroke(); context.fill(); context.closePath(); context.restore(); }); }; rebindAll(point, base, exclude('baseValue', 'bandwidth', 'align')); rebind(point, symbol, 'size', 'type', 'context'); return point; };
const strategyInterceptor = (strategy) => { const interceptor = (layout) => { const start = new Date(); const finalLayout = strategy(layout); const time = new Date() - start; // record some statistics on this strategy if (!interceptor.time) { Object.defineProperty(interceptor, 'time', { enumerable: false, writable: true }); Object.defineProperty(interceptor, 'hidden', { enumerable: false, writable: true }); Object.defineProperty(interceptor, 'overlap', { enumerable: false, writable: true }); } const visibleLabels = finalLayout.filter((d) => !d.hidden); interceptor.time = time; interceptor.hidden = finalLayout.length - visibleLabels.length; interceptor.overlap = sum(visibleLabels.map((label, index) => { return sum(visibleLabels.filter((_, i) => i !== index) .map((d) => layoutIntersect(d, label))); })); return finalLayout; }; rebind(interceptor, strategy, 'bounds'); return interceptor; };
export default function() { var dataBucketer = bucket(); var x = (d) => d; var y = (d) => d; const largestTriangleOneBucket = (data) => { if (dataBucketer.bucketSize() >= data.length) { return data; } var pointAreas = calculateAreaOfPoints(data); var pointAreaBuckets = dataBucketer(pointAreas); var buckets = dataBucketer(data.slice(1, data.length - 1)); var subsampledData = buckets.map((thisBucket, i) => { var pointAreaBucket = pointAreaBuckets[i]; var maxArea = max(pointAreaBucket); var currentMaxIndex = pointAreaBucket.indexOf(maxArea); return thisBucket[currentMaxIndex]; }); // First and last data points are their own buckets. return [].concat([data[0]], subsampledData, [data[data.length - 1]]); }; function calculateAreaOfPoints(data) { var xyData = data.map((point) => [x(point), y(point)]); var pointAreas = range(1, xyData.length - 1).map((i) => { var lastPoint = xyData[i - 1]; var thisPoint = xyData[i]; var nextPoint = xyData[i + 1]; return 0.5 * Math.abs((lastPoint[0] - nextPoint[0]) * (thisPoint[1] - lastPoint[1]) - (lastPoint[0] - thisPoint[0]) * (nextPoint[1] - lastPoint[1])); }); return pointAreas; } rebind(largestTriangleOneBucket, dataBucketer, 'bucketSize'); largestTriangleOneBucket.x = function(d) { if (!arguments.length) { return x; } x = d; return largestTriangleOneBucket; }; largestTriangleOneBucket.y = function(d) { if (!arguments.length) { return y; } y = d; return largestTriangleOneBucket; }; return largestTriangleOneBucket; }