import { CIQ } from "https://jsfiddle.chartiq.com/chart/js/advanced.js"; import "https://jsfiddle.chartiq.com/chart/js/addOns.js"; // Activate the License Key import getLicenseKey from "https://jsfiddle.chartiq.com/chart/key.js"; getLicenseKey(CIQ); // example animation code for reference /*CIQ.Animation = function (config) { if (!config) throw new Error("Invalid constructor arguments."); var stx, animationParameters, easeMachine; if (config instanceof CIQ.ChartEngine) { // legacy constructor stx = arguments[0]; animationParameters = arguments[1]; easeMachine = arguments[2]; } else { stx = config.stx; animationParameters = config.animationParameters; easeMachine = config.easeMachine; } if (!stx) return console.warn( "No CIQ.ChartEngine provided. Cannot properly create CIQ.Animation instance" ); var params = { stayPut: false, ticksFromEdgeOfScreen: 5, granularity: 1000000 }; animationParameters = CIQ.extend(params, animationParameters); if (params.tension) stx.chart.tension = animationParameters.tension; stx.tickAnimator = easeMachine || new CIQ.EaseMachine(Math.easeOutCubic, 1000); var scrollAnimator = new CIQ.EaseMachine(Math.easeInOutCubic, 1000); var flashingColors = ["#0298d3", "#19bcfc", "#5dcffc", "#9ee3ff"]; var flashingColorIndex = 0; var flashingColorThrottle = 20; var flashingColorThrottleCounter = 0; var filterSession = false; var nextBoundary = null; function initMarketSessionFlags() { filterSession = false; nextBoundary = null; } stx.addEventListener(["symbolChange", "layout"], function (obj) { initMarketSessionFlags(); }); stx.prepend("updateCurrentMarketData", function ( data, chart, symbol, params ) { if (!chart) chart = this.chart; if ( params && params.fromTrade && (chart.closePendingAnimation || chart.closePendingAnimation === 0) ) { params.finalClose = chart.closePendingAnimation; } }); stx.prepend("updateChartData", function (appendQuotes, chart, params) { var self = this; if (!chart) { chart = self.chart; } if ( !chart || !chart.defaultChartStyleConfig || chart.defaultChartStyleConfig == "none" ) return; if (params !== undefined) { if (params.animationEntry || params.secondarySeries) return; } if (!chart.dataSegment) return; function completeLastBar(record) { if (!chart.masterData) return; for (var md = chart.masterData.length - 1; md >= 0; md--) { var bar = chart.masterData[md]; if (bar.Close || bar.Close === 0) { bar.Close = record.Close; if (record.LastSize) bar.LastSize = record.LastSize; if (record.LastTime) bar.LastTime = record.LastTime; self.updateCurrentMarketData({ Close: bar.Close, DT: bar.DT, LastSize: bar.LastSize, LastTime: bar.LastTime }); self.createDataSet(null, null, { appending: true }); return; } } } function unanimateScroll() { if (chart.animatingHorizontalScroll) { chart.animatingHorizontalScroll = false; self.micropixels = self.nextMicroPixels = self.previousMicroPixels; // <-- Reset self.nextMicroPixels here chart.lastTickOffset = 0; } if (chart.closePendingAnimation !== null) { var close = chart.closePendingAnimation; chart.closePendingAnimation = null; completeLastBar({ Close: close }); } } var tickAnimator = self.tickAnimator; // These chart types are the only types supported by animation var supportedChartType = this.mainSeriesRenderer && this.mainSeriesRenderer.supportsAnimation; if (supportedChartType) { if (!tickAnimator) { console.warn( "Animation plug-in can not run because the tickAnimator has not been declared. See instructions in animation.js" ); return; } // If symbol changes then reset all of our variables if (this.prevSymbol != chart.symbol) { this.prevQuote = 0; chart.closePendingAnimation = null; this.prevSymbol = chart.symbol; } unanimateScroll(); tickAnimator.stop(); if (appendQuotes.length > 2) { return; } } var newParams = CIQ.clone(params); if (!newParams) newParams = {}; newParams.animationEntry = true; newParams.bypassGovernor = true; newParams.noCreateDataSet = false; newParams.appending = true; //newParams.allowReplaceOHL = true; newParams.firstLoop = true; var symbol = this.chart.symbol; var period = this.layout.periodicity; var interval = this.layout.interval; var timeUnit = this.layout.timeUnit; function cb(quote, prevQuote, chartJustAdvanced) { return function (newData) { var newClose = newData.Close; if ( !chart.dataSet.length || symbol != chart.symbol || period != self.layout.periodicity || interval != self.layout.interval || timeUnit != self.layout.timeUnit ) { //console.log ("---- STOP animating: Old",symbol,' New : ',chart.symbol, Date()) tickAnimator.stop(); unanimateScroll(); return; // changed symbols mid animation } var q = CIQ.clone(quote); q.Adj_Close = null; // Don't use this, it will mess up our calculated close q.Close = Math.round(newClose * animationParameters.granularity) / animationParameters.granularity; //<<------ IMPORTANT! Use 1000000 for small price increments, otherwise animation will be in increments of .0001 //q.Close = Math.round(newClose*chart.roundit)/chart.roundit; // to ensure decimal points don't go out too far for interim values if (chartJustAdvanced) { if (!q.Open && q.Open !== 0) q.Open = q.Close; if (!q.High && q.High !== 0) q.High = Math.max(q.Open, q.Close); if (!q.Low && q.Low !== 0) q.Low = Math.min(q.Open, q.Close); } else { if (quote.Close > prevQuote.High) q.High = q.Close; if (quote.Close < prevQuote.Low) q.Low = q.Close; } if (chart.animatingHorizontalScroll) { self.micropixels = newData.micropixels; chart.lastTickOffset = newData.lineOffset; } newParams.updateDataSegmentInPlace = !tickAnimator.hasCompleted; //console.log("animating: Old",symbol,' New : ',chart.symbol); var updateQuotes = [q]; // Don't include previous quote if tick mode. It will append, duplicating the quote if (chartJustAdvanced && self.layout.interval !== "tick") updateQuotes.unshift(prevQuote); self.updateChartData(updateQuotes, chart, newParams); newParams.firstLoop = false; if (tickAnimator.hasCompleted) { unanimateScroll(); } }; } if (supportedChartType) { var quote = appendQuotes[appendQuotes.length - 1]; this.prevQuote = this.currentQuote(); // <---- prevQuote logic has been changed to prevent forward/back jitter when more than one tick comes in between animations var chartJustAdvanced = false; // When advancing, we need special logic to deal with the open var dontScroll = false; if (period == 1 && appendQuotes.length == 2) { // Don't do this if consolidating this.prevQuote = appendQuotes[0]; var dataSetLength = chart.dataSet.length; completeLastBar(this.prevQuote); if (dataSetLength == chart.dataSet.length) dontScroll = true; } if (!quote || !quote.Close || !this.prevQuote || !this.prevQuote.Close) return false; if (this.extendedHours && chart.market.market_def) { // Filter out unwanted sessions var dtToFilter = quote.DT; if (CIQ.ChartEngine.isDailyInterval(interval)) { filterSession = !chart.market.isMarketDate(dtToFilter); } else { if (!nextBoundary || nextBoundary <= dtToFilter) { var session = chart.market.getSession(dtToFilter); filterSession = session !== "" && (!this.layout.marketSessions || !this.layout.marketSessions[session]); nextBoundary = chart.market[ filterSession ? "getNextOpen" : "getNextClose" ](dtToFilter); } } if (filterSession) { this.draw(); return false; } } var barSpan = period; if (interval == "second" || timeUnit == "second") barSpan *= 1000; else if (interval == "minute" || timeUnit == "minute") barSpan *= 60000; if (!isNaN(interval)) barSpan *= interval; if (interval == "day" || timeUnit == "day") chartJustAdvanced = quote.DT.getDate() != this.prevQuote.DT.getDate(); else if (interval == "week" || timeUnit == "week") chartJustAdvanced = quote.DT.getDate() >= this.prevQuote.DT.getDate() + 7; else if (interval == "month" || timeUnit == "month") chartJustAdvanced = quote.DT.getMonth() != this.prevQuote.DT.getMonth(); else chartJustAdvanced = quote.DT.getTime() >= this.prevQuote.DT.getTime() + barSpan; var linearChart = !this.mainSeriesRenderer || !this.mainSeriesRenderer.standaloneBars; var beginningOffset = 0; if (chartJustAdvanced) { if (this.animations.zoom.hasCompleted) { var candleWidth = this.layout.candleWidth; if (chart.scroll <= chart.maxTicks) { while (this.micropixels > 0) { // If micropixels is larger than a candle then scroll back further chart.scroll++; this.micropixels -= candleWidth; } } if (chart.scroll <= chart.maxTicks) { this.previousMicroPixels = this.micropixels; this.nextMicroPixels = this.micropixels + candleWidth; beginningOffset = candleWidth * -1; if ( chart.dataSegment.length < chart.maxTicks - animationParameters.ticksFromEdgeOfScreen && !animationParameters.stayPut ) { this.nextMicroPixels = this.micropixels; chart.scroll++; } chart.animatingHorizontalScroll = linearChart; // When the chart advances we also animate the horizontal scroll by incrementing micropixels chart.previousDataSetLength = chart.dataSet.length; } else { if (!dontScroll) chart.scroll++; } } else { return false; } } chart.closePendingAnimation = quote.Close; var start = chartJustAdvanced && !linearChart ? quote.Open : this.prevQuote.Close; tickAnimator.run( cb(quote, CIQ.clone(this.prevQuote), chartJustAdvanced), { Close: start, micropixels: this.nextMicroPixels, lineOffset: beginningOffset }, { Close: quote.Close, micropixels: this.micropixels, lineOffset: 0 } ); return true; // bypass default behavior in favor of animation } }); stx.append("draw", function () { if (filterSession) return; if ( this.chart.dataSet && this.chart.dataSet.length && this.mainSeriesRenderer && this.mainSeriesRenderer.supportsAnimation ) { if (flashingColorThrottleCounter % flashingColorThrottle === 0) { flashingColorIndex++; flashingColorThrottleCounter = 0; } flashingColorThrottleCounter++; var context = this.chart.context; var panel = this.chart.panel; var currentQuote = this.currentQuote(); if (!currentQuote) return; var price = currentQuote.Close; var x = this.pixelFromTick(currentQuote.tick, this.chart); if (this.chart.lastTickOffset) x = x + this.chart.lastTickOffset; var y = this.pixelFromPrice(price, panel); if ( this.chart.yAxis.left > x && this.chart.yAxis.top <= y && this.chart.yAxis.bottom >= y ) { if (flashingColorIndex >= flashingColors.length) flashingColorIndex = 0; context.beginPath(); context.moveTo(x, y); context.arc( x, y, 2 + flashingColorIndex * 1.07, 0, Math.PI * 2, false ); context.fillStyle = flashingColors[flashingColorIndex]; context.fill(); } } }); };*/ // Declare a CIQ.ChartEngine object. This is the main object for drawing charts. const stxx = new CIQ.ChartEngine({ container: document.querySelector(".chartContainer"), layout: { chartType: 'mountain', crosshair: true, period: 1, interval: 'millisecond', candleWidth: 5, } }); // Link an animator to each chart you want to animate by adding a line like this: new CIQ.Animation(stxx, { tension: 0.3 }) // Set tension if you want to soften the curves on a line or mountain chart. stxx.chart.xAxis.timeUnit = CIQ.MILLISECOND; stxx.chart.xAxis.futureTicks=false; let now = new Date(); var sampleData = [{ "DT": new Date(now.setMilliseconds(8)), "Open": 152.13, "High": 152.19, "Low": 152.08, "Close": 152.11, "Volume": 4505569 }, { "DT": new Date(now.setMilliseconds(60)), "Open": 151.76, "High": 151.83, "Low": 151.65, "Close": 151.79, "Volume": 2799990 }, { "DT": new Date(now.setMilliseconds(102)), "Open": 151.79, "High": 151.8, "Low": 151.6, "Close": 151.75, "Volume": 1817706 }, { "DT": new Date(now.setMilliseconds(103)), "Open": 151.74, "High": 151.96, "Low": 151.74, "Close": 151.84, "Volume": 2127911 }, { "DT": new Date(now.setMilliseconds(104)), "Open": 151.84, "High": 152.03, "Low": 151.79, "Close": 151.95, "Volume": 1640306 }, { "DT": new Date(now.setMilliseconds(105)), "Open": 151.95, "High": 152.09, "Low": 151.84, "Close": 152.07, "Volume": 1420396 }, { "DT": new Date(now.setMilliseconds(106)), "Open": 152.07, "High": 152.08, "Low": 151.87, "Close": 151.91, "Volume": 1312368 }, { "DT": new Date(now.setMilliseconds(107)), "Open": 151.92, "High": 152.02, "Low": 151.88, "Close": 151.95, "Volume": 1351448 }, { "DT": new Date(now.setMilliseconds(108)), "Open": 151.95, "High": 152.02, "Low": 151.87, "Close": 151.98, "Volume": 1171601 }, { "DT": new Date(now.setMilliseconds(109)), "Open": 151.97, "High": 151.99, "Low": 151.72, "Close": 151.73, "Volume": 1340956 }, { "DT": new Date(now.setMilliseconds(110)), "Open": 151.73, "High": 151.85, "Low": 151.71, "Close": 151.82, "Volume": 931909 }, { "DT": new Date(now.setMilliseconds(111)), "Open": 151.81, "High": 151.87, "Low": 151.68, "Close": 151.75, "Volume": 864346 }, { "DT": new Date(now.setMilliseconds(112)), "Open": 151.74, "High": 151.81, "Low": 151.69, "Close": 151.73, "Volume": 1070323 }, { "DT": new Date(now.setMilliseconds(113)), "Open": 151.73, "High": 151.85, "Low": 151.71, "Close": 151.82, "Volume": 789665 }, { "DT": new Date(now.setMilliseconds(114)), "Open": 151.82, "High": 151.84, "Low": 151.68, "Close": 151.84, "Volume": 868275 }, { "DT": new Date(now.setMilliseconds(115)), "Open": 151.84, "High": 152.01, "Low": 151.83, "Close": 151.95, "Volume": 1160535 }, { "DT": new Date(now.setMilliseconds(116)), "Open": 151.95, "High": 152.07, "Low": 151.95, "Close": 152.03, "Volume": 1008658 }, { "DT": new Date(now.setMilliseconds(117)), "Open": 152.03, "High": 152.13, "Low": 151.99, "Close": 152.03, "Volume": 974990 }, { "DT": new Date(now.setMilliseconds(118)), "Open": 152.02, "High": 152.03, "Low": 151.91, "Close": 152.01, "Volume": 737028 }, { "DT": new Date(now.setMilliseconds(119)), "Open": 152.02, "High": 152.03, "Low": 151.91, "Close": 152.01, "Volume": 737028 }, { "DT": new Date(now.setMilliseconds(120)), "Open": 152.13, "High": 152.19, "Low": 152.08, "Close": 152.11, "Volume": 4505569 }, { "DT": new Date(now.setMilliseconds(121)), "Open": 151.76, "High": 151.83, "Low": 151.65, "Close": 151.79, "Volume": 2799990 }, { "DT": new Date(now.setMilliseconds(122)), "Open": 151.79, "High": 151.8, "Low": 151.6, "Close": 151.75, "Volume": 1817706 }, { "DT": new Date(now.setMilliseconds(123)), "Open": 151.74, "High": 151.96, "Low": 151.74, "Close": 151.84, "Volume": 2127911 }, { "DT": new Date(now.setMilliseconds(124)), "Open": 151.84, "High": 152.03, "Low": 151.79, "Close": 151.95, "Volume": 1640306 }, { "DT": new Date(now.setMilliseconds(125)), "Open": 151.95, "High": 152.09, "Low": 151.84, "Close": 152.07, "Volume": 1420396 }, { "DT": new Date(now.setMilliseconds(126)), "Open": 152.07, "High": 152.08, "Low": 151.87, "Close": 151.91, "Volume": 1312368 }, { "DT": new Date(now.setMilliseconds(127)), "Open": 151.92, "High": 152.02, "Low": 151.88, "Close": 151.95, "Volume": 1351448 }, { "DT": new Date(now.setMilliseconds(128)), "Open": 151.95, "High": 152.02, "Low": 151.87, "Close": 151.98, "Volume": 1171601 }, { "DT": new Date(now.setMilliseconds(129)), "Open": 151.97, "High": 151.99, "Low": 151.72, "Close": 151.73, "Volume": 1340956 }, { "DT": new Date(now.setMilliseconds(130)), "Open": 152.13, "High": 152.19, "Low": 152.08, "Close": 152.11, "Volume": 4505569 }, { "DT": new Date(now.setMilliseconds(131)), "Open": 151.76, "High": 151.83, "Low": 151.65, "Close": 151.79, "Volume": 2799990 }, { "DT": new Date(now.setMilliseconds(132)), "Open": 151.79, "High": 151.8, "Low": 151.6, "Close": 151.75, "Volume": 1817706 }, { "DT": new Date(now.setMilliseconds(133)), "Open": 151.74, "High": 151.96, "Low": 151.74, "Close": 151.84, "Volume": 2127911 }, { "DT": new Date(now.setMilliseconds(134)), "Open": 151.84, "High": 152.03, "Low": 151.79, "Close": 151.95, "Volume": 1640306 }, { "DT": new Date(now.setMilliseconds(135)), "Open": 151.95, "High": 152.09, "Low": 151.84, "Close": 152.07, "Volume": 1420396 }, { "DT": new Date(now.setMilliseconds(136)), "Open": 152.07, "High": 152.08, "Low": 151.87, "Close": 151.91, "Volume": 1312368 }, { "DT": new Date(now.setMilliseconds(137)), "Open": 151.92, "High": 152.02, "Low": 151.88, "Close": 151.95, "Volume": 1351448 }, { "DT": new Date(now.setMilliseconds(138)), "Open": 151.95, "High": 152.02, "Low": 151.87, "Close": 151.98, "Volume": 1171601 }, { "DT": new Date(now.setMilliseconds(139)), "Open": 151.97, "High": 151.99, "Low": 151.72, "Close": 151.73, "Volume": 1340956 } ]; stxx.loadChart("TEST", sampleData, function() { streamSimulation(); }); /******************* Stream simulation for demo purposes only ************************/ /************************************************************************************/ /************************************************************************************/ /************************************************************************************/ function streamSimulation() { let price = 151.73; // if there is something in the masterData use the last element as the basis for our random seed if (stxx.chart.masterData.length) { price = stxx.chart.masterData[stxx.chart.masterData.length - 1].Close; } let change = (price * .02) * Math.random() - (price * .01); // random between +/-1% of current price price = price + parseFloat(change.toFixed(2)); let volume = Math.floor(Math.random() * 10) + 1 stxx.updateChartData({ "Last": price, "Volume": volume }, null, { bypassGovernor: true }); setTimeout(streamSimulation, 500); }
<link rel="stylesheet" type="text/css" href="https://jsfiddle.chartiq.com/chart/css/stx-chart.css" media="screen" /> <div class="chartContainer" style="width:100%;height:400px;position:relative;"></div> <!--[if IE 8]><script>alert("This template is not compatible with IE8");</script><![endif]-->