Hey mate nice one. Sounds a lot like the z score described here: https://stackoverflow.com/questions/22583391/peak-signal-detection-in-realtime-timeseries-data/54507329#54507329, which I have written an implementation of in JS:

//supporting functions for SZS
function mean(values) {
return (values.reduce((previous, current) => current += previous)/values.length);
}
const sd = (values, usePopulation = false) => {
const mean = values.reduce((previous, current) => current += previous)/values.length;
return Math.sqrt(
values.reduce((acc, val) => acc.concat((val — mean) ** 2), []).reduce((acc, val) => acc + val, 0) /
(values.length — (usePopulation ? 0 : 1))
);
}
/*
An implementation of the Smoothed Z-Score algorithm for peak detection
Originally from
https://stackoverflow.com/questions/22583391/peak-signal-detection-in-realtime-timeseries-data/54507329#54507329
*/
function SZS(y,lag,threshold,influence) {
var signals = Array.from(Array(y.length), () => 0);
var filteredY = y.slice(0,lag);
var avgFilter = new Array(y.length);
var stdFilter = new Array(y.length);
avgFilter[lag] = mean(y.slice(0,lag));
stdFilter[lag] = sd(y.slice(0,lag));
for (var i = (lag+1); i<y.length; i++) {
if (Math.abs(y[i]-avgFilter[i-1]) > threshold*stdFilter[i-1]) {
if (y[i] > avgFilter[i-1]) {
signals[i] = 1;
} else {
signals[i] = -1;
}
filteredY[i] = influence*y[i]+(1-influence)*filteredY[i-1];
} else {
signals[i] = 0
filteredY[i] = y[i]
}
avgFilter[i] =mean(filteredY.slice((i-lag),i))
stdFilter[i] =sd(filteredY.slice((i-lag),i))
}
return(signals)
}

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store