class Charts { constructor(idata) { // Unpack the initialization data. this.chart1_id = idata.chart1_id; this.chart2_id = idata.chart2_id; this.chart3_id = idata.chart3_id; this.chart4_id = idata.chart4_id; this.trading_pair = idata.trading_pair; this.price_history = idata.price_history; /* A list of bound charts this is necessary for maintaining a dynamic number of charts with their position and zoom factors bound.*/ this.bound_charts=[]; // Only the main chart is created by default. this.create_main_chart(); } create_main_chart() { // Pass the id of the element to create the main chart in. // This function returns the main chart object. this.chart_1 = this.create_chart(this.chart1_id); // Display the trading pair as a watermark overlaying the chart. this.addWatermark(this.chart_1, this.trading_pair); // - Create the candle stick series for our chart this.candleSeries = this.chart_1.addCandlestickSeries(); // Initialize the candlestick series if price_history is available if (this.price_history && this.price_history.length > 0) { this.candleSeries.setData(this.price_history); console.log('Candle series init:', this.price_history); } else { console.error('Price history is not available or is empty.'); } this.bind_charts(this.chart_1); } update_main_chart(new_candle){ // Update candlestick series this.candleSeries.update(new_candle); } create_RSI_chart(){ this.chart2 = this.create_chart(this.chart2_id, 100); this.set_priceScale(this.chart2, 0.3, 0.0); // Put the name of the chart in a watermark in the chart. this.addWatermark(this.chart2, 'RSI'); // Todo: Not sure how to set this //this.chart2.applyOptions({ priceRange: {minValue:0,maxValue:100} }); this.bind_charts(this.chart2); } create_MACD_chart(){ this.chart3 = this.create_chart(this.chart3_id, 100); this.addWatermark(this.chart3, 'MACD'); this.bind_charts(this.chart3); } create_PercentB_chart(){ this.chart4 = this.create_chart(this.chart4_id, 100); this.set_priceScale(this.chart4, 0.3, 0.0); // Put the name of the chart in a watermark this.addWatermark(this.chart4, '%B'); this.bind_charts(this.chart4); } create_chart(target_id, height=500){ // Accepts a target element to place the chart in. // Returns the chart object. let container = document.getElementById(target_id); //Create a lightweight chart object. let chart = LightweightCharts.createChart(container, { width: 1000, height: height, crosshair: { mode: LightweightCharts.CrosshairMode.Normal, }, priceScale: { borderColor: 'rgba(197, 203, 206, 0.8)', }, timeScale: { borderColor: 'rgba(197, 203, 206, 0.8)', timeVisible: true, secondsVisible: false, barSpacing: 6 }, handleScroll: true }); return chart; } set_priceScale(chart, top, bottom){ chart.priceScale('right').applyOptions({ scaleMargins: { top: top, bottom: bottom, }, }); } addWatermark(chart,text){ chart.applyOptions({ watermark: {visible: true, color: '#DBC29E', text: text, fontSize: 30, fontFamily: 'Roboto', fontStyle: 'bold', vertAlign: 'center' } }); } bind_charts(chart){ // keep a list of charts and bind all their position and spacing. // Add (arg1) to bound_charts this.add_to_list(chart); // Get the number of objects in bound_charts let bcl = Object.keys(this.bound_charts).length; // if bound_charts has two element in it bind them if (bcl == 2) { this.bind2charts(); } // if bound_charts has three elements in it bind them if (bcl == 3) { this.bind3charts(); } // if bound_charts has four elements in it bind them if (bcl == 4) { this.bind4charts(); } return; } add_to_list(chart){ // If the chart isn't already included in the list, add it. if ( !this.bound_charts.includes(chart) ){ this.bound_charts.push(chart); } } bind2charts(){ //On change in chart 1 change chart 2 let syncHandler1 = (e) => { // Get the barSpacing(zoom) and position of 1st chart. let barSpacing1 = this.bound_charts[0].timeScale().getBarSpacing(); let scrollPosition1 = this.bound_charts[0].timeScale().scrollPosition(); // Apply barSpacing(zoom) and position to 2nd chart. this.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 }); } this.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncHandler1); //On change in chart 2 change chart 1 let syncHandler2 = (e) => { // Get the barSpacing(zoom) and position of chart 2 let barSpacing2 = this.bound_charts[1].timeScale().getBarSpacing(); let scrollPosition2 = this.bound_charts[1].timeScale().scrollPosition(); // Apply barSpacing(zoom) and position to chart 1 this.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 }); } this.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncHandler2); } bind3charts(){ //On change to chart 1 change chart 2 and 3 let syncHandler = (e) => { // Get the barSpacing(zoom) and position of chart 1 let barSpacing1 = this.bound_charts[0].timeScale().getBarSpacing(); let scrollPosition1 = this.bound_charts[0].timeScale().scrollPosition(); // Apply barSpacing(zoom) and position to new chart this.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 }); this.bound_charts[2].timeScale().applyOptions({ rightOffset: scrollPosition1, barSpacing: barSpacing1 }); } this.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncHandler); //On change to chart 2 change chart 1 and 3 let syncHandler2 = (e) => { // Get the barSpacing(zoom) and position of chart 2 let barSpacing2 = this.bound_charts[1].timeScale().getBarSpacing(); let scrollPosition2 = this.bound_charts[1].timeScale().scrollPosition(); // Apply barSpacing(zoom) and position to chart 1 and 3 this.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 }); this.bound_charts[2].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 }); } this.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncHandler2); //On change to chart 3 change chart 1 and 2 let syncHandler3 = (e) => { // Get the barSpacing(zoom) and position of new chart let barSpacing2 = this.bound_charts[2].timeScale().getBarSpacing(); let scrollPosition2 = this.bound_charts[2].timeScale().scrollPosition(); // Apply barSpacing(zoom) and position to parent chart this.bound_charts[0].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 }); this.bound_charts[1].timeScale().applyOptions({ rightOffset: scrollPosition2, barSpacing: barSpacing2 }); } this.bound_charts[2].timeScale().subscribeVisibleTimeRangeChange(syncHandler3); } bind4charts(){ // Sync all 4 charts together let syncFromChart = (sourceIndex) => { return (e) => { let barSpacing = this.bound_charts[sourceIndex].timeScale().getBarSpacing(); let scrollPosition = this.bound_charts[sourceIndex].timeScale().scrollPosition(); for (let i = 0; i < 4; i++) { if (i !== sourceIndex) { this.bound_charts[i].timeScale().applyOptions({ rightOffset: scrollPosition, barSpacing: barSpacing }); } } } } this.bound_charts[0].timeScale().subscribeVisibleTimeRangeChange(syncFromChart(0)); this.bound_charts[1].timeScale().subscribeVisibleTimeRangeChange(syncFromChart(1)); this.bound_charts[2].timeScale().subscribeVisibleTimeRangeChange(syncFromChart(2)); this.bound_charts[3].timeScale().subscribeVisibleTimeRangeChange(syncFromChart(3)); } }