techan-js
Version:
A visual, technical analysis and charting (Candlestick, OHLC, indicators) library built on D3.
1,875 lines (1,541 loc) • 138 kB
JavaScript
'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