var _ = require("underscore");
var gimage = require('google-image-chart').charts;
var timeseries = function(data, options) {
/*
Data Format:
[
[Date Object, value],
[Date Object, value]
]
*/
this.options = _.extend({
}, options);
this.data = data;
this.original = data.slice(0);
this.buffer = [];
this.saved = [];
return this;
}
// Output the data
timeseries.prototype.output = function() {
return this.data;
}
// Save the data
timeseries.prototype.save = function(name, options) {
options = _.extend({
color: 'AUTO'
}, options);
this.saved.push({
name: name,
color: options.color,
data: this.data.slice(0)
});
return this;
}
// Chart the data
timeseries.prototype.chart = function(options) {
options = _.extend({
main: false,
width: 800,
height: 200,
bands: [],
lines: [],
points: []
}, options);
// Google Chart
var chart = new gimage.line({
width: options.width,
height: options.height,
bands: options.bands,
hlines: options.lines,
points: options.points,
autoscale: true
});
chart.fromTimeseries(this.data);
// Include the original data
if (options.main) {
chart.fromTimeseries(this.original);
}
// Include saved data
_.each(this.saved, function(saved) {
chart.fromTimeseries(saved.data);
});
return chart.render();
}
// Basic utilities: Array fill, data cloning...
// Returns an array filled with the specified value.
timeseries.prototype.fill = function(value, n) {
var array = [];
var i;
for (i=0;i<n;i++) {
array.push(value);
}
return array;
}
// Returns a clone of the data
timeseries.prototype.clone = function() {
var buffer = _.map(this.data, function(point) {
return [
point[0],
point[1]*1
];
});
return buffer;
}
// Reset the data to its original dataset
timeseries.prototype.reset = function() {
this.data = this.original;
return this;
}
// Convert the data to a 1D array
timeseries.prototype.toArray = function() {
return _.map(this.data, function(datapoint) {
return datapoint[1];
});
}
// Stats: Min, Max, Mean, Stdev
timeseries.prototype.min = function() {
var array = this.toArray();
return _.min(array);
}
timeseries.prototype.max = function() {
var array = this.toArray();
return _.max(array);
}
timeseries.prototype.mean = function(data) {
if (!data) {
var data = this.data;
}
var sum = 0;
var n = 0;
_.each(data, function(datapoint) {
sum += datapoint[1];
n++;
});
return sum/n;
}
timeseries.prototype.stdev = function(data) {
if (!data) {
var data = this.data;
}
var sum = 0;
var n = 0;
var mean = this.mean();
_.each(data, function(datapoint) {
sum += (datapoint[1]-mean)*(datapoint[1]-mean);
n++;
});
return Math.sqrt(sum/n);
}
// Offet the data
timeseries.prototype.offset = function(value, data, ret) {
if (!data) {
var data = this.data;
}
var i;
var j;
var l = data.length;
var sum = 0;
// Reset the buffer
this.buffer = data.slice(0);
for (i=0;i<l;i++) {
this.buffer[i] = [
this.buffer[i][0],
this.buffer[i][1]+value
];
}
if (!ret) {
this.data = this.buffer;
return this;
} else {
return this.buffer;
}
}
// Moving Average
timeseries.prototype.ma = function(options) {
options = _.extend({
period: 12
}, options);
var i;
var j;
var l = this.data.length;
var sum = 0;
// Reset the buffer
this.buffer = [];
// Leave the datapoints [0;period[ intact
this.buffer = this.data.slice(0, options.period);
for (i=options.period;i<l;i++) {
sum = 0;
for (j=options.period;j>0;j--) {
sum += this.data[i-j][1];
}
this.buffer[i] = [this.data[i][0], sum/options.period];
}
this.data = this.buffer;
return this;
}
timeseries.prototype.ema = function(options) {
options = _.extend({
period: 12
}, options);
var i;
var j;
var l = this.data.length;
var sum = 0;
// Reset the buffer
this.buffer = [];
// Leave the datapoints [0;period[ intact
this.buffer = this.data.slice(0, options.period);
var m = 2/(options.period+1); // Multiplier
for (i=options.period;i<l;i++) {
this.buffer[i] = [
this.data[i][0],
(this.data[i][1]-this.data[i-1][1])*m+this.data[i-1][1]
];
}
this.data = this.buffer;
return this;
}
timeseries.prototype.lwma = function(options) {
options = _.extend({
period: 12
}, options);
var i;
var j;
var l = this.data.length;
var sum = 0;
var n = 0;
// Reset the buffer
this.buffer = [];
// Leave the datapoints [0;period[ intact
this.buffer = this.data.slice(0, options.period);
for (i=options.period;i<l;i++) {
sum = 0;
n = 0;
for (j=options.period;j>0;j--) {
sum += this.data[i-j][1]*j;
n += j;
}
this.buffer[i] = [this.data[i][0], sum/n];
}
this.data = this.buffer;
return this;
}
// DSL, iTrend
timeseries.prototype.dsp_itrend = function(options) {
// By Ehler
// http://www.davenewberg.com/Trading/TS_Code/Ehlers_Indicators/iTrend_Ind.html
options = _.extend({
alpha: 0.7,
use: 'main'
}, options);
var i;
var j;
var l = this.data.length;
var trigger = [];
// Reset the buffer
this.buffer = [];
// Leave the datapoints [0;period[ intact
this.buffer = this.data.slice(0, 3);
this.trigger = this.data.slice(0, 3);
for (i=3;i<l;i++) {
this.buffer[i] = [
this.data[i][0],
(options.alpha-(options.alpha*options.alpha)/4)*this.data[i][1] + (0.5*(options.alpha*options.alpha)*this.data[i-1][1]) - (options.alpha - 0.75*(options.alpha*options.alpha)) * this.data[i-2][1] + 2*(1-options.alpha)*this.buffer[i-1][1] - (1-options.alpha)*(1-options.alpha)*this.buffer[i-2][1]
];
this.trigger[i] = [
this.data[i][0],
2*this.buffer[i][1]-this.buffer[i-2][1]
]
}
if (options.use == 'trigger') {
this.data = this.trigger;
} else{
this.data = this.buffer;
}
return this;
}
// Pixelize - Domain reduction
timeseries.prototype.pixelize = function(options) {
options = _.extend({
grid: 20
}, options);
// Calculate the grid values
var min = this.min();
var max = this.max();
var tile = (max-min)/options.grid;
this.buffer = _.map(this.data, function(datapoint) {
datapoint[1] = Math.round(datapoint[1]/tile)*tile;
return datapoint;
});
this.data = this.buffer;
return this;
}
// Iterative Noise Removal
timeseries.prototype.smoother = function(options) {
options = _.extend({
period: 1
}, options);
var i;
var j;
var l = this.data.length;
var sum = 0;
// Reset the buffer
this.buffer = this.data.slice(0);
for (j=0;j<options.period;j++) {
for (i=3;i<l;i++) {
this.buffer[i-1] = [
this.buffer[i-1][0],
(this.buffer[i-2][1]+this.buffer[i][1])/2
];
}
}
this.data = this.buffer;
return this;
}
// Extract the noise out of the data
timeseries.prototype.noiseData = function() {
var i;
var j;
var l = this.data.length;
var sum = 0;
// Reset the buffer
this.buffer = [];
for (i=0;i<l;i++) {
this.buffer[i] = [
this.data[i][0],
this.original[i][1]-this.data[i][1]
];
}
this.data = this.buffer;
return this;
}
// Oscillator function
timeseries.prototype.osc = function() {
var i;
var j;
var l = this.data.length;
var sum = 0;
// Reset the buffer
this.buffer = [];
for (i=0;i<l;i++) {
if (i<=1) {
this.buffer[i] = [
this.data[i][0],
0
];
} else {
this.buffer[i] = [
this.data[i][0],
this.data[i][1]-this.data[i-1][1]
];
}
}
this.data = this.buffer;
return this;
}
// Find the supports and resistances. Wrong algorithm.
timeseries.prototype.supports = function(options) {
options = _.extend({
grid: 40,
threshold: 10
}, options);
// Calculate the grid values
var min = this.min();
var max = this.max();
var tile = (max-min)/options.grid;
var prices = {
};
_.each(this.data, function(datapoint) {
var val = Math.round(datapoint[1]/tile)*tile;
if (!prices[val]) {
prices[val] = 0;
}
prices[val]++;
});
var ordered = [];
var i;
for (i in prices) {
ordered.push({
price: i,
count: prices[i]
});
}
ordered = ordered.sort(function(a,b) {
return b.count-a.count;
});
ordered = _.filter(ordered, function(support) {
return support.count >= options.threshold;
});
if (options.stats) {
return ordered;
}
return _.map(ordered, function(support) {
return support.price;
});
}
// Standardize the data
timeseries.prototype.standardize = function(options) {
options = _.extend({}, options);
var stdev = this.stdev();
var mean = this.mean();
this.data = _.map(this.data, function(datapoint) {
datapoint[1] = (datapoint[1]-mean)/stdev;
return datapoint;
});
return this;
}
// Slice the data
timeseries.prototype.slice = function(from, to) {
if (!from) {
from = 0;
}
if (!to) {
to = this.data.length-1;
}
this.data = this.data.splice(from, to)
return this;
}
// Find the cycle in the data
timeseries.prototype.cycle = function(options) {
options = _.extend({
period: 10,
forecast: false,
forecast_length: 20
}, options);
// Smooth the data
this.smoother(options);
// Copy the data
var buffer = [];
var buffer_forecast = [];
var i;
var j;
var l = this.data.length;
for (i=0;i<2;i++) {
buffer[i] = ([
this.data[i][0],
this.data[i][1]
]);
buffer_forecast[i] = ([
this.data[i][0],
this.data[i][1]
]);
}
for (i=2;i<l;i++) {
// We find the ratio
var d1 = this.data[i][1]-this.data[i-1][1];
var d2 = this.data[i][1]-this.data[i-2][1];
var ratio = d1/d2;
console.log("ratio",ratio, d1, d2);
buffer[i] = ([
this.data[i][0],
this.data[i][1]
]);
buffer_forecast[i] = ([
this.data[i][0],
this.data[i][1],
ratio,
d1>0,
d2>0
]);
}
if (options.forecast) {
for (i=2;i<l;i++) {
if (options.forecast == i) {
// Generate a two cycles sin wave
var sin = [];
for (j=0;j<720;j++) {
sin.push(Math.sin(j*Math.PI/180));
}
console.log("sin",sin);
// Find the closest sin wave
var MSE = [];
var minMSE = 10000000;
var pos;
for (j=2;j<720;j++) {
var d1 = sin[j]-sin[j-1];
var d2 = sin[j]-sin[j-2];
var ratio = d1/d2;
var mse = (ratio-buffer_forecast[i][2])*(ratio-buffer_forecast[i][2]);
if (mse <= minMSE && ((d1>0)==buffer_forecast[i][3]) && ((d2>0)==buffer_forecast[i][3])) {
minMSE = mse;
pos = j;
}
}
console.log("minMSE",minMSE, pos);
for (j=0;j<=options.forecast_length;j++) {
buffer_forecast[i+j][1] = Math.sin((pos+j)*Math.PI/180);
//buffer_forecast[i+j][1] = sin[pos+j];
console.log("buffer_forecast["+(i+j)+"]", pos+j, buffer_forecast[i+j][1]);
}
break;
}
}
this.data = buffer_forecast;
} else {
this.data = buffer;
}
return this;
}
// Get the outliers from the dataset
timeseries.prototype.outliers = function(options) {
// Original code by Professor Hossein Arsham - http://home.ubalt.edu/ntsbarsh/Business-stat/otherapplets/Outlier.htm
// Re-written for timeseries-analysis.
options = _.extend({
threshold: 2.5
}, options);
// Create a copy of the data;
this.buffer = this.data.slice(0);
// standardize the data
this.standardize();
var outliers = [];
_.each(this.data, function(datapoint) {
if (Math.abs(datapoint[1]) > options.threshold) {
outliers.push(datapoint);
}
});
// restore the data
this.data = this.buffer.slice(0);
delete this.buffer;
return outliers;
}
/* EXPERIMENTAL - AutoRegression Analysis */
timeseries.prototype.regression_forecast = function(options) {
options = _.extend({
method: 'ARMaxEntropy', // ARMaxEntropy | ARLeastSquare
sample: 50, // points int he sample
start: 100, // Where to start
n: 5, // How many points to forecast
degree: 5
},options);
var i;
var j;
var l = this.data.length;
var mean = this.mean();
this.offset(-mean);
var backup = this.clone();
var buffer = this.clone();
var sample = buffer.slice(options.start-1-options.sample, options.start);
// The current data to process is only a sample of the real data.
this.data = sample;
// Get the AR coeffs
var coeffs = this[options.method]({degree: options.degree});
console.log("coeffs",coeffs);
for (i=options.start;i<options.start+options.n;i++) {
buffer[i][1] = 0;
for (j=0;j<coeffs.length;j++) {
if (options.method == 'ARMaxEntropy') {
buffer[i][1] -= buffer[i-1-j][1]*coeffs[j];
} else {
buffer[i][1] += buffer[i-1-j][1]*coeffs[j];
}
}
console.log("buffer["+i+"][1]",buffer[i][1]);
}
this.data = buffer;
this.offset(mean);
return this;
}
timeseries.prototype.regression_forecast_optimize = function(options) {
options = _.extend({
data: this.data,
maxPct: 0.2,
maxSampleSize: false
},options);
var l = options.data.length;
var maxSampleSize = Math.round(l*options.maxPct);
if (options.maxSampleSize) {
maxSampleSize = Math.min(maxSampleSize, options.maxSampleSize);
}
var maxDegree = Math.round(maxSampleSize);
var methods = ['ARMaxEntropy', 'ARLeastSquare'];
var ss; // sample size
var deg; // degree
var MSEData = [];
var i;
for (i=0;i<methods.length;i++) {
for (ss=3;ss<=maxSampleSize;ss++) {
for (deg=1;deg<=maxDegree;deg++) {
if (deg<=ss) {
var mse = this.regression_forecast_mse({
method: methods[i],
sample: ss,
degree: deg,
data: options.data
});
console.log("Trying method("+methods[i]+") degree("+deg+") sample("+ss+")\t"+mse);
if (!isNaN(mse)) {
MSEData.push({
MSE: mse,
method: methods[i],
degree: deg,
sample: ss
});
}
} else {
break;
}
}
}
}
// Now we sort by MSE
MSEData = MSEData.sort(function(a,b) {
return a.MSE>b.MSE;
});
console.log("Best Settings: ",MSEData[0]);
// Return the best settings
return MSEData[0];
}
// Calculate the MSE for a forecast, for a set of parameters
timeseries.prototype.regression_forecast_mse = function(options) {
options = _.extend({
method: 'ARMaxEntropy', // ARMaxEntropy | ARLeastSquare
sample: 50,
degree: 5,
data: this.data
},options);
var i;
var j;
var l = options.data.length;
var mean = this.mean(options.data);
options.data = this.offset(-mean, options.data, true);
var backup = _.map(options.data, function(item) {
return [
item[0],
item[1]*1
];
});
var buffer = _.map(options.data, function(item) {
return [
item[0],
item[1]*1
];
});
var MSE = 0;
var n = 0;
for (i=options.sample;i<l-1;i++) {
var sample = buffer.slice(i-options.sample, i);
// Get the AR coeffs
var coeffs = this[options.method]({degree:options.degree, data:sample});
var knownValue = buffer[i+1][1]*1;
buffer[i+1][1] = 0;
for (j=0;j<coeffs.length;j++) {
if (options.method == 'ARMaxEntropy') {
buffer[i+1][1] -= backup[i-j][1]*coeffs[j];
} else {
buffer[i+1][1] += backup[i-j][1]*coeffs[j];
}
}
MSE += (knownValue-buffer[i+1][1])*(knownValue-buffer[i+1][1]);
n++;
}
MSE /= n;
//this.data = buffer;
// Put back the mean
//this.offset(mean);
return MSE;
}
timeseries.prototype.sliding_regression_forecast = function(options) {
options = _.extend({
method: 'ARMaxEntropy', // ARMaxEntropy | ARLeastSquare
sample: 50,
degree: 5
},options);
var i;
var j;
var l = this.data.length;
var mean = this.mean();
this.offset(-mean);
var backup = this.clone();
var buffer = this.clone();
for (i=options.sample;i<l-1;i++) {
var sample = buffer.slice(i-options.sample, i);
// The current data to process is only a sample of the real data.
this.data = sample;
// Get the AR coeffs
var coeffs = this[options.method]({degree:options.degree});
buffer[i+1][1] = 0; //backup[i][1]*1;
for (j=0;j<coeffs.length;j++) {
if (options.method == 'ARMaxEntropy') {
buffer[i+1][1] -= backup[i-j][1]*coeffs[j];
} else {
buffer[i+1][1] += backup[i-j][1]*coeffs[j];
}
}
//buffer[i+1][1] -
}
this.data = buffer;
// Put back the mean
this.offset(mean);
return this;
}
// Autoregression method: MaxEntropy
timeseries.prototype.ARMaxEntropy = function(options) {
// Credits to Alex Sergejew, Nick Hawthorn, Rainer Hegger (1998)
// Zero-Indexed arrays modification by Paul Sanders (the arrays were One-indexed, FORTRAN style)
// Ported to Javascript by Julien Loutre for timeseries-analysis, from Paul Bourke's C code.
options = _.extend({
degree: 5,
data: this.data,
intermediates: false // Generates and returns the intermediates, a 2D array, instead of the coefficients.
}, options);
var scope = this;
var i;
var length = options.data.length;
var pef = this.fill(0, length);
var per = this.fill(0, length);
var ar = this.fill([], options.degree+1);
ar = _.map(ar, function(d1) {
return scope.fill(0, options.degree+1);
});
var h = this.fill(0, length);
var g = this.fill(0, options.degree+2);
var t1, t2;
var n;
var coef = [];
for (n=1; n <= options.degree; n++)
{
var sn = 0.0;
var sd = 0.0;
var j;
var jj = length - n;
for (j = 0; j < jj; j++)
{
t1 = options.data[j + n][1] + pef[j];
t2 = options.data[j][1] + per[j];
sn -= 2.0 * t1 * t2;
sd += (t1 * t1) + (t2 * t2);
}
t1 = g[n] = sn / sd;
if (n != 1)
{
for (j = 1; j < n; j++) {
h[j] = g[j] + t1 * g[n - j];
}
for (j = 1; j < n; j++) {
g[j] = h[j];
}
jj--;
}
for (j = 0; j < jj; j++)
{
per [j] += t1 * pef[j] + t1 * options.data[j + n][1];
pef [j] = pef[j + 1] + t1 * per[j + 1] + t1 * options.data[j + 1][1];
}
if (options.intermediates) {
for (j = 0; j < n; j++) {
ar[n][j] = g[j + 1];
}
}
}
if (!options.intermediates) {
for (n = 0; n < options.degree; n++) {
coef[n] = g[n + 1];
}
return coef;
} else {
return ar;
}
}
// Autoregression method: Least Square
timeseries.prototype.ARLeastSquare = function(options) {
// Credits to Rainer Hegger (1998)
// Ported to Javascript by Julien Loutre for timeseries-analysis, from Paul Bourke's C code.
var scope = this;
options = _.extend({
degree: 5,
data: this.data
}, options);
var i,j,k,hj,hi;
var coefficients = [];
var length = options.data.length;
var mat = this.fill([], options.degree);
mat = _.map(mat, function(d1) {
return scope.fill(0, options.degree);
});
for (i=0;i < options.degree;i++) {
coefficients[i] = 0.0;
for (j=0;j< options.degree;j++) {
mat[i][j] = 0.0;
}
}
for (i=options.degree-1;i < length-1;i++) {
hi = i + 1;
for (j=0;j < options.degree;j++) {
hj = i - j;
coefficients[j] += (options.data[hi][1] * options.data[hj][1]);
for (k=j;k < options.degree;k++) {
mat[j][k] += (options.data[hj][1] * options.data[i-k][1]);
}
}
}
for (i=0;i < options.degree;i++) {
coefficients[i] /= (length - options.degree);
for (j=i;j < options.degree;j++) {
mat[i][j] /= (length - options.degree);
mat[j][i] = mat[i][j];
}
}
var solved = this.SolveLE(mat,coefficients,options.degree);
return coefficients;
}
timeseries.prototype.SolveLE = function(mat, vec, n) {
// Gaussian elimination solver.
// Use the coefficients from the Least Square method and make it into the real AR coefficients.
// Original code by Rainer Hegger (1998). Modified by Paul Bourke.
// Ported to Javascript by Julien Loutre for timeseries-analysis, from Paul Bourke's C code.
var i,j,k,maxi;
var vswap = [];
var mswap = [];
var hvec = [];
var max,h,pivot,q;
for (i=0;i<n-1;i++) {
max = Math.abs(mat[i][i]);
maxi = i;
for (j=i+1;j<n;j++) {
if ((h = Math.abs(mat[j][i])) > max) {
max = h;
maxi = j;
}
}
if (maxi != i) {
mswap = mat[i];
mat[i] = mat[maxi];
mat[maxi] = mswap;
vswap = vec[i];
vec[i] = vec[maxi];
vec[maxi] = vswap;
}
hvec = mat[i];
pivot = hvec[i];
if (Math.abs(pivot) == 0.0) {
console.log("Singular matrix - fatal!");
return false;
}
for (j=i+1;j<n;j++) {
q = - mat[j][i] / pivot;
mat[j][i] = 0.0;
for (k=i+1;k<n;k++) {
mat[j][k] += q * hvec[k];
}
vec[j] += (q * vec[i]);
}
}
vec[n-1] /= mat[n-1][n-1];
for (i=n-2;i>=0;i--) {
hvec = mat[i];
for (j=n-1;j>i;j--) {
vec[i] -= (hvec[j] * vec[j]);
}
vec[i] /= hvec[i];
}
return vec;
}
// Regression analysis. Will most likely be re-written in the future.
timeseries.prototype.regression_analysis = function(options) {
// Original code by Professor Hossein Arsham - http://home.ubalt.edu/ntsbarsh/Business-stat/otherapplets/Trend.htm
// Re-written for timeseries-analysis.
options = _.extend({
threshold: 2.5
}, options);
var output = {};
var i;
var j;
var E = this.data.length; //total number of input spaces
var N = 0;
var N1 = 0;
var N2 = 0;
var SUM = 0.0;
var R = 1;
var Median = 0;
var theList = new Array();
var cval = new Array();
// Run through all the input, add those that have valid values
var a = 0;
for(i=0;i < E;i++) {
SUM += this.data[i][1];
theList[a] = this.data[i][1];
cval[a] = this.data[i][1];
N++;
a++;
}
//check for insufficient data
if(N <= 10) {
console.log("Insufficient data (min 10)");
return false;
}
//sort the list
for(i=0; i<theList.length-1; i++) {
for(j=i+1;j<theList.length; j++) {
if (theList[j] < theList[i]) {
temp = theList[i];
theList[i] = theList[j];
theList[j] = temp;
}
}
}
//calculate Median
var aux = 0;
if(N%2 == 1) {
aux = Math.floor(N/2);
Median = theList[aux];
} else {
Median = (theList[N/2]+theList[((N/2)-1)])/2;
}
// Do the math
var x = Median;
var y = Math.round(100000*x);
var z = y/100000;
// run through each value and compare it with mean
for(i = 0; i < E; i++) {
//check if a value is present and discard the ties
if(this.data[i][1] != x) {
//check if it is greater than mean then adds one
if (this.data[i][1] > x) {
N1++;
a = i;
while (a > 0) {
a--;
if(this.data[a][1] != x) {
break;
}
}
if (this.data[a][1] < x) {
R++;
}
}
//if it is less than mean
else if (this.data[i][1] < x) {
N2++;
a = i;
while (a > 0) {
a--;
if(this.data[a][1] != x) {
break;
}
}
if (this.data[a][1] > x) {
R++;
}
}
}
}
//form.NR.value = R; //value of x or "Scores"
// What is the runs' statistic? I don't know...
// Is it http://en.wikipedia.org/wiki/Wald%E2%80%93Wolfowitz_runs_test ?
output.runs = R;
//compute the expected mean and variance of R
var EM = 1 + (2*N1*N2)/(N1+N2); //Mean "Mu"
var SD1 = [2*N1*N2*(2*N1*N2-N1-N2)];
var SD2 = Math.pow( (N1 + N2), 2);
var SD3 = N1 + N2 - 1;
var SD4 = SD1 / (SD2 * SD3); //Standard deviation "Sigma"
var SD = Math.sqrt(SD4);
//calculating P value MStyle
var z1 = (R - EM)/SD;
var z2 = Math.abs(z1);
var z = z2;
/* Thanks to Jan de Leeuw for the following function */
var t = (z > 0) ? z : (-z);
var P1 = Math.pow((1+t*(0.049867347 + t*(0.0211410061 + t*(0.0032776263 + t*(0.0000380036 + t*(0.0000488906 + t*(0.000005383))))))), -16);
var p = 1 - P1 / 2;
var t = 1-((z > 0) ? p : 1-p); //this is P-value
//rounding the value
var t1 = Math.round(100000*t);
var t2 = t1/100000; //this is P-value too
//form.PV.value = t2;
//determine the conclusion
// Encoding the trend value from 0 (no trend) to 3 (strong strend evidence)
if (t2 < 0.01) {
//form.CON.value = "Strong evidence for trend";
output.trend = 3;
} else if (t2 < 0.05 && t2 >= 0.01) {
//form.CON.value = "Moderate evidence for trend";
output.trend = 2;
} else if (t2 < 0.10 && t2 >= 0.05) {
//form.CON.value = "Suggestive evidence for trend";
output.trend = 1;
} else if (t2 >= 0.10) {
//form.CON.value = "Little or no real evidences for trend";
output.trend = 0;
} else {
//form.CON.value = "Strong evidence for trend";
output.trend = 3;
}
//AUTO CORRELATION
var DWNN = 0;
var DWND = (cval[0]*cval[0]);
for (i=1; i<cval.length; i++) {
DWNN = DWNN +(cval[i]- cval[i-1])*(cval[i]-cval[i-1]) ;
DWND = DWND +(cval[i]*cval[i]);
}
var DW = DWNN/DWND;
DW = Math.round(DW*100000)/100000;
//form.DW.value = DW;
output.durbinWatson = DW;
var Q01 = 2-4.6527/(Math.sqrt(N+2));
var Q05 = 2-3.2897/(Math.sqrt(N+2));
//determine the conclusion
// Encode the correlation between 1 and 3
if((DW>=Q01) || (DW<=(4 - Q01))) {
//form.COND.value = "Moderate evidence againt autocorrelation";
output.autocorrelation = 2;
} else if((DW >= Q05)&&(DW<=(4 - Q05))) {
//form.COND.value = "Strong evidences against autocorrelation";
output.autocorrelation = 3;
} else {
//form.COND.value = "Suggestive evidences for autocorrelation";
output.autocorrelation = 1;
}
return output;
}
// Get the Durbin-Watson statistic
// http://en.wikipedia.org/wiki/Durbin%E2%80%93Watson_statistic
timeseries.prototype.durbinWatson = function() {
return this.regression_analysis().durbinWatson;
}
// Get the Durbin-Watson statistic
// http://en.wikipedia.org/wiki/Durbin%E2%80%93Watson_statistic
timeseries.prototype.regression_analysis = function() {
return this.regression_analysis().durbinWatson;
}
// Data adapters
var adapter = {
};
adapter.fromDB = function(data, options) {
options = _.extend({
value: 'close',
date: 'date'
}, options);
return _.map(data, function(datapoint) {
return [new Date(datapoint[options.date]).getTime(), datapoint[options.value]];
});
};
adapter.fromArray = function(data) {
return _.map(data, function(datapoint) {
return [new Date(), datapoint];
});
};
adapter.geometric = function(options) {
options = _.extend({
}, options);
var i;
var j;
var output = [];
for (i=0;i<128;i++) {
output.push([
new Date(),
Math.cos(i*0.01)+0.75*Math.cos(i*0.03)+0.5*Math.cos(i*0.05)+0.25*Math.cos(i*0.11)
]);
}
return output;
};
adapter.complex = function(options) {
options = _.extend({
cycles: 10,
quality: 1,
inertia: 0
}, options);
var i;
var j;
var output = [];
for (i=0;i<options.cycles;i++) {
for (j=0;j<360;j+=options.quality) {
output.push([
new Date(),
(Math.sin(j*Math.PI/180)+Math.cos(j*3*Math.PI/180)-Math.sin(j*2.4*Math.PI/180))*100
]);
options.quality += options.inertia;
}
}
return output;
};
adapter.sin = function(options) {
options = _.extend({
cycles: 4,
quality: 2,
inertia: 0
}, options);
var i;
var j;
var output = [];
for (i=0;i<options.cycles;i++) {
for (j=0;j<360;j+=options.quality) {
output.push([
new Date(),
Math.cos(j*Math.PI/180)*100
]);
options.quality += options.inertia;
}
console.log("options.quality",options.quality);
}
return output;
};
adapter.tan = function(options) {
options = _.extend({
cycles: 1
}, options);
var i;
var j;
var output = [];
for (i=0;i<options.cycles;i++) {
for (j=0;j<360;j++) {
output.push([
new Date(),
Math.tan(j*Math.PI/180)
]);
}
}
return output;
};
exports.main = timeseries;
exports.adapter = adapter;
exports.version = "1.0.11";