renderTimeMarker(d) { const textStyle = { fontSize: 11, textAnchor: "left", fill: "#bdbdbd" }; let dateStr = `${d}`; if (this.props.timeFormat === "day") { const formatter = timeFormat("%d"); dateStr = formatter(d); } else if (this.props.timeFormat === "month") { const formatter = timeFormat("%B"); dateStr = formatter(d); } else if (this.props.timeFormat === "year") { const formatter = timeFormat("%Y"); dateStr = formatter(d); } else if (this.props.timeFormat === "relative") { dateStr = moment.duration(+d).format(); } else if (_.isString(this.props.timeFormat)) { const formatter = timeFormat(this.props.timeFormat); dateStr = formatter(d); } else if (_.isFunction(this.props.timeFormat)) { dateStr = this.props.timeFormat(d); } return ( <text x={0} y={0} dy="1.2em" style={textStyle}> {dateStr} </text> ); }
constructor(props) { super(props) const date = new Date() date.setMinutes(0) date.setSeconds(0) date.setMilliseconds(0) this.state = { dataA: range(100).map(i => ({ x: time.timeMinute.offset(date, i * 30), y: 10 + Math.round(Math.random() * 20), })), dataB: range(100).map(i => ({ x: time.timeMinute.offset(date, i * 30), y: 30 + Math.round(Math.random() * 20), })), dataC: range(100).map(i => ({ x: time.timeMinute.offset(date, i * 30), y: 60 + Math.round(Math.random() * 20), })), } this.formatTime = timeFormat('%Y %b %d') }
function save () { const recipientIds = _.map(vm.surveyRunForm.recipients, 'id'); if (recipientIds.length == 0) { alert("Please provide at least one recipient"); return; } const command = { name: vm.surveyRunForm.name, description: vm.surveyRunForm.description, surveyTemplateId: vm.selectedTemplate.id, selectionOptions: { entityReference: vm.parentEntityRef, scope: 'EXACT', }, involvementKindIds: [], issuanceKind: vm.surveyRunForm.issuanceKind, dueDate: vm.surveyRunForm.dueDate ? timeFormat('%Y-%m-%d')(vm.surveyRunForm.dueDate) : null, contactEmail: vm.surveyRunForm.contactEmail }; serviceBroker .execute(CORE_API.SurveyRunStore.create, [command]) .then(r => r.data.id) .then(runId => serviceBroker .execute(CORE_API.SurveyRunStore.createSurveyInstances, [ runId, recipientIds ]) .then(() => runId)) .then(runId => serviceBroker .execute(CORE_API.SurveyRunStore.updateStatus, [runId, {newStatus: 'ISSUED'}])) .then(() => { notification.success('Survey issued successfully'); vm.onDismissCreateForm(); }); }
render() { const { type, data: initialData, width, ratio } = 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]; return ( <ChartCanvas height={400} ratio={ratio} width={width} 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]]}> <XAxis axisAt="bottom" orient="bottom"/> <YAxis axisAt="right" orient="right" ticks={5} /> <MouseCoordinateY at="right" orient="right" displayFormat={format(".2f")} /> <CandlestickSeries /> <OHLCTooltip forChart={1} origin={[-40, 0]}/> </Chart> <Chart id={2} height={150} yExtents={d => d.volume} 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} fill={(d) => d.close > d.open ? "#6BA583" : "#FF0000"} /> </Chart> <CrossHairCursor /> </ChartCanvas> ); }
/** * Creates the d3 x and y axis, setting orientations * @private */ function buildAxis() { let dataSpan = yScale.domain()[1] - yScale.domain()[0]; let yTickNumber = dataSpan < verticalTicks - 1 ? dataSpan : verticalTicks; let minor, major; if (forceAxisSettings === 'custom' && typeof forcedXFormat === 'string') { minor = { tick: forcedXTicks, format: d3TimeFormat.timeFormat(forcedXFormat) }; major = null; } else { ({minor, major} = getXAxisSettings(dataByDate, width, forceAxisSettings, locale)); xMonthAxis = d3Axis.axisBottom(xScale) .ticks(major.tick) .tickSize(0, 0) .tickFormat(major.format); } xAxis = d3Axis.axisBottom(xScale) .ticks(minor.tick) .tickSize(10, 0) .tickPadding(tickPadding) .tickFormat(minor.format); yAxis = d3Axis.axisRight(yScale) .ticks(yTickNumber) .tickSize([0]) .tickPadding(tickPadding) .tickFormat(getFormattedValue); drawGridLines(minor.tick, yTickNumber); }
return (function (date) { return (timeSecond(date) < date ? timeFormat(millisecond) : timeMinute(date) < date ? timeFormat(second) : timeHour(date) < date ? timeFormat(minute) : timeDay(date) < date ? timeFormat(hour) : timeMonth(date) < date ? (timeWeek(date) < date ? timeFormat(day) : timeFormat(week)) : timeYear(date) < date ? timeFormat(month) : timeFormat(year))(date); });
const inputIndex = index.map(each => { const { format } = each; return { ...each, date: new Date(each.date), format: timeFormat(format), }; });
export const getFormatter = (format, scale) => { if (!format || isFunction(format)) return format if (scale.type === 'time') { const f = timeFormat(format) return d => f(new Date(d)) } return d3Format(format) }
getTimeTickValues() { const timeTicks = this.timeScale.ticks(3); const timePositions = timeTicks.map(t => this.timeScale(t.valueOf())); const formatTime = d3TimeFormat.timeFormat('%H:%M'); return timeTicks.map( (t, index) => { return { x: timePositions[index], y: defaultVolumeChartHeight, time: formatTime(timeTicks[index]) }; }); }
getData() { let parseDate = d3.timeParse("%Y-%m-%d"); this.startDate.setDate(this.startDate.getDate() + 1); var d = d3_time_format.timeFormat("%Y-%m-%d")(this.startDate); // console.log(formatTime) var t = this.rand(200) t = Math.abs(t) var item = {"date": parseDate(d), "data": t} this.dataArray.push(item); }
buildAxisElements() { this.xAxis = axisBottom().scale(this.xScale).tickFormat(timeFormat('%d-%m')).ticks(timeDay.every(1)); this.yAxis = axisLeft().scale(this.yScale); this.gX = select(this.axisNode) .append('g') .attr('class','xAxis') .call(this.xAxis); this.gY = select(this.axisNode) .append('g') .attr('class','yAxis') .call(this.yAxis); }
function drawXAxis(section, points = [], scale) { if (isEmpty(points)) return; const dates = pickDatesForXAxis(scale); const dateFormat = timeFormat("%d/%m"); const xAxis = axisBottom() .tickValues(dates) .tickSize(6) .scale(scale) .tickFormat(d => { return dateFormat(new Date(d)); }); section .call(xAxis); }
vm.updateDueDate = (newVal) => { if (!newVal) { notification.error('Due date cannot be blank'); } else { if (confirm('This will update the due date of all the instances under this run. ' + 'Are you sure you want to continue?')) { surveyRunStore.updateDueDate(id, { newDateVal: timeFormat('%Y-%m-%d')(newVal) }) .then(r => { notification.success('Survey run due date updated successfully'); loadSurveyRun(); loadInstances(); }, r => notification.error('Failed to update survey run due date') ); } } };
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 smaVolume50 = sma() .id(3) .options({ windowSize: 50, sourcePath: "volume", }) .merge((d, c) => {d.smaVolume50 = c;}) .accessor(d => d.smaVolume50); const { type, data: initialData, width, ratio } = this.props; const calculatedData = macdCalculator(smaVolume50(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 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, }, ]} /> <ClickCallback onMouseMove={ (moreProps, e) => { console.log("onMouseMove", moreProps, e); } } onMouseDown={ (moreProps, e) => { console.log("onMouseDown", moreProps, e); } } onClick={ (moreProps, e) => { console.log("onClick", moreProps, e); } } onDoubleClick={ (moreProps, e) => { console.log("onDoubleClick", moreProps, e); } } onContextMenu={ (moreProps, e) => { console.log("onContextMenu", moreProps, e); } } onPan={ (moreProps, e) => { console.log("onPan", moreProps, e); } } onPanEnd={ (moreProps, e) => { console.log("onPanEnd", moreProps, e); } } /> </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> ); }
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 = { onClick: console.log.bind(console), }; const longAnnotationProps = { ...defaultAnnotationProps, y: ({ yScale, datum }) => yScale(datum.low), fill: "#006517", path: buyPath, tooltip: "Go long", }; const shortAnnotationProps = { ...defaultAnnotationProps, y: ({ yScale, datum }) => yScale(datum.high), fill: "#FF0000", path: sellPath, 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={SvgPathAnnotation} when={d => d.longShort === "LONG"} usingProps={longAnnotationProps} /> <Annotate with={SvgPathAnnotation} when={d => d.longShort === "SHORT"} usingProps={shortAnnotationProps} /> </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> ); }
import { computed } from '@ember/object'; import moment from 'moment'; import d3TimeFormat from 'd3-time-format'; import d3Format from 'd3-format'; import d3Scale from 'd3-scale'; import d3Array from 'd3-array'; import LineChart from 'nomad-ui/components/line-chart'; import formatDuration from 'nomad-ui/utils/format-duration'; export default LineChart.extend({ xProp: 'timestamp', yProp: 'percent', timeseries: true, xFormat() { return d3TimeFormat.timeFormat('%H:%M:%S'); }, yFormat() { return d3Format.format('.1~%'); }, // Specific a11y descriptors title: 'Stats Time Series Chart', description: computed('data.[]', 'xProp', 'yProp', function() { const { xProp, yProp, data } = this; const yRange = d3Array.extent(data, d => d[yProp]); const xRange = d3Array.extent(data, d => d[xProp]); const yFormatter = this.yFormat();
test("loading d3-time-format", function(assert) { let f = timeFormat("%c"); assert.equal(f(+new Date(1990, 0, 1)), "Mon Jan 1 00:00:00 1990"); });
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(".2s")}/> <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() { 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> ); }
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> ); }
test('loading d3-time-format', function(assert) { let f = timeFormat('%c') assert.equal(f(+new Date(1990, 0, 1)), '1/1/1990, 12:00:00 AM') })
* Scaling paths from their center is tricky. This function * helps do that be generating a translate/scale transform * string with the correct numbers. * * @param {number} x - the x data point where you want the path to be centered at * @param {number} y - the y data point where you want the path to be centered at * @param {number} xCenter - the x coordinate of the center of the path you're trying to transform * @param {number} yCenter - the x coordinate of the center of the path you're trying to transform * @param {number} scale - number to scale to, 2 would be 2x bigger * @return {string} - transform string */ export function transformFromCenter(x, y, xCenter, yCenter, scale) { return `translate(${((1 - scale) * xCenter) + (x - xCenter)}, ${((1 - scale) * yCenter) + (y - yCenter)}) scale(${scale})`; } const FORMAT_MILLISECOND = d3TimeFormat.timeFormat('.%L'); const FORMAT_SECOND = d3TimeFormat.timeFormat(':%S'); const FORMAT_MINUTE = d3TimeFormat.timeFormat('%I:%M'); const FORMAT_HOUR = d3TimeFormat.timeFormat('%I %p'); const FORMAT_DAY = d3TimeFormat.timeFormat('%a %d'); const FORMAT_WEEK = d3TimeFormat.timeFormat('%b %d'); const FORMAT_MONTH = d3TimeFormat.timeFormat('%b'); const FORMAT_YEAR = d3TimeFormat.timeFormat('%Y'); /** * formatDate * * This function was written to be used for tick formatting with d3 time * scales. * * @param {date} date - input date
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> ); }
import React from 'react'; import { BarStackHorizontal } from '@vx/shape'; import { Group } from '@vx/group'; import { AxisBottom, AxisLeft } from '@vx/axis'; import { cityTemperature } from '@vx/mock-data'; import { scaleBand, scaleLinear, scaleOrdinal } from '@vx/scale'; import { timeParse, timeFormat } from 'd3-time-format'; import { withTooltip, Tooltip } from '@vx/tooltip'; import { LegendOrdinal } from '@vx/legend'; import { extent, max } from 'd3-array'; const data = cityTemperature.slice(0, 12); const keys = Object.keys(data[0]).filter(d => d !== 'date'); const parseDate = timeParse('%Y%m%d'); const format = timeFormat('%b %d'); const formatDate = date => format(parseDate(date)); const totals = data.reduce((ret, cur) => { const t = keys.reduce((dailyTotal, k) => { dailyTotal += +cur[k]; return dailyTotal; }, 0); ret.push(t); return ret; }, []); export default withTooltip( ({ width, height, events = false,
import React from 'react'; import { AreaClosed, Line, Bar } from '@vx/shape'; import { appleStock } from '@vx/mock-data'; import { curveMonotoneX } from '@vx/curve'; import { LinearGradient } from '@vx/gradient'; import { GridRows, GridColumns } from '@vx/grid'; import { scaleTime, scaleLinear } from '@vx/scale'; import { withTooltip, Tooltip } from '@vx/tooltip'; import { localPoint } from '@vx/event'; import { extent, max, bisector } from 'd3-array'; import { timeFormat } from 'd3-time-format'; const stock = appleStock.slice(800); const formatDate = timeFormat("%b %d, '%y"); // accessors const xStock = d => new Date(d.date); const yStock = d => d.close; const bisectDate = bisector(d => new Date(d.date)).left; class Area extends React.Component { constructor(props) { super(props); this.handleTooltip = this.handleTooltip.bind(this); } handleTooltip({ event, data, xStock, xScale, yScale }) { const { showTooltip } = this.props; const { x } = localPoint(event); const x0 = xScale.invert(x); const index = bisectDate(data, x0, 1); const d0 = data[index - 1];
const x = datum[this.xProp]; return this.xFormat(this.timeseries)(x); }), activeDatumValue: computed('activeDatum', function() { const datum = this.activeDatum; if (!datum) return; const y = datum[this.yProp]; return this.yFormat()(y); }), // Overridable functions that retrurn formatter functions xFormat(timeseries) { return timeseries ? d3TimeFormat.timeFormat('%b') : d3Format.format(','); }, yFormat() { return d3Format.format(',.2~r'); }, tooltipPosition: null, tooltipStyle: styleStringProperty('tooltipPosition'), xScale: computed('data.[]', 'xProp', 'timeseries', 'yAxisOffset', function() { const xProp = this.xProp; const scale = this.timeseries ? d3Scale.scaleTime() : d3Scale.scaleLinear(); const data = this.data; const domain = data.length ? d3Array.extent(this.data, d => d[xProp]) : [0, 1];
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> ); }
/* * This file is part of the nivo project. * * Copyright 2016-present, Raphaël Benitte. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import PropTypes from 'prop-types' import { timeFormat } from 'd3-time-format' import { noop, boxAlignments } from '@nivo/core' import { LegendPropShape } from '@nivo/legends' const monthLabelFormat = timeFormat('%b') const commonPropTypes = { from: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]).isRequired, to: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]).isRequired, data: PropTypes.arrayOf( PropTypes.shape({ day: PropTypes.string.isRequired, value: PropTypes.number.isRequired, }) ).isRequired, align: PropTypes.oneOf(boxAlignments).isRequired, originX: PropTypes.number.isRequired, originY: PropTypes.number.isRequired, calendarWidth: PropTypes.number.isRequired, calendarHeight: PropTypes.number.isRequired,
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> ); }