示例#1
0
export default function() {

    let multiplier = 2;

    const slidingWindow = _slidingWindow()
        .accumulator(values => {
            const stdDev = values && deviation(values);
            const average = values && mean(values);
            return {
                average: average,
                upper: convertNaN(average + multiplier * stdDev),
                lower: convertNaN(average - multiplier * stdDev)
            };
        });

    const bollingerBands = data => slidingWindow(data);

    bollingerBands.multiplier = (...args) => {
        if (!args.length) {
            return multiplier;
        }
        multiplier = args[0];
        return bollingerBands;
    };

    rebind(bollingerBands, slidingWindow, 'period', 'value');

    return bollingerBands;
}
示例#2
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;
};
示例#3
0
文件: area.js 项目: ScottLogic/d3fc
export default () => {
    const base = xyBase();

    const areaData = areaShape();

    const join = dataJoin('path', 'area');

    const area = (selection) => {

        if (selection.selection) {
            join.transition(selection);
        }

        areaData.defined(base.defined());

        selection.each((data, index, group) => {

            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);

            const path = join(select(group[index]), [data]);

            path.enter()
              .attr('fill', colors.gray);

            path.attr('d', areaData);

            base.decorate()(path, data, index);
        });
    };

    rebindAll(area, base, exclude('bandwidth', 'align'));
    rebind(area, join, 'key');
    rebind(area, areaData, 'curve');

    return area;
};
示例#4
0
文件: multi.js 项目: ScottLogic/d3fc
export default () => {

    const base = multiBase();

    const innerJoin = dataJoin('g');

    const join = dataJoin('g', 'multi');

    const multi = (selection) => {

        if (selection.selection) {
            join.transition(selection);
            innerJoin.transition(selection);
        }

        const mapping = base.mapping();
        const series = base.series();
        const xScale = base.xScale();
        const yScale = base.yScale();

        selection.each((data, index, group) => {

            const container = join(select(group[index]), series);

            // iterate over the containers, 'call'-ing the series for each
            container.each((dataSeries, seriesIndex, seriesGroup) => {
                dataSeries.xScale(xScale)
                         .yScale(yScale);

                const seriesData = mapping(data, seriesIndex, series);
                const innerContainer = innerJoin(select(seriesGroup[seriesIndex]), [seriesData]);

                innerContainer.call(dataSeries);
            });

            const unwrappedSelection = container.selection ? container.selection() : container;
            unwrappedSelection.order();

            base.decorate()(container, data, index);
        });
    };

    rebindAll(multi, base);
    rebind(multi, join, 'key');

    return multi;
};
示例#5
0
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;
}
示例#6
0
export default () => {

    const base = boxPlotBase();

    const pathGenerator = shapeBoxPlot()
        .value(0);

    const boxPlot = (data) => {
        const filteredData = data.filter(base.defined());
        const context = pathGenerator.context();

        pathGenerator.orient(base.orient());

        filteredData.forEach((d, i) => {
            context.save();

            const values = base.values(d, i);
            context.translate(values.origin[0], values.origin[1]);
            context.beginPath();

            pathGenerator.median(values.median)
                .upperQuartile(values.upperQuartile)
                .lowerQuartile(values.lowerQuartile)
                .high(values.high)
                .width(values.width)
                .low(values.low)([d]);

            context.fillStyle = colors.gray;
            context.strokeStyle = colors.black;

            base.decorate()(context, d, i);

            context.fill();
            context.stroke();
            context.closePath();

            context.restore();
        });
    };

    rebindAll(boxPlot, base);
    rebind(boxPlot, pathGenerator, 'cap', 'context');

    return boxPlot;
};
示例#7
0
export default (pathGenerator) => {

    const base = ohlcBase();

    const candlestick = (data) => {
        const filteredData = data.filter(base.defined());
        const context = pathGenerator.context();

        filteredData.forEach((d, i) => {
            context.save();

            const values = base.values(d, i);
            context.translate(values.cross, values.high);
            context.beginPath();

            pathGenerator.x(0)
                .open(() => values.open - values.high)
                .width(values.width)
                .high(0)
                .low(() => values.low - values.high)
                .close(() => values.close - values.high)([d]);

            const color = values.direction === 'up' ? colors.green : colors.red;
            context.strokeStyle = color;
            context.fillStyle = color;

            base.decorate()(context, d, i);

            context.fill();
            context.stroke();
            context.closePath();

            context.restore();
        });
    };

    rebind(candlestick, pathGenerator, 'context');
    rebindAll(candlestick, base);

    return candlestick;

};
示例#8
0
文件: point.js 项目: ScottLogic/d3fc
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(d, i);

            context.strokeStyle = colors.black;
            context.fillStyle = colors.gray;

            base.decorate()(context, d, i);

            context.fill();
            context.stroke();
            context.closePath();

            context.restore();
        });
    };

    rebindAll(point, base, exclude('baseValue', 'bandwidth', 'align'));
    rebind(point, symbol, 'size', 'type', 'context');

    return point;

};
示例#9
0
export default () => {
    const event = dispatch('point');

    function mousemove() {
        const point = mouse(this);
        event.call('point', this, [{ x: point[0], y: point[1] }]);
    }

    function mouseleave() {
        void event.call('point', this, []);
    }

    const instance = (selection) => {
        selection
            .on('mouseenter.pointer', mousemove)
            .on('mousemove.pointer', mousemove)
            .on('mouseleave.pointer', mouseleave);
    };

    rebind(instance, event, 'on');

    return instance;
};
示例#10
0
文件: demo.js 项目: ScottLogic/d3fc
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;
};
示例#11
0
文件: line.js 项目: ScottLogic/d3fc
export default () => {

    let xScale = scaleIdentity();
    let yScale = scaleIdentity();
    let value = d => d;
    let label = value;
    let decorate = () => {};
    let orient = 'horizontal';

    const lineData = lineShape();

    const instance = (data) => {
        if (orient !== 'horizontal' && orient !== 'vertical') {
            throw new Error('Invalid orientation');
        }
        const horizontal = orient === 'horizontal';
        const context = lineData.context();
        // the value scale which the annotation 'value' relates to, the crossScale
        // is the other. Which is which depends on the orienation!
        const crossScale = horizontal ? xScale : yScale;
        const valueScale = horizontal ? yScale : xScale;
        const crossDomain = crossScale.domain();
        const textOffsetX = horizontal ? 9 : 0;
        const textOffsetY = horizontal ? 0 : 9;
        const textAlign = horizontal ? 'left' : 'center';
        const textBaseline = horizontal ? 'middle' : 'hanging';

        data.forEach((d, i) => {
            context.save();
            context.beginPath();
            context.strokeStyle = '#bbb';
            context.fillStyle = '#000';
            context.textAlign = textAlign;
            context.textBaseline = textBaseline;

            decorate(context, d, i);

            // Draw line
            lineData.context(context)(crossDomain.map(extent => {
                const point = [crossScale(extent), valueScale(value(d))];
                return horizontal ? point : point.reverse();
            }));

            // Draw label
            const x = horizontal ? crossScale(crossDomain[1]) : valueScale(value(d));
            const y = horizontal ? valueScale(value(d)) : crossScale(crossDomain[1]);
            context.fillText(label(d), x + textOffsetX, y + textOffsetY);

            context.fill();
            context.stroke();
            context.closePath();
            context.restore();
        });
    };

    instance.xScale = (...args) => {
        if (!args.length) {
            return xScale;
        }
        xScale = args[0];
        return instance;
    };
    instance.yScale = (...args) => {
        if (!args.length) {
            return yScale;
        }
        yScale = args[0];
        return instance;
    };
    instance.value = (...args) => {
        if (!args.length) {
            return value;
        }
        value = constant(args[0]);
        return instance;
    };
    instance.label = (...args) => {
        if (!args.length) {
            return label;
        }
        label = constant(args[0]);
        return instance;
    };
    instance.decorate = (...args) => {
        if (!args.length) {
            return decorate;
        }
        decorate = args[0];
        return instance;
    };
    instance.orient = (...args) => {
        if (!args.length) {
            return orient;
        }
        orient = args[0];
        return instance;
    };

    rebind(instance, lineData, 'context');

    return instance;
};
示例#12
0
export default () => {

    let x = (d) => d.x;
    let y = (d) => d.y;
    let xScale = scaleIdentity();
    let yScale = scaleIdentity();

    const point = seriesCanvasPoint();

    const horizontalLine = annotationLine();

    const verticalLine = annotationLine()
        .orient('vertical');

    // The line annotations and point series used to render the crosshair are positioned using
    // screen coordinates. This function constructs an identity scale for these components.
    const xIdentity = scaleIdentity();
    const yIdentity = scaleIdentity();

    const multi = seriesCanvasMulti()
        .series([horizontalLine, verticalLine, point])
        .xScale(xIdentity)
        .yScale(yIdentity)
        .mapping((data) => [data]);

    const instance = (data) => {

        data.forEach(d => {
            // Assign the identity scales an accurate range to allow the line annotations to cover
            // the full width/height of the chart.
            xIdentity.range(xScale.range());
            yIdentity.range(yScale.range());

            point.crossValue(x)
                .mainValue(y);

            horizontalLine.value(y);

            verticalLine.value(x);

            multi(d);
        });
    };

    // Don't use the xValue/yValue convention to indicate that these values are in screen
    // not domain co-ordinates and are therefore not scaled.
    instance.x = (...args) => {
        if (!args.length) {
            return x;
        }
        x = args[0];
        return instance;
    };
    instance.y = (...args) => {
        if (!args.length) {
            return y;
        }
        y = args[0];
        return instance;
    };
    instance.xScale = (...args) => {
        if (!args.length) {
            return xScale;
        }
        xScale = args[0];
        return instance;
    };
    instance.yScale = (...args) => {
        if (!args.length) {
            return yScale;
        }
        yScale = args[0];
        return instance;
    };

    const lineIncludes = include('label', 'decorate');
    rebindAll(instance, horizontalLine, lineIncludes, prefix('y'));
    rebindAll(instance, verticalLine, lineIncludes, prefix('x'));
    rebind(instance, point, 'decorate');
    rebind(instance, multi, 'context');

    return instance;
};