Skip to content

Commit

Permalink
Add new indicators
Browse files Browse the repository at this point in the history
- ADX
- Aroon
- Bollinger bands
- Stochastic oscillator
- Williams %R

All indicators have tests.

Original code taken from Fojt's branch and refactored for the lastest
Techan master.

Signed-off-by: Pekka Riikonen <[email protected]>
  • Loading branch information
fojt authored and priikone committed Dec 12, 2015
1 parent c26c751 commit 44007a6
Show file tree
Hide file tree
Showing 33 changed files with 1,330 additions and 12 deletions.
49 changes: 49 additions & 0 deletions src/accessor/adx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict';

module.exports = 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);
}

// TODO use d3.rebind to obtain this from 'super class'
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() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.d = date;
accessor.adx = adx;
accessor.plusDi = plusDi;
accessor.minusDi = minusDi;

return accessor;
}

return bind();
};
73 changes: 73 additions & 0 deletions src/accessor/aroon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use strict';

module.exports = 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);
}

// TODO use d3.rebind to obtain this from 'super class'
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() {
// TODO These methods will need to know if the variables are functions or values and execute as such
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();
};
49 changes: 49 additions & 0 deletions src/accessor/bollinger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict';

module.exports = 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);
}

// TODO use d3.rebind to obtain this from 'super class'
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() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.d = date;
accessor.middle = middle;
accessor.upper = upper;
accessor.lower = lower;

return accessor;
}

return bind();
};
9 changes: 7 additions & 2 deletions src/accessor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ module.exports = function() {
value: require('./value'),
volume: require('./volume'),
tick: require('./tick'),
trade: require('./trade')
trade: require('./trade'),
adx: require('./adx'),
aroon: require('./aroon'),
stochastic: require('./stochastic'),
williams: require('./williams'),
bollinger: require('./bollinger')
};
};
};
64 changes: 64 additions & 0 deletions src/accessor/stochastic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use strict';

module.exports = 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; },
middle = function(d) { return d.middle; };

function accessor(d) {
return accessor.r(d);
}

// TODO use d3.rebind to obtain this from 'super class'
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();
};

accessor.middle = function(_) {
if (!arguments.length) return middle;
middle = _;
return bind();
};

function bind() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.d = date;
accessor.k = stochasticK;
accessor.sd = stochasticD;
accessor.ob = overbought;
accessor.os = oversold;
accessor.m = middle;

return accessor;
}

return bind();
};
33 changes: 33 additions & 0 deletions src/accessor/williams.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

module.exports = function() {
var date = function(d) { return d.date; },
williams = function(d) { return d.williams; };

function accessor(d) {
return accessor.r(d);
}

// TODO use d3.rebind to obtain this from 'super class'
accessor.date = function(_) {
if (!arguments.length) return date;
date = _;
return bind();
};

accessor.williams = function(_) {
if (!arguments.length) return williams;
williams = _;
return bind();
};

function bind() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.d = date;
accessor.w = williams;

return accessor;
}

return bind();
};
74 changes: 74 additions & 0 deletions src/indicator/adx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
'use strict';

module.exports = function(indicatorMixin, accessor_ohlc, indicator_ema) { // Injected dependencies
return function() { // Closure function
var p = {}, // Container for private, direct access mixed in variables
period = 14;

function indicator(data) {
var plusDmEma = indicator_ema().accessor(indicator.accessor()).period(period).init(),
minusDmEma = indicator_ema().accessor(indicator.accessor()).period(period).init(),
trEma = indicator_ema().accessor(indicator.accessor()).period(period).init(),
adxEma = indicator_ema().accessor(indicator.accessor()).period(period).init();

period = parseInt(period);
return data.map(function(d, i) {
if(i < 1) return datum(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>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 >= period*2) {
return datum(p.accessor.d(d), adx, plusDi, minusDi);
}else return datum(p.accessor.d(d));
}else return datum(p.accessor.d(d));
}).filter(function(d) { return d.adx; });
}

indicator.period = function(_) {
if (!arguments.length) return period;
period = _;
return indicator;
};

// Mixin 'superclass' methods and variables
indicatorMixin(indicator, p).accessor(accessor_ohlc());

return indicator;
};
};

function datum(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 };
}
}
Loading

0 comments on commit 44007a6

Please sign in to comment.