function prettifyNumber(number) { // show "billion" instead of "giga" return number > 10 ? format('.2s')(number).replace('G', 'B') : (''+number).slice(0, 3); }
// FIXME fieldStats.max isn't always the longest function getMaxNumberLength(encoding, et, fieldStats) { var format = encoding.numberFormat(et, fieldStats); return d3_format.format(format)(fieldStats.max).length; }
import { select, selectAll, event as currentEvent } from 'd3-selection'; import { scaleLinear } from 'd3-scale'; import { NETWORK_FAILURE_ALERT, LOADING_ICON, getParameterByName } from '../utils'; fetch('/data/statetax/data.min.json') .then(response => response.json()) .then(data => { $(() => { app(data); }); }) .catch(error => console.error('oops', error)); const dollars = format('$00'); //// format function for dollars //// props // https://derekswingley.com/2016/08/01/using-and-bundling-individual-d3-modules/ const app = (data) => { const $link = $('#link'); const $stateMap = $('#state_map'); const max = {}; //// maximum values for primary areas const min = {}; const rgbs = {}; const scales = {}; //// scaling functions by area const colorcode = { //// color multipliers r: 0.2, g: 1,
render() { const kagiCalculator = kagi(); const { type, data: initialData, width, ratio } = this.props; const calculatedData = kagiCalculator(initialData); const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(calculatedData); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas height={400} width={width} ratio={ratio} margin={{ left: 80, right: 80, top: 10, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents}> <Chart id={1} yExtents={d => [d.high, d.low]} padding={{ top: 10, bottom: 20 }}> <XAxis axisAt="bottom" orient="bottom"/> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <KagiSeries /> <OHLCTooltip origin={[-40, 0]}/> </Chart> <Chart id={2} yExtents={d => d.volume} height={150} origin={(w, h) => [0, h - 150]}> <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".0s")}/> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} stroke fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} opacity={0.5} /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
render() { const { type, width, ratio } = this.props; const { data, xExtents, xScale, xAccessor, displayXAccessor, brushEnabled } = this.state; const yExtents1 = isDefined(this.state.yExtents1) ? this.state.yExtents1 : [d => [d.high, d.low], ema26.accessor(), ema12.accessor()]; const yExtents3 = isDefined(this.state.yExtents3) ? this.state.yExtents3 : macdCalculator.accessor(); return ( <ChartCanvas height={600} width={width} ratio={ratio} margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents} > <Chart id={1} height={400} yPanEnabled={isDefined(this.state.yExtents1)} yExtents={yExtents1} padding={{ top: 10, bottom: 20 }} > <XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} /> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <LineSeries yAccessor={ema26.accessor()} stroke={ema26.stroke()}/> <LineSeries yAccessor={ema12.accessor()} stroke={ema12.stroke()}/> <CurrentCoordinate yAccessor={ema26.accessor()} fill={ema26.stroke()} /> <CurrentCoordinate yAccessor={ema12.accessor()} fill={ema12.stroke()} /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <OHLCTooltip origin={[-40, 0]}/> <MovingAverageTooltip onClick={e => console.log(e)} origin={[-38, 15]} options={[ { yAccessor: ema26.accessor(), type: ema26.type(), stroke: ema26.stroke(), windowSize: ema26.options().windowSize, }, { yAccessor: ema12.accessor(), type: ema12.type(), stroke: ema12.stroke(), windowSize: ema12.options().windowSize, }, ]} /> <Brush ref={this.saveInteractiveNode(1)} enabled={brushEnabled} type={BRUSH_TYPE} onBrush={this.handleBrush1}/> </Chart> <Chart id={2} height={150} yExtents={[d => d.volume, smaVolume50.accessor()]} origin={(w, h) => [0, h - 300]} > <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".2s")}/> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} /> <AreaSeries yAccessor={smaVolume50.accessor()} stroke={smaVolume50.stroke()} fill={smaVolume50.fill()}/> </Chart> <Chart id={3} height={150} yExtents={yExtents3} yPanEnabled={isDefined(this.state.yExtents3)} origin={(w, h) => [0, h - 150]} padding={{ top: 10, bottom: 10 }} > <XAxis axisAt="bottom" orient="bottom"/> <YAxis axisAt="right" orient="right" ticks={2} /> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <Brush ref={this.saveInteractiveNode(3)} enabled={brushEnabled} type={BRUSH_TYPE} onBrush={this.handleBrush3}/> <MACDSeries yAccessor={d => d.macd} {...macdAppearance} /> <MACDTooltip origin={[-38, 15]} yAccessor={d => d.macd} options={macdCalculator.options()} appearance={macdAppearance} /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
/** * Calculates percentage of value from total * @param {Number} value Value to check * @param {Number} total Sum of values * @param {String} decimals Specifies number of decimals https://github.com/d3/d3-format * @return {String} Percentage */ function calculatePercent(value, total, decimals) { return d3Format.format(decimals)(value / total * 100); }
// @flow weak import React, { PureComponent, PropTypes } from 'react'; import { format } from 'd3-format'; import palette from 'docs/src/utils/palette'; import { dims } from '../module'; const numberFormat = format(','); class Tick extends PureComponent { static propTypes = { data: PropTypes.shape({ val: React.PropTypes.number.isRequired, }).isRequired, duration: PropTypes.number.isRequired, prevScale: PropTypes.func.isRequired, currScale: PropTypes.func.isRequired, removeNode: PropTypes.func.isRequired, }; tick = null; // Root node ref set in render method onAppear() { const { prevScale, currScale, data: { val }, duration } = this.props; return { tick: { opacity: [1e-6, 1], transform: [ `translate(0,${prevScale(val)})`, `translate(0,${currScale(val)})`,
render() { const { type, data: initialData, width, ratio } = this.props; const { channels_1 } = this.state; const xScaleProvider = discontinuousTimeScaleProvider.inputDateAccessor( d => d.date ); const { data, xScale, xAccessor, displayXAccessor } = xScaleProvider( initialData ); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas ref={this.saveCanvasNode} height={400} width={width} ratio={ratio} margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents} > <Chart id={1} yExtents={[d => [d.high, d.low]]} padding={{ top: 10, bottom: 20 }} > <YAxis axisAt="right" orient="right" ticks={5} /> <XAxis axisAt="bottom" orient="bottom" /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <CandlestickSeries /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => (d.close > d.open ? "#6BA583" : "#FF0000")} /> <OHLCTooltip origin={[-40, 0]} /> <StandardDeviationChannel ref={this.saveInteractiveNodes("StandardDeviationChannel", 1)} enabled={this.state.enableInteractiveObject} onStart={() => console.log("START")} onComplete={this.onDrawComplete} channels={channels_1} /> </Chart> <CrossHairCursor /> <DrawingObjectSelector enabled={!this.state.enableInteractiveObject} getInteractiveNodes={this.getInteractiveNodes} drawingObjectMap={{ StandardDeviationChannel: "channels" }} onSelect={this.handleSelection} /> </ChartCanvas> ); }
PropTypes.array, PropTypes.func ]).isRequired, fontFamily: PropTypes.string, fontSize: PropTypes.number, onClick: PropTypes.func, displayValues: PropTypes.func, volumeFormat: PropTypes.func, textFill: PropTypes.string, labelFill: PropTypes.string, }; OHLCTooltip.defaultProps = { accessor: (d) => { return { date: d.date, open: d.open, high: d.high, low: d.low, close: d.close, volume: d.volume }; }, xDisplayFormat: timeFormat("%Y-%m-%d"), volumeFormat: format(".4s"), ohlcFormat: format(".2f"), displayValues: displayValues, origin: [0, 0], }; function displayValues(props, moreProps) { const { xDisplayFormat, accessor, volumeFormat, ohlcFormat } = props; const { displayXAccessor, currentItem } = moreProps; let displayDate, open, high, low, close, volume; displayDate = open = high = low = close = volume = "n/a"; if (isDefined(currentItem) && isDefined(accessor(currentItem))
strokeWidth: 3, strokeDasharray: '2, 2' } }, { name: 'Chevrolet', mileage: 5, price: 4, safety: 6, performance: 4, interior: 5, warranty: 6 } ]; const basicFormat = format('.2r'); const wideFormat = format('.3r'); export default function BasicParallelCoordinates(props) { return ( <ParallelCoordinates data={DATA} tickFormat={t => wideFormat(t)} margin={50} colorRange={['#172d47', '#911116', '#998965']} domains={[ {name: 'mileage', domain: [0, 10]}, { name: 'price', domain: [2, 16], tickFormat: t => `$${basicFormat(t)}`,
render() { const { activeTile, tiles, loading, moreTilesLoading, settings: { date, weeks, bands }, setRecentImagerySettings, setRecentImageryLoading, setModalMetaSettings, onClickClose, error } = this.props; const selected = this.state.selected || activeTile || {}; return ( <div className="c-recent-imagery-settings prompts-recent-imagery"> <div className="top-section"> <div className="recent-menu"> <div className="title">Recent satellite imagery</div> <div className="recent-actions"> <Button className="info-btn" theme="theme-button-tiny theme-button-grey-filled square" onClick={() => setModalMetaSettings({ metakey: 'recent_satellite_imagery' }) } > <Icon icon={infoIcon} /> </Button> <button className="close-btn" onClick={onClickClose}> <Icon icon={closeIcon} className="icon-close" /> </button> </div> </div> <div className="dates"> <div className="title">ACQUISITION DATE</div> <div className="buttons"> <Dropdown theme="theme-dropdown-button" value={weeks} options={WEEKS} onChange={option => { setRecentImagerySettings({ weeks: option }); track('recentImageryDateRange'); }} native /> <div className="before">before</div> <Datepicker date={date ? moment(date) : moment()} handleOnDateChange={d => { setRecentImagerySettings({ date: d.format('YYYY-MM-DD') }); track('recentImageryDate'); }} settings={{ minDate: '2013-01-01', maxDate: moment().format('YYYY-MM-DD'), hideKeyboardShortcutsPanel: true, noBorder: true, readOnly: true, displayFormat: 'D MMM YYYY', isOutsideRange: d => d.isAfter(moment()) || d.isBefore(moment('2000-01-01')), block: true }} /> </div> </div> <div className="clouds"> <div className="title">MAXIMUM CLOUD COVER PERCENTAGE</div> <Slider className="theme-slider-green" value={this.state.clouds} marks={{ 0: '0%', 25: '25%', 50: '50%', 75: '75%', 100: '100%' }} marksOnTop step={5} dots onChange={this.handleCloundsChange} onAfterChange={d => { setRecentImagerySettings({ clouds: d }); track('recentImageryClouds'); }} /> </div> </div> <div className="thumbnails"> {tiles && !!tiles.length && ( <Fragment> <div key="thumbnails-header" className="header"> <div className="description"> <p> {moment(selected.dateTime) .format('DD MMM YYYY') .toUpperCase()} </p> <p>{format('.0f')(selected.cloudScore)}% cloud coverage</p> <p>{startCase(selected.instrument)}</p> </div> <Dropdown className="band-selector" theme="theme-dropdown-button" value={bands} options={BANDS} onChange={option => { setRecentImagerySettings({ bands: option, selected: null, selectedIndex: 0 }); track('recentImageryImageType'); }} native /> </div> <div className="thumbnail-grid"> {tiles && !!tiles.length && tiles.map((tile, i) => ( <RecentImageryThumbnail key={tile.id} id={i} tile={tile} selected={!!activeTile && activeTile.id === tile.id} handleClick={() => { setRecentImagerySettings({ selected: tile.id, selectedIndex: i }); }} handleMouseEnter={() => { this.setState({ selected: tile }); }} handleMouseLeave={() => { this.setState({ selected: null }); }} /> ))} </div> </Fragment> )} {error && ( <RefreshButton refetchFn={() => { setRecentImageryLoading({ loading: false, error: false }); track('refetchDataBtn', { label: 'Recent imagery' }); }} /> )} {!error && (!tiles || !tiles.length) && !loading && ( <NoContent className="placeholder" message="We can't find additional images for the selection" /> )} {loading && !moreTilesLoading && <Loader className="placeholder" />} </div> </div> ); }
const getExponent = x => { if (x === 0) { return 0; } return Math.floor(log10(Math.abs(x))); }; const removeRedundantZeros = ((() => { const zerosAfterDot = /\.0+([^\d].*)?$/; const zerosAfterNotZero = /(\.\d+?)0+([^\d].*)?$/; return str => str .replace(zerosAfterDot, '$1') .replace(zerosAfterNotZero, '$1$2'); }))(); const d3Format3S = d3.format('.3s'); const shortNumFormat = num => removeRedundantZeros(d3Format3S(num)); const getNumberFormatter = (start, end) => { const max = Math.max(Math.abs(start), Math.abs(end)); const absExp = getExponent(max); const diff = (start * end > 0 ? Math.abs(end - start) : max); const diffExp = getExponent(diff); const absExpVsDiffExp = Math.abs(absExp - diffExp); if (Math.abs(absExp) > 3 && absExpVsDiffExp <= 3) { // Short format // 1k, 500k, 1M. return shortNumFormat; }
import React from 'react'; import { Group } from '@vx/group'; import { ViolinPlot, BoxPlot } from '@vx/stats'; import { LinearGradient } from '@vx/gradient'; import { scaleBand, scaleLinear } from '@vx/scale'; import { genStats } from '@vx/mock-data'; import { withTooltip, Tooltip } from '@vx/tooltip'; import { extent } from 'd3-array'; import { format } from 'd3-format'; import { PatternLines } from '@vx/pattern'; const data = genStats(5); const twoDecimalFormat = format('.2f'); // accessors const x = d => d.boxPlot.x; const min = d => d.boxPlot.min; const max = d => d.boxPlot.max; const median = d => d.boxPlot.median; const firstQuartile = d => d.boxPlot.firstQuartile; const thirdQuartile = d => d.boxPlot.thirdQuartile; const outliers = d => d.boxPlot.outliers; export default withTooltip( ({ width, height, events = false, tooltipOpen, tooltipLeft, tooltipTop,
render() { const ema26 = ema() .id(0) .options({ windowSize: 26 }) .merge((d, c) => { d.ema26 = c; }) .accessor(d => d.ema26); const ema12 = ema() .id(1) .options({ windowSize: 12 }) .merge((d, c) => {d.ema12 = c;}) .accessor(d => d.ema12); const macdCalculator = macd() .options({ fast: 12, slow: 26, signal: 9, }) .merge((d, c) => {d.macd = c;}) .accessor(d => d.macd); const { type, data: initialData, width, ratio } = this.props; const calculatedData = macdCalculator(ema12(ema26(initialData))); const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(calculatedData); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas ref={this.saveCanvasNode} height={600} width={width} ratio={ratio} margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents} > <Chart id={1} height={400} yExtents={[d => [d.high, d.low], ema26.accessor(), ema12.accessor()]} padding={{ top: 10, bottom: 20 }} > <XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} /> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <LineSeries yAccessor={ema26.accessor()} stroke={ema26.stroke()}/> <LineSeries yAccessor={ema12.accessor()} stroke={ema12.stroke()}/> <CurrentCoordinate yAccessor={ema26.accessor()} fill={ema26.stroke()} /> <CurrentCoordinate yAccessor={ema12.accessor()} fill={ema12.stroke()} /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <OHLCTooltip origin={[-40, 0]}/> <MovingAverageTooltip onClick={e => console.log(e)} origin={[-38, 15]} options={[ { yAccessor: ema26.accessor(), type: ema26.type(), stroke: ema26.stroke(), windowSize: ema26.options().windowSize, }, { yAccessor: ema12.accessor(), type: ema12.type(), stroke: ema12.stroke(), windowSize: ema12.options().windowSize, }, ]} /> <TrendLine ref={this.saveInteractiveNodes("Trendline", 1)} enabled={this.state.enableTrendLine} type="RAY" snap={false} snapTo={d => [d.high, d.low]} onStart={() => console.log("START")} onComplete={this.onDrawCompleteChart1} trends={this.state.trends_1} /> </Chart> <Chart id={2} height={150} yExtents={[d => d.volume]} origin={(w, h) => [0, h - 300]} > <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".2s")}/> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} /> </Chart> <Chart id={3} height={150} yExtents={macdCalculator.accessor()} origin={(w, h) => [0, h - 150]} padding={{ top: 10, bottom: 10 }} > <XAxis axisAt="bottom" orient="bottom"/> <YAxis axisAt="right" orient="right" ticks={2} /> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <TrendLine ref={this.saveInteractiveNodes("Trendline", 3)} enabled={this.state.enableTrendLine} type="RAY" snap={false} snapTo={d => [d.high, d.low]} onStart={() => console.log("START")} onComplete={this.onDrawCompleteChart3} trends={this.state.trends_3} /> <MACDSeries yAccessor={d => d.macd} {...macdAppearance} /> <MACDTooltip origin={[-38, 15]} yAccessor={d => d.macd} options={macdCalculator.options()} appearance={macdAppearance} /> </Chart> <CrossHairCursor /> <DrawingObjectSelector enabled={!this.state.enableTrendLine} getInteractiveNodes={this.getInteractiveNodes} drawingObjectMap={{ Trendline: "trends" }} onSelect={this.handleSelection} /> </ChartCanvas> ); }
return function module() { let margin = { top: 20, right: 20, bottom: 30, left: 40 }, width = 960, height = 500, ease = d3Ease.easeQuadInOut, data, chartWidth, chartHeight, xScale, yScale, numOfVerticalTicks = 6, xAxis, xAxisLabel, yAxis, yAxisLabel, xAxisLabelOffset = 45, yAxisLabelOffset = -20, xAxisPadding = { top: 0, left: 0, bottom: 0, right: 0 }, yTickPadding = 8, svg, valueLabel = 'value', nameLabel = 'key', maskGridLines, baseLine, // Dispatcher object to broadcast the mouse events // Ref: https://github.com/mbostock/d3/wiki/Internals#d3_dispatch dispatcher = d3Dispatch.dispatch('customMouseOver', 'customMouseOut', 'customMouseMove'), // Formats yAxisTickFormat = d3Format.format('.3'), // extractors getKey = ({key}) => key, getValue = ({value}) => value; /** * This function creates the graph using the selection as container * @param {D3Selection} _selection A d3 selection that represents * the container(s) where the chart(s) will be rendered * @param {StepChartData} _data The data to attach and generate the chart */ function exports(_selection){ _selection.each(function(_data){ // Make space on the left of the graph for the y axis label chartWidth = width - margin.left - margin.right; chartHeight = height - margin.top - margin.bottom; data = cleanData(_data); buildScales(); buildAxis(); buildSVG(this); drawGridLines(); drawSteps(); drawAxis(); }); } /** * Creates the d3 x and y axis, setting orientations * @private */ function buildAxis(){ xAxis = d3Axis.axisBottom(xScale); yAxis = d3Axis.axisLeft(yScale) .ticks(numOfVerticalTicks) .tickPadding(yTickPadding) .tickFormat(yAxisTickFormat); } /** * Builds containers for the chart, the axis and a wrapper for all of them * Also applies the Margin convention * @private */ function buildContainerGroups(){ let container = svg .append('g') .classed('container-group', true) .attr('transform', `translate(${margin.left}, ${margin.top})`); container .append('g') .classed('grid-lines-group', true); container .append('g') .classed('chart-group', true); container .append('g') .classed('x-axis-group axis', true) .append('g') .classed('x-axis-label', true); container .append('g') .classed('y-axis-group axis', true) .append('g') .classed('y-axis-label', true); container .append('g').classed('metadata-group', true); } /** * Creates the x and y scales of the graph * @private */ function buildScales(){ xScale = d3Scale.scaleBand() .domain(data.map(getKey)) .rangeRound([0, chartWidth]) .paddingInner(0); yScale = d3Scale.scaleLinear() .domain([0, d3Array.max(data, getValue)]) .rangeRound([chartHeight, 0]); } /** * Builds the SVG element that will contain the chart * @param {HTMLElement} container DOM element that will work as the container of the graph * @private */ function buildSVG(container){ if (!svg) { svg = d3Selection.select(container) .append('svg') .classed('britechart step-chart', true); buildContainerGroups(); } svg .attr('width', width) .attr('height', height); } /** * Cleaning data adding the proper format * @param {StepChartData} data Data * @private */ function cleanData(data) { return data.map((d) => { d.value = +d[valueLabel]; d.key = String(d[nameLabel]); return d; }); } /** * Draws the x and y axis on the svg object within their * respective groups * @private */ function drawAxis(){ svg.select('.x-axis-group.axis') .attr('transform', `translate(0, ${chartHeight})`) .call(xAxis); if (xAxisLabel) { svg.select('.x-axis-label') .append('text') .attr('text-anchor', 'middle') .attr('x', chartWidth / 2) .attr('y', xAxisLabelOffset) .text(xAxisLabel); } svg.select('.y-axis-group.axis') .call(yAxis); if (yAxisLabel) { svg.select('.y-axis-label') .append('text') .attr('x', -chartHeight / 2) .attr('y', yAxisLabelOffset) .attr('text-anchor', 'middle') .attr('transform', 'rotate(270 0 0)') .text(yAxisLabel); } } /** * Draws the step elements within the chart group * @private */ function drawSteps(){ let steps = svg.select('.chart-group').selectAll('.step').data(data); // Enter steps.enter() .append('rect') .classed('step', true) .attr('x', chartWidth) // Initially drawing the steps at the end of Y axis .attr('y', ({value}) => yScale(value)) .attr('width', xScale.bandwidth()) .attr('height', (d) => (chartHeight - yScale(d.value))) .on('mouseover', function() { dispatcher.call('customMouseOver', this); }) .on('mousemove', function(d) { dispatcher.call('customMouseMove', this, d, d3Selection.mouse(this), [chartWidth, chartHeight]); }) .on('mouseout', function() { dispatcher.call('customMouseOut', this); }) .merge(steps) .transition() .ease(ease) .attr('x', ({key}) => xScale(key)) .attr('y', function(d) { return yScale(d.value); }) .attr('width', xScale.bandwidth()) .attr('height', function(d) { return chartHeight - yScale(d.value); }); // Exit steps.exit() .transition() .style('opacity', 0) .remove(); } /** * Draws grid lines on the background of the chart * @return void */ function drawGridLines(){ maskGridLines = svg.select('.grid-lines-group') .selectAll('line.horizontal-grid-line') .data(yScale.ticks(numOfVerticalTicks)) .enter() .append('line') .attr('class', 'horizontal-grid-line') .attr('x1', (xAxisPadding.left)) .attr('x2', chartWidth) .attr('y1', (d) => yScale(d)) .attr('y2', (d) => yScale(d)); //draw a horizontal line to extend x-axis till the edges baseLine = svg.select('.grid-lines-group') .selectAll('line.extended-x-line') .data([0]) .enter() .append('line') .attr('class', 'extended-x-line') .attr('x1', (xAxisPadding.left)) .attr('x2', chartWidth) .attr('y1', height - margin.bottom - margin.top) .attr('y2', height - margin.bottom - margin.top); } /** * Chart exported to png and a download action is fired * @public */ exports.exportChart = function(filename) { exportChart.call(exports, svg, filename); }; /** * Gets or Sets the margin of the chart * @param {object} _x Margin object to get/set * @return { margin | module} Current margin or Step Chart module to chain calls * @public */ exports.margin = function(_x) { if (!arguments.length) { return margin; } margin = _x; return this; }; /** * Gets or Sets the width of the chart * @param {number} _x Desired width for the graph * @return { width | module} Current width or step Chart module to chain calls * @public */ exports.width = function(_x) { if (!arguments.length) { return width; } width = _x; return this; }; /** * Gets or Sets the height of the chart * @param {number} _x Desired width for the graph * @return { height | module} Current height or Step Chart module to chain calls * @public */ exports.height = function(_x) { if (!arguments.length) { return height; } height = _x; return this; }; /** * Gets or Sets the number of vertical ticks on the chart * @param {number} _x Desired width for the graph * @return { height | module} Current height or Step Chart module to chain calls * @public */ exports.numOfVerticalTicks = function(_x) { if (!arguments.length) { return numOfVerticalTicks; } numOfVerticalTicks = _x; return this; }; /** * Gets or Sets the text of the xAxisLabel on the chart * @param {text} _x Desired text for the label * @return { text | module} label or Step Chart module to chain calls * @public */ exports.xAxisLabel = function(_x) { if (!arguments.length) { return xAxisLabel; } xAxisLabel = _x; return this; }; /** * Gets or Sets the offset of the xAxisLabel on the chart * @param {integer} _x Desired offset for the label * @return { integer | module} label or Step Chart module to chain calls * @public */ exports.xAxisLabelOffset = function(_x) { if (!arguments.length) { return xAxisLabelOffset; } xAxisLabelOffset = _x; return this; }; /** * Gets or Sets the text of the yAxisLabel on the chart * @param {text} _x Desired text for the label * @return { text | module} label or Step Chart module to chain calls * @public */ exports.yAxisLabel = function(_x) { if (!arguments.length) { return yAxisLabel; } yAxisLabel = _x; return this; }; /** * Gets or Sets the offset of the yAxisLabel on the chart * @param {integer} _x Desired offset for the label * @return { integer | module} label or Step Chart module to chain calls * @public */ exports.yAxisLabelOffset = function(_x) { if (!arguments.length) { return yAxisLabelOffset; } yAxisLabelOffset = _x; return this; }; /** * Exposes an 'on' method that acts as a bridge with the event dispatcher * We are going to expose this events: * customMouseOver, customMouseMove and customMouseOut * * @return {module} Bar Chart * @public */ exports.on = function() { let value = dispatcher.on.apply(dispatcher, arguments); return value === dispatcher ? exports : value; }; /** * Chart exported to png and a download action is fired * @public */ exports.exportChart = function(filename, title) { exportChart.call(exports, svg, filename, title); }; return exports; };
render() { const ema20 = ema() .id(0) .options({ windowSize: 20 }) .merge((d, c) => {d.ema20 = c;}) .accessor(d => d.ema20); const ema50 = ema() .id(2) .options({ windowSize: 50 }) .merge((d, c) => {d.ema50 = c;}) .accessor(d => d.ema50); const smaVolume70 = sma() .id(3) .options({ windowSize: 70, sourcePath: "volume" }) .merge((d, c) => {d.smaVolume70 = c;}) .accessor(d => d.smaVolume70); const { type, data: initialData, width, ratio } = this.props; const calculatedData = ema20(ema50(smaVolume70(initialData))); const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(calculatedData); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas height={400} ratio={ratio} width={width} margin={{ left: 90, right: 90, top: 70, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents}> <Chart id={2} yExtents={[d => d.volume, smaVolume70.accessor()]} height={150} origin={(w, h) => [0, h - 150]}> <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".0s")}/> <BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} /> <AreaSeries yAccessor={smaVolume70.accessor()} stroke={smaVolume70.stroke()} fill={smaVolume70.fill()}/> <CurrentCoordinate yAccessor={smaVolume70.accessor()} fill={smaVolume70.stroke()} /> <CurrentCoordinate yAccessor={d => d.volume} fill="#9B0A47" /> <EdgeIndicator itemType="first" orient="left" edgeAt="left" yAccessor={d => d.volume} displayFormat={format(".4s")} fill="#0F0F0F"/> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.volume} displayFormat={format(".4s")} fill="#0F0F0F"/> <EdgeIndicator itemType="first" orient="left" edgeAt="left" yAccessor={smaVolume70.accessor()} displayFormat={format(".4s")} fill={smaVolume70.fill()}/> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={smaVolume70.accessor()} displayFormat={format(".4s")} fill={smaVolume70.fill()}/> </Chart> <Chart id={1} yPan yExtents={[d => [d.high, d.low], ema20.accessor(), ema50.accessor()]} padding={{ top: 10, bottom: 20 }}> <XAxis axisAt="bottom" orient="bottom" /> <XAxis axisAt="top" orient="top" flexTicks /> <YAxis axisAt="right" orient="right" ticks={5} /> <CandlestickSeries /> <LineSeries yAccessor={ema20.accessor()} stroke={ema20.stroke()} highlightOnHover /> <LineSeries yAccessor={ema50.accessor()} stroke={ema50.stroke()} highlightOnHover /> <CurrentCoordinate yAccessor={ema20.accessor()} fill={ema20.stroke()} /> <CurrentCoordinate yAccessor={ema50.accessor()} fill={ema50.stroke()} /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={ema20.accessor()} fill={ema20.fill()}/> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={ema50.accessor()} fill={ema50.fill()}/> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <EdgeIndicator itemType="first" orient="left" edgeAt="left" yAccessor={ema20.accessor()} fill={ema20.fill()}/> <EdgeIndicator itemType="first" orient="left" edgeAt="left" yAccessor={ema50.accessor()} fill={ema50.fill()}/> <EdgeIndicator itemType="first" orient="left" edgeAt="left" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <MouseCoordinateX at="top" orient="top" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <MouseCoordinateY at="left" orient="left" displayFormat={format(".2f")} /> <OHLCTooltip origin={[-40, -65]}/> <MovingAverageTooltip onClick={e => console.log(e)} origin={[-38, 15]} options={[ { yAccessor: ema20.accessor(), type: ema20.type(), stroke: ema20.stroke(), windowSize: ema20.options().windowSize, }, { yAccessor: ema50.accessor(), type: ema50.type(), stroke: ema50.stroke(), windowSize: ema50.options().windowSize, }, ]} /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
render() { /* Styling the hard way const style = { in: { normal: {fill: "#A5C8E1"}, highlighted: {fill: "#BFDFF6"}, selected: {fill: "#2DB3D1"}, muted: {fill: "#A5C8E1", opacity: 0.4} } }; const altStyle = { out: { normal: {fill: "#FFCC9E"}, highlighted: {fill: "#fcc593"}, selected: {fill: "#2DB3D1"}, muted: {fill: "#FFCC9E", opacity: 0.4} } }; const combinedStyle = { in: { normal: {fill: "#A5C8E1"}, highlighted: {fill: "#BFDFF6"}, selected: {fill: "#2DB3D1"}, muted: {fill: "#A5C8E1", opacity: 0.4} }, out: { normal: {fill: "#FFCC9E"}, highlighted: {fill: "#fcc593"}, selected: {fill: "#2DB3D1"}, muted: {fill: "#FFCC9E", opacity: 0.4} } }; */ const style = styler([ { key: "in", color: "#A5C8E1", selected: "#2CB1CF" }, { key: "out", color: "#FFCC9E", selected: "#2CB1CF" }, { key: netTrafficColumnNames[1], color: "#A5C8E1", selected: "#2CB1CF" }, { key: netTrafficColumnNames[2], color: "#FFCC9E", selected: "#2CB1CF" }, { key: netTrafficColumnNames[3], color: "#DEB887", selected: "#2CB1CF" } ]); const formatter = format(".2s"); const selectedDate = this.state.selection ? this.state.selection.event.index().toNiceString() : "--"; const selectedValue = this.state.selection ? `${formatter(+this.state.selection.event.value(this.state.selection.column))}b` : "--"; const highlight = this.state.highlight; let infoValues = []; let infoNetValues = []; if (highlight) { const trafficText = `${formatter(highlight.event.get(highlight.column))}`; infoValues = [{ label: "Traffic", value: trafficText }]; infoNetValues = [{ label: "Traffic " + highlight.column, value: trafficText }]; } return ( <div> <div className="row"> <div className="col-md-12"> <b>October 2014 Total Traffic</b> <p style={{ color: "#808080" }}> Selected: {selectedDate} - {selectedValue} </p> </div> </div> <hr /> <div className="row"> <div className="col-md-12"> <Resizable> <ChartContainer utc={false} timeRange={this.state.timerange} format="day" enablePanZoom={true} onTimeRangeChanged={this.handleTimeRangeChange} onBackgroundClick={() => this.setState({ selection: null })} maxTime={new Date(1414827330868)} minTime={new Date(1412143472795)} minDuration={1000 * 60 * 60 * 24 * 5} > <ChartRow height="150"> <YAxis id="traffic" label="Traffic In (B)" min={0} max={max} width="70" /> <Charts> <BarChart axis="traffic" style={style} columns={["in"]} series={octoberTrafficSeries} info={infoValues} infoTimeFormat={index => moment(index.begin()).format("Do MMM 'YY") } highlighted={this.state.highlight} onHighlightChange={highlight => this.setState({ highlight }) } selected={this.state.selection} onSelectionChange={selection => this.setState({ selection }) } /> </Charts> <YAxis id="traffic-rate" label="Avg Traffic Rate In (bps)" min={0} max={max / (24 * 60 * 60) * 8} width="70" /> </ChartRow> </ChartContainer> </Resizable> </div> </div> <div className="row"> <div className="col-md-12"> <hr /> Alternatively we can display bars side by side using the 'spacing' and 'offset' props: <hr /> </div> </div> <div className="row"> <div className="col-md-12"> <Resizable> <ChartContainer timeRange={octoberTrafficSeries.range()} format="day" onBackgroundClick={() => this.setState({ selection: null })} > <ChartRow height="150"> <YAxis id="traffic-volume" label="Traffic (B)" classed="traffic-in" min={0} max={max} width="70" type="linear" /> <Charts> <BarChart axis="traffic-volume" style={style} size={10} offset={5.5} columns={["in"]} series={octoberTrafficSeries} highlighted={this.state.highlight} info={infoValues} infoTimeFormat="%m/%d/%y" onHighlightChange={highlight => this.setState({ highlight }) } selected={this.state.selection} onSelectionChange={selection => this.setState({ selection }) } /> <BarChart axis="traffic-volume" style={style} size={10} offset={-5.5} columns={["out"]} series={octoberTrafficSeries} info={infoValues} highlighted={this.state.highlight} onHighlightChange={highlight => this.setState({ highlight }) } selected={this.state.selection} onSelectionChange={selection => this.setState({ selection }) } /> </Charts> </ChartRow> </ChartContainer> </Resizable> </div> </div> <div className="row"> <div className="col-md-12"> <hr /> Or of course you can stack them: <hr /> </div> </div> <div className="row"> <div className="col-md-12"> <Resizable> <ChartContainer timeRange={octoberTrafficSeries.range()} format="day" onBackgroundClick={() => this.setState({ selection: null })} > <ChartRow height="150"> <YAxis id="traffic-volume" label="Traffic (B)" classed="traffic-in" min={0} max={max} width="70" type="linear" /> <Charts> <BarChart axis="traffic-volume" style={style} spacing={3} columns={["in", "out"]} series={octoberTrafficSeries} info={infoValues} highlighted={this.state.highlight} onHighlightChange={highlight => this.setState({ highlight }) } selected={this.state.selection} onSelectionChange={selection => this.setState({ selection }) } /> </Charts> </ChartRow> </ChartContainer> </Resizable> </div> </div> <div className="row"> <div className="col-md-12"> <hr /> BarChart can display negative values as well, as shown below for a stacked format. Note that all bars representing positive values are stacked together above the x-axis and the bars for negative values are stacked below the x-axis. <hr /> </div> </div> <div className="row"> <div className="col-md-12"> <Resizable> <ChartContainer timeRange={octoberNetTrafficSeries.range()} format="day" onBackgroundClick={() => this.setState({ selection: null })} > <ChartRow height="150"> <YAxis id="net-traffic-volume" label="Net Traffic (B)" classed="traffic-in" min={minTotalTraffic} max={maxTotalTraffic} width="70" type="linear" /> <Charts> <BarChart axis="net-traffic-volume" style={style} spacing={3} columns={netTrafficColumnNames.slice( 1, netTrafficColumnNames.length )} series={octoberNetTrafficSeries} info={infoNetValues} infoWidth={140} highlighted={this.state.highlight} onHighlightChange={highlight => this.setState({ highlight }) } selected={this.state.selection} onSelectionChange={selection => this.setState({ selection }) } /> </Charts> </ChartRow> </ChartContainer> </Resizable> </div> </div> </div> ); }
render() { const { type, data: initialData, width, ratio } = this.props; const ema26 = ema() .id(0) .options({ windowSize: 26 }) .merge((d, c) => { d.ema26 = c; }) .accessor(d => d.ema26); const ema12 = ema() .id(1) .options({ windowSize: 12 }) .merge((d, c) => {d.ema12 = c;}) .accessor(d => d.ema12); const macdCalculator = macd() .options({ fast: 12, slow: 26, signal: 9, }) .merge((d, c) => {d.macd = c;}) .accessor(d => d.macd); const smaVolume50 = sma() .id(3) .options({ windowSize: 50, sourcePath: "volume", }) .merge((d, c) => {d.smaVolume50 = c;}) .accessor(d => d.smaVolume50); const calculatedData = smaVolume50(macdCalculator(ema12(ema26(initialData)))); const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(calculatedData); return ( <ChartCanvas height={600} width={width} ratio={ratio} margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor}> <Chart id={1} height={400} yExtents={[d => [d.high, d.low], ema26.accessor(), ema12.accessor()]} padding={{ top: 10, bottom: 20 }}> <XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} /> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <LineSeries yAccessor={ema26.accessor()} stroke={ema26.stroke()}/> <LineSeries yAccessor={ema12.accessor()} stroke={ema12.stroke()}/> <CurrentCoordinate yAccessor={ema26.accessor()} fill={ema26.stroke()} /> <CurrentCoordinate yAccessor={ema12.accessor()} fill={ema12.stroke()} /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <OHLCTooltip origin={[-40, 0]}/> <MovingAverageTooltip onClick={e => console.log(e)} origin={[-38, 15]} options={[ { yAccessor: ema26.accessor(), type: "EMA", stroke: ema26.stroke(), windowSize: ema26.options().windowSize, }, { yAccessor: ema12.accessor(), type: "EMA", stroke: ema12.stroke(), windowSize: ema12.options().windowSize, }, ]} /> </Chart> <Chart id={2} height={150} yExtents={[d => d.volume, smaVolume50.accessor()]} origin={(w, h) => [0, h - 300]}> <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".0s")}/> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} /> <AreaSeries yAccessor={smaVolume50.accessor()} stroke={smaVolume50.stroke()} fill={smaVolume50.fill()}/> </Chart> <Chart id={3} height={150} yExtents={macdCalculator.accessor()} origin={(w, h) => [0, h - 150]} padding={{ top: 10, bottom: 10 }} > <XAxis axisAt="bottom" orient="bottom"/> <YAxis axisAt="right" orient="right" ticks={2} /> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <MACDSeries yAccessor={d => d.macd} {...macdAppearance} /> <MACDTooltip origin={[-38, 15]} yAccessor={d => d.macd} options={macdCalculator.options()} appearance={macdAppearance} /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
render() { const ema26 = ema() .id(0) .options({ windowSize: 26 }) .merge((d, c) => {d.ema26 = c;}) .accessor(d => d.ema26); const ema12 = ema() .id(1) .options({ windowSize: 12 }) .merge((d, c) => {d.ema12 = c;}) .accessor(d => d.ema12); const smaVolume50 = sma() .id(3) .options({ windowSize: 50, sourcePath: "volume" }) .merge((d, c) => {d.smaVolume50 = c;}) .accessor(d => d.smaVolume50); const rsiCalculator = rsi() .options({ windowSize: 14 }) .merge((d, c) => {d.rsi = c;}) .accessor(d => d.rsi); const atr14 = atr() .options({ windowSize: 14 }) .merge((d, c) => {d.atr14 = c;}) .accessor(d => d.atr14); const { type, data: initialData, width, ratio } = this.props; const calculatedData = ema26(ema12(smaVolume50(rsiCalculator(atr14(initialData))))); const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(calculatedData); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas height={600} width={width} ratio={ratio} margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents} > <Chart id={1} height={300} yExtents={[d => [d.high, d.low], ema26.accessor(), ema12.accessor()]} padding={{ top: 10, bottom: 20 }} > <XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} /> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <LineSeries yAccessor={ema26.accessor()} stroke={ema26.stroke()}/> <LineSeries yAccessor={ema12.accessor()} stroke={ema12.stroke()}/> <CurrentCoordinate yAccessor={ema26.accessor()} fill={ema26.stroke()} /> <CurrentCoordinate yAccessor={ema12.accessor()} fill={ema12.stroke()} /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <OHLCTooltip origin={[-40, 0]}/> <MovingAverageTooltip onClick={e => console.log(e)} origin={[-38, 15]} options={[ { yAccessor: ema26.accessor(), type: "EMA", stroke: ema26.stroke(), windowSize: ema26.options().windowSize, }, { yAccessor: ema12.accessor(), type: "EMA", stroke: ema12.stroke(), windowSize: ema12.options().windowSize, }, ]} /> </Chart> <Chart id={2} height={150} yExtents={[d => d.volume, smaVolume50.accessor()]} origin={(w, h) => [0, h - 400]} > <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".2s")}/> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} /> <AreaSeries yAccessor={smaVolume50.accessor()} stroke={smaVolume50.stroke()} fill={smaVolume50.fill()}/> </Chart> <Chart id={3} yExtents={[0, 100]} height={125} origin={(w, h) => [0, h - 250]} > <XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} /> <YAxis axisAt="right" orient="right" tickValues={[30, 50, 70]}/> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <RSISeries yAccessor={d => d.rsi} /> <RSITooltip origin={[-38, 15]} yAccessor={d => d.rsi} options={rsiCalculator.options()} /> </Chart> <Chart id={8} yExtents={atr14.accessor()} height={125} origin={(w, h) => [0, h - 125]} padding={{ top: 10, bottom: 10 }} > <XAxis axisAt="bottom" orient="bottom" /> <YAxis axisAt="right" orient="right" ticks={2}/> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <LineSeries yAccessor={atr14.accessor()} stroke={atr14.stroke()}/> <SingleValueTooltip yAccessor={atr14.accessor()} yLabel={`ATR (${atr14.options().windowSize})`} yDisplayFormat={format(".2f")} /* valueStroke={atr14.stroke()} - optional prop */ /* labelStroke="#4682B4" - optional prop */ origin={[-40, 15]}/> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
import { format } from 'd3-format' import { isUndefined } from 'underscore' const formatSingleDigitPrecision = format('.1f') const formatInteger = format('.0f') export function basicPrecisionPxFormatter (d) { if (isUndefined(d)) return null const res = d % 1 return ( (res < 0.05 || 1 - res < 0.05) ? formatInteger(d) : formatSingleDigitPrecision(d) ) + 'px' }
render() { const fi = forceIndex() .merge((d, c) => {d.fi = c;}) .accessor(d => d.fi); const fiEMA13 = ema() .id(1) .options({ windowSize: 13, sourcePath: "fi" }) .merge((d, c) => {d.fiEMA13 = c;}) .accessor(d => d.fiEMA13); const { type, data: initialData, width, ratio } = this.props; const calculatedData = fiEMA13(fi(initialData)); const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(calculatedData); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas height={550} width={width} ratio={ratio} margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents} > <Chart id={1} height={300} yExtents={d => [d.high, d.low]} padding={{ top: 10, right: 0, bottom: 20, left: 0 }} > <YAxis axisAt="right" orient="right" ticks={5} /> <XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} /> <OHLCTooltip origin={[-40, -10]}/> </Chart> <Chart id={2} height={150} yExtents={d => d.volume} origin={(w, h) => [0, h - 350]} > <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".2s")}/> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={(d) => d.close > d.open ? "#6BA583" : "#FF0000"} opacity={0.5} /> </Chart> <Chart id={3} height={100} yExtents={fi.accessor()} origin={(w, h) => [0, h - 200]} padding={{ top: 10, right: 0, bottom: 10, left: 0 }} > <XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} /> <YAxis axisAt="right" orient="right" ticks={4} tickFormat={format(".2s")}/> <MouseCoordinateY at="right" orient="right" displayFormat={format(".4s")} /> <AreaSeries baseAt={scale => scale(0)} yAccessor={fi.accessor()} /> <StraightLine yValue={0} /> <SingleValueTooltip yAccessor={fi.accessor()} yLabel="ForceIndex (1)" yDisplayFormat={format(".4s")} origin={[-40, 15]}/> </Chart> <Chart id={4} height={100} yExtents={fiEMA13.accessor()} origin={(w, h) => [0, h - 100]} padding={{ top: 10, right: 0, bottom: 10, left: 0 }} > <XAxis axisAt="bottom" orient="bottom" /> <YAxis axisAt="right" orient="right" ticks={4} tickFormat={format(".2s")}/> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".4s")} /> <AreaSeries baseAt={scale => scale(0)} yAccessor={fiEMA13.accessor()} /> <StraightLine yValue={0} /> <SingleValueTooltip yAccessor={fiEMA13.accessor()} yLabel={`ForceIndex (${fiEMA13.options().windowSize})`} yDisplayFormat={format(".4s")} origin={[-40, 15]}/> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
function updateFeed(feed) { const filteredData = feed.data.filter(d => (filterByDateRange(d) && d.measurementValue > 0)), filteredTrendData = feed.trendData.filter(d => (filterByDateRange(d) && d.measurementValue > 0)), maxMeasurement = max(filteredData, d => d.measurementValue), minMeasurement = min(filteredData, d => d.measurementValue), yBase = feedPadding * (feedIndices[feed.feedInfo.feedId] + 1) + feedHeight * feedIndices[feed.feedInfo.feedId], yTickFormat = (maxMeasurement > 1000) ? ".2s" : ".3", measurementScale = scaleLinear().range([feedHeight, 0]).domain([minMeasurement, maxMeasurement]), baselineDataY = [yBase, yBase + feedHeight], labelData = [yBase + feedHeight], baseLine = select('#baseLine' + feed.feedInfo.feedId).selectAll('line.baseline').data(baselineDataY), label = select('#label' + feed.feedInfo.feedId).selectAll('text').data(labelData), measurements = select('#data' + feed.feedInfo.feedId).selectAll('circle').data(filteredData), trendLine = select('#trend' + feed.feedInfo.feedId).selectAll('path').data([filteredTrendData]), yAxis = select("#yAxis" + feed.feedInfo.feedId), yAxisSettings = axisLeft(measurementScale) .tickSize(-4) .tickFormat(format(yTickFormat)) .ticks(5) .tickPadding(5); baseLine.enter().append('line') .attr('class', 'baseline') .attr("x1", 0) .attr("x2", timelineSize.width) .attr("stroke", "#eee") .attr("stroke-width", 1) .merge(baseLine) .attr("y1", d => d) .attr("y2", d => d); baseLine.exit().remove(); label.enter().append('text') .attr("class", "feedLabel") .attr('text-anchor', 'end') .attr("x", -35) .on('click', function () { selectedFeed = feed.feedInfo.feedId; select('#timelineOuter').selectAll('text.selected').classed('selected', false); select(this).classed('selected', true); select('#timelineInner').selectAll('circle.selected').classed('selected', false); select('#timelineInner').selectAll('path.selected').classed('selected', false); select('#data' + feed.feedInfo.feedId).selectAll('circle').classed('selected', true); select('#trend' + feed.feedInfo.feedId).selectAll('path').classed('selected', true); dataFeedSelectedRef(feed); } ) .merge(label) .attr("y", d => (d - feedHeight * 0.5)) .text(feed.feedInfo.measurementLabel); label.exit().remove(); measurements.enter().append('circle') .merge(measurements) // merge causes below to be applied to new and existing data .classed('selected', feed.feedInfo.feedId === selectedFeed) .on('mouseover', function (d) { return measurementTooltip.show(d); }) .on('mouseleave', function (d) { return measurementTooltip.hide(d); }) .attr("cx", function (d) { return sharedTimeScale(d.timestamp); }) .attr("cy", function (d) { return measurementScale(d.measurementValue) + yBase; }); measurements.exit().remove(); let interpLine = line() .x(function (d) { return sharedTimeScale(d.timestamp); }) .y(function (d) { return measurementScale(d.measurementValue) + yBase; }) .curve(curveLinear); trendLine.enter().append('path') .merge(trendLine) .classed('selected', feed.feedInfo.feedId === selectedFeed) .attr("d", d => interpLine(d)); trendLine.exit().remove(); yAxis.attr("transform", "translate(" + timelineMargin.left + "," + (timelineMargin.top / 2 + yBase) + ")"); yAxis.select("g.axis-y") .call(yAxisSettings); }
test('loading d3-format', function(assert) { assert.equal(format('$,.2f')(1200.98), '$1,200.98', 'loaded d3 format') })
render() { const { type, data: initialData, width, ratio } = this.props; const { mouseMoveEvent, panEvent, zoomEvent, zoomAnchor } = this.props; const { clamp } = this.props; const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(initialData); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; const margin = { left: 70, right: 70, top: 20, bottom: 30 }; const height = 400; const gridHeight = height - margin.top - margin.bottom; const gridWidth = width - margin.left - margin.right; const showGrid = true; const yGrid = showGrid ? { innerTickSize: -1 * gridWidth, tickStrokeOpacity: 0.2 } : {}; const xGrid = showGrid ? { innerTickSize: -1 * gridHeight, tickStrokeOpacity: 0.2 } : {}; return ( <ChartCanvas ref={this.saveNode} height={height} ratio={ratio} width={width} margin={{ left: 70, right: 70, top: 10, bottom: 30 }} mouseMoveEvent={mouseMoveEvent} panEvent={panEvent} zoomEvent={zoomEvent} clamp={clamp} zoomAnchor={zoomAnchor} type={type} seriesName="MSFT" data={data} xScale={xScale} xExtents={xExtents} xAccessor={xAccessor} displayXAccessor={displayXAccessor} > <Chart id={1} yExtents={d => [d.high, d.low]} > <XAxis axisAt="bottom" orient="bottom" zoomEnabled={!zoomEvent} {...xGrid} /> <YAxis axisAt="right" orient="right" ticks={5} zoomEnabled={!zoomEvent} {...yGrid} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <OHLCTooltip origin={[-40, 0]}/> <ZoomButtons /> </Chart> <Chart id={2} yExtents={d => d.volume} height={150} origin={(w, h) => [0, h - 150]} > <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".0s")} zoomEnabled={!zoomEvent} /> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={(d) => d.close > d.open ? "#6BA583" : "#FF0000"} /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
render() { const ema20 = ema() .options({ windowSize: 20, // optional will default to 10 sourcePath: "close", // optional will default to close as the source }) .skipUndefined(true) // defaults to true .merge((d, c) => {d.ema20 = c;}) // Required, if not provided, log a error .accessor(d => d.ema20) // Required, if not provided, log an error during calculation .stroke("blue"); // Optional const sma20 = sma() .options({ windowSize: 20 }) .merge((d, c) => {d.sma20 = c;}) .accessor(d => d.sma20); const wma20 = wma() .options({ windowSize: 20 }) .merge((d, c) => {d.wma20 = c;}) .accessor(d => d.wma20); const tma20 = tma() .options({ windowSize: 20 }) .merge((d, c) => {d.tma20 = c;}) .accessor(d => d.tma20); const ema50 = ema() .options({ windowSize: 50 }) .merge((d, c) => {d.ema50 = c;}) .accessor(d => d.ema50); const smaVolume50 = sma() .options({ windowSize: 20, sourcePath: "volume" }) .merge((d, c) => {d.smaVolume50 = c;}) .accessor(d => d.smaVolume50) .stroke("#4682B4") .fill("#4682B4"); const { type, data: initialData, width, ratio } = this.props; const calculatedData = ema20(sma20(wma20(tma20(ema50(smaVolume50(initialData)))))); const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(calculatedData); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas height={400} width={width} ratio={ratio} margin={{ left: 70, right: 70, top: 10, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents} > <Chart id={1} yExtents={[d => [d.high, d.low], sma20.accessor(), wma20.accessor(), tma20.accessor(), ema20.accessor(), ema50.accessor()]} padding={{ top: 10, bottom: 20 }} > <XAxis axisAt="bottom" orient="bottom"/> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <LineSeries yAccessor={sma20.accessor()} stroke={sma20.stroke()}/> <LineSeries yAccessor={wma20.accessor()} stroke={wma20.stroke()}/> <LineSeries yAccessor={tma20.accessor()} stroke={tma20.stroke()}/> <LineSeries yAccessor={ema20.accessor()} stroke={ema20.stroke()}/> <LineSeries yAccessor={ema50.accessor()} stroke={ema50.stroke()}/> <CurrentCoordinate yAccessor={sma20.accessor()} fill={sma20.stroke()} /> <CurrentCoordinate yAccessor={wma20.accessor()} fill={wma20.stroke()} /> <CurrentCoordinate yAccessor={tma20.accessor()} fill={tma20.stroke()} /> <CurrentCoordinate yAccessor={ema20.accessor()} fill={ema20.stroke()} /> <CurrentCoordinate yAccessor={ema50.accessor()} fill={ema50.stroke()} /> <OHLCTooltip origin={[-40, 0]}/> <MovingAverageTooltip onClick={e => console.log(e)} origin={[-38, 15]} options={[ { yAccessor: sma20.accessor(), type: "SMA", stroke: sma20.stroke(), windowSize: sma20.options().windowSize, echo: "some echo here", }, { yAccessor: wma20.accessor(), type: "WMA", stroke: wma20.stroke(), windowSize: wma20.options().windowSize, echo: "some echo here", }, { yAccessor: tma20.accessor(), type: "TMA", stroke: tma20.stroke(), windowSize: tma20.options().windowSize, echo: "some echo here", }, { yAccessor: ema20.accessor(), type: "EMA", stroke: ema20.stroke(), windowSize: ema20.options().windowSize, echo: "some echo here", }, { yAccessor: ema50.accessor(), type: "EMA", stroke: ema50.stroke(), windowSize: ema50.options().windowSize, echo: "some echo here", }, ]} /> </Chart> <Chart id={2} yExtents={[d => d.volume, smaVolume50.accessor()]} height={150} origin={(w, h) => [0, h - 150]} > <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".2s")}/> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "red"} /> <AreaSeries yAccessor={smaVolume50.accessor()} stroke={smaVolume50.stroke()} fill={smaVolume50.fill()}/> <CurrentCoordinate yAccessor={smaVolume50.accessor()} fill={smaVolume50.stroke()} /> <CurrentCoordinate yAccessor={d => d.volume} fill="#9B0A47" /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
render() { const { type, data, width, ratio } = this.props; const xAccessor = d => d.date; const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas height={400} ratio={ratio} width={width} margin={{ left: 80, right: 80, top: 10, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={scaleTime()} xAccessor={xAccessor} xExtents={xExtents}> <Chart id={2} yExtents={[d => d.volume]} height={150} origin={(w, h) => [0, h - 150]}> <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".0s")}/> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} /> <CurrentCoordinate yAccessor={d => d.volume} fill="#9B0A47" /> <EdgeIndicator itemType="first" orient="left" edgeAt="left" yAccessor={d => d.volume} displayFormat={format(".4s")} fill="#0F0F0F"/> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.volume} displayFormat={format(".4s")} fill="#0F0F0F"/> </Chart> <Chart id={1} yExtents={[d => [d.high, d.low]]} padding={{ top: 40, bottom: 20 }}> <XAxis axisAt="bottom" orient="bottom"/> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateX rectWidth={60} at="bottom" orient="bottom" displayFormat={timeFormat("%H:%M:%S")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <OHLCTooltip origin={[-40, 0]} xDisplayFormat={timeFormat("%Y-%m-%d %H:%M:%S")}/> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
render() { const { type, data: initialData, width, ratio } = this.props; const ema20 = ema() .id(0) .options({ windowSize: 13 }) .merge((d, c) => { d.ema20 = c; }) .accessor(d => d.ema20); const ema50 = ema() .id(2) .options({ windowSize: 50 }) .merge((d, c) => { d.ema50 = c; }) .accessor(d => d.ema50); const buySell = algo() .windowSize(2) .accumulator(([prev, now]) => { const { ema20: prevShortTerm, ema50: prevLongTerm } = prev; const { ema20: nowShortTerm, ema50: nowLongTerm } = now; if (prevShortTerm < prevLongTerm && nowShortTerm > nowLongTerm) return "LONG"; if (prevShortTerm > prevLongTerm && nowShortTerm < nowLongTerm) return "SHORT"; }) .merge((d, c) => { d.longShort = c; }); const defaultAnnotationProps = { fontFamily: "Glyphicons Halflings", fontSize: 20, opacity: 0.8, onClick: console.log.bind(console), }; const longAnnotationProps = { ...defaultAnnotationProps, fill: "#006517", text: "\ue093", y: ({ yScale, datum }) => yScale(datum.low) + 20, tooltip: "Go long", }; const shortAnnotationProps = { ...defaultAnnotationProps, fill: "#E20000", text: "\ue094", y: ({ yScale, datum }) => yScale(datum.high), tooltip: "Go short", }; const margin = { left: 80, right: 80, top: 30, bottom: 50 }; const height = 400; const [yAxisLabelX, yAxisLabelY] = [width - margin.left - 40, margin.top + (height - margin.top - margin.bottom) / 2]; const calculatedData = buySell(ema50(ema20(initialData))); const xScaleProvider = discontinuousTimeScaleProvider .inputDateAccessor(d => d.date); const { data, xScale, xAccessor, displayXAccessor, } = xScaleProvider(calculatedData); const start = xAccessor(last(data)); const end = xAccessor(data[Math.max(0, data.length - 150)]); const xExtents = [start, end]; return ( <ChartCanvas height={height} width={width} ratio={ratio} margin={margin} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} xExtents={xExtents}> <Chart id={1} yExtents={[d => [d.high, d.low], ema20.accessor(), ema50.accessor()]} padding={{ top: 10, bottom: 20 }}> <XAxis axisAt="bottom" orient="bottom"/> <Label x={(width - margin.left - margin.right) / 2} y={height - 45} fontSize="12" text="XAxis Label here" /> <YAxis axisAt="right" orient="right" ticks={5} /> <Label x={yAxisLabelX} y={yAxisLabelY} rotate={-90} fontSize="12" text="YAxis Label here" /> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <LineSeries yAccessor={ema20.accessor()} stroke={ema20.stroke()}/> <LineSeries yAccessor={ema50.accessor()} stroke={ema50.stroke()}/> <CurrentCoordinate yAccessor={ema20.accessor()} fill={ema20.stroke()} /> <CurrentCoordinate yAccessor={ema50.accessor()} fill={ema50.stroke()} /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <OHLCTooltip origin={[-40, 0]}/> <MovingAverageTooltip onClick={e => console.log(e)} origin={[-38, 15]} options={[ { yAccessor: ema20.accessor(), type: "EMA", stroke: ema20.stroke(), windowSize: ema20.options().windowSize, }, { yAccessor: ema50.accessor(), type: "EMA", stroke: ema50.stroke(), windowSize: ema50.options().windowSize, }, ]} /> <Annotate with={LabelAnnotation} when={d => d.longShort === "LONG"} usingProps={longAnnotationProps} /> <Annotate with={LabelAnnotation} when={d => d.longShort === "SHORT"} usingProps={shortAnnotationProps} /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {scaleLinear} from 'd3-scale'; import {format} from 'd3-format'; import {AnimationPropType} from 'animation'; import XYPlot from 'plot/xy-plot'; import {DISCRETE_COLOR_RANGE} from 'theme'; import {MarginPropType} from 'utils/chart-utils'; import PolygonSeries from 'plot/series/polygon-series'; import LabelSeries from 'plot/series/label-series'; import DecorativeAxis from 'plot/axis/decorative-axis'; const predefinedClassName = 'rv-radar-chart'; const DEFAULT_FORMAT = format('.2r'); /** * Generate axes for each of the domains * @param {Object} props - props.animation {Boolean} - props.domains {Array} array of object specifying the way each axis is to be plotted - props.style {object} style object for the whole chart - props.tickFormat {Function} formatting function for axes - props.startingAngle {number} the initial angle offset * @return {Array} the plotted axis components */ function getAxes(props) { const { animation, domains, startingAngle,
displayFormat: PropTypes.func.isRequired, origin: PropTypes.array.isRequired, displayValuesFor: PropTypes.func, onClick: PropTypes.func, fontFamily: PropTypes.string, fontSize: PropTypes.number, width: PropTypes.number, // "width" only be used, if layout is "horizontal" or "horizontalRows". verticalSize: PropTypes.number, // "verticalSize" only be used, if layout is "vertical", "verticalRows". options: PropTypes.arrayOf( PropTypes.shape( { yLabel: PropTypes.oneOfType( [ PropTypes.string, PropTypes.func] ).isRequired, yAccessor: PropTypes.func.isRequired, labelFill: PropTypes.string, valueFill: PropTypes.string, withShape: PropTypes.bool, // "withShape" is ignored, if layout is "horizontalInline" or "vertical". } ) ), }; GroupTooltip.defaultProps = { className: "react-stockcharts-tooltip react-stockcharts-group-tooltip", layout: "horizontal", displayFormat: format( ".2f" ), displayValuesFor: displayValuesFor, origin: [0, 0], width: 60, verticalSize: 13, }; export default GroupTooltip;
render() { const { type, width, ratio } = this.props; const { data, ema26, ema12, macdCalculator, smaVolume50, xScale, xAccessor, displayXAccessor } = this.state; return ( <ChartCanvas ratio={ratio} width={width} height={600} margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type} seriesName="MSFT" data={data} xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor} onLoadMore={this.handleDownloadMore}> <Chart id={1} height={400} yExtents={[d => [d.high, d.low], ema26.accessor(), ema12.accessor()]} padding={{ top: 10, bottom: 20 }}> <XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} /> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <LineSeries yAccessor={ema26.accessor()} stroke={ema26.stroke()}/> <LineSeries yAccessor={ema12.accessor()} stroke={ema12.stroke()}/> <CurrentCoordinate yAccessor={ema26.accessor()} fill={ema26.stroke()} /> <CurrentCoordinate yAccessor={ema12.accessor()} fill={ema12.stroke()} /> <EdgeIndicator itemType="last" orient="right" edgeAt="right" yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/> <OHLCTooltip origin={[-40, 0]}/> <MovingAverageTooltip onClick={(e) => console.log(e)} origin={[-38, 15]} options={[ { yAccessor: ema26.accessor(), type: ema26.type(), stroke: ema26.stroke(), ...ema26.options(), }, { yAccessor: ema12.accessor(), type: ema12.type(), stroke: ema12.stroke(), ...ema12.options(), }, ]} /> </Chart> <Chart id={2} height={150} yExtents={[d => d.volume, smaVolume50.accessor()]} origin={(w, h) => [0, h - 300]}> <YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".2s")}/> <MouseCoordinateY at="left" orient="left" displayFormat={format(".4s")} /> <BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} /> <AreaSeries yAccessor={smaVolume50.accessor()} stroke={smaVolume50.stroke()} fill={smaVolume50.fill()}/> </Chart> <Chart id={3} height={150} yExtents={macdCalculator.accessor()} origin={(w, h) => [0, h - 150]} padding={{ top: 10, bottom: 10 }} > <XAxis axisAt="bottom" orient="bottom"/> <YAxis axisAt="right" orient="right" ticks={2} /> <MouseCoordinateX at="bottom" orient="bottom" displayFormat={timeFormat("%Y-%m-%d")} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <MACDSeries yAccessor={d => d.macd} {...macdAppearance} /> <MACDTooltip origin={[-38, 15]} yAccessor={d => d.macd} options={macdCalculator.options()} appearance={macdAppearance} /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }