UNPKG

techan-js

Version:

A visual, technical analysis and charting (Candlestick, OHLC, indicators) library built on D3.

1,875 lines (1,541 loc) 138 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var d3$1 = _interopDefault(require('d3')); var atrtrailingstop = function() { var date = function(d) { return d.date; }, up = function(d) { return d.up; }, down = function(d) { return d.down; }; function accessor(d) { return accessor.up(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.up = function(_) { if (!arguments.length) return up; up = _; return bind(); }; accessor.down = function(_) { if (!arguments.length) return down; down = _; return bind(); }; function bind() { accessor.d = date; accessor.up = up; accessor.dn = down; return accessor; } return bind(); }; var crosshair = function() { /** * Supports getter and setter. Watch out if used in d3 and the second parameter is an index!! * This approach needs further thought. * @param d Underlying data object to get or set the value * @param _ If passed turns into a setter. This is the value to set * @returns {*} */ var x = function(d, _) { if(arguments.length < 2) return d.x; d.x = _; return accessor; }, /** * Supports getter and setter. Watch out if used in d3 and the second parameter is an index!! * This approach needs further thought. * @param d Underlying data object to get or set the value * @param _ If passed turns into a setter. This is the value to set * @returns {*} */ y = function(d, _) { if(arguments.length < 2) return d.y; d.y = _; return accessor; }; function accessor(d) { return accessor.xv(d); } accessor.x = function(_) { if (!arguments.length) return x; x = _; return bind(); }; accessor.y = function(_) { if (!arguments.length) return y; y = _; return bind(); }; function bind() { accessor.xv = x; accessor.yv = y; return accessor; } return bind(); }; var ichimoku = function() { var date = function(d) { return d.date; }, tenkanSen = function(d) { return d.tenkanSen; }, // Conversion line kijunSen = function(d) { return d.kijunSen; }, // Base Line senkouSpanA = function(d) { return d.senkouSpanA; }, // Leading Span A senkouSpanB = function(d) { return d.senkouSpanB;}, // Leading Span B chikouSpan = function(d) { return d.chikouSpan;}, // Lagging Span // Functions to get to the parameters ptenanSen = function(d) { return d.parameters.tenkanSen; }, // Parameter: Conversion Line Period pkijunSen = function(d) { return d.parameters.kijunSen; }, // Parameter: Base Line Period, Offset psenkouSpanB = function(d) { return d.parameters.senkouSpanB; }; // Parameter: Senkou Span B Period, Offset function accessor(d) { return accessor.ts(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.tenkanSen = function(_) { if (!arguments.length) return tenkanSen; tenkanSen = _; return bind(); }; accessor.kijunSen = function(_) { if (!arguments.length) return kijunSen; kijunSen = _; return bind(); }; accessor.senkouSpanA = function(_) { if (!arguments.length) return senkouSpanA; senkouSpanA = _; return bind(); }; accessor.senkouSpanB = function(_) { if (!arguments.length) return senkouSpanB; senkouSpanB = _; return bind(); }; accessor.chikouSpan = function(_) { if (!arguments.length) return chikouSpan; chikouSpan = _; return bind(); }; accessor.ptenanSen = function(_) { if (!arguments.length) return ptenanSen; ptenanSen = _; return bind(); }; accessor.pkijunSen = function(_) { if (!arguments.length) return pkijunSen; pkijunSen = _; return bind(); }; accessor.psenkouSpanB = function(_) { if (!arguments.length) return psenkouSpanB; psenkouSpanB = _; return bind(); }; function bind() { accessor.d = date; accessor.ts = tenkanSen; accessor.ks = kijunSen; accessor.sa = senkouSpanA; accessor.sb = senkouSpanB; accessor.c = chikouSpan; accessor.pts = ptenanSen; accessor.pks = pkijunSen; accessor.pssb = psenkouSpanB; return accessor; } return bind(); }; var macd = function() { var date = function(d) { return d.date; }, macd = function(d) { return d.macd; }, zero = function(d) { return d.zero; }, signal = function(d) { return d.signal;}, difference = function(d) { return d.difference;}; function accessor(d) { return accessor.m(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.macd = function(_) { if (!arguments.length) return macd; macd = _; return bind(); }; accessor.signal = function(_) { if (!arguments.length) return signal; signal = _; return bind(); }; accessor.difference = function(_) { if (!arguments.length) return difference; difference = _; return bind(); }; function bind() { accessor.d = date; accessor.m = macd; accessor.s = signal; accessor.dif = difference; accessor.z = zero; return accessor; } return bind(); }; var ohlc = function() { var date = function(d) { return d.date; }, open = function(d) { return d.open; }, high = function(d) { return d.high; }, low = function(d) { return d.low; }, close = function(d) { return d.close;}, volume = function(d) { return d.volume; }; function accessor(d) { return accessor.c(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.open = function(_) { if (!arguments.length) return open; open = _; return bind(); }; accessor.high = function(_) { if (!arguments.length) return high; high = _; return bind(); }; accessor.low = function(_) { if (!arguments.length) return low; low = _; return bind(); }; accessor.close = function(_) { if (!arguments.length) return close; close = _; return bind(); }; accessor.volume = function(_) { if (!arguments.length) return volume; volume = _; return bind(); }; function bind() { accessor.d = date; accessor.o = open; accessor.h = high; accessor.l = low; accessor.c = close; accessor.v = volume; return accessor; } return bind(); }; var rsi = function() { var date = function(d) { return d.date; }, rsi = function(d) { return d.rsi; }, overbought = function(d) { return d.overbought; }, oversold = function(d) { return d.oversold; }, middle = function(d) { return d.middle; }; function accessor(d) { return accessor.r(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.rsi = function(_) { if (!arguments.length) return rsi; rsi = _; return bind(); }; accessor.overbought = function(_) { if (!arguments.length) return overbought; overbought = _; return bind(); }; accessor.oversold = function(_) { if (!arguments.length) return oversold; oversold = _; return bind(); }; accessor.middle = function(_) { if (!arguments.length) return middle; middle = _; return bind(); }; function bind() { accessor.d = date; accessor.r = rsi; accessor.ob = overbought; accessor.os = oversold; accessor.m = middle; return accessor; } return bind(); }; var trendline = function() { var startDate = function(d, _) { if(arguments.length < 2) return d.start.date; d.start.date = _; }, startValue = function(d, _) { if(arguments.length < 2) return d.start.value; d.start.value = _; }, endDate = function(d, _) { if(arguments.length < 2) return d.end.date; d.end.date = _; }, endValue = function(d, _) { if(arguments.length < 2) return d.end.value; d.end.value = _; }; function accessor(d) { return accessor.sv(d); } accessor.startDate = function(_) { if (!arguments.length) return startDate; startDate = _; return bind(); }; accessor.startValue = function(_) { if (!arguments.length) return startValue; startValue = _; return bind(); }; accessor.endDate = function(_) { if (!arguments.length) return endDate; endDate = _; return bind(); }; accessor.endValue = function(_) { if (!arguments.length) return endValue; endValue = _; return bind(); }; function bind() { accessor.sd = startDate; accessor.sv = startValue; accessor.ed = endDate; accessor.ev = endValue; return accessor; } return bind(); }; var value = function() { var date = function(d) { return d.date; }, /** * Supports getter and setter * @param d Underlying data object to get or set the value * @param _ If passed turns into a setter. This is the value to set * @returns {*} */ value = function(d, _) { if(arguments.length < 2) return d.value; d.value = _; return accessor; }, zero = function(d) { return 0; }; function accessor(d) { return accessor.v(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.value = function(_) { if (!arguments.length) return value; value = _; return bind(); }; accessor.zero = function(_) { if (!arguments.length) return zero; zero = _; return bind(); }; function bind() { accessor.d = date; accessor.v = value; accessor.z = zero; return accessor; } return bind(); }; var volume = function() { var date = function(d) { return d.date; }, volume = function(d) { return d.volume; }; function accessor(d) { return accessor.v(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.volume = function(_) { if (!arguments.length) return volume; volume = _; return bind(); }; function bind() { accessor.d = date; accessor.v = volume; return accessor; } return bind(); }; var tick = function() { var date = function(d) { return d.date; }, high = function(d) { return d.high; }, low = function(d) { return d.low; }, spread = function(d) { return d.spread; }; function accessor(d) { bind(); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.high = function(_) { if (!arguments.length) return high; high = _; return bind(); }; accessor.low = function(_) { if (!arguments.length) return low; low = _; return bind(); }; accessor.spread = function(_) { if (!arguments.length) return spread; spread = _; return bind(); }; function bind() { accessor.d = date; accessor.h = high; accessor.l = low; accessor.s = spread; return accessor; } return bind(); }; var trade = function() { var date = function(d) { return d.date; }, type = function(d) { return d.type; }, price = function(d) { return d.price; }; function accessor(d) { return accessor.p(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; /** * A function which returns a string representing the type of this trade * @param _ A constant string or function which takes a data point and returns a string of valid classname format */ accessor.type = function(_) { if (!arguments.length) return type; type = _; return bind(); }; accessor.price = function(_) { if (!arguments.length) return price; price = _; return bind(); }; function bind() { accessor.d = date; accessor.t = type; accessor.p = price; return accessor; } return bind(); }; var adx = function() { var date = function(d) { return d.date; }, adx = function(d) { return d.adx; }, plusDi = function(d) { return d.plusDi; }, minusDi = function(d) { return d.minusDi; }; function accessor(d) { return accessor.r(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.adx = function(_) { if (!arguments.length) return adx; adx = _; return bind(); }; accessor.plusDi = function(_) { if (!arguments.length) return plusDi; plusDi = _; return bind(); }; accessor.minusDi = function(_) { if (!arguments.length) return minusDi; minusDi = _; return bind(); }; function bind() { accessor.d = date; accessor.adx = adx; accessor.plusDi = plusDi; accessor.minusDi = minusDi; return accessor; } return bind(); }; var aroon = function() { var date = function(d) { return d.date; }, up = function(d) { return d.up; }, down = function(d) { return d.down; }, oscillator = function(d) { return d.oscillator; }, overbought = function(d) { return d.overbought; }, oversold = function(d) { return d.oversold; }, middle = function(d) { return d.middle; }; function accessor(d) { return accessor.r(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.up = function(_) { if (!arguments.length) return up; up = _; return bind(); }; accessor.down = function(_) { if (!arguments.length) return down; down = _; return bind(); }; accessor.oscillator = function(_) { if (!arguments.length) return oscillator; oscillator = _; return bind(); }; accessor.overbought = function(_) { if (!arguments.length) return overbought; overbought = _; return bind(); }; accessor.oversold = function(_) { if (!arguments.length) return oversold; oversold = _; return bind(); }; accessor.middle = function(_) { if (!arguments.length) return middle; middle = _; return bind(); }; function bind() { accessor.d = date; accessor.up = up; accessor.down = down; accessor.oscillator = oscillator; accessor.ob = overbought; accessor.os = oversold; accessor.m = middle; return accessor; } return bind(); }; var stochastic = function() { var date = function(d) { return d.date; }, stochasticK = function(d) { return d.stochasticK; }, stochasticD = function(d) { return d.stochasticD; }, overbought = function(d) { return d.overbought; }, oversold = function(d) { return d.oversold; }; function accessor(d) { return accessor.r(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.stochasticK = function(_) { if (!arguments.length) return stochasticK; stochasticK = _; return bind(); }; accessor.stochasticD = function(_) { if (!arguments.length) return stochasticD; stochasticD = _; return bind(); }; accessor.overbought = function(_) { if (!arguments.length) return overbought; overbought = _; return bind(); }; accessor.oversold = function(_) { if (!arguments.length) return oversold; oversold = _; return bind(); }; function bind() { accessor.d = date; accessor.k = stochasticK; accessor.sd = stochasticD; accessor.ob = overbought; accessor.os = oversold; return accessor; } return bind(); }; var supstance = function() { var start = function(d) { return d.start; }, end = function(d) { return d.end; }, /** * Supports getter and setter * @param d Underlying data object to get or set the value * @param _ If passed turns into a setter. This is the value to set * @returns {*} */ value = function(d, _) { if(arguments.length < 2) return d.value; d.value = _; return accessor; }; function accessor(d) { return accessor.v(d); } accessor.start = function(_) { if (!arguments.length) return start; start = _; return bind(); }; accessor.end = function(_) { if (!arguments.length) return end; end = _; return bind(); }; accessor.value = function(_) { if (!arguments.length) return value; value = _; return bind(); }; function bind() { accessor.s = start; accessor.e = end; accessor.v = value; return accessor; } return bind(); }; var williams = function() { var date = function(d) { return d.date; }, williams = function(d) { return d.williams; }; function accessor(d) { return accessor.r(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.williams = function(_) { if (!arguments.length) return williams; williams = _; return bind(); }; function bind() { accessor.d = date; accessor.w = williams; return accessor; } return bind(); }; var bollinger = function() { var date = function(d) { return d.date; }, middle = function(d) { return d.middleBand; }, upper = function(d) { return d.upperBand; }, lower = function(d) { return d.lowerBand; }; function accessor(d) { return accessor.r(d); } accessor.date = function(_) { if (!arguments.length) return date; date = _; return bind(); }; accessor.middle = function(_) { if (!arguments.length) return middle; middle = _; return bind(); }; accessor.upper = function(_) { if (!arguments.length) return upper; upper = _; return bind(); }; accessor.lower = function(_) { if (!arguments.length) return lower; lower = _; return bind(); }; function bind() { accessor.d = date; accessor.middle = middle; accessor.upper = upper; accessor.lower = lower; return accessor; } return bind(); }; // Provide IDs for all accessors. Default to date, but at least provide an option var accessor = function() { return { atrtrailingstop: atrtrailingstop, crosshair: crosshair, ichimoku: ichimoku, macd: macd, ohlc: ohlc, rsi: rsi, trendline: trendline, value: value, volume: volume, tick: tick, trade: trade, adx: adx, aroon: aroon, stochastic: stochastic, supstance: supstance, williams: williams, bollinger: bollinger }; }; var indicatormixin = function() { return function(source, priv) { var indicatorMixin = {}; indicatorMixin.period = function(period) { priv.period = period; source.period = function(_) { if (!arguments.length) return priv.period; priv.period = +_; return source; }; source.preroll = function() { return priv.period; }; return indicatorMixin; }; indicatorMixin.accessor = function(accessor) { priv.accessor = accessor; // Mixin the functions to the source source.accessor = function (_) { if (!arguments.length) return priv.accessor; priv.accessor = _; return source; }; return indicatorMixin; }; indicatorMixin.preroll = function(pr) { source.preroll = pr; return indicatorMixin; }; source.preroll = function() { return 0; }; return indicatorMixin; }; }; var ema = function(indicatorMixin, accessor_ohlc, alpha_init) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables previous, alpha, initialTotal, initialCount; function indicator(data) { indicator.init(); return data.map(ma).filter(function(d) { return d.value !== null; }); } indicator.init = function() { previous = null; alpha = alpha_init(p.period); initialTotal = 0; initialCount = 0; return indicator; }; function ma(d, i) { var value = indicator.average(p.accessor(d)); if (i+1 < p.period) { value = null; } return { date: p.accessor.d(d), value: value }; } indicator.average = function(value) { if(initialCount < p.period) return (previous = (initialTotal += value)/++initialCount); else return (previous = previous + alpha*(value-previous)); }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p) .accessor(accessor_ohlc()) .period(10); return indicator; }; }; var sma = function(indicatorMixin, accessor_ohlc) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables samples, currentIndex, total; function indicator(data) { indicator.init(); return data.map(ma).filter(function(d) { return d.value !== null; }); } indicator.init = function() { total = 0; samples = []; currentIndex = 0; return indicator; }; function ma(d, i) { var value = indicator.average(p.accessor(d)); if (i+1 < p.period) value = null; return { date: p.accessor.d(d), value: value }; } indicator.average = function(value) { total += value; if(samples.length+1 < p.period) { samples.push(value); return total/++currentIndex; } else { if(samples.length < p.period) { samples.push(value); total += value; } total -= samples[currentIndex]; samples[currentIndex] = value; if(++currentIndex === p.period) { currentIndex = 0; } return total/p.period; } }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p) .accessor(accessor_ohlc()) .period(10); return indicator; }; }; var atr = function(indicatorMixin, accessor_ohlc, indicator_sma) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables initialAtr = indicator_sma(), previous = null, averageTrueRange = 0, currentIndex = 0; function indicator(data) { indicator.init(); return data.map(function(d, i) { var value = indicator.atr(d); if(i >= p.period) return datum(p.accessor.d(d), value); else return datum(p.accessor.d(d)); }).filter(function(d) { return d.value !== null; }); } indicator.init = function() { initialAtr.accessor(indicator.accessor()).period(p.period).init(); previous = null; averageTrueRange = 0; currentIndex = 0; return indicator; }; indicator.atr = function(d) { var trueRange = previous === null ? p.accessor.h(d)-p.accessor.l(d) : Math.max(p.accessor.h(d)-p.accessor.l(d), Math.abs(p.accessor.h(d)-p.accessor.c(previous)), Math.abs(p.accessor.l(d)-p.accessor.c(previous)) ); previous = d; // http://en.wikipedia.org/wiki/Average_true_range averageTrueRange = currentIndex++ <= p.period ? initialAtr.average(trueRange) : (averageTrueRange*(p.period-1)+trueRange)/p.period; return averageTrueRange; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p) .accessor(accessor_ohlc()) .period(14); return indicator; }; }; function datum(date, atr) { if(atr) return { date: date, value: atr }; else return { date: date, value: null }; } /** * http://www.embedded.com/electronics-blogs/embedded-round-table/4419407/The-ring-buffer */ var circularbuffer = function CircularBuffer(size) { var samples = [], currentIndex = size-1; CircularBuffer.push = function(value) { currentIndex = ++currentIndex % size; if(samples.length < size) samples.push(value); else samples[currentIndex] = value; }; CircularBuffer.get = function(index) { return samples[(currentIndex+samples.length-index) % samples.length]; }; CircularBuffer.head = function() { return CircularBuffer.get(0); }; CircularBuffer.last = function() { return CircularBuffer.get(samples.length-1); }; CircularBuffer.size = function() { return size; }; CircularBuffer.samples = function() { return samples; }; CircularBuffer.primed = function() { return samples.length === size; }; return CircularBuffer; }; var util = function() { return { circularbuffer: circularbuffer, rebindCallback: rebindCallback, rebind: function(target, source) { var newArgs = Array.prototype.slice.call(arguments, 0); newArgs.splice(2, 0, undefined); return rebindCallback.apply(this, newArgs); }, // https://github.com/d3/d3/blob/v3.5.17/src/core/functor.js functor: function(v) { return typeof v === "function" ? v : function() { return v; }; } }; }; /* Slight modification to d3.rebind taking a post set callback https://github.com/mbostock/d3/blob/master/src/core/rebind.js */ function rebindCallback(target, source, postSetCallback) { var i = 2, n = arguments.length, method; while (++i < n) target[method = arguments[i]] = doRebind(target, source, source[method], postSetCallback); return target; } function doRebind(target, source, method, postSetCallback) { return function() { var value = method.apply(source, arguments); if(postSetCallback && value === source) postSetCallback(); return value === source ? target : value; }; } var sroc = function(techan_util_circularbuffer, indicatorMixin, accessor_ohlc, indicator_smoothing, smoothed_period) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables smoothing = indicator_smoothing().period(smoothed_period), buffer; function indicator(data) { indicator.init(); return data.map(rocDataPoint).filter(function(d) { return d.value !== null; }); } indicator.init = function() { smoothing.init(); buffer = techan_util_circularbuffer(p.period); return indicator; }; function rocDataPoint(d, i) { var value = indicator.roc(p.accessor(d)); if (!buffer.primed() || i+1 < smoothing.period()) value = null; return { date: p.accessor.d(d), value: value }; } indicator.roc = function(value) { buffer.push(smoothing.average(value)); return (buffer.head() - buffer.last())/buffer.last(); }; indicator.ema = function(_) { if(!arguments.length) return smoothing; smoothing = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p) .accessor(accessor_ohlc()) .period(21); return indicator; }; }; var vwap = function(indicatorMixin, accessor_ohlc) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables cumul_total, cumul_volume, prev_date; function indicator(data) { indicator.init(); return data.map(vwap).filter(function(d) { return d.value !== null; }); } indicator.init = function() { cumul_total = 0; cumul_volume = 0; return indicator; }; function vwap(d, i) { // VWAP restarts when day changes if (i > 0 && prev_date.getDate() != p.accessor.d(d).getDate()) { cumul_total = 0; cumul_volume = 0; } var price = (p.accessor.h(d) + p.accessor.l(d) + p.accessor.c(d)) / 3; cumul_total += price * p.accessor.v(d); cumul_volume += p.accessor.v(d); prev_date = p.accessor.d(d); return { date: p.accessor.d(d), value: cumul_total / cumul_volume }; } // Mixin 'superclass' methods and variables indicatorMixin(indicator, p) .accessor(accessor_ohlc()) .period(1); return indicator; }; }; var atrtrailingstop$2 = function(indicatorMixin, accessor_ohlc, indicator_atr) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables multiplier = 3, atr = indicator_atr(); function indicator(data) { atr.accessor(p.accessor).period(p.period).init(); return data.map(function(d, i) { var close = p.accessor.c(d), stop = atr.atr(d)*multiplier; if(i >= p.period) return { date: p.accessor.d(d), close: close, up: close-stop, down: close+stop }; else return { date: p.accessor.d(d), up: null, down: null }; }) .filter(function(d) { return d.up !== null && d.down !== null; }) // Filter out empties .reduce(function(result, d, i) { // Reduce to access the previous result array var prev = result[i-1], up = i === 0 ? d.up : null, // Always start with an up trend? down = null; if(prev && prev.up !== null) { if(d.close > prev.up) up = Math.max(d.up, prev.up); else down = d.down; } if(prev && prev.down !== null) { if(d.close < prev.down) down = Math.min(d.down, prev.down); else up = d.up; } result.push({ date: d.date, up: up, down: down }); return result; }, []); } indicator.multiplier = function(_) { if (!arguments.length) return multiplier; multiplier = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p) .accessor(accessor_ohlc()) .period(14); return indicator; }; }; var heikinashi = function(indicatorMixin, accessor_ohlc, min, max) { // Injected dependencies return function() { // Closure function var p = {}; // Container for private, direct access mixed in variables function indicator(data) { var previousHa; return data.map(function(d) { var ha = { date: p.accessor.d(d), open: (previousHa === undefined ? p.accessor.o(d) + p.accessor.c(d) : previousHa.open + previousHa.close)/2, close: (p.accessor.o(d) + p.accessor.h(d) + p.accessor.l(d) + p.accessor.c(d))/4 }; ha.high = max([ha.open, ha.close, p.accessor.h(d)]); ha.low = min([ha.open, ha.close, p.accessor.l(d)]); if(p.accessor.v !== undefined && p.accessor.v(d) !== undefined) ha.volume = p.accessor.v(d); return (previousHa = ha); }); } // Mixin 'superclass' methods and variables indicatorMixin(indicator, p) .accessor(accessor_ohlc()); return indicator; }; }; var ichimoku$2 = function(indicatorMixin, accessor_ohlc) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables tenkanSen = 9, kijunSen = 26, senkouSpanB = 52; function indicator(data) { var parameters = { tenkanSen: tenkanSen, kijunSen: kijunSen, senkouSpanB: senkouSpanB }, result = new Array(data.length); // Iterate backwards through the data for(var index = result.length-1; index >= 0; index--) { result[index] = calculate(parameters, data, index); } return result; } function calculate(parameters, data, index) { var d = data[index], min = p.accessor.l(d), max = p.accessor.h(d), current = datum$1(parameters, p.accessor.d(d), p.accessor.c(d)); // Iterate backwards through the data up to sendouSpanB count to calculate averages for(var i = 0, pos = i+1; i < parameters.senkouSpanB && index-i >= 0; i++, pos = i+1) { d = data[index-i]; min = Math.min(min, p.accessor.l(d)); max = Math.max(max, p.accessor.h(d)); // Grab a snapshot of average of min and max for each of the parameter periods current.tenkanSen = pos === parameters.tenkanSen ? average(min, max) : current.tenkanSen; current.kijunSen = pos === parameters.kijunSen ? average(min, max) : current.kijunSen; current.senkouSpanB = pos === parameters.senkouSpanB ? average(min, max) : current.senkouSpanB; } // Initialise if there is enough data current.senkouSpanA = senkouSpanA(current.tenkanSen, current.kijunSen); return current; } indicator.tenkanSen = function(_) { if (!arguments.length) return tenkanSen; tenkanSen = _; return indicator; }; indicator.kijunSen = function(_) { if (!arguments.length) return kijunSen; kijunSen = _; return indicator; }; indicator.senkouSpanB = function(_) { if (!arguments.length) return senkouSpanB; senkouSpanB = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p).accessor(accessor_ohlc()); return indicator; }; }; function datum$1(parameters, date, chikouSpan) { return { parameters: parameters, date: date, chikouSpan: chikouSpan, tenkanSen: null, kijunSen: null, senkouSpanA: null, senkouSpanB: null }; } function senkouSpanA(tenkanSen, kijunSen) { return tenkanSen !== null && kijunSen !== null ? average(tenkanSen, kijunSen) : null; } function average(v1, v2) { return (v1+v2)/2; } var macd$2 = function(indicatorMixin, accessor_ohlc, indicator_ema) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables fast = 12, slow = 26, signal = 9, signalLine = indicator_ema(), fastAverage = indicator_ema(), slowAverage = indicator_ema(); function indicator(data) { var minFastSlow = Math.max(fast, slow) - 1, minCount = minFastSlow + signal - 1; signalLine.accessor(indicator.accessor()).period(signal).init(); fastAverage.accessor(indicator.accessor()).period(fast).init(); slowAverage.accessor(indicator.accessor()).period(slow).init(); return data.map(function(d, i) { var macd = fastAverage.average(p.accessor(d)) - slowAverage.average(p.accessor(d)), signalValue = i >= minFastSlow ? signalLine.average(macd) : null; if(i >= minCount) return datum$2(p.accessor.d(d), macd, signalValue, macd - signalValue, 0); else return datum$2(p.accessor.d(d)); }).filter(function(d) { return d.macd !== null; }); } indicator.fast = function(_) { if (!arguments.length) return fast; fast = _; return indicator; }; indicator.slow = function(_) { if (!arguments.length) return slow; slow = _; return indicator; }; indicator.signal = function(_) { if (!arguments.length) return signal; signal = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p).accessor(accessor_ohlc()).preroll(indicator.slow); return indicator; }; }; function datum$2(date, macd, signal, difference, zero) { if(macd) return { date: date, macd: macd, signal: signal, difference: difference, zero: zero }; else return { date: date, macd: null, signal: null, difference: null, zero: null }; } var rsi$2 = function(indicatorMixin, accessor_ohlc, indicator_ema) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables overbought = 70, middle = 50, oversold = 30, lossAverage = indicator_ema(), gainAverage = indicator_ema(); function indicator(data) { lossAverage.accessor(indicator.accessor()).period(p.period).init(); gainAverage.accessor(indicator.accessor()).period(p.period).init(); return data.map(function(d, i) { if(i < 1) return datum$3(p.accessor.d(d)); var difference = p.accessor(d) - p.accessor(data[i-1]), averageGain = gainAverage.average(Math.max(difference, 0)), averageLoss = Math.abs(lossAverage.average(Math.min(difference, 0))); if(i >= p.period) { var rsi = 100 - (100/(1+(averageGain/averageLoss))); return datum$3(p.accessor.d(d), rsi, middle, overbought, oversold); } else return datum$3(p.accessor.d(d)); }).filter(function(d) { return d.rsi !== null; }); } indicator.overbought = function(_) { if (!arguments.length) return overbought; overbought = _; return indicator; }; indicator.middle = function(_) { if (!arguments.length) return middle; middle = _; return indicator; }; indicator.oversold = function(_) { if (!arguments.length) return oversold; oversold = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p) .accessor(accessor_ohlc()) .period(14); return indicator; }; }; function datum$3(date, rsi, middle, overbought, oversold) { if(rsi) return { date: date, rsi: rsi, middle: middle, overbought: overbought, oversold: oversold }; else return { date: date, rsi: null, middle: null, overbought: null, oversold: null }; } var aroon$2 = function(indicatorMixin, accessor_ohlc) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables overbought = 70, middle = 0, oversold = 30; function indicator(data) { return data.map(function(d, i) { if(i >= (p.period-1)){ var max = 0; var maxi = 0; var min = 10000; var mini = 0; for (var j = 0; j < p.period; j++) { if( p.accessor.h(data[i-j]) > max){ max = p.accessor.h(data[i-j]); maxi = j; } if( p.accessor.l(data[i-j]) < min){ min = p.accessor.l(data[i-j]); mini = j; } } var up = ((p.period-maxi)/p.period)*100; var down = ((p.period-mini)/p.period)*100; var oscillator = up - down; return datum$4(p.accessor.d(d), up,down, oscillator, middle, overbought, oversold); } else return datum$4(p.accessor.d(d)); }).filter(function(d) { return d.up; }); } indicator.overbought = function(_) { if (!arguments.length) return overbought; overbought = _; return indicator; }; indicator.middle = function(_) { if (!arguments.length) return middle; middle = _; return indicator; }; indicator.oversold = function(_) { if (!arguments.length) return oversold; oversold = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p).accessor(accessor_ohlc()).period(20); return indicator; }; }; function datum$4(date, up,down,oscillator, middle, overbought, oversold) { if(up) return { date: date, up: up,down:down,oscillator:oscillator, middle: middle, overbought: overbought, oversold: oversold }; else return { date: date, up: null,down:null,oscillator:null, middle: null, overbought: null, oversold: null }; } var stochastic$2 = function(indicatorMixin, accessor_ohlc) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables periodD = 3, overbought = 80, oversold = 20; function indicator(data) { var periodLength = indicator.preroll(); return data.map(function(d, i) { if(i >= periodLength ){ var max = []; var min = []; var stochasticKBuffer = []; for (var per = 0; per < periodD; per++) { max.push(0); min.push(10000); stochasticKBuffer.push(0); } var stochasticD = 0; for (var k = 0; k < periodD; k++) { for (var j = 0; j < p.period; j++) { if(p.accessor.h(data[i-j-k]) > max[k]){ max[k] = p.accessor.h(data[i-j-k]); } if(p.accessor.l(data[i-j-k]) < min[k]){ min[k] = p.accessor.l(data[i-j-k]); } } var diff = (max[k]-min[k]); if(diff > 0) { stochasticKBuffer[k] = ((p.accessor.c(data[i - k]) - min[k]) / (max[k] - min[k])) * 100; }else{ stochasticKBuffer[k] = 50; } stochasticD +=stochasticKBuffer[k]; } var stochasticK =stochasticKBuffer[0];// ((d.close-min)/(max-min))*100; stochasticD /= periodD; return datum$5(p.accessor.d(d), stochasticK,stochasticD, overbought, oversold); } else return datum$5(p.accessor.d(d), null, null, overbought, oversold); }).filter(function(d) { return d.stochasticK; }); } indicator.periodD = function(_) { if (!arguments.length) return periodD; periodD = +_; return indicator; }; indicator.overbought = function(_) { if (!arguments.length) return overbought; overbought = _; return indicator; }; indicator.oversold = function(_) { if (!arguments.length) return oversold; oversold = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p).accessor(accessor_ohlc()).period(20) .preroll(function() { return p.period+periodD; }); return indicator; }; }; function datum$5(date, stochasticK, stochasticD, overbought, oversold) { if(stochasticK) return { date: date, stochasticK: stochasticK, stochasticD: stochasticD, overbought: overbought, oversold: oversold }; else return { date: date, stochasticK: null, stochasticD: null, overbought: overbought, oversold: oversold }; } var williams$2 = function(indicatorMixin, accessor_ohlc) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables overbought = 80, middle = 50, oversold = 20; function indicator(data) { return data.map(function(d, i) { if(i >= p.period){ var max = 0; var min = 10000; for (var j = 0; j < p.period; j++) { if(p.accessor.h(data[i-j]) > max){ max = p.accessor.h(data[i-j]); } if(p.accessor.l(data[i-j]) < min){ min = p.accessor.l(data[i-j]); } } var williams = ((p.accessor.c(data[i]) - min )/( max - min ))*100; return datum$6(p.accessor.d(d), williams, middle, overbought, oversold); } else return datum$6(p.accessor.d(d)); }).filter(function(d) { return d.williams; }); } indicator.overbought = function(_) { if (!arguments.length) return overbought; overbought = _; return indicator; }; indicator.middle = function(_) { if (!arguments.length) return middle; middle = _; return indicator; }; indicator.oversold = function(_) { if (!arguments.length) return oversold; oversold = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p).accessor(accessor_ohlc()).period(20); return indicator; }; }; function datum$6(date, williams, middle, overbought, oversold) { if(williams) return { date: date, williams: williams, middle: middle, overbought: overbought, oversold: oversold }; else return { date: date, williams: null, middle: null, overbought: null, oversold: null }; } var adx$2 = function(d3_max, indicatorMixin, accessor_ohlc, indicator_ema) { // Injected dependencies return function() { // Closure function var p = {}; // Container for private, direct access mixed in variables function indicator(data) { var plusDmEma = indicator_ema().accessor(indicator.accessor()).period(p.period).init(), minusDmEma = indicator_ema().accessor(indicator.accessor()).period(p.period).init(), trEma = indicator_ema().accessor(indicator.accessor()).period(p.period).init(), adxEma = indicator_ema().accessor(indicator.accessor()).period(p.period).init(); return data.map(function(d, i) { if(i < 1) return datum$7(p.accessor.d(d)); var upMove = p.accessor.h(data[i]) - p.accessor.h(data[i-1]); var downMove = p.accessor.l(data[i-1]) - p.accessor.l(data[i]); var plusDM = 0; if(upMove > downMove && upMove>0){ plusDM = upMove; } var minusDM = 0; if(downMove > upMove && downMove > 0){ minusDM = downMove; } var TR = d3_max([ (p.accessor.h(d) - p.accessor.l(d)), Math.abs(p.accessor.h(d) - p.accessor.c(data[i-1])),Math.abs(p.accessor.l(d) - p.accessor.c(data[i-1])) ]); var plusDmAverage = plusDmEma.average(plusDM), minusDmAverage = minusDmEma.average(minusDM), trEmaAverage = trEma.average(TR); if(i>p.period) { var plusDi = 100 * plusDmAverage / trEmaAverage, minusDi = 100 * minusDmAverage / trEmaAverage, adxValue = 0; if(plusDi - minusDi !== 0){ adxValue = Math.abs( (plusDi - minusDi)/(plusDi + minusDi) ); } var adx = 100 * adxEma.average(adxValue); if(i >= p.period*2) { return datum$7(p.accessor.d(d), adx, plusDi, minusDi); }else return datum$7(p.accessor.d(d)); }else return datum$7(p.accessor.d(d)); }).filter(function(d) { return d.adx; }); } // Mixin 'superclass' methods and variables indicatorMixin(indicator, p).accessor(accessor_ohlc()).period(14); return indicator; }; }; function datum$7(date, adx, plusDi, minusDi) { if(plusDi) { return { date: date, adx: adx, plusDi: plusDi, minusDi: minusDi }; }else{ return { date: date, adx: null, plusDi: null, minusDi: null }; } } var bollinger$2 = function(indicatorMixin, accessor_ohlc, indicator_sma) { // Injected dependencies return function() { // Closure function var p = {}, // Container for private, direct access mixed in variables sdMultiplication = 2, sd; function indicator(data) { var signalLine = indicator_sma().accessor(indicator.accessor()).period(p.period).init(); var j; return data.map(function(d, i) { var middleBand = signalLine.average(p.accessor(d)); if(i >= p.period) { var sum = 0; for(j = 0;j<p.period;j++){ sum += (Math.pow( (p.accessor.c(data[i-j]) - middleBand) ,2 ) ); } sd = Math.sqrt( sum/p.period ); var upperBand = middleBand+sdMultiplication*sd, lowerBand = middleBand-sdMultiplication*sd; return datum$8(p.accessor.d(d), middleBand, upperBand, lowerBand); } else return datum$8(p.accessor.d(d)); }).filter(function(d) { return d.middleBand; }); } indicator.sdMultiplication = function(_) { if (!arguments.length) return sdMultiplication; sdMultiplication = _; return indicator; }; // Mixin 'superclass' methods and variables indicatorMixin(indicator, p).accessor(accessor_ohlc()).period(20); return indicator; }; }; function datum$8(date, middleBand, upperBand, lowerBand) { if(middleBand) return { date: date, middleBand: middleBand, upperBand: upperBand, lowerBand: lowerBand}; else return { date: date, middleBand: null, upperBand: null, lowerBand: null}; } var indicator = function(d3) { var indicatorMixin = indicatormixin(), accessor$$1 = accessor(), ema_init = ema, ema$$1 = ema_init(indicatorMixin, accessor$$1.ohlc, ema_alpha_init), sma$$1 = sma(indicatorMixin, accessor$$1.ohlc), atr$$1 = atr(indicatorMixin, accessor$$1.ohlc, sma$$1), circularbuffer = util().circularbuffer, sroc_init = sroc, vwap$$1 = vwap(indicatorMixin, accessor$$1.ohlc); return { atr: atr$$1, atrtrailingstop: atrtrailingstop$2(indicatorMixin, accessor$$1.ohlc, atr$$1), ema: ema$$1, heikinashi: heikinashi(indicatorMixin, accessor$$1.ohlc, d3.min, d3.max), ichimoku: ichimoku$2(indicatorMixin, accessor$$1.ohlc), macd: macd$2(indicatorMixin, accessor$$1.ohlc, ema$$1), rsi: rsi$2(indicatorMixin, accessor$$1.ohlc, ema$$1), sma: sma$$1, wilderma: ema_init(indicatorMixin, accessor$$1.ohlc, wilder_alpha_init), aroon: aroon$2(indicatorMixin, accessor$$1.ohlc), roc: sroc_init(circularbuffer, indicatorMixin, accessor$$1.ohlc, ema$$1, 1), sroc: sroc_init(circularbuffer, indicatorMixin, accessor$$1.ohlc, ema$$1, 13), stochastic: stochastic$2(indicatorMixin, accessor$$1.ohlc, ema$$1), williams: williams$2(indicatorMixin, accessor$$1.ohlc, ema$$1), adx: adx$2(d3.max, indicatorMixin, accessor$$1.ohlc, ema$$1), bollinger: bollinger$2(indicatorMixin, accessor$$1.ohlc, sma$$1), vwap: vwap$$1 }; }; function ema_alpha_init(period) { return 2/(period+1); } function w