Esempio n. 1
0
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;
}
Esempio n. 3
0
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;
};
Esempio n. 4
0
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;
}
Esempio n. 5
0
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;
};
Esempio n. 6
0
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;
};
Esempio n. 7
0
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;
}
Esempio n. 10
0
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;
};
Esempio n. 11
0
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;
}
Esempio n. 16
0
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;

};
Esempio n. 17
0
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;
}