function createSVG(o) { return d3.create(o); } function createDOM(o) { return document.createElement(o); } function removeAppend(p, c) { return removeChildNodes(p).appendChild(c); f } function formatDate(dt) { var m = dt.getMonth() + 1; var y = dt.getFullYear(); var d = dt.getDate(); if (m < 10) { m = "0" + m; } if (d < 10) { d = "0" + d; } return y + "-" + m + "-" + d; } function sum(x) { var t = 0; for (var i = 0; i < x.length; i++) { t += x[i]; } return t; } function mean(x) { return x.length === 0 ? 0.0 : sum(x) / x.length; } function sumProduct(a, b) { var t = 0; for (var i = 0; i < Math.min(a.length, b.length); i++) { t += a[i] * b[i]; } return t; } function sumSquares(x) { return sumProduct(x, x); } function variance(x) { if (x !== null && x.length > 2) { var sX = sum(x), ssX = sumSquares(x); return (ssX - sX * sX / x.length) / (x.length - 1); } return 0.0; } function covariance(x, y) { if (x !== null && x.length > 2) { var sX = sum(x), sY = sum(y), cross = sumProduct(x, y); return (cross - sX * sY / x.length) / (x.length - 1); } return 0.0; } function correlation(x, y) { if (x !== null && x.length > 2) { var vX = variance(x), vY = variance(y), cov = covariance(x, y); return cov / Math.sqrt(vX, vY); } return 0.0; } function getDurbinWatson(x) { var dw = 0; for (var i = 1; i < x.length; i++) { var d = x[i] - x[i - 1]; dw += d * d; } return dw / sumProduct(x, x); } function getRegression(y, x) { var cov = covariance(y, x); var v = variance(x); var b = cov / v; var c = mean(y) - b * mean(x); return {Beta: b, Const: c}; } function fft_1d(array) { var u_r, u_i, w_r, w_i, t_r, t_i; var ln, nv2, k, l, le, le1, j, ip, i, n; n = array.length; ln = Math.floor(Math.log(n) / Math.LN2 + 0.5); nv2 = n / 2; j = 1; for (i = 1; i < n; i++) { if (i < j) { t_r = array[i - 1][0]; t_i = array[i - 1][1]; array[i - 1][0] = array[j - 1][0]; array[i - 1][1] = array[j - 1][1]; array[j - 1][0] = t_r; array[j - 1][1] = t_i; } k = nv2; while (k < j) { j -= k; k /= 2; } j = j + k; } for (l = 1; l <= ln; l++) /* loops thru stages */ { le = Math.floor(Math.exp(l * Math.LN2) + 0.5); le1 = le / 2; u_r = 1.0; u_i = 0.0; w_r = Math.cos(Math.PI / le1); w_i = -Math.sin(Math.PI / le1); for (j = 1; j <= le1; j++) /* loops thru 1/2 twiddle values per stage */ { for (i = j; i <= n; i += le) /* loops thru points per 1/2 twiddle */ { ip = i + le1; t_r = array[ip - 1][0] * u_r - u_i * array[ip - 1][1]; t_i = array[ip - 1][1] * u_r + u_i * array[ip - 1][0]; array[ip - 1][0] = array[i - 1][0] - t_r; array[ip - 1][1] = array[i - 1][1] - t_i; array[i - 1][0] = array[i - 1][0] + t_r; array[i - 1][1] = array[i - 1][1] + t_i; } t_r = u_r * w_r - w_i * u_i; u_i = w_r * u_i + w_i * u_r; u_r = t_r; } } return array; } function ifft_1d(array) { var LN_2 = Math.log(2.0); var u_r, u_i, w_r, w_i, t_r, t_i; var ln, nv2, k, l, le, le1, j, ip, i, n; n = array.length; ln = parseInt((Math.log(n) / LN_2 + 0.5)); nv2 = n / 2; j = 1; for (i = 1; i < n; i++) { if (i < j) { t_r = array[i - 1][0]; t_i = array[i - 1][1]; array[i - 1][0] = array[j - 1][0]; array[i - 1][1] = array[j - 1][1]; array[j - 1][0] = t_r; array[j - 1][1] = t_i; } k = nv2; while (k < j) { j = j - k; k = k / 2; } j = j + k; } for (l = 1; l <= ln; l++) /* loops thru stages */ { le = parseInt((Math.exp(l * LN_2) + 0.5)); le1 = le / 2; u_r = 1.0; u_i = 0.0; w_r = Math.cos(Math.PI / le1); w_i = Math.sin(Math.PI / le1); for (j = 1; j <= le1; j++) /* loops thru 1/2 twiddle values per stage */ { for (i = j; i <= n; i += le) /* loops thru points per 1/2 twiddle */ { ip = i + le1; t_r = array[ip - 1][0] * u_r - u_i * array[ip - 1][1]; t_i = array[ip - 1][1] * u_r + u_i * array[ip - 1][0]; array[ip - 1][0] = array[i - 1][0] - t_r; array[ip - 1][1] = array[i - 1][1] - t_i; array[i - 1][0] = array[i - 1][0] + t_r; array[i - 1][1] = array[i - 1][1] + t_i; } t_r = u_r * w_r - w_i * u_i; u_i = w_r * u_i + w_i * u_r; u_r = t_r; } } return array; } function addTableItems(t, dt, items, f, showzeros) { var tr = document.createElement("tr"); showzeros = showzeros === undefined ? true : showzeros; for (var i = 0; i < items.length; i++) { var n = items[i]; var th = document.createElement(dt); tr.appendChild(th); var type = typeof (n); if (type === 'string' || type === 'number') { if (f !== undefined) { th.style.background = f(i); } if (type === 'number') { if (n === 0) { n = showzeros ? n : ""; } } th.appendChild(document.createTextNode(n)); } else if (n instanceof Date) { th.appendChild(document.createTextNode(formatDate(n))); } else if (n === undefined) { th.appendChild(document.createTextNode('undefined')); } else { th.appendChild(n); } } t.appendChild(tr); return t; } function createArray(z, f) { var m = []; for (var i = 0; i < z; i++) { m.push(f(i)); } return m; } function append(p, c) { if (typeof (p) === 'string') { p = document.getElementById(p); } p.appendChild(c); return p; } function removeChildNodes(parent) { if (typeof (parent) === 'string') { parent = document.getElementById(parent); } while (parent.firstChild) { parent.removeChild(parent.firstChild); } return parent; } function createHeader(s, t) { var h = document.createElement(s); h.setAttribute('class', 'Header'); h.innerHTML = t; return h; } class PlotTool { Margin; Width; Height; SVG; G; X; Y; YRight; constructor(t, r, b, l, w, h) { this.Margin = {top: t, right: r, bottom: b, left: l}; this.Width = w - this.Margin.left - this.Margin.right; this.Height = h - this.Margin.top - this.Margin.bottom; this.SVG = createSVG('svg'); this.SVG.attr("width", this.Width + this.Margin.left + this.Margin.right) .attr("height", this.Height + this.Margin.top + this.Margin.bottom); this.G = this.SVG.append("g") .attr("transform", "translate(" + this.Margin.left + "," + this.Margin.top + ")"); } getWidth() { return this.Width; } appendSVGTitle(t) { this.G.append('svg:title').text(t); } getHeight() { return this.Height; } setTitle(t) { this.addText(t, this.getWidth() / 2, this.Margin.top / 2); } addText(t, x, y, c, a) { c = c === undefined ? 'black' : c; if (a === undefined ? a = 'middle' : a) this.SVG.append("text") .attr("x", (x)) .attr("y", (y)) .attr("text-anchor", a) .style("font-size", "16px") .style("fill", c) //.style("text-decoration", "underline") .text(t); } makeXY(xA, yA) { var d = []; for (var i = 0; i < xA.length; i++) { d.push({x: xA[i], y: yA[i]}); } return d; } createLinearXScale(x) { return d3.scaleLinear() .domain([d3.min(x), d3.max(x)]) .range([0, this.getWidth()]); } createLinearYScale(y) { var maxY = d3.max(y); var inc = .10 * Math.abs(maxY); return d3.scaleLinear() .domain([d3.min(y) - inc, maxY + inc]) .range([this.getHeight(), 0]); } createLinearYScaleLowHigh(l, h) { this.setYScale(d3.scaleLinear() .domain([l, h]) .range([this.getHeight(), 0])); } createAndSetLinearXScale(x) { this.setXScale(this.createLinearXScale(x)); } createAndSetLinearYScale(y) { this.setYScale(this.createLinearYScale(y)); } setXYScaleSeries(series) { this.setXScale(d3.scaleTime() .domain(d3.extent(series.values, function (d) { return d.mktDate; })) .range([0, this.getWidth()])); this.setYScale(d3.scaleLinear() .domain([series.minValue, series.maxValue]) .range([this.getHeight(), 0])); } setXScale(x) { this.X = x; this.G.append("g") .attr("transform", "translate(" + 0 + "," + this.getHeight() + ")") .call(d3.axisBottom(x)); } setXScaleSeries(series) { this.setXScale(d3.scaleTime() .domain(d3.extent(series.values, function (d) { return d.mktDate; })) .range([0, this.getWidth()])); } setYScale(y) { this.Y = y; this.G.append("g") .call(d3.axisLeft(y)); } setYScaleRight(y) { this.YRight = y; this.G.append("g") .call(d3.axisRight(y)); } plot(xA, yA) { var g = this.G; var x = this.X = this.createLinearXScale(xA); g.append("g") .attr("transform", "translate(" + 0 + "," + this.getHeight() + ")") .call(d3.axisBottom(x)); // Add Y axis var y = this.Y = this.createLinearYScale(yA); g.append("g") .call(d3.axisLeft(y)); var data = this.makeXY(xA, yA); // Add the line g.append("path") .datum(data) .attr("fill", "none") .attr("stroke", "steelblue") .attr("stroke-width", 1.5) .attr("d", d3.line() .x(function (d) { return x(d.x); }) .y(function (d) { return y(d.y); }) ); } addCircles(xA, yA) { var g = this.G; var data = this.makeXY(xA, yA); var x = this.X; var y = this.Y; data.forEach(function (d) { g.append('circle') .attr("cx", x(d.x)) .attr("cy", y(d.y)) .attr("r", 3) .style("fill", "red"); }); } addLine(xA, yA, c) { c === undefined ? 'blue' : c; var data = this.makeXY(xA, yA); var x = this.X; var y = this.Y; var g = this.G; g.append("path") .datum(data) .attr("fill", "none") .attr("stroke", c) .attr("stroke-width", 1) .attr("d", d3.line() .x(function (d) { return x(d.x); }) .y(function (d) { return y(d.y); }) ); } addSeriesLineSide(series, c, side) { c === undefined ? 'blue' : c; var x = this.X; var y = side; var g = this.G; g.append("path") .datum(series.values) .attr("fill", "none") .attr("stroke", c) .attr("stroke-width", 1) .attr("d", d3.line() .x(function (d) { return x(d.mktDate); }) .y(function (d) { return y(d.value); }) ); } addSeriesLine(series, c) { this.addSeriesLineSide(series, c, this.Y); } addSeriesLineRight(series, c) { this.addSeriesLineSide(series, c, this.YRight); } addSeriesLeftandRight(l, r, cl, cr) { this.setXYScaleSeries(l); this.addSeriesLine(l, cl); this.setYScaleRight( d3.scaleLinear() .domain([r.minValue, r.maxValue]) .range([this.getHeight(), 0])); this.addSeriesLineRight(r, cr); this.addText(l.seriesName, 200, 50, cl, 'right'); this.addText(r.seriesName, 200, 70, cr, 'right'); } node() { return this.SVG.node(); } } class SeriesWrapper { series; constructor(s) { this.series = s; for (var i = 0; i < s.values.length; i++) { if (typeof (s.values[i].mktDate) === 'string') { s.values[i].mktDate = d3.timeParse("%Y-%m-%d")(s.values[i].mktDate); } } this.series.minValue = this.getMin(); this.series.maxValue = this.getMax(); } getCode() { return this.series.code; } size() { return this.series.values.length; } getValue(i) { return this.series.values[i].value; } getSeriesName() { return this.series.seriesName; } getStartDate() { return this.series.values[0].mktDate; } getEndDate() { return this.series.values[this.series.values.length - 1].mktDate; } getDateValue(d) { var s = this.series; for (var i = 1; i < s.values.length; i++) { if (d.getTime() === s.values[i].mktDate.getTime()) { return s.values[i]; } } return null; } getDates() { var d = []; var s = this.series; for (var i = 1; i < s.values.length; i++) { d.push(s.values[i].mktDate); } return d; } getSameDates(da) { var s = this.series; var ls = {code: s.code, seriesName: s.seriesName, values: [], minValue: 0, maxValue: 0}; for (var i = 0; i < da.length; i++) { var x = this.getDateValue(da[i]); if (x !== null) { ls.values.push(x); ls.maxValue = Math.max(ls.maxValue, x.value); ls.minValue = Math.min(ls.minValue, x.value); } } return new SeriesWrapper(ls); } getSeriesBetween(a, z) { var s = this.series; var ls = {code: s.code, seriesName: s.seriesName, values: [], minValue: 0, maxValue: 0}; for (var i = 0; i < s.values.length; i++) { var d = s.values[i].mktDate; if (d.getTime() >= a.getTime() && d.getTime() <= z.getTime()) { var v = s.values[i]; ls.values.push(v); } } return new SeriesWrapper(ls); } getLogDiff() { var s = this.series; var ls = {code: s.code + '_logDiff', seriesName: s.seriesName + '_logDiff', values: [], minValue: 0, maxValue: 0}; for (var i = 1; i < s.values.length; i++) { var x = {mktDate: s.values[i].mktDate, value: 100 * Math.log(s.values[i].value / s.values[i - 1].value)}; ls.values.push(x); } return new SeriesWrapper(ls); } getPolyLag(l, n) { var s = this.series; var ls = {code: s.code + '_polyLag_' + l + "_" + n, seriesName: s.seriesName + '_polyLag_' + l + "_" + n, values: [], minValue: 0, maxValue: 0}; for (var i = l; i < s.values.length; i++) { var v = 0; for (var j = 0; j < l; j++) { var p = s.values[i - j]; v += Math.pow(j, n) * p.value; } var x = {mktDate: s.values[i].mktDate, value: v}; ls.values.push(x); } return new SeriesWrapper(ls); } toArray() { var v = this.series.values; var x = []; for (var i = 0; i < v.length; i++) { x.push(v[i].value); } return x; } plotSeries(t, r, b, l, w, h, duration) { var series = this.series; if (duration === undefined) duration = 5; var margin = {top: t, right: r, bottom: b, left: l}, width = w - margin.left - margin.right, height = h - margin.top - margin.bottom; var svg = createSVG('svg'); var g = svg.attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); for (var i = 0; i < series.values.length; i++) { if (typeof (series.values[i].mktDate) === 'string') { series.values[i].mktDate = d3.timeParse("%Y-%m-%d")(series.values[i].mktDate); } } var x = d3.scaleTime() .domain(d3.extent(series.values, function (d) { return d.mktDate; })) .range([0, width]); g.append("g") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); var y = d3.scaleLinear() .domain([series.minValue, series.maxValue]) .range([height, 0]); g.append("g") .call(d3.axisLeft(y)); g.append("linearGradient") .attr("id", "line-gradient") .attr("gradientUnits", "userSpaceOnUse") .attr("x1", 0) .attr("y1", y(0)) .attr("x2", 0) .attr("y2", y(series.maxValue)) .selectAll("stop") .data([ {offset: "0%", color: "blue"}, {offset: "100%", color: "red"} ]) .enter().append("stop") .attr("offset", function (d) { return d.offset; }) .attr("stop-color", function (d) { return d.color; }); g.append("path") .datum(series.values) .transition().duration(duration) .attr("fill", "none") .attr("stroke", "url(#line-gradient)") .attr("stroke-width", 2) .attr("d", d3.line() .x(function (d) { return x(d.mktDate); }) .y(function (d) { return y(d.value); }) ); g.append("text") .attr("x", (width / 2)) .attr("y", -(margin.top / 2)) .attr("text-anchor", "middle") .style("font-size", "16px") .style("text-decoration", "underline") .text(series.seriesName.replace("Average Price: ", "") .replace(": Wednesday Level", "") ); return svg; } getAnnualExpGrowthRate() { var s = this.series; var p0 = s.values[0]; var p1 = s.values[s.values.length - 1]; var days = (p1.mktDate.getTime() - p0.mktDate.getTime()) / (1000 * 60 * 60 * 24); var yrs = days / 365; return Math.log(p1.value / p0.value) / yrs; } getExpTrendCurve() { var s = this.series; var ls = {code: s.code + '_expTrend', seriesName: s.seriesName + '_expTrend', values: [], minValue: 0, maxValue: 0}; var p0 = s.values[0]; var x = {mktDate: p0.mktDate, value: p0.value}; ls.values.push(x); ls.maxValue = Math.max(ls.maxValue, x.value); ls.minValue = Math.min(ls.minValue, x.value); var scale = 1000 * 60 * 60 * 24; var gr = this.getAnnualExpGrowthRate(); for (var i = 1; i < s.values.length; i++) { var p1 = s.values[i]; var days = (p1.mktDate.getTime() - p0.mktDate.getTime()) / scale; var yrs = days / 365; var x = {mktDate: s.values[i].mktDate, value: p0.value * Math.exp(gr * yrs)}; ls.values.push(x); } ls.wrapper = new SeriesWrapper(ls); return ls; } getDataHistVolGraphDiv() { var d = createDOM('div'); append(d, this.plotSeries(30, 30, 30, 30, 500, 300).node()); var hv = this.getHistVol(90); append(d, hv.plotSeries(30, 30, 30, 30, 500, 300).node()); return d; } getHistVol(lag) { var ld = this.getLogDiff(); var v, m = 0, vv = 0; for (var i = 0; i < lag; i++) { v = ld.getValue(i); m += v; vv += v * v; } var s = this.series; var ls = {code: s.code + '_hv(' + lag + ')', seriesName: s.seriesName + '_hv(' + lag + ')', values: [], minValue: 0, maxValue: 0}; for (var i = lag; i < ld.size(); i++) { var p0 = ld.series.values[i]; var pl = ld.series.values[i - lag]; m += p0.value - pl.value; vv += p0.value * p0.value - pl.value * pl.value; var mmd = m * m / lag; var x = {mktDate: p0.mktDate, value: (Math.sqrt(255.0 * (vv - mmd) / (lag - 1)))}; ls.values.push(x); } return new SeriesWrapper(ls); } ema(tp) { const k = 2 / (tp + 1); const km1 = 1 - k; var s = this.series; var ls = {code: s.code + '_ema(' + tp + ')', seriesName: s.seriesName + '_ema(' + tp + ')', values: [], minValue: 0, maxValue: 0}; var p0 = s.values[0]; var x = {mktDate: p0.mktDate, value: p0.value}; ls.values.push(x); for (let i = 1; i < s.values.length; i++) { var v = (s.values[i].value * k) + (s.values[i - 1].value * km1); var x = {mktDate: p0.mktDate, value: v}; ls.values.push(x); } return new SeriesWrapper(ls); } getMin() { var m = this.getValue(0); for (var i = 0; i < this.size(); i++) { m = Math.min(m, this.getValue(i)); } return m; } getMax() { var m = this.getValue(0); for (var i = 0; i < this.size(); i++) { m = Math.max(m, this.getValue(i)); } return m; } getExpTrendDiffCurve() { var s = this.series; var sgr = this.getExpTrendCurve(); var ls = {code: s.code + '_expTrendDiff', seriesName: s.seriesName + '_expTrendDiff', values: [], minValue: 0, maxValue: 0}; for (var i = 0; i < s.values.length; i++) { var x = {mktDate: s.values[i].mktDate, value: s.values[i].value - sgr.values[i].value}; ls.values.push(x); } ls.wrapper = new SeriesWrapper(ls); return ls; } normalize() { var s = this.series; var n = normalize(s.wrapper.toArray()); var ls = {code: s.code + '_normalized', seriesName: s.seriesName + '_normalized', values: [], minValue: 0, maxValue: 0}; for (var i = 0; i < s.values.length; i++) { var x = {mktDate: s.values[i].mktDate, value: n[i]}; ls.values.push(x); } ls.wrapper = new SeriesWrapper(ls); return ls; } getSeriesSince(a) { var s = this.series; var ls = {code: s.code, seriesName: s.seriesName, values: [], minValue: 0, maxValue: 0}; for (var i = 0; i < s.values.length; i++) { if (s.values[i].mktDate.getTime() >= a.getTime()) { var x = {mktDate: s.values[i].mktDate, value: s.values[i].value}; ls.values.push(x); } } return new SeriesWrapper(ls); } } class SeriesList { Series_List; constructor(s) { this.Series_List = s; } getMinDate() { var d = this.Series_List[0].getStartDate(); for (var i = 1; i < this.Series_List.length; i++) { if (this.Series_List[i].getStartDate().getTime() > d.getTime()) { d = this.Series_List[i].getStartDate(); } } return d; } getMaxDate() { var d = this.Series_List[0].getEndDate(); for (var i = 1; i < this.Series_List.length; i++) { if (this.Series_List[i].getEndDate().getTime() < d.getTime()) { d = this.Series_List[i].getEndDate(); } } return d; } getSeries(s) { return this.Series_List[s]; } getDataTable() { var t = createDOM('table'); var items = ['Date']; for (var i = 0; i < this.size(); i++) { items.push(this.getSeries(i).getCode()); } addTableItems(t, 'th', items); var s = this.getSeries(0); var dts = s.getDates(); for (var i = 0; i < dts.length; i++) { items = [dts[i]]; for (var j = 0; j < this.size(); j++) { var v = this.getSeries(j).getValue(i); items.push(v === undefined ? "N/A" : v.toFixed(4)); } addTableItems(t, 'td', items); } return t; } getSeriesMatrix(s) { var m = []; for (var i = 0; i < s.length; i++) { m.push(this.getSeries(s[i]).toArray()); } return numeric.transpose(m); } getSeriesTable(n){ var t=createDOM('table'); var items=['Date']; var z=this.size(); for(var i=0;i