diff --git a/data/charts.html b/data/charts.html new file mode 100644 index 0000000..315d9c4 --- /dev/null +++ b/data/charts.html @@ -0,0 +1,23 @@ + + + + + + Charts + + + + + +
+ + + diff --git a/data/charts.js b/data/charts.js new file mode 100644 index 0000000..267bcf5 --- /dev/null +++ b/data/charts.js @@ -0,0 +1,342 @@ +const urlParams = new URLSearchParams(window.location.search); +const symbol = urlParams.get('symbol'); +const time = urlParams.get('time'); + +//window.dispatchEvent(new Event('resize')); + +function timeToLocal(originalTime) { + const d = new Date(originalTime * 1000); + return Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()) / 1000; +} + + function parseCSV(data) { + const rows = data.split("\n"); + const result = []; + let start = Math.max(rows.length - 298, 0); + let lastElements = rows.slice(start) + for (let i = start; i < rows.length; i++) { + const cols = rows[i].split(","); + if (cols.length >= 23 && cols.every(element => element !== undefined && element !== null)) { // check for existing lines + // parse the date so seconds since 1970 + cols[0] = Date.parse(cols[0])/1000,result.push(cols); + cols[0] = timeToLocal(cols[0]); + // coloring for MACD-Histogram + if (cols[20] < 0) { + cols[100] = "orange"; + if (cols[23] > 20) { + cols[100] = "red"; + } + } + else { + cols[100] = "lightgreen"; + if (cols[23] > 20) { + cols[100] = "green"; + } + } + } + else { + console.log("invalid line on linenr " + i + ": " +rows[i]); + } + } + return result; + } + + // Create the Lightweight Chart within the container element + const chart = LightweightCharts.createChart(document.getElementById('container'), + { + rightPriceScale: { + minimumWidth: 100, + borderVisible: false + }, + + height: 500, + crosshair: { + mode: 0, + }, + timeScale: { + timeVisible: true, + secondsVisible: false, + }, + layout: { + background: { + type: 'solid', + color: '#222', + }, + textColor: '#DDD', + }, + grid: { + vertLines: { color: '#444' }, + horzLines: { color: '#444' }, + }, + }); + + chart.applyOptions({ + watermark: { + visible: true, + fontSize: 18, + horzAlign: 'top', + vertAlign: 'left', + color: '#DDD', + text: symbol + " " + time, + }}); + + + // define chart + const candleSeries = chart.addCandlestickSeries(); + const lineSeriesEMA12 = chart.addLineSeries({ color: 'red', lineWidth: 1, priceLineVisible: false}); + const lineSeriesEMA26 = chart.addLineSeries({ color: 'pink', lineWidth: 1, lineStyle: 2, priceLineVisible: false}); + const lineSeriesEMA50 = chart.addLineSeries({ color: 'cyan', lineWidth: 1, priceLineVisible: false}); + const lineSeriesEMA100 = chart.addLineSeries({ color: 'yellow', lineWidth: 1, priceLineVisible: false}); + const lineSeriesEMA200 = chart.addLineSeries({ color: 'white', lineWidth: 1, priceLineVisible: false}); + const lineSeriesEMA400 = chart.addLineSeries({ color: 'orange', lineWidth: 1, priceLineVisible: false}); + const lineSeriesEMA800 = chart.addLineSeries({ color: 'purple', lineWidth: 1, priceLineVisible: false}); + + + // RSI Chart + const chartrsi = LightweightCharts.createChart(document.getElementById("container"), + { + rightPriceScale: { + minimumWidth: 100, + borderVisible: false + }, + height: 200, + timeScale: { + visible: false, + }, + layout: { + background: { + type: 'solid', + color: '#222', + }, + textColor: '#DDD', + }, + grid: { + vertLines: { color: '#444' }, + horzLines: { color: '#444' }, + }, + }); + + chartrsi.applyOptions({ + watermark: { + visible: true, + fontSize: 18, + horzAlign: 'top', + vertAlign: 'left', + color: '#DDD', + text: 'RSI 5,14,21', + }}); + + const lineSeriesRSI5 = chartrsi.addLineSeries({ color: 'orange', lineWidth: 1, lineStyle: 2, priceLineVisible: false}); + const lineSeriesRSI14 = chartrsi.addLineSeries({ color: 'yellow', lineWidth: 2, priceLineVisible: false}); + const lineSeriesRSI21 = chartrsi.addLineSeries({ color: 'lightgreen', lineWidth: 1, lineStyle: 2, priceLineVisible: false}); + + // MACD Chart + const chartmacd = LightweightCharts.createChart(document.getElementById("container"), + { + rightPriceScale: { + minimumWidth: 100, + borderVisible: false + }, + + height: 200, + timeScale: { + timeVisible: true, + secondsVisible: false, + }, + layout: { + background: { + type: 'solid', + color: '#222', + }, + textColor: '#DDD', + }, + grid: { + vertLines: { color: '#444' }, + horzLines: { color: '#444' }, + }, + }); + + chartmacd.applyOptions({ + watermark: { + visible: true, + fontSize: 18, + horzAlign: 'top', + vertAlign: 'left', + color: '#DDD', + text: 'MACD 12 26', + }}); + + const lineSeriesMACD = chartmacd.addLineSeries({ color: 'blue', lineWidth: 1, lineStyle: 0, priceLineVisible: false}); + const lineSeriesMACDSignal = chartmacd.addLineSeries({ color: 'orange', lineWidth: 1, lineStyle: 0, priceLineVisible: false}); + const histogramSeriesMACD = chartmacd.addHistogramSeries({ + priceFormat: { + type: 'volume', + color: 'orange', + }, + //priceScaleId: '', // set as an overlay by setting a blank priceScaleId + }); + + + + fetch("/botdata/asset-histories/" + symbol + ".history." + time + ".csv") + .then(response => response.text()) + .then(data => { + const parsedData = parseCSV(data); + + // OHLC Data + const bars = parsedData.map(item => ({ + time: item[0], + open: item[1], + high: item[2], + low: item[3], + close: item[4] + })); + candleSeries.setData(bars); + + // EMA Data + candleSeries.setData(bars); + const lineSeriesEMA12Data = parsedData.map(item => ({ + time: item[0], + value: item[8] + })); + lineSeriesEMA12.setData(lineSeriesEMA12Data); + + const lineSeriesEMA26Data = parsedData.map(item => ({ + time: item[0], + value: item[9] + })); + lineSeriesEMA26.setData(lineSeriesEMA26Data); + + const lineSeriesEMA50Data = parsedData.map(item => ({ + time: item[0], + value: item[10] + })); + lineSeriesEMA50.setData(lineSeriesEMA50Data); + + const lineSeriesEMA100Data = parsedData.map(item => ({ + time: item[0], + value: item[11] + })); + lineSeriesEMA100.setData(lineSeriesEMA100Data); + + const lineSeriesEMA200Data = parsedData.map(item => ({ + time: item[0], + value: item[12] + })); + lineSeriesEMA200.setData(lineSeriesEMA200Data); + + const lineSeriesEMA400Data = parsedData.map(item => ({ + time: item[0], + value: item[13] + })); + lineSeriesEMA400.setData(lineSeriesEMA400Data); + + const lineSeriesEMA800Data = parsedData.map(item => ({ + time: item[0], + value: item[14] + })); + lineSeriesEMA800.setData(lineSeriesEMA800Data); + + // RSI Data + const lineSeriesRSI5Data = parsedData.map(item => ({ + time: item[0], + value: item[15] + })); + lineSeriesRSI5.setData(lineSeriesRSI5Data); + + const lineSeriesRSI14Data = parsedData.map(item => ({ + time: item[0], + value: item[16] + })); + lineSeriesRSI14.setData(lineSeriesRSI14Data); + + const lineSeriesRSI21Data = parsedData.map(item => ({ + time: item[0], + value: item[17] + })); + lineSeriesRSI21.setData(lineSeriesRSI21Data); + + // MACD Data + const lineSeriesMACDData = parsedData.map(item => ({ + time: item[0], + value: item[18] + })); + lineSeriesMACD.setData(lineSeriesMACDData); + + const lineSeriesMACDSignalData = parsedData.map(item => ({ + time: item[0], + value: item[19] + })); + lineSeriesMACDSignal.setData(lineSeriesMACDSignalData); + + const histogramSeriesMACDData = parsedData.map(item => ({ + time: item[0], + value: item[20], + color: item[100] + })); + histogramSeriesMACD.setData(histogramSeriesMACDData); + }); + + + // Lines for price levels + fetch("/botdata/asset-histories/" + symbol + ".history.csv.levels") + .then(response => response.text()) + .then(text => { + const levels = text.split('\n'); + levels.forEach(function(level) { + candleSeries.createPriceLine({price: level, color: "darkblue", lineWidth: 0.5, lineStyle: 0, axisLabelVisible: true, title: 'Level'}); + }); + }); + + + // Sync charts timeScale + chart.timeScale().fitContent(); + chart.timeScale().subscribeVisibleLogicalRangeChange(timeRange => { + chartrsi.timeScale().setVisibleLogicalRange(timeRange); + chartmacd.timeScale().setVisibleLogicalRange(timeRange); + }); + + chartrsi.timeScale().subscribeVisibleLogicalRangeChange(timeRange => { + chart.timeScale().setVisibleLogicalRange(timeRange); + }); + + chartmacd.timeScale().subscribeVisibleLogicalRangeChange(timeRange => { + chart.timeScale().setVisibleLogicalRange(timeRange); + }); + + +function getCrosshairDataPoint(series, param) { + if (!param.time) { + return null; + } + const dataPoint = param.seriesData.get(series); + return dataPoint || null; +} + +function syncCrosshair(chart, series, dataPoint) { + if (dataPoint) { + chart.setCrosshairPosition(dataPoint.value, dataPoint.time, series); + return; + } + chart.clearCrosshairPosition(); +} +chart.subscribeCrosshairMove(param => { + const dataPoint = getCrosshairDataPoint(lineSeriesEMA50, param); + syncCrosshair(chartrsi, lineSeriesRSI14, dataPoint); + const dataPointmacd = getCrosshairDataPoint(lineSeriesEMA50, param); + syncCrosshair(chartmacd, lineSeriesMACD, dataPointmacd); +}); +chartrsi.subscribeCrosshairMove(param => { + const dataPoint = getCrosshairDataPoint(lineSeriesRSI14, param); + syncCrosshair(chart, lineSeriesEMA50, dataPoint); + const dataPointmacd = getCrosshairDataPoint(lineSeriesRSI14, param); + syncCrosshair(chartmacd, lineSeriesMACD, dataPointmacd); + +}); +chartmacd.subscribeCrosshairMove(param => { + const dataPoint = getCrosshairDataPoint(lineSeriesMACD, param); + syncCrosshair(chart, lineSeriesEMA50, dataPoint); + const dataPointrsi = getCrosshairDataPoint(lineSeriesMACD, param); + syncCrosshair(chartrsi, lineSeriesRSI14, dataPointrsi); +}); +