Derived data properties
LightningChart DataSet class supports defining custom derivation rules, meaning data values can be derived from other properties' values.
// Example of derived data properties using built-in derivation implementation: Simple Moving Average (SMA)
const dataSet = new DataSet({
schema: {
x: { pattern: 'progressive' },
y: { pattern: null },
sma: { pattern: null, derived: builtInDataDerivations.SMA({ source: 'y', window: 100 }) },
},
})
chart.addLineSeries().setName('Y').setDataSet(dataSet, { x: 'x', y: 'y' })
chart.addLineSeries().setName('SMA').setDataSet(dataSet, { x: 'x', y: 'sma' })


In above example, you would only feed in data values for x and y -> sma is automatically calculated.
You can specify any custom callback to do this kind of calculations:
const dataSet = new DataSet({
schema: {
x: { pattern: 'progressive' },
y: { pattern: null },
example: {
pattern: null,
derived: (info) => {
// other properties' values can be referenced through `info`
return new Array(info.newCount).fill(0).map(Math.random)
},
},
},
})
If your derivation needs to reference previously supplied data point values, it is recommended to use DataSet.iterateOverRingBuffers for optimal performance. For reference, here is the implementation of the built-in SMA derivation:
const SMA = (arg: { window?: number; source: string }): DataSetDerivedPropertyConfig => {
const smaWindow = arg?.window ?? 20
const source = arg.source
return (state) => {
if (state.newCount <= 0) return []
const rawValuesNew = state.newValues[source]
const rawRingBuffer = state.dataSet.ringBuffers[source]
const rawValuesPrev =
rawRingBuffer && state.firstNewSampleIndex >= smaWindow
? Array.from(state.dataSet.iterateOverRingBuffers({ first: state.firstNewSampleIndex - smaWindow })).map(
(iData) => rawRingBuffer[iData],
)
: new Array(smaWindow).fill(Number.NaN)
const rawValuesTrailing = [...rawValuesPrev, ...rawValuesNew]
const newSMA = new Float64Array(state.newCount)
for (let i = 0; i < newSMA.length; i += 1) {
let sum = 0
for (let offset = 0; offset < smaWindow; offset += 1) {
const v = rawValuesTrailing[i + smaWindow - offset]
sum += v
}
const sma = Number.isNaN(sum) ? Number.NaN : sum / smaWindow
newSMA[i] = sma
}
return newSMA
}
}