Close Editor Run Reset Auto Update CJS const lcjsTrader = require('@lightningchart/lcjs-trader')
const lcjs = require('@lightningchart/lcjs')
const { HelperRoutines, TechnicalAnalysisMethods } = lcjsTrader
const { ColorRGBA, SolidLine, Themes } = lcjs
lcjsTrader.trader(TRADER_LICENSE).then(async (trader) => {
const exampleContainer = document.getElementById('chart') || document.body
if (exampleContainer === document.body) {
exampleContainer.style.width = '100vw'
exampleContainer.style.height = '100vh'
exampleContainer.style.margin = '0px'
}
exampleContainer.style.display = 'flex'
exampleContainer.style.flexDirection = 'row'
exampleContainer.style.flexWrap = 'wrap'
const chartContainer = document.createElement('div')
chartContainer.style.width = 'calc(100% - 260px)'
exampleContainer.append(chartContainer)
const uiContainer = document.createElement('div')
uiContainer.style.width = '260px'
exampleContainer.append(uiContainer)
let periodCount = 14
let stepSize = 1
let highThreshold = 70
let lowThreshold = 30
let isGradient = true
let selectedRow = 1
// Create a trading chart.
const taChart = trader.tradingChart({ parentElement: chartContainer, loadFromStorage: false })
const taMethods = new TechnicalAnalysisMethods(taChart)
const helperRoutines = new HelperRoutines()
// Add a default dataset.
await fetch(`${document.head.baseURI}examples/assets/2001/Apple Inc. (AAPL).csv`).then((res) => res.text()).then((text) => {
taChart.loadCsvString(text, 'Apple Inc. (AAPL)')
createHeatmap()
})
// Hard-code some data for the DataGrid.
const data = [
[ 182,491, 184.318, 183.05, 186.28, 187.43],
[ 105.44, 105.8, 105.79, 105.83, 105.35],
[ 12.15, 12.13, 11.99, 12.33, 12.44],
[ 169.38, 169.96, 168.65, 169.14, 170.34],
[ 62.33, 61.6, 61.86, 62.49, 62.56],
[ 410.54, 412.32, 414.74, 413.72, 416.56],
[ 904.12, 887.47, 898.78, 903.99, 913.56],
[ 174.72, 171.97, 168.47, 171.89, 177.55]
]
// Create the DataGrid control.
const dataGrid = trader.lightningChart().DataGrid({ container: uiContainer })
.setTitle('Securities')
.setPadding({ left: 10, right: 0 })
.setColumnWidth(2, 100)
.setTableContent([
['Security', 'Price', 'Last week'],
['AAPL', data[0][4], ''],
['DIS', data[1][4], ''],
['F', data[2][4], ''],
['GOOGL', data[3][4], ''],
['K', data[4][4], ''],
['MSFT', data[5][4], ''],
['NVDA', data[6][4], ''],
['TSLA', data[7][4], '']
])
const theme = dataGrid.getTheme()
for (let i = 1; i < 9; i++) {
dataGrid.setCellContent(2, i, {
type: 'spark-area',
data: [{ x: 0, y: data[i - 1][0] }, { x: 1, y: data[i - 1][1] }, { x: 2, y: data[i - 1][2] }, { x: 3, y: data[i - 1][3] }, { x: 4, y: data[i - 1][4] }],
strokeStyle: data[i - 1][0] - data[i - 1][4] < 0 ? new SolidLine({ fillStyle: theme.examples.positiveFillStyle, thickness: 2 }) : new SolidLine({ fillStyle: theme.examples.negativeFillStyle, thickness: 2 }),
fillStyle: data[i - 1][0] - data[i - 1][4] < 0 ? theme.examples.positiveAreaFillStyle : theme.examples.negativeAreaFillStyle
})
.setRowHeight(i, 50)
}
dataGrid.setRowHighlight(selectedRow, true)
dataGrid.cells.addEventListener('click', async (_, cell) => {
let stock = ''
switch (cell.row) {
case 1:
stock = 'Apple Inc. (AAPL)'
break
case 2:
stock = 'Walt Disney Co (The) (DIS)'
break
case 3:
stock = 'Ford Motor Company (F)'
break
case 4:
stock = 'Alphabet Inc - Class A (GOOGL)'
break
case 5:
stock = 'Kellanova Company (K)'
break
case 6:
stock = 'Microsoft Corporation (MSFT)'
break
case 7:
stock = 'NVIDIA Corp (NVDA)'
break
case 8:
stock = 'Tesla Inc. (TSLA)'
break
default:
break
}
if (stock != '') {
await fetch(`${document.head.baseURI}examples/assets/2001/` + stock + `.csv`).then((res) => res.text()).then((text) => {
taChart.loadCsvString(text, stock)
createHeatmap()
})
dataGrid.setRowHighlight(selectedRow, false)
selectedRow = cell.row
dataGrid.setRowHighlight(cell.row, true)
}
})
dataGrid.cells.addEventListener('pointerenter', (_, cell) => {
if (cell.row > 0) {
dataGrid.setRowHighlight(cell.row, true)
}
})
dataGrid.cells.addEventListener('pointerleave', (_, cell) => {
if (selectedRow != cell.row) {
dataGrid.setRowHighlight(cell.row, false)
}
})
function createHeatmap() {
taChart.disposeAllHeatmaps()
const data = taChart.getData(true)
const highValues = helperRoutines.extractHighValues(data)
const lowValues = helperRoutines.extractLowValues(data)
const closeValues = helperRoutines.extractCloseValues(data)
const max = Math.ceil(Math.max(...highValues))
const min = Math.floor(Math.min(...lowValues))
const stepsX = closeValues.length - periodCount
const stepsY = Math.ceil((max - min) / stepSize)
const smaValues = taMethods.calculateSimpleMovingAverage(closeValues, periodCount)
const rsiValue = taMethods.calculateRelativeStrengthIndex(closeValues, periodCount)
const heatmapValues = []
for (let i = 0; i < stepsX; i++) {
const nodeValues = []
const slope = rsiValue[i] == 50 ? 1 : (closeValues[periodCount - 1 + i] - smaValues[i]) / (rsiValue[i] - 50)
const intercept = smaValues[i] - slope * rsiValue[i]
for (let j = 0; j < stepsY; j++) {
nodeValues[j] = (min + j * stepSize - intercept) / slope
}
heatmapValues.push(nodeValues)
}
// Add the heatmap series.
const heatmap = taChart.addHeatmap(periodCount - 1, min, closeValues.length - 1, max, heatmapValues)
heatmap.setInterpolate(isGradient)
// Modify heatmap color steps.
heatmap.setPalette([
{ value: 0, color: ColorRGBA(255, 0, 0, 10) },
{ value: lowThreshold, color: ColorRGBA(255, 50, 50, 50) },
{ value: 50, color: ColorRGBA(0, 0, 0, 50) },
{ value: highThreshold, color: ColorRGBA(50, 255, 50, 50) },
{ value: 100, color: ColorRGBA(0, 255, 0, 10) }
])
}
// Generate the UI controls.
const buttonDiv = document.createElement('div')
buttonDiv.style.fontFamily = 'Arial'
if (theme == Themes.light || theme == Themes.lightNature) {
buttonDiv.style.color = '#000000'
} else {
buttonDiv.style.color = '#FFFFFF'
}
buttonDiv.style.position = 'absolute'
buttonDiv.style.top = '480px'
buttonDiv.style.padding = '24px'
uiContainer.append(buttonDiv)
const periodLabel = document.createElement('label')
periodLabel.textContent = 'Period count: '
buttonDiv.append(periodLabel)
const periodValue = document.createElement('label')
periodValue.textContent = periodCount.toString()
periodValue.style.position = 'absolute'
periodValue.style.right = '24px'
buttonDiv.append(periodValue)
const periodSlider = document.createElement('input')
periodSlider.style.display = 'block'
periodSlider.type = 'range'
periodSlider.min = '1'
periodSlider.max = '100'
periodSlider.step = '1'
periodSlider.value = '14'
periodSlider.style.width = '200px'
periodSlider.style.margin = '8px 0px 14px'
buttonDiv.append(periodSlider)
periodSlider.addEventListener('change', () => {
periodCount = periodSlider.valueAsNumber
periodValue.textContent = periodCount.toString()
createHeatmap()
})
const highThresholdLabel = document.createElement('label')
highThresholdLabel.textContent = 'High threshold: '
buttonDiv.append(highThresholdLabel)
const htValue = document.createElement('label')
htValue.textContent = highThreshold.toString()
htValue.style.position = 'absolute'
htValue.style.right = '24px'
buttonDiv.append(htValue)
const htSlider = document.createElement('input')
htSlider.style.display = 'block'
htSlider.type = 'range'
htSlider.min = '50'
htSlider.max = '100'
htSlider.step = '1'
htSlider.value = '70'
htSlider.style.width = '200px'
htSlider.style.margin = '8px 0px 14px'
buttonDiv.append(htSlider)
htSlider.addEventListener('change', () => {
highThreshold = htSlider.valueAsNumber
htValue.textContent = highThreshold.toString()
createHeatmap()
})
const lowThresholdLabel = document.createElement('label')
lowThresholdLabel.textContent = 'Low threshold: '
buttonDiv.append(lowThresholdLabel)
const ltValue = document.createElement('label')
ltValue.textContent = lowThreshold.toString()
ltValue.style.position = 'absolute'
ltValue.style.right = '24px'
buttonDiv.append(ltValue)
const ltSlider = document.createElement('input')
ltSlider.style.display = 'block'
ltSlider.type = 'range'
ltSlider.min = '0'
ltSlider.max = '50'
ltSlider.step = '1'
ltSlider.value = '30'
ltSlider.style.width = '200px'
ltSlider.style.margin = '8px 0px 14px'
buttonDiv.append(ltSlider)
ltSlider.addEventListener('change', () => {
lowThreshold = ltSlider.valueAsNumber
ltValue.textContent = lowThreshold.toString()
createHeatmap()
})
const gradientButton = document.createElement('input')
gradientButton.type = 'radio'
gradientButton.name = 'fillType'
gradientButton.id = 'gradient'
gradientButton.value = '0'
gradientButton.checked = 'true'
buttonDiv.append(gradientButton)
const gradientLabel = document.createElement('label')
gradientLabel.textContent = 'Gradient'
buttonDiv.append(gradientLabel)
const uniformButton = document.createElement('input')
uniformButton.type = 'radio'
uniformButton.name = 'fillType'
uniformButton.id = 'uniform'
uniformButton.value = '1'
uniformButton.style.marginLeft = '45px'
buttonDiv.append(uniformButton)
const uniformLabel = document.createElement('label')
uniformLabel.textContent = 'Uniform'
buttonDiv.append(uniformLabel)
gradientButton.addEventListener('change', () => {
isGradient = gradientButton.checked
createHeatmap()
})
uniformButton.addEventListener('change', () => {
isGradient = gradientButton.checked
createHeatmap()
})
}) Advanced heatmap Example - Editor
Example of using a heatmap to create custom indicators. Relative Strength Index and Simple Moving Average are used to build the heatmap, which is then drawn on top of the trading data. Indicator settings can be modified via the right panel. Data Grid component is used to list various securities. Clicking an entry will load the respective dataset to the chart.
The example loads data from a csv-file.