Create a gist now

Instantly share code, notes, and snippets.

@andredumas /README.md forked from fojt/data.csv
Last active Aug 29, 2015

Pull request review

facebook-201404-201409.png

Date Open High Low Close Volume
19-Sep-14 77.40 78.30 76.44 77.91 76671330
18-Sep-14 76.77 77.33 76.52 77.00 23030936
17-Sep-14 75.96 77.25 75.57 76.43 28693348
16-Sep-14 73.52 76.23 73.07 76.08 37742482
15-Sep-14 77.15 77.25 73.43 74.58 49679130
12-Sep-14 78.02 78.30 77.10 77.48 26744356
11-Sep-14 77.13 78.36 77.05 77.92 32247724
10-Sep-14 76.52 77.95 76.51 77.43 29908606
9-Sep-14 77.59 78.17 76.20 76.67 28881803
8-Sep-14 77.26 78.17 77.01 77.89 28183156
5-Sep-14 76.10 77.38 75.60 77.26 29475854
4-Sep-14 75.89 76.93 75.53 75.95 26676444
3-Sep-14 77.14 77.48 75.60 75.83 32389102
2-Sep-14 75.01 76.70 74.82 76.68 34979438
29-Aug-14 74.30 74.82 74.01 74.82 26241162
28-Aug-14 74.00 74.43 73.73 73.86 21946738
27-Aug-14 75.27 75.49 74.46 74.63 36323060
26-Aug-14 75.00 75.99 74.73 75.96 23992668
25-Aug-14 74.94 75.28 74.79 75.02 19734103
22-Aug-14 74.34 74.73 73.57 74.57 20897393
21-Aug-14 74.92 75.19 74.41 74.57 20101979
20-Aug-14 74.97 75.18 74.62 74.81 22916678
19-Aug-14 74.81 75.58 74.77 75.29 26678640
18-Aug-14 74.00 74.72 73.96 74.59 23973658
15-Aug-14 74.32 74.65 73.00 73.63 38909161
14-Aug-14 73.97 74.38 73.69 74.30 22207019
13-Aug-14 73.12 74.25 73.05 73.77 29265662
12-Aug-14 73.09 73.33 72.22 72.83 27418983
11-Aug-14 73.46 73.91 73.06 73.44 24591177
8-Aug-14 73.40 73.43 72.56 73.06 27202325
7-Aug-14 73.00 74.00 72.70 73.17 38140550
6-Aug-14 72.02 73.72 71.79 72.47 30985533
5-Aug-14 73.51 73.59 72.18 72.69 34986147
4-Aug-14 72.36 73.88 72.36 73.51 30776819
1-Aug-14 72.22 73.22 71.55 72.36 43535314
31-Jul-14 74.00 74.16 72.44 72.65 43991772
30-Jul-14 74.21 75.19 74.13 74.68 36853018
29-Jul-14 74.72 74.92 73.42 73.71 41324470
28-Jul-14 75.17 75.50 73.85 74.92 41725249
25-Jul-14 74.99 75.67 74.66 75.19 45917435
24-Jul-14 75.96 76.74 74.51 74.98 124167936
23-Jul-14 69.74 71.33 69.61 71.29 78434716
22-Jul-14 69.76 69.77 68.61 69.27 40397693
21-Jul-14 68.81 69.96 68.50 69.40 49539121
18-Jul-14 66.80 68.46 66.16 68.42 42455649
17-Jul-14 67.03 67.85 66.04 66.41 38188432
16-Jul-14 67.54 67.94 67.07 67.66 29593589
15-Jul-14 67.96 68.09 66.26 67.16 44292944
14-Jul-14 67.13 68.17 66.90 67.90 38536924
11-Jul-14 65.28 66.59 64.79 66.34 39212022
10-Jul-14 63.31 65.34 63.05 64.87 44421915
9-Jul-14 63.41 65.12 63.15 64.97 51431582
8-Jul-14 65.06 65.56 62.21 62.76 68926036
7-Jul-14 66.30 66.57 65.12 65.29 28745099
3-Jul-14 66.86 67.00 65.76 66.29 25203215
2-Jul-14 68.04 68.30 65.79 66.45 41895220
1-Jul-14 67.58 68.44 67.39 68.06 33243166
30-Jun-14 67.46 67.92 67.13 67.29 27201749
27-Jun-14 67.31 67.70 66.84 67.60 46460627
26-Jun-14 68.00 68.00 66.90 67.13 47713944
25-Jun-14 65.58 67.48 65.57 67.44 44308249
24-Jun-14 65.36 67.17 65.27 65.72 57334867
23-Jun-14 64.32 65.66 64.22 65.37 34560121
20-Jun-14 64.46 64.81 63.35 64.50 46466073
19-Jun-14 65.46 65.58 64.21 64.34 34245182
18-Jun-14 64.49 65.75 64.05 65.60 35570154
17-Jun-14 64.10 64.88 63.93 64.40 27714816
16-Jun-14 64.16 64.88 63.75 64.19 31045811
13-Jun-14 64.70 64.97 63.83 64.50 29418910
12-Jun-14 65.85 66.47 64.06 64.29 55729828
11-Jun-14 65.32 65.80 64.90 65.78 44241926
10-Jun-14 63.53 65.82 63.50 65.77 69338140
9-Jun-14 62.40 63.34 61.79 62.88 37617413
6-Jun-14 63.37 63.48 62.15 62.50 42442096
5-Jun-14 63.66 64.36 62.82 63.19 47352368
4-Jun-14 62.45 63.59 62.07 63.34 36513991
3-Jun-14 62.62 63.42 62.32 62.87 32216707
2-Jun-14 63.23 63.59 62.05 63.08 35995537
30-May-14 63.95 64.17 62.56 63.30 45283577
29-May-14 63.84 64.30 63.51 63.83 42699670
28-May-14 63.39 64.14 62.62 63.51 47795088
27-May-14 61.62 63.51 61.57 63.48 55681663
23-May-14 60.41 61.45 60.15 61.35 38293993
22-May-14 60.94 61.48 60.40 60.52 54200116
21-May-14 58.56 60.50 58.25 60.49 58991505
20-May-14 59.50 60.19 58.18 58.56 53931469
19-May-14 57.89 59.56 57.57 59.21 43033925
16-May-14 58.31 58.45 57.31 58.02 47933075
15-May-14 59.26 59.38 57.52 57.92 56813940
14-May-14 59.53 60.45 58.95 59.23 47428583
13-May-14 59.66 60.89 59.51 59.83 48525476
12-May-14 57.98 59.90 57.98 59.83 48575487
9-May-14 56.85 57.65 56.38 57.24 52583858
8-May-14 57.23 58.82 56.50 56.76 61251053
7-May-14 58.77 59.30 56.26 57.39 78587247
6-May-14 60.98 61.15 58.49 58.53 55900809
5-May-14 59.67 61.35 59.18 61.22 46057411
2-May-14 61.30 61.89 60.18 60.46 54189197
1-May-14 60.43 62.28 60.21 61.15 82428606
30-Apr-14 57.58 59.85 57.16 59.78 76093004
29-Apr-14 56.09 58.28 55.84 58.15 75557202
28-Apr-14 58.05 58.31 54.66 56.14 107757756
25-Apr-14 59.97 60.01 57.57 57.71 92501529
24-Apr-14 63.60 63.65 59.77 60.87 138769345
23-Apr-14 63.45 63.48 61.26 61.36 96564750
22-Apr-14 62.65 63.44 62.22 63.03 60631312
21-Apr-14 59.46 61.24 59.15 61.24 60363619
17-Apr-14 59.30 60.58 58.72 58.94 88040346
16-Apr-14 59.79 60.19 57.74 59.72 78773521
15-Apr-14 59.29 59.68 55.88 59.09 108622706
14-Apr-14 60.09 60.45 57.78 58.89 72324603
11-Apr-14 57.60 60.31 57.31 58.53 91451960
10-Apr-14 63.08 63.18 58.68 59.16 114987616
9-Apr-14 59.63 62.46 59.19 62.41 100215307
8-Apr-14 57.68 58.71 57.17 58.19 78835935
7-Apr-14 55.90 58.00 55.44 56.95 108487569
4-Apr-14 59.94 60.20 56.32 56.75 125465774
3-Apr-14 62.55 63.17 59.13 59.49 83859330
2-Apr-14 63.21 63.91 62.21 62.72 66276613
1-Apr-14 60.46 62.66 60.24 62.62 59291210
31-Mar-14 60.78 61.52 59.87 60.24 53011205
28-Mar-14 61.34 61.95 59.34 60.01 67051528
27-Mar-14 60.51 61.90 57.98 60.97 112649694
26-Mar-14 64.74 64.95 60.37 60.38 97689774
25-Mar-14 64.89 66.19 63.78 64.89 68785500
24-Mar-14 67.19 67.36 63.36 64.10 85695872
21-Mar-14 67.53 67.92 66.18 67.24 60041228
20-Mar-14 68.01 68.23 66.82 66.97 44438500
19-Mar-14 69.17 69.29 67.46 68.24 43980558
18-Mar-14 68.76 69.60 68.30 69.19 40827226
17-Mar-14 68.18 68.95 66.62 68.74 52196699
14-Mar-14 68.49 69.43 67.46 67.72 48226824
13-Mar-14 71.29 71.35 68.15 68.83 57091157
12-Mar-14 69.86 71.35 69.00 70.88 46400431
11-Mar-14 72.50 72.59 69.96 70.10 59615238
10-Mar-14 70.77 72.15 70.51 72.03 59949746
7-Mar-14 71.08 71.18 69.47 69.80 38985763
6-Mar-14 71.88 71.89 70.25 70.84 46126260
5-Mar-14 69.69 71.97 69.62 71.57 74649486
4-Mar-14 68.66 68.90 67.62 68.80 42164222
3-Mar-14 66.96 68.05 66.51 67.41 56900444
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.aroon path {
fill: none;
stroke-width: 1;
}
.aroon {
stroke: #000000;
}
.aroon.up {
stroke: #00AA00;
stroke-width: 1.5;
}
.aroon.down {
stroke: #FF0000;
stroke-width: 1.5;
}
.aroon.oscillator {
stroke: #0000FF;
stroke-width: 0.5;
}
.aroon.oscillatorArea{
fill: #0000FF;
opacity:0.025;
}
.aroon path.overbought, .aroon path.oversold {
stroke: #FF9999;
stroke-width: 1;
stroke-dasharray: 2, 2;
}
.aroon path.middle, path.zero {
stroke: #BBBBBB;
stroke-dasharray: 5, 5;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="techan.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = techan.scale.financetime()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var aroon = techan.plot.aroon()
.xScale(x)
.yScale(y);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(",.3s"));
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var aroonPeriod = 14;
d3.csv("data.csv", function(error, data) {
var accessor = aroon.accessor();
data = data.map(function(d) {
// Open, high, low, close generally not required, is being used here to demonstrate colored volume
// bars
return {
date: parseDate(d.Date),
volume: +d.Volume,
open: +d.Open,
high: +d.High,
low: +d.Low,
close: +d.Close
};
}).sort(function(a, b) { return d3.ascending(accessor.d(a), accessor.d(b)); });
var aroonData = techan.indicator.aroon()(data.slice(0, aroonPeriod+200));
x.domain(aroonData.map(accessor.d));
y.domain(techan.scale.plot.aroon(aroonData.slice(aroonPeriod,aroonPeriod+200), accessor).domain());
svg.append("g")
.datum(aroonData)
.attr("class", "aroon")
.call(aroon);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("aroon");
});
</script>
/*
TechanJS v0.2.0
(c) 2014 - 2014 Andre Dumas | https://github.com/andredumas/techan.js
*/
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.techan=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
'use strict';module.exports='0.2.0';
},{}],2:[function(_dereq_,module,exports){
'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();
};
},{}],3:[function(_dereq_,module,exports){
'use strict';
// TODO Could these be singletons? Generally will be accessing the same data and data structures at the same time
module.exports = function() {
return {
ohlc: _dereq_('./ohlc'),
volume: _dereq_('./volume'),
macd: _dereq_('./macd'),
rsi: _dereq_('./rsi'),
aroon: _dereq_('./aroon'),
stochastic: _dereq_('./stochastic'),
williams: _dereq_('./williams'),
trendline: _dereq_('./trendline'),
value: _dereq_('./value')
};
};
},{"./aroon":2,"./macd":4,"./ohlc":5,"./rsi":6,"./stochastic":7,"./trendline":8,"./value":9,"./volume":10,"./williams":11}],4:[function(_dereq_,module,exports){
'use strict';
module.exports = 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);
}
// TODO use d3.rebind to obtain this from 'super class'
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() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.d = date;
accessor.m = macd;
accessor.s = signal;
accessor.dif = difference;
accessor.z = zero;
return accessor;
}
return bind();
};
},{}],5:[function(_dereq_,module,exports){
'use strict';
module.exports = 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);
}
// TODO use d3.rebind to obtain this from 'super class'
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() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.d = date;
accessor.o = open;
accessor.h = high;
accessor.l = low;
accessor.c = close;
accessor.v = volume;
return accessor;
}
return bind();
};
},{}],6:[function(_dereq_,module,exports){
'use strict';
module.exports = 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);
}
// TODO use d3.rebind to obtain this from 'super class'
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() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.d = date;
accessor.r = rsi;
accessor.ob = overbought;
accessor.os = oversold;
accessor.m = middle;
return accessor;
}
return bind();
};
},{}],7:[function(_dereq_,module,exports){
'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();
};
},{}],8:[function(_dereq_,module,exports){
'use strict';
module.exports = 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() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.sd = startDate;
accessor.sv = startValue;
accessor.ed = endDate;
accessor.ev = endValue;
return accessor;
}
return bind();
};
},{}],9:[function(_dereq_,module,exports){
'use strict';
module.exports = function() {
var date = function(d) { return d.date; },
value = function(d, _) {
if(arguments.length < 2) return d.value;
d.value = _;
},
zero = function(d) { return d.zero; };
function accessor(d) {
return accessor.v(d);
}
// TODO use d3.rebind to obtain this from 'super class'
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() {
// TODO These methods will need to know if the variables are functions or values and execute as such
accessor.d = date;
accessor.v = value;
accessor.z = zero;
return accessor;
}
return bind();
};
},{}],10:[function(_dereq_,module,exports){
'use strict';
module.exports = function() {
var date = function(d) { return d.date; },
volume = function(d) { return d.volume; };
function accessor(d) {
return accessor.v(d);
}
// TODO use d3.rebind to obtain this from 'super class'
accessor.date = function(_) {
if (!arguments.length) return date;
date = _;
return bind();
};
accessor.volume = function(_) {
if (!arguments.length) return volume;
volume = _;
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.v = volume;
return accessor;
}
return bind();
};
},{}],11:[function(_dereq_,module,exports){
'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();
};
},{}],12:[function(_dereq_,module,exports){
'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 = 20,
overbought = 70,
middle = 0,
oversold = 30;
function indicator(data) {
return data.map(function(d, i) {
if(i >= (period-1)){
var max = 0;
var maxi = 0;
var min = 10000;
var mini = 0;
for (var j = 0; j < period; j++) {
if(data[i-j].high > max){
max = data[i-j].high;
maxi = j;
}
if(data[i-j].low < min){
min = data[i-j].low;
mini = j;
}
}
var up = ((period-maxi)/period)*100;
var down = ((period-mini)/period)*100;
var oscillator = up - down;
return datum(p.accessor.d(d), up,down, oscillator, middle, overbought, oversold);
}
else return datum(p.accessor.d(d));
}).filter(function(d) { return d.up; });
}
indicator.period = function(_) {
if (!arguments.length) return period;
period = _;
return indicator;
};
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_ohlc());
return indicator;
};
};
function datum(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 };
}
},{}],13:[function(_dereq_,module,exports){
'use strict';
module.exports = function(indicatorMixin, accessor_ohlc) { // Injected dependencies
return function() { // Closure function
var p = {}, // Container for private, direct access mixed in variables
period = 10,
previous,
alpha,
initialTotal,
initialCount;
function indicator(data) {
indicator.init();
return data.map(ma).filter(function(d) { return d.value; });
}
indicator.init = function() {
previous = null;
alpha = 2/(period+1);
initialTotal = 0;
initialCount = 0;
return indicator;
};
function ma(d, i) {
var value = indicator.average(p.accessor(d));
if (i+1 < period) {
value = null;
}
return { date: p.accessor.d(d), value: value };
}
indicator.average = function(value) {
if(initialCount < period) return (initialTotal += value)/++initialCount;
else {
if(initialCount === period) {
previous = initialTotal/initialCount++;
}
return (previous = previous + alpha*(value-previous));
}
};
indicator.period = function(_) {
if (!arguments.length) return period;
period = _;
return indicator;
};
// Mixin 'superclass' methods and variables
indicatorMixin(indicator, p, accessor_ohlc());
return indicator;
};
};
},{}],14:[function(_dereq_,module,exports){
'use strict';
module.exports = function() {
var indicatorMixin = _dereq_('./indicatormixin')(),
accessor = _dereq_('../accessor')(),
ema = _dereq_('./ema')(indicatorMixin, accessor.ohlc);
return {
ema: ema,
macd: _dereq_('./macd')(indicatorMixin, accessor.ohlc, ema),
rsi: _dereq_('./rsi')(indicatorMixin, accessor.ohlc, ema),
aroon: _dereq_('./aroon')(indicatorMixin, accessor.ohlc, ema),
stochastic: _dereq_('./stochastic')(indicatorMixin, accessor.ohlc, ema),
williams: _dereq_('./williams')(indicatorMixin, accessor.ohlc, ema),
sma: _dereq_('./sma')(indicatorMixin, accessor.ohlc)
};
};
},{"../accessor":3,"./aroon":12,"./ema":13,"./indicatormixin":15,"./macd":16,"./rsi":17,"./sma":18,"./stochastic":19,"./williams":20}],15:[function(_dereq_,module,exports){
'use strict';
module.exports = function() {
return function(source, priv, accessor) {
// Mixin the functions to the source
source.accessor = function(_) {
if (!arguments.length) return accessor;
accessor = _;
return bind();
};
// Add in the private, direct access variables
function bind() {
priv.accessor = accessor;
return source;
}
bind();
};
};
},{}],16:[function(_dereq_,module,exports){
'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
fast = 12,
slow = 26,
signal = 9;
function indicator(data) {
var minFastSlow = Math.max(fast, slow) - 1,
minCount = minFastSlow + signal - 1,
signalLine = indicator_ema().accessor(indicator.accessor()).period(signal).init(),
fastAverage = indicator_ema().accessor(indicator.accessor()).period(fast).init(),
slowAverage = indicator_ema().accessor(indicator.accessor()).period(slow).init();
return data.map(function(d, i) {
slow = fastAverage.average(p.accessor(d));
fast = slowAverage.average(p.accessor(d));
var macd = slow - fast,
signalValue = i >= minFastSlow ? signalLine.average(macd) : null;
if(i >= minCount) return datum(p.accessor.d(d), macd, signalValue, macd - signalValue, 0);
else return datum(p.accessor.d(d));
}).filter(function(d) { return d.macd; });
}
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_ohlc());
return indicator;
};
};
function datum(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 };
}
},{}],17:[function(_dereq_,module,exports){
'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,
overbought = 70,
middle = 50,
oversold = 30;
function indicator(data) {
var lossAverage = indicator_ema().accessor(indicator.accessor()).period(period).init(),
gainAverage = indicator_ema().accessor(indicator.accessor()).period(period).init();
return data.map(function(d, i) {
if(i < 1) return datum(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 >= period) {
var rsi = 100 - (100/(1+(averageGain/averageLoss)));
return datum(p.accessor.d(d), rsi, middle, overbought, oversold);
}
else return datum(p.accessor.d(d));
}).filter(function(d) { return d.rsi; });
}
indicator.period = function(_) {
if (!arguments.length) return period;
period = _;
return indicator;
};
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_ohlc());
return indicator;
};
};
function datum(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 };
}
},{}],18:[function(_dereq_,module,exports){
'use strict';
module.exports = function(indicatorMixin, accessor_ohlc) { // Injected dependencies
return function() { // Closure function
var p = {}, // Container for private, direct access mixed in variables
period = 10,
samples,
currentIndex,
total;
function indicator(data) {
indicator.init();
return data.map(ma).filter(function(d) { return d.value; });
}
indicator.init = function() {
total = 0;
samples = [];
currentIndex = 0;
return indicator;
};
function ma(d, i) {
var value = indicator.average(p.accessor(d));
if (i+1 < period) value = null;
return { date: p.accessor.d(d), value: value };
}
indicator.average = function(value) {
total += value;
if(samples.length+1 < period) {
samples.push(value);
return total/++currentIndex;
}
else {
if(samples.length < period) {
samples.push(value);
total += value;
}
total -= samples[currentIndex];
samples[currentIndex] = value;
if(++currentIndex === period) {
currentIndex = 0;
}
return total/period;
}
};
indicator.period = function(_) {
if (!arguments.length) return period;
period = _;
return indicator;
};
// Mixin 'superclass' methods and variables
indicatorMixin(indicator, p, accessor_ohlc());
return indicator;
};
};
},{}],19:[function(_dereq_,module,exports){
'use strict';
module.exports = function(indicatorMixin, accessor_ohlc) { // Injected dependencies
return function() { // Closure function
var p = {}, // Container for private, direct access mixed in variables
period = 20,
periodD = 3,
overbought = 80,
middle = 50,
oversold = 20;
function indicator(data) {
return data.map(function(d, i) {
if(i >= period+periodD){
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 < period; j++) {
if(data[i-j-k].high > max[k]){
max[k] = data[i-j-k].high;
}
if(data[i-j-k].low < min[k]){
min[k] = data[i-j-k].low;
}
}
stochasticKBuffer[k] = ((data[i-k].close-min[k])/(max[k]-min[k]))*100;
stochasticD +=stochasticKBuffer[k];
}
var stochasticK =stochasticKBuffer[0];// ((d.close-min)/(max-min))*100;
stochasticD /= periodD;
return datum(p.accessor.d(d), stochasticK,stochasticD, middle, overbought, oversold);
}
else return datum(p.accessor.d(d));
}).filter(function(d) { return d.stochasticK; });
}
indicator.period = function(_) {
if (!arguments.length) return period;
period = _;
return indicator;
};
indicator.periodD = function(_) {
if (!arguments.length) return periodD;
periodD = _;
return indicator;
};
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_ohlc());
return indicator;
};
};
function datum(date, stochasticK,stochasticD, middle, overbought, oversold) {
if(stochasticK) return { date: date, stochasticK: stochasticK,stochasticD:stochasticD, middle: middle, overbought: overbought, oversold: oversold };
else return { date: date, stochasticK: null,stochasticD:null, middle: null, overbought: null, oversold: null };
}
},{}],20:[function(_dereq_,module,exports){
'use strict';
module.exports = function(indicatorMixin, accessor_ohlc) { // Injected dependencies
return function() { // Closure function
var p = {}, // Container for private, direct access mixed in variables
period = 20,
overbought = 80,
middle = 50,
oversold = 20;
function indicator(data) {
return data.map(function(d, i) {
if(i >= period){
var max = 0;
var maxi = 0;
var min = 10000;
var mini = 0;
for (var j = 0; j < period; j++) {
if(data[i-j].high > max){
max = data[i-j].high;
maxi = j;
}
if(data[i-j].low < min){
min = data[i-j].low;
mini = j;
}
}
var williams = ((data[i].close-min)/(max-min))*100;
return datum(p.accessor.d(d), williams, middle, overbought, oversold);
}
else return datum(p.accessor.d(d));
}).filter(function(d) { return d.williams; });
}
indicator.period = function(_) {
if (!arguments.length) return period;
period = _;
return indicator;
};
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_ohlc());
return indicator;
};
};
function datum(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 };
}
},{}],21:[function(_dereq_,module,exports){
'use strict';
module.exports = function(accessor_aroon, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function aroon(g) {
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d);
group.entry.append('path').attr('class', 'overbought');
group.entry.append('path').attr('class', 'oversold');
group.entry.append('path').attr('class', 'aroon oscillator');
group.entry.append('path').attr('class', 'aroon oscillatorArea');
group.entry.append('path').attr('class', 'aroon middle');
group.entry.append('path').attr('class', 'aroon up');
group.entry.append('path').attr('class', 'aroon down');
aroon.refresh(g);
}
aroon.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale, plot);
};
// Mixin 'superclass' methods and variables
plotMixin(aroon, p, accessor_aroon());
return aroon;
};
};
function refresh(g, accessor, x, y, plot) {
g.selectAll('path.overbought').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.ob, y));
g.selectAll('path.oversold').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.os, y));
g.selectAll('path.aroon.oscillator').attr('d', plot.pathLine(accessor.d, x, accessor.oscillator, y));
g.selectAll('path.aroon.oscillatorArea').attr('d', plot.pathArea(accessor.d, x, accessor.oscillator, y,0));
g.selectAll('path.aroon.middle').attr('d', plot.pathLine(accessor.d, x, accessor.m, y));
g.selectAll('path.aroon.up').attr('d', plot.pathLine(accessor.d, x, accessor.up, y));
g.selectAll('path.aroon.down').attr('d', plot.pathLine(accessor.d, x, accessor.down, y));
}
},{}],22:[function(_dereq_,module,exports){
'use strict';
/**
* TODO Refactor this to techan.plot.annotation.axis()?
*/
module.exports = function(d3_svg_axis, plot) { // Injected dependencies
return function() { // Closure function
var axis = d3_svg_axis(),
format,
point = 4,
height = 14,
width = 50,
translate = [0, 0];
function annotation(g) {
g.selectAll('g.translate').data(plot.dataMapper.array).enter()
.append('g').attr('class', 'translate');
annotation.refresh(g);
}
annotation.refresh = function(g) {
var fmt = format ? format : (axis.tickFormat() ? axis.tickFormat() : axis.scale().tickFormat());
refresh(g, plot, axis, fmt, height, width, point, translate);
};
annotation.axis = function(_) {
if(!arguments.length) return axis;
axis = _;
return annotation;
};
annotation.format = function(_) {
if(!arguments.length) return format;
format = _;
return annotation;
};
annotation.height = function(_) {
if(!arguments.length) return height;
height = _;
return annotation;
};
annotation.width = function(_) {
if(!arguments.length) return width;
width = _;
return annotation;
};
annotation.translate = function(_) {
if(!arguments.length) return translate;
translate = _;
return annotation;
};
return annotation;
};
};
function refresh(g, plot, axis, format, height, width, point, translate) {
var neg = axis.orient() === 'left' || axis.orient() === 'top' ? -1 : 1,
translateSelection = g.select('g.translate'),
dataGroup = plot.groupSelect(translateSelection, filterInvalidValues(axis.scale()));
dataGroup.entry.append('path');
dataGroup.entry.append('text');
translateSelection.attr('transform', 'translate(' + translate[0] + ',' + translate[1] + ')');
dataGroup.selection.selectAll('path').attr('d', backgroundPath(axis, height, width, point, neg));
dataGroup.selection.selectAll('text').text(textValue(format)).call(textAttributes, axis, neg);
}
function filterInvalidValues(scale) {
return function(data) {
var range = scale.range(),
start = range[0],
end = range[range.length - 1];
range = start < end ? [start, end] : [end, start];
return data.filter(function (d) {
if (!d.value) return false;
var value = scale(d.value);
return value && !isNaN(value) && range[0] <= value && value <= range[1];
});
};
}
function textAttributes(text, axis, neg) {
var scale = axis.scale();
switch(axis.orient()) {
case 'left':
case 'right':
text.attr({
x: neg*(Math.max(axis.innerTickSize(), 0) + axis.tickPadding()),
y: textPosition(scale),
dy: '.32em'
}).style('text-anchor', neg < 0 ? 'end' : 'start');
break;
case 'top':
case 'bottom':
text.attr({
x: textPosition(scale),
y: neg*(Math.max(axis.innerTickSize(), 0) + axis.tickPadding()),
dy: neg < 0 ? '0em' : '.72em'
}).style('text-anchor', 'middle');
break;
}
}
function textPosition(scale) {
return function(d) {
return scale(d.value);
};
}
function textValue(format) {
return function(d) {
return format(d.value);
};
}
function backgroundPath(axis, height, width, point, neg) {
return function(d) {
var scale = axis.scale(),
value = scale(d.value),
pt = point;
switch(axis.orient()) {
case 'left':
case 'right':
var h = 0;
if(height/2 < point) pt = height/2;
else h = height/2-point;
return [
'M', 0, value,
'l', neg*axis.innerTickSize(), -pt,
'l', 0, -h,
'l', neg*width, 0,
'l', 0, height,
'l', neg*-width, 0,
'l', 0, -h
].join(' ');
case 'top':
case 'bottom':
var w = 0;
if(width/2 < point) pt = width/2;
else w = width/2-point;
return [
'M', value, 0,
'l', -pt, neg*axis.innerTickSize(),
'l', -w, 0,
'l', 0, neg*height,
'l', width, 0,
'l', 0, neg*-height,
'l', -w, 0
].join(' ');
default: throw "Unsupported axis.orient() = " + axis.orient();
}
};
}
},{}],23:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3_scale_linear, d3_extent, accessor_ohlc, plot, plotMixin) { // Injected dependencies
return function() { // Closure constructor
var p = {}, // Container for private, direct access mixed in variables
volumeOpacity = false;
function candlestick(g) {
var group = plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d);
// Two path's as wick and body can be styled slightly differently (stroke and fills)
group.entry.append('path').attr('class', 'candle body');
group.entry.append('path').attr('class', 'candle wick');
candlestick.refresh(g);
}
candlestick.refresh = function(g) {
if(volumeOpacity) opacity(g, d3_scale_linear, d3_extent, p.accessor.v);
refresh(g, plot, p.accessor, p.xScale, p.yScale);
};
candlestick.volumeOpacity = function(_) {
if (!arguments.length) return volumeOpacity;
volumeOpacity = _;
return candlestick;
};
// Mixin 'superclass' methods and variables
plotMixin(candlestick, p, accessor_ohlc());
return candlestick;
};
};
function refresh(g, plot, accessor, x, y) {
g.selectAll('path.candle.body').attr('d', bodyPath(accessor, x, y)).classed(plot.classedUpDown(accessor));
g.selectAll('path.candle.wick').attr('d', wickPath(accessor, x, y)).classed(plot.classedUpDown(accessor));
}
function bodyPath(accessor, x, y) {
return function(d) {
var path = [],
open = y(accessor.o(d)),
close = y(accessor.c(d)),
rangeBand = x.band(),
xValue = x(accessor.d(d)) - rangeBand/2;
path.push(
'M', xValue, open,
'l', rangeBand, 0
);
// Draw body only if there is a body (there is no stroke, so will not appear anyway)
if(open != close) {
path.push(
'L', xValue + rangeBand, close,
'l', -rangeBand, 0,
'L', xValue, open
);
}
return path.join(' ');
};
}
function wickPath(accessor, x, y) {
return function(d) {
var path = [],
open = y(accessor.o(d)),
close = y(accessor.c(d)),
rangeBand = x.band(),
xPoint = x(accessor.d(d)),
xValue = xPoint - rangeBand/2;
// Top
path.push(
'M', xPoint, y(accessor.h(d)),
'L', xPoint, Math.min(open, close)
);
// Draw another cross wick if there is no body
if(open == close) {
path.push(
'M', xValue, open,
'l', rangeBand, 0
);
}
// Bottom
path.push(
'M', xPoint, Math.max(open, close),
'L', xPoint, y(accessor.l(d))
);
return path.join(' ');
};
}
function opacity(g, d3_scale_linear, d3_extent, accessor_volume) {
var selection = g.selectAll('g.data'),
volumeOpacityScale = d3_scale_linear()
.domain(d3_extent(selection.data().map(accessor_volume).filter(function(d) { return !isNaN(d); })))
.range([0.2, 1]);
selection.selectAll('path.candle').style('opacity', function(d) {
var volume = accessor_volume(d);
return isNaN(volume) ? null : volumeOpacityScale(volume);
});
}
},{}],24:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3_select, d3_event, d3_mouse, axisannotation) { // Injected dependencies
return function() { // Closure function
var xAnnotation = [axisannotation()],
yAnnotation = [axisannotation()],
verticalWireRange,
horizontalWireRange;
function crosshair(g) {
var group = g.selectAll('g.data').data([0]),
groupEnter = group.enter().append('g').attr('class', 'data').call(display, 'none');
groupEnter.append('path').attr('class', 'horizontal wire');
groupEnter.append('path').attr('class', 'vertical wire');
appendAnnotation(group, groupEnter, d3_select, ['axisannotation', 'x'], xAnnotation);
appendAnnotation(group, groupEnter, d3_select, ['axisannotation', 'y'], yAnnotation);
g.selectAll('rect').data([0]).enter()
.append('rect').style({ fill: 'none', 'pointer-events': 'all'});
crosshair.refresh(g);
}
crosshair.refresh = function(g) {
var xRange = xAnnotation[0].axis().scale().range(),
yRange = yAnnotation[0].axis().scale().range(),
group = g.selectAll('g.data'),
mouseSelection = g.selectAll('rect');
mouseSelection.attr({
x: Math.min(xRange[0], xRange[xRange.length-1]),
y: Math.min(yRange[0], yRange[yRange.length-1]),
height: Math.abs(yRange[yRange.length-1] - yRange[0]),
width: Math.abs(xRange[xRange.length-1] - xRange[0])
})
.on('mouseenter', display(g, 'inline'))
.on('mouseout', display(g, 'none'))
.on('mousemove', mousemoveRefresh(group, d3_select, d3_mouse, xAnnotation, yAnnotation, verticalWireRange, horizontalWireRange));
refresh(d3_select, xAnnotation, yAnnotation,
g.select('path.vertical'), g.select('path.horizontal'),
g.selectAll('g.axisannotation.x > g'), g.selectAll('g.axisannotation.y > g'),
verticalWireRange, horizontalWireRange
);
};
crosshair.xAnnotation = function(_) {
if(!arguments.length) return xAnnotation;
xAnnotation = _ instanceof Array ? _ : [_];
return crosshair;
};
crosshair.yAnnotation = function(_) {
if(!arguments.length) return yAnnotation;
yAnnotation = _ instanceof Array ? _ : [_];
return crosshair;
};
crosshair.verticalWireRange = function(_) {
if(!arguments.length) return verticalWireRange;
verticalWireRange = _;
return crosshair;
};
crosshair.horizontalWireRange = function(_) {
if(!arguments.length) return horizontalWireRange;
horizontalWireRange = _;
return crosshair;
};
return crosshair;
};
};
function display(g, style) {
return function() {
g.selectAll('g.data').style('display', style);
};
}
function mousemoveRefresh(group, d3_select, d3_mouse, xAnnotation, yAnnotation, verticalWireRange, horizontalWireRange) {
return function() {
var coords = d3_mouse(this),
x = xAnnotation[0].axis().scale(),
y = yAnnotation[0].axis().scale();
refresh(d3_select, xAnnotation, yAnnotation,
group.select('path.vertical').datum(x.invert(coords[0])),
group.select('path.horizontal').datum(y.invert(coords[1])),
group.selectAll('g.axisannotation.x > g').each(updateAnnotationValue(xAnnotation, coords[0])),
group.selectAll('g.axisannotation.y > g').each(updateAnnotationValue(yAnnotation, coords[1])),
verticalWireRange, horizontalWireRange
);
};
}
function refresh(d3_select, xAnnotation, yAnnotation, xPath, yPath,
xAnnotationSelection, yAnnotationSelection,
verticalWireRange, horizontalWireRange) {
var x = xAnnotation[0].axis().scale(),
y = yAnnotation[0].axis().scale();
xPath.attr('d', verticalPathLine(x, verticalWireRange || y.range()));
yPath.attr('d', horizontalPathLine(y, horizontalWireRange || x.range()));
xAnnotationSelection.each(refreshAnnotation(d3_select, xAnnotation));
yAnnotationSelection.each(refreshAnnotation(d3_select, yAnnotation));
}
function horizontalPathLine(y, range) {
return function(d) {
if(!d) return "M 0 0";
var value = y(d);
return ['M', range[0], value, 'L', range[range.length-1], value].join(' ');
};
}
function verticalPathLine(x, range) {
return function(d) {
if(!d) return "M 0 0";
var value = x(d);
return ['M', value, range[0], 'L', value, range[range.length-1]].join(' ');
};
}
function updateAnnotationValue(annotations, value) {
return function(d, i) {
// d[0] because only ever 1 value for crosshairs
d[0].value = annotations[i].axis().scale().invert(value);
};
}
function appendAnnotation(selection, selectionEnter, d3_select, classes, annotation) {
selectionEnter.append('g').attr('class', classes.join(' '));
var annotationSelection = selection.select('g.' + classes.join('.')).selectAll('g')
.data(annotation.map(function() { return [{ value: null }]; }));
annotationSelection.exit().remove();
annotationSelection.enter().append('g').attr('class', function(d, i) { return i; })
.each(function(d, i) { annotation[i](d3_select(this)); });
}
function refreshAnnotation(d3_select, annotation) {
return function(d, i) {
annotation[i].refresh(d3_select(this));
};
}
},{}],25:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3) {
var scale = _dereq_('../scale')(d3),
accessor = _dereq_('../accessor')(),
plot = _dereq_('./plot')(d3),
plotMixin = _dereq_('./plotmixin')(d3.scale.linear, scale.financetime),
line = _dereq_('./line'),
axisannotation = _dereq_('./axisannotation')(d3.svg.axis, plot);
return {
axisannotation: axisannotation,
candlestick: _dereq_('./candlestick')(d3.scale.linear, d3.extent, accessor.ohlc, plot, plotMixin),
crosshair: _dereq_('./crosshair')(d3.select, d3_event, d3.mouse, axisannotation),
ema: line(accessor.value, plot, plotMixin),
ohlc: _dereq_('./ohlc')(d3.scale.linear, d3.extent, accessor.ohlc, plot, plotMixin),
close: line(accessor.ohlc, plot, plotMixin),
volume: _dereq_('./volume')(accessor.volume, plot, plotMixin),
rsi: _dereq_('./rsi')(accessor.rsi, plot, plotMixin),
aroon: _dereq_('./aroon')(accessor.aroon, plot, plotMixin),
stochastic: _dereq_('./stochastic')(accessor.stochastic, plot, plotMixin),
williams: _dereq_('./williams')(accessor.williams, plot, plotMixin),
macd: _dereq_('./macd')(accessor.macd, plot, plotMixin),
momentum: line(accessor.value, plot, plotMixin, true),
moneyflow: line(accessor.value, plot, plotMixin, true),
sma: line(accessor.value, plot, plotMixin),
supstance: _dereq_('./supstance')(d3.behavior.drag, d3_event, d3.select, accessor.value, plot, plotMixin),
trendline: _dereq_('./trendline')(d3.behavior.drag, d3_event, d3.select, accessor.trendline, plot, plotMixin)
};
};
function d3_event() {
return d3.event;
}
},{"../accessor":3,"../scale":38,"./aroon":21,"./axisannotation":22,"./candlestick":23,"./crosshair":24,"./line":26,"./macd":27,"./ohlc":28,"./plot":29,"./plotmixin":30,"./rsi":31,"./stochastic":32,"./supstance":33,"./trendline":34,"./volume":35,"./williams":36}],26:[function(_dereq_,module,exports){
'use strict';
module.exports = function(accessor_value, plot, plotMixin, showZero) { // Injected dependencies
showZero = showZero || false;
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function line(g) {
var group = plot.groupSelect(g, plot.dataMapper.array);
group.entry.append('path').attr('class', 'line');
if(showZero) {
group.selection.append('path').attr('class', 'zero');
}
line.refresh(g);
}
line.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale, plot, showZero);
};
// Mixin 'superclass' methods and variables
plotMixin(line, p, accessor_value());
return line;
};
};
function refresh(g, accessor, x, y, plot, showZero) {
g.selectAll('path.line').attr('d', plot.pathLine(accessor.d, x, accessor, y));
if(showZero) {
g.selectAll('path.zero').attr('d', plot.horizontalPathLine(x, accessor.z, y));
}
}
},{}],27:[function(_dereq_,module,exports){
'use strict';
module.exports = function(accessor_macd, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function macd(g) {
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d);
var histogramSelection = group.selection
.append('g').attr('class', 'difference')
.selectAll('g.difference').data(function(data) { return data; });
histogramSelection.exit().remove();
histogramSelection.enter().append('path').attr('class', 'difference');
group.selection.append('path').attr('class', 'zero');
group.selection.append('path').attr('class', 'macd');
group.selection.append('path').attr('class', 'signal');
macd.refresh(g);
}
macd.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale, plot);
};
// Mixin 'superclass' methods and variables
plotMixin(macd, p, accessor_macd());
return macd;
};
};
function refresh(g, accessor, x, y, plot) {
g.selectAll('path.difference').attr('d', differencePath(accessor, x, y));
g.selectAll('path.zero').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.z, y));
g.selectAll('path.macd').attr('d', plot.pathLine(accessor.d, x, accessor.m, y));
g.selectAll('path.signal').attr('d', plot.pathLine(accessor.d, x, accessor.s, y));
}
function differencePath(accessor, x, y) {
return function(d) {
var zero = y(0),
height = y(accessor.dif(d)) - zero,
rangeBand = x.band(),
xValue = x(accessor.d(d)) - rangeBand/2;
return [
'M', xValue, zero,
'l', 0, height,
'l', rangeBand, 0,
'l', 0, -height
].join(' ');
};
}
},{}],28:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3_scale_linear, d3_extent, accessor_ohlc, plot, plotMixin) { // Injected dependencies
return function() { // Closure constructor
var p = {}; // Container for private, direct access mixed in variables
function ohlc(g) {
plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d)
.entry.append('path').attr({ class: 'ohlc' });
ohlc.refresh(g);
}
ohlc.refresh = function(g) {
refresh(g, plot, p.accessor, p.xScale, p.yScale);
};
// Mixin 'superclass' methods and variables
plotMixin(ohlc, p, accessor_ohlc());
return ohlc;
};
};
function refresh(g, plot, accessor, x, y) {
g.selectAll('path.ohlc').attr({ d: ohlcPath(accessor, x, y) }).classed(plot.classedUpDown(accessor));
}
function ohlcPath(accessor, x, y) {
return function(d) {
var open = y(accessor.o(d)),
close = y(accessor.c(d)),
rangeBand = x.band(),
xPoint = x(accessor.d(d)),
xValue = xPoint - rangeBand/2;
return [
'M', xValue, open,
'l', rangeBand/2, 0,
'M', xPoint, y(accessor.h(d)),
'L', xPoint, y(accessor.l(d)),
'M', xPoint, close,
'l', rangeBand/2, 0
].join(' ');
};
}
},{}],29:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3) {
function dataSelection(g, dataMapper, accessor_date) {
var selection = g.selectAll('g.data').data(dataMapper, accessor_date);
selection.exit().remove();
return selection;
}
function dataEntry(dataSelection) {
return dataSelection.enter().append('g').attr('class', 'data');
}
return {
dataMapper: {
unity: function(d) { return d; },
array: function(d) { return [d]; }
},
dataSelection: dataSelection,
dataEntry: dataEntry,
groupSelect: function(g, dataMapper, accessor_date) {
var selection = dataSelection(g, dataMapper, accessor_date),
entry = dataEntry(selection);
return {
selection: selection,
entry: entry
};
},
classedUpDown: function(accessor) {
return {
up: function(d) { return accessor.o(d) < accessor.c(d); },
down: function(d) { return accessor.o(d) > accessor.c(d); }
};
},
horizontalPathLine: function(accessor_date, x, accessor_value, y) {
return function(d) {
var firstDatum = d[0],
lastDatum = d[d.length-1];
return [
'M', x(accessor_date(firstDatum)), y(accessor_value(firstDatum)),
'L', x(accessor_date(lastDatum)), y(accessor_value(lastDatum))
].join(' ');
};
},
pathLine: function(accessor_date, x, accessor_value, y) {
return d3.svg.line().interpolate('monotone')
.x(function(d) { return x(accessor_date(d)); } )
.y(function(d) { return y(accessor_value(d)); } );
},
pathArea: function(accessor_date, x, accessor_value, y, yBase) {
return d3.svg.area().interpolate('monotone')
.x(function(d) { return x(accessor_date(d)); } )
.y0(function(d) { return y(yBase);})
.y1(function(d) { return y(accessor_value(d)); } );
}
};
};
},{}],30:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3_scale_linear, techan_scale_financetime) {
function plotMixin(source, priv, accessor) {
var xScale = techan_scale_financetime(),
yScale = d3_scale_linear();
// Mixin the functions to the source
source.accessor = function(_) {
if (!arguments.length) return accessor;
accessor = _;
return bind();
};
source.xScale = function(_) {
if (!arguments.length) return xScale;
xScale = _;
return bind();
};
source.yScale = function(_) {
if (!arguments.length) return yScale;
yScale = _;
return bind();
};
// Add in the private, direct access variables
function bind() {
priv.xScale = xScale;
priv.yScale = yScale;
priv.accessor = accessor;
return source;
}
bind();
}
return plotMixin;
};
},{}],31:[function(_dereq_,module,exports){
'use strict';
module.exports = function(accessor_rsi, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function rsi(g) {
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d);
group.entry.append('path').attr('class', 'overbought');
group.entry.append('path').attr('class', 'middle');
group.entry.append('path').attr('class', 'oversold');
group.entry.append('path').attr('class', 'rsi');
rsi.refresh(g);
}
rsi.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale, plot);
};
// Mixin 'superclass' methods and variables
plotMixin(rsi, p, accessor_rsi());
return rsi;
};
};
function refresh(g, accessor, x, y, plot) {
g.selectAll('path.overbought').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.ob, y));
g.selectAll('path.middle').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.m, y));
g.selectAll('path.oversold').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.os, y));
g.selectAll('path.rsi').attr('d', plot.pathLine(accessor.d, x, accessor.r, y));
}
},{}],32:[function(_dereq_,module,exports){
'use strict';
module.exports = function(accessor_stochastic, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function stochastic(g) {
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d);
group.entry.append('path').attr('class', 'overbought');
group.entry.append('path').attr('class', 'oversold');
group.entry.append('path').attr('class', 'stochastic up');
group.entry.append('path').attr('class', 'stochastic down');
stochastic.refresh(g);
}
stochastic.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale, plot);
};
// Mixin 'superclass' methods and variables
plotMixin(stochastic, p, accessor_stochastic());
return stochastic;
};
};
function refresh(g, accessor, x, y, plot) {
g.selectAll('path.overbought').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.ob, y));
g.selectAll('path.oversold').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.os, y));
g.selectAll('path.stochastic.up').attr('d', plot.pathLine(accessor.d, x, accessor.k, y));
g.selectAll('path.stochastic.down').attr('d', plot.pathLine(accessor.d, x, accessor.sd, y));
}
},{}],33:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3_behavior_drag, d3_event, d3_select, accessor_value, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function supstance(g) {
var group = plot.groupSelect(g, plot.dataMapper.unity);
group.entry.append('path').attr('class', 'supstance');
group.entry.append('g').attr('class', 'interaction').style({ opacity: 0, fill: 'none' })
.append('path').style('stroke-width', 16);
supstance.refresh(g);
}
supstance.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale);
};
supstance.drag = function(g) {
g.selectAll('.interaction path')
.call(dragBody(d3_behavior_drag, d3_event, d3_select, p.accessor, p.xScale, p.yScale));
};
// Mixin 'superclass' methods and variables
plotMixin(supstance, p, accessor_value());
return supstance;
};
};
function refresh(g, accessor, x, y) {
g.selectAll('path.supstance').attr('d', supstancePath(accessor, x, y));
g.selectAll('.interaction path').attr('d', supstancePath(accessor, x, y));
}
function supstancePath(accessor, x, y) {
return function(d) {
var path = [],
range = x.range();
path.push('M', range[0], y(accessor.v(d)));
path.push('L', range[range.length-1], y(accessor.v(d)));
return path.join(' ');
};
}
function dragBody(d3_behavior_drag, d3_event, d3_select, accessor, x, y) {
return d3_behavior_drag()
.origin(function(d) {
// TODO Fire listeners
return { x: 0, y: y(accessor.v(d)) };
})
.on('drag', function(d) {
accessor.v(d, y.invert(d3_event().y));
refresh(d3_select(this.parentNode.parentNode), accessor, x, y);
// TODO Fire listeners
});
}
},{}],34:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3_behavior_drag, d3_event, d3_select, accessor_trendline, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function trendline(g) {
var group = plot.groupSelect(g, plot.dataMapper.unity);
group.entry.append('path').attr('class', 'trendline');
var interaction = group.entry.append('g').attr('class', 'interaction').style({ opacity: 0, fill: 'none' });
interaction.append('path').attr('class', 'body').style('stroke-width', 16);
interaction.append('circle').attr({ class: 'start', r: 8 });
interaction.append('circle').attr({ class: 'end', r: 8 });
trendline.refresh(g);
}
trendline.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale);
};
trendline.drag = function(g) {
// TODO Emit events
g.selectAll('.interaction circle.start')
.call(dragEnd(d3_behavior_drag, d3_event, d3_select, p.accessor, p.accessor.sd, p.xScale, p.accessor.sv, p.yScale));
g.selectAll('.interaction circle.end')
.call(dragEnd(d3_behavior_drag, d3_event, d3_select, p.accessor, p.accessor.ed, p.xScale, p.accessor.ev, p.yScale));
g.selectAll('.interaction path.body')
.call(dragBody(d3_behavior_drag, d3_event, d3_select, p.accessor, p.xScale, p.yScale));
};
// TODO Add mouseover, mouseout listener support for drag/user update events
// Mixin 'superclass' methods and variables
plotMixin(trendline, p, accessor_trendline());
return trendline;
};
};
function refresh(g, accessor, x, y) {
g.selectAll('path.trendline').attr('d', trendlinePath(accessor, x, y));
g.selectAll('.interaction path.body').attr('d', trendlinePath(accessor, x, y));
g.selectAll('.interaction circle.start').attr(interactionEnds(accessor.sd, x, accessor.sv, y));
g.selectAll('.interaction circle.end').attr(interactionEnds(accessor.ed, x, accessor.ev, y));
}
function trendlinePath(accessor, x, y) {
return function(d) {
var path = [];
path.push('M', x(accessor.sd(d)), y(accessor.sv(d)));
path.push('L', x(accessor.ed(d)), y(accessor.ev(d)));
return path.join(' ');
};
}
function interactionEnds(accessor_x, x, accessor_y, y) {
return {
cx: function(d) { return x(accessor_x(d)); },
cy: function(d) { return y(accessor_y(d)); }
};
}
function dragEnd(d3_behavior_drag, d3_event, d3_select, accessor, accessor_x, x, accessor_y, y) {
return d3_behavior_drag()
.origin(function(d) {
// TODO Fire listeners dragstart
return { x: x(accessor_x(d)), y: y(accessor_y(d)) };
})
.on('drag', function(d) {
updateEnd(accessor_x, x, d3_event().x, accessor_y, y, d3_event().y, d);
refresh(d3_select(this.parentNode.parentNode), accessor, x, y);
// TODO Fire listeners dragging
});
}
function dragBody(d3_behavior_drag, d3_event, d3_select, accessor, x, y) {
var dragStart = {}; // State information, grabs the start coords of the line
return d3_behavior_drag()
.origin(function(d) {
dragStart.start = { date: x(accessor.sd(d)), value: y(accessor.sv(d)) };
dragStart.end = { date: x(accessor.ed(d)), value: y(accessor.ev(d)) };
// TODO Fire listeners
return { x: 0, y: 0 };
})
.on('drag', function(d) {
updateEnd(accessor.sd, x, d3_event().x + dragStart.start.date,
accessor.sv, y, d3_event().y + dragStart.start.value,
d);
updateEnd(accessor.ed, x, d3_event().x + dragStart.end.date,
accessor.ev, y, d3_event().y + dragStart.end.value,
d);
refresh(d3_select(this.parentNode.parentNode), accessor, x, y);
// TODO Fire listeners
});
}
function updateEnd(accessor_x, x, xValue, accessor_y, y, yValue, d) {
var date = x.invert(xValue);
if(date) accessor_x(d, date);
accessor_y(d, y.invert(yValue));
}
},{}],35:[function(_dereq_,module,exports){
'use strict';
module.exports = function(accessor_volume, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function volume(g) {
var group = plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d)
.entry.append('path')
.attr('class', 'volume');
if(p.accessor.o && p.accessor.c) {
group.classed(plot.classedUpDown(p.accessor));
}
volume.refresh(g);
}
volume.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale);
};
// Mixin 'superclass' methods and variables
plotMixin(volume, p, accessor_volume());
return volume;
};
};
function refresh(g, accessor, x, y) {
g.selectAll('path.volume').attr('d', volumePath(accessor, x, y));
}
function volumePath(accessor, x, y) {
return function(d) {
var vol = accessor.v(d);
if(isNaN(vol)) return null;
var zero = y(0),
height = y(vol) - zero,
rangeBand = x.band(),
xValue = x(accessor.d(d)) - rangeBand/2;
return [
'M', xValue, zero,
'l', 0, height,
'l', rangeBand, 0,
'l', 0, -height
].join(' ');
};
}
},{}],36:[function(_dereq_,module,exports){
'use strict';
module.exports = function(accessor_williams, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
function williams(g) {
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d);
group.entry.append('path').attr('class', 'williams up');
williams.refresh(g);
}
williams.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale, plot);
};
// Mixin 'superclass' methods and variables
plotMixin(williams, p, accessor_williams());
return williams;
};
};
function refresh(g, accessor, x, y, plot) {
g.selectAll('path.williams.up').attr('d', plot.pathLine(accessor.d, x, accessor.w, y));
}
},{}],37:[function(_dereq_,module,exports){
'use strict';
/*
Finance time scale which is not necessarily continuous, is required to be plot continuous. Finance scale
generally contains data points on days where a market is open but no points when closed, such as weekday
and weekends respectively. When plot, is done so without weekend gaps.
*/
module.exports = function(d3_scale_linear, d3_time, d3_bisect, techan_util_rebindCallback, widen, zoomable) { // Injected dependencies
function financetime(index, domain) {
var dateIndexMap,
tickState = { tickFormat: dailyTickMethod[dailyTickMethod.length-1][2] },
band = 3;
index = index || d3_scale_linear();
domain = domain || [new Date(0), new Date(1)];
/**
* Scales the value to domain. If the value is not within the domain, will currently brutally round the data:
* - If before min domain, will round to 1 index value before min domain
* - If after max domain, will round to 1 index value after min domain
* - If within domain, but not mapped to domain value, uses d3.bisect to find nearest domain index
*
* This logic was not required until the domain was being updated and scales re-rendered and this line
* https://github.com/mbostock/d3/blob/abbe1c75c16c3e9cb08b1d0872f4a19890d3bb58/src/svg/axis.js#L107 was causing error.
* New scale generated ticks that old scale did not have, causing error during transform. To avoid error this logic
* was added.
*
* @param x The value to scale
* @returns {*}
*/
function scale(x) {
var mappedIndex = dateIndexMap[+x];
// Make sure the value has been mapped, if not, determine if it's just before, round in, or just after domain
if(mappedIndex === undefined) {
if(domain[0] > x) mappedIndex = -1; // Less than min, round just out of domain
else mappedIndex = d3_bisect(domain, x); // else let bisect determine where in or just after than domain it is
}
return index(mappedIndex);
}
scale.invert = function(y) {
var i = scale.invertToIndex(y);
return i === null ? null : domain[i];
};
scale.invertToIndex = function(y) {
var i = Math.round(index.invert(y));
return domain[i] ? Math.abs(i) : null;
};
/**
* As the underlying structure relies on a full array, ensure the full domain is passed here,
* not just min and max values.
*
* @param _ The full domain array
* @returns {*}
*/
scale.domain = function(_) {
if (!arguments.length) {
var visible = index.domain();
visible = [
Math.ceil(visible[0]), // If min is fraction, it is partially out of view, round up (ceil)
Math.floor(visible[visible.length-1]) // If max is fraction, is partially out of view, round down (floor)
];
return domain.slice(visible[0], visible[visible.length-1]+1); // Grab visible domain, inclusive
}
domain = _;
domainMap();
index.domain([0, domain.length-1]);
zoomed();
// Widen the outer edges by pulling the domain in to ensure start and end bands are fully visible
index.domain(index.range().map(widen(0.65, band)).map(index.invert));
return zoomed();
};
function zoomed() {
band = rangeBand(index, domain);
return scale;
}
function domainMap() {
dateIndexMap = lookupIndex(domain);
}
scale.copy = function() {
return financetime(index.copy(), domain);
};
/**
* Equivalent to d3's ordinal.rangeBand(). It could not be named rangeBand as d3 uses the method
* to determine how axis ticks should be rendered. This scale is a hybrid ordinal and linear scale,
* such that scale(x) returns y at center of the band as does d3.scale.linear()(x) does, whereas
* d3.scale.ordinal()(x) returns y at the beginning of the band. When rendering svg axis, d3
* compensates for this checking if rangeBand is defined and compensates as such.
* @returns {number}
*/
scale.band = function() {
return band;
};
scale.zoomable = function() {
return zoomable(index, zoomed);
};
/*
* Ticks based heavily on d3 implementation. Attempted to implement this using composition with d3.time.scale,
* but in the end there were sufficient differences to 'roll my own'.
* - Different base tick steps: millis not required (yet!)
* - State based tick formatting given the non continuous, even steps of ticks
* - Supporting daily and intraday continuous (no gaps) plotting
* https://github.com/mbostock/d3/blob/e03b6454294e1c0bbe3125f787df56c468658d4e/src/time/scale.js#L67
*/
/**
* Generates ticks as continuous as possible against the underlying domain. Where continuous time ticks
* fall on where there is no matching domain (such as weekend or holiday day), it will be replaced with
* the nearest domain datum ahead of the tick to keep close to continuous.
* @param interval
* @param steps
* @returns {*}
*/
scale.ticks = function(interval, steps) {
var visibleDomain = scale.domain();
if(!visibleDomain.length) return []; // Nothing is visible, no ticks to show
var method = interval === undefined ? tickMethod(visibleDomain, 10) :
typeof interval === 'number' ? tickMethod(visibleDomain, interval) : null;
tickState.tickFormat = method ? method[2] : tickMethod(visibleDomain, 10)[2];
if(method) {
interval = method[0];
steps = method[1];
}
var intervalRange = interval.range(visibleDomain[0], +visibleDomain[visibleDomain.length-1]+1, steps);
return intervalRange // Interval, possibly contains values not in domain
.map(domainTicks(visibleDomain)) // Line up interval ticks with domain, possibly adding duplicates
.reduce(sequentialDuplicates, []); // Filter out duplicates, produce new 'reduced' array
};
/**
* NOTE: The type of tick format returned is dependant on ticks that were generated. To obtain the correct
* format for ticks, ensure ticks function is called first, otherwise a default tickFormat will be returned
* which may not be the optimal representation of the current domain state.
* @returns {Function}
*/
scale.tickFormat = function() {
return function(date) {
return tickState.tickFormat(date);
};
};
techan_util_rebindCallback(scale, index, zoomed, 'range', 'interpolate', 'clamp', 'nice');
domainMap();
return zoomed();
}
function rangeBand(linear, domain) {
return (Math.abs(linear(domain.length-1) - linear(0))/Math.max(1, domain.length-1))*0.8;
}
var dayFormat = d3_time.format('%b %e'),
yearFormat = d3_time.format.multi([
['%b %Y', function(d) { return d.getMonth(); }],
['%Y', function() { return true; }]
]),
intraDayFormat = d3_time.format.multi([
[":%S", function(d) { return d.getSeconds(); }],
["%I:%M", function(d) { return d.getMinutes(); }],
["%I %p", function(d) { return d.getHours(); }]
]),
genericTickMethod = [d3_time.second, 1, d3_time.format.multi([
[":%S", function(d) { return d.getSeconds(); }],
["%I:%M", function(d) { return d.getMinutes(); }],
["%I %p", function(d) { return d.getHours(); }],
['%b %e', function() { return true; }]
])
];
var dailyStep = 864e5,
dailyTickSteps = [
dailyStep, // 1-day
6048e5, // 1-week
2592e6, // 1-month
7776e6, // 3-month
31536e6 // 1-year
];
var dailyTickMethod = [
[d3_time.day, 1, dayFormat],
[d3_time.monday, 1, dayFormat],
[d3_time.month, 1, yearFormat],
[d3_time.month, 3, yearFormat],
[d3_time.year, 1, yearFormat]
];
var intraDayTickSteps = [
1e3, // 1-second
5e3, // 5-second
15e3, // 15-second
3e4, // 30-second
6e4, // 1-minute
3e5, // 5-minute
9e5, // 15-minute
18e5, // 30-minute
36e5, // 1-hour
108e5, // 3-hour
216e5, // 6-hour
432e5, // 12-hour
864e5 // 1-day
];
var intraDayTickMethod = [
[d3_time.second, 1, intraDayFormat],
[d3_time.second, 5, intraDayFormat],
[d3_time.second, 15, intraDayFormat],
[d3_time.second, 30, intraDayFormat],
[d3_time.minute, 1, intraDayFormat],
[d3_time.minute, 5, intraDayFormat],
[d3_time.minute, 15, intraDayFormat],
[d3_time.minute, 30, intraDayFormat],
[d3_time.hour, 1, intraDayFormat],
[d3_time.hour, 3, intraDayFormat],
[d3_time.hour, 6, intraDayFormat],
[d3_time.hour, 12, intraDayFormat],
[d3_time.day, 1, dayFormat]
];
function tickMethod(visibleDomain, count) {
if(visibleDomain.length == 1) return genericTickMethod; // If we only have 1 to display, show the generic tick method
// Determine whether we're showing daily or intraday data
var intraDay = (visibleDomain[visibleDomain.length-1] - visibleDomain[0])/dailyStep < 1,
tickMethods = intraDay ? intraDayTickMethod : dailyTickMethod,
tickSteps = intraDay ? intraDayTickSteps : dailyTickSteps,
target = (visibleDomain[visibleDomain.length-1] - visibleDomain[0])/count,
i = d3_bisect(tickSteps, target);
return i ? tickMethods[target/tickSteps[i-1] < tickSteps[i]/target ? i-1 : i] : tickMethods[i];
}
function lookupIndex(array) {
var lookup = {};
array.forEach(function(d, i) { lookup[+d] = i; });
return lookup;
}
function domainTicks(visibleDomain) {
var visibleDomainLookup = lookupIndex(visibleDomain); // Quickly lookup index of the domain
return function(d) {
var value = visibleDomainLookup[+d];
if (value !== undefined) return visibleDomain[value];
return visibleDomain[d3_bisect(visibleDomain, d)];
};
}
function sequentialDuplicates(previous, current) {
if(previous.length === 0 || previous[previous.length-1] !== current) previous.push(current);
return previous;
}
return financetime;
};
},{}],38:[function(_dereq_,module,exports){
'use strict';
module.exports = function(d3) {
var zoomable = _dereq_('./zoomable')(),
util = _dereq_('../util')(),
accessors = _dereq_('../accessor')(),
financetime = _dereq_('./financetime')(d3.scale.linear, d3.time, d3.bisect, util.rebindCallback, widen, zoomable);
return {
financetime: financetime,
analysis: {
supstance: function(data, accessor) {
return d3.scale.linear();
},
trendline: function(data, accessor) {
return d3.scale.linear();
}
},
plot: {
time: function(data, accessor) {
accessor = accessor || accessors.value();
return financetime().domain(data.map(accessor.d));
},
percent: function (scale, reference) {
var domain = scale.domain();
reference = reference || domain[0];
return scale.copy().domain([domain[0], domain[domain.length-1]].map(function(d) { return (d-reference)/reference; }));
},
ohlc: function (data, accessor) {
accessor = accessor || accessors.ohlc();
return d3.scale.linear()
.domain([d3.min(data.map(accessor.low())), d3.max(data.map(accessor.high()))].map(widen(0.02)))
.range([1, 0]);
},
volume: function (data, accessor) {
accessor = accessor || accessors.ohlc().v;
return d3.scale.linear()
.domain([0, d3.max(data.map(accessor))*1.15])
.range([1, 0]);
},
rsi: function () {
return d3.scale.linear().domain([0, 100])
.range([1, 0]);
},
aroon: function () {
return d3.scale.linear().domain([-100, 100])
.range([1, 0]);
},
stochastic: function () {
return d3.scale.linear().domain([0, 100])
.range([1, 0]);
},
williams: function () {
return d3.scale.linear().domain([0, 100])
.range([1, 0]);
},
momentum: function(data, accessor) {
accessor = accessor || accessors.value();
return pathScale(d3, data, accessor, 0.04);
},
moneyflow: function(data, accessor) {
accessor = accessor || accessors.value();
return pathScale(d3, data, accessor, 0.04);
},
macd: function(data, accessor) {
accessor = accessor || accessors.macd();
return pathScale(d3, data, accessor, 0.04);
},
movingaverage: function(data, accessor) {
accessor = accessor || accessors.value();
return pathScale(d3, data, accessor);
}
},
position: {
}
};
};
function pathDomain(d3, data, accessor, widening) {
return data.length > 0 ? d3.extent(data, accessor).map(widen(widening)) : null;
}
function pathScale(d3, data, accessor, widening) {
return d3.scale.linear().domain(pathDomain(d3, data, accessor, widening))
.range([1, 0]);
}
/**
* Only to be used on an array of 2 elements [min, max]
* @param padding
* @param width
* @returns {Function}
*/
function widen(widening, width) {
widening = widening || 0;
return function(d, i, array) {
if(array.length > 2) throw "array.length > 2 unsupported. array.length = " + array.length;
width = width || (array[array.length-1] - array[0]);
return d + (i*2-1)*width*widening;
};
}
},{"../accessor":3,"../util":41,"./financetime":37,"./zoomable":39}],39:[function(_dereq_,module,exports){
'use strict';
/**
* Creates a decorated zoomable view of the passed scale. As the finance scale deals with an array and integer positions within the
* array, it does not support the d3 zoom behaviour. d3 zoom behaviour rescales the input domain.
* Finance scale is composed of an array of dates which is fixed in length and position and a linear scale mapping index
* to range. The linear scale can be zoomed. This object decorates the scale with only the methods required by zoom
* (invert, domain, copy). On zoom, calls the based zoomed callback.
*
* NOTE: This is not a complete scale, it will throw errors if it is used for anything else but zooming
*/
module.exports = function() {
function zoomable(linear, zoomed) {
var scale = {},
domainLimit = linear.domain();
scale.invert = linear.invert;
scale.domain = function(_) {
if(!arguments.length) throw "zoomable is a write only domain. Use this scale for zooming only";
linear.domain([Math.max(domainLimit[0], _[0]), Math.min(domainLimit[1], _[1])]);
if(zoomed) zoomed(); // Callback to that we have been zoomed
return scale;
};
scale.range = function(_) {
if(!arguments.length) return linear.range();
throw "zoomable is a read only range. Use this scale for zooming only";
};
scale.copy = function() {
return zoomable(linear.copy(), zoomed);
};
return scale;
}
return zoomable;
};
},{}],40:[function(_dereq_,module,exports){
'use strict';
module.exports = (function(d3) {
return {
version: _dereq_('../build/version'),
accessor: _dereq_('./accessor')(),
indicator: _dereq_('./indicator')(),
plot: _dereq_('./plot')(d3),
scale: _dereq_('./scale')(d3)
};
})(d3);
},{"../build/version":1,"./accessor":3,"./indicator":14,"./plot":25,"./scale":38}],41:[function(_dereq_,module,exports){
'use strict';
module.exports = function() {
return {
rebindCallback: rebindCallback,
rebind: function(target, source) {
var newArgs = Array.prototype.slice.call(arguments, 0);
newArgs.splice(2, 0, undefined);
return rebindCallback.apply(this, newArgs);
}
};
};
/*
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;
};
}
},{}]},{},[40])
(40)
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment