Created
October 20, 2014 13:46
-
-
Save fojt/45fc5fc06d968ce31379 to your computer and use it in GitHub Desktop.
Williams %R
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Date | Open | High | Low | Close | Volume | |
---|---|---|---|---|---|---|
28-Feb-14 | 69.47 | 69.88 | 67.38 | 68.46 | 66900863 | |
27-Feb-14 | 69.34 | 70.01 | 68.87 | 68.94 | 41695855 | |
26-Feb-14 | 70.19 | 71.22 | 68.85 | 69.26 | 55400399 | |
25-Feb-14 | 70.95 | 71.00 | 69.45 | 69.85 | 52189031 | |
24-Feb-14 | 68.74 | 71.44 | 68.54 | 70.78 | 76951946 | |
21-Feb-14 | 69.69 | 69.96 | 68.45 | 68.59 | 70991892 | |
20-Feb-14 | 67.73 | 70.11 | 65.73 | 69.63 | 131043748 | |
19-Feb-14 | 67.05 | 69.08 | 67.00 | 68.06 | 64258631 | |
18-Feb-14 | 66.94 | 67.54 | 66.07 | 67.30 | 43862297 | |
14-Feb-14 | 67.50 | 67.58 | 66.72 | 67.09 | 36786427 | |
13-Feb-14 | 64.18 | 67.33 | 64.05 | 67.33 | 62013396 | |
12-Feb-14 | 64.92 | 65.06 | 64.05 | 64.45 | 47409857 | |
11-Feb-14 | 63.75 | 65.00 | 63.35 | 64.85 | 45746832 | |
10-Feb-14 | 64.30 | 64.49 | 63.47 | 63.55 | 43736562 | |
7-Feb-14 | 62.27 | 64.57 | 62.22 | 64.32 | 60835746 | |
6-Feb-14 | 61.46 | 62.78 | 61.46 | 62.16 | 42153754 | |
5-Feb-14 | 62.74 | 63.16 | 61.27 | 62.19 | 53032420 | |
4-Feb-14 | 62.05 | 63.14 | 61.82 | 62.75 | 46064897 | |
3-Feb-14 | 63.03 | 63.77 | 60.70 | 61.48 | 75105994 | |
31-Jan-14 | 60.47 | 63.37 | 60.17 | 62.57 | 87930298 | |
30-Jan-14 | 62.12 | 62.50 | 60.46 | 61.08 | 150438699 | |
29-Jan-14 | 54.61 | 54.95 | 53.19 | 53.53 | 98089932 | |
28-Jan-14 | 54.02 | 55.28 | 54.00 | 55.14 | 48364998 | |
27-Jan-14 | 54.73 | 54.94 | 51.85 | 53.55 | 74142331 | |
24-Jan-14 | 56.15 | 56.42 | 54.40 | 54.45 | 55545338 | |
23-Jan-14 | 56.37 | 56.68 | 55.69 | 56.63 | 47996403 | |
22-Jan-14 | 58.85 | 59.31 | 57.10 | 57.51 | 61495880 | |
21-Jan-14 | 56.60 | 58.58 | 56.50 | 58.51 | 48734147 | |
17-Jan-14 | 57.30 | 57.82 | 56.07 | 56.30 | 40883205 | |
16-Jan-14 | 57.26 | 58.02 | 56.83 | 57.19 | 34599775 | |
15-Jan-14 | 57.98 | 58.57 | 57.27 | 57.60 | 33730619 | |
14-Jan-14 | 56.46 | 57.78 | 56.10 | 57.74 | 37590987 | |
13-Jan-14 | 57.91 | 58.25 | 55.38 | 55.91 | 63106519 | |
10-Jan-14 | 57.13 | 58.30 | 57.06 | 57.94 | 42529258 | |
9-Jan-14 | 58.65 | 58.96 | 56.65 | 57.22 | 92349222 | |
8-Jan-14 | 57.60 | 58.41 | 57.23 | 58.23 | 56800776 | |
7-Jan-14 | 57.70 | 58.55 | 57.22 | 57.92 | 77329009 | |
6-Jan-14 | 54.42 | 57.26 | 54.05 | 57.20 | 68974359 | |
3-Jan-14 | 55.02 | 55.65 | 54.53 | 54.56 | 38287706 | |
2-Jan-14 | 54.83 | 55.22 | 54.19 | 54.71 | 43257622 | |
31-Dec-13 | 54.12 | 54.86 | 53.91 | 54.65 | 43152127 | |
30-Dec-13 | 54.93 | 55.18 | 53.43 | 53.71 | 68307317 | |
27-Dec-13 | 57.48 | 57.68 | 55.25 | 55.44 | 60465751 | |
26-Dec-13 | 58.32 | 58.38 | 57.37 | 57.73 | 55101367 | |
24-Dec-13 | 58.27 | 58.58 | 56.91 | 57.96 | 46617754 | |
23-Dec-13 | 55.50 | 58.32 | 55.45 | 57.77 | 98296983 | |
20-Dec-13 | 54.91 | 55.15 | 54.23 | 55.12 | 239823912 | |
19-Dec-13 | 54.34 | 55.19 | 53.95 | 55.05 | 89825393 | |
18-Dec-13 | 55.57 | 55.89 | 53.75 | 55.57 | 76003479 | |
17-Dec-13 | 54.75 | 55.18 | 54.24 | 54.86 | 78751463 | |
16-Dec-13 | 53.27 | 54.50 | 52.91 | 53.81 | 85118518 | |
13-Dec-13 | 51.61 | 53.50 | 51.34 | 53.32 | 82640992 | |
12-Dec-13 | 51.03 | 52.07 | 50.66 | 51.83 | 92723034 | |
11-Dec-13 | 50.56 | 50.77 | 49.01 | 49.38 | 65776366 | |
10-Dec-13 | 48.62 | 50.77 | 48.54 | 50.24 | 68478561 | |
9-Dec-13 | 48.06 | 48.97 | 47.74 | 48.84 | 36055891 | |
6-Dec-13 | 48.98 | 49.39 | 47.71 | 47.94 | 42937659 | |
5-Dec-13 | 48.15 | 48.70 | 47.87 | 48.34 | 43855036 | |
4-Dec-13 | 46.46 | 48.77 | 46.26 | 48.62 | 60890176 | |
3-Dec-13 | 46.75 | 47.20 | 46.29 | 46.73 | 32085905 | |
2-Dec-13 | 46.90 | 47.54 | 46.26 | 47.06 | 50773647 | |
29-Nov-13 | 46.75 | 47.21 | 46.50 | 47.01 | 22953916 | |
27-Nov-13 | 45.97 | 46.67 | 45.53 | 46.49 | 44993195 | |
26-Nov-13 | 44.66 | 46.17 | 43.55 | 45.89 | 82016490 | |
25-Nov-13 | 46.36 | 46.65 | 44.04 | 44.82 | 82565324 | |
22-Nov-13 | 47.04 | 47.27 | 45.96 | 46.23 | 40545375 | |
21-Nov-13 | 46.99 | 47.46 | 46.68 | 46.70 | 34886170 | |
20-Nov-13 | 46.61 | 47.55 | 46.31 | 46.43 | 53932698 | |
19-Nov-13 | 46.26 | 47.00 | 45.72 | 46.36 | 75602413 | |
18-Nov-13 | 48.47 | 48.84 | 45.80 | 45.83 | 85909884 | |
15-Nov-13 | 49.11 | 49.48 | 48.71 | 49.01 | 42452937 | |
14-Nov-13 | 48.70 | 49.57 | 48.03 | 48.99 | 75117049 | |
13-Nov-13 | 46.23 | 48.74 | 46.06 | 48.71 | 79245346 | |
12-Nov-13 | 46.00 | 47.37 | 45.83 | 46.60 | 68195832 | |
11-Nov-13 | 47.04 | 47.53 | 45.73 | 46.20 | 80909626 | |
8-Nov-13 | 47.81 | 48.65 | 47.25 | 47.53 | 70731178 | |
7-Nov-13 | 49.24 | 49.87 | 47.30 | 47.56 | 97127618 | |
6-Nov-13 | 50.26 | 50.45 | 48.71 | 49.12 | 67889337 | |
5-Nov-13 | 47.79 | 50.18 | 47.51 | 50.10 | 76835006 | |
4-Nov-13 | 49.36 | 49.75 | 48.02 | 48.22 | 80371218 | |
1-Nov-13 | 50.85 | 52.09 | 49.72 | 49.75 | 95032876 | |
31-Oct-13 | 47.16 | 52.00 | 46.50 | 50.20 | 248809006 | |
30-Oct-13 | 50.00 | 50.21 | 48.75 | 49.01 | 127072652 | |
29-Oct-13 | 50.73 | 50.79 | 49.25 | 49.40 | 102143469 | |
28-Oct-13 | 51.54 | 51.70 | 49.61 | 50.23 | 73472347 | |
25-Oct-13 | 53.18 | 53.24 | 51.88 | 51.95 | 45085348 | |
24-Oct-13 | 52.38 | 52.84 | 51.59 | 52.44 | 46775185 | |
23-Oct-13 | 51.75 | 52.25 | 51.13 | 51.90 | 57207154 | |
22-Oct-13 | 54.33 | 54.76 | 52.20 | 52.68 | 83203892 | |
21-Oct-13 | 54.68 | 54.81 | 53.51 | 53.85 | 58235283 | |
18-Oct-13 | 54.18 | 54.82 | 53.60 | 54.22 | 88260093 | |
17-Oct-13 | 51.12 | 52.22 | 50.95 | 52.21 | 71521899 | |
16-Oct-13 | 50.04 | 51.24 | 49.90 | 51.14 | 64678247 | |
15-Oct-13 | 49.99 | 51.00 | 49.18 | 49.50 | 81166571 | |
14-Oct-13 | 48.31 | 49.63 | 47.91 | 49.51 | 68780552 | |
11-Oct-13 | 49.18 | 49.87 | 48.79 | 49.11 | 58428451 | |
10-Oct-13 | 47.86 | 49.68 | 47.83 | 49.05 | 99773784 | |
9-Oct-13 | 47.38 | 47.84 | 45.26 | 46.77 | 147296862 | |
8-Oct-13 | 50.60 | 50.60 | 47.08 | 47.14 | 136081330 | |
7-Oct-13 | 50.73 | 51.29 | 50.40 | 50.52 | 57203957 | |
4-Oct-13 | 49.77 | 51.16 | 49.57 | 51.04 | 74446947 | |
3-Oct-13 | 50.47 | 50.72 | 49.06 | 49.18 | 82045323 | |
2-Oct-13 | 50.13 | 51.10 | 49.95 | 50.28 | 62834429 | |
1-Oct-13 | 49.97 | 51.03 | 49.45 | 50.42 | 98113699 | |
30-Sep-13 | 50.14 | 51.60 | 49.80 | 50.23 | 100095417 | |
27-Sep-13 | 50.29 | 51.28 | 49.86 | 51.24 | 81410460 | |
26-Sep-13 | 50.01 | 50.60 | 49.50 | 50.39 | 98220046 | |
25-Sep-13 | 49.23 | 49.54 | 48.46 | 49.46 | 87879619 | |
24-Sep-13 | 48.50 | 49.66 | 48.16 | 48.45 | 136716101 | |
23-Sep-13 | 47.28 | 47.55 | 46.29 | 47.19 | 75319202 | |
20-Sep-13 | 46.32 | 47.60 | 45.74 | 47.49 | 115508400 | |
19-Sep-13 | 45.51 | 46.05 | 45.23 | 45.98 | 63972369 | |
18-Sep-13 | 44.84 | 45.47 | 44.40 | 45.23 | 79316945 | |
17-Sep-13 | 42.50 | 45.44 | 42.43 | 45.07 | 91934557 | |
16-Sep-13 | 44.85 | 44.94 | 42.43 | 42.51 | 70807761 | |
13-Sep-13 | 45.04 | 45.08 | 43.93 | 44.31 | 52765299 | |
12-Sep-13 | 45.53 | 45.62 | 44.65 | 44.75 | 68072239 | |
11-Sep-13 | 43.39 | 45.09 | 43.11 | 45.04 | 71676653 | |
10-Sep-13 | 44.24 | 44.26 | 43.23 | 43.60 | 54540282 | |
9-Sep-13 | 44.36 | 44.79 | 43.70 | 44.04 | 75794696 | |
6-Sep-13 | 43.09 | 44.61 | 42.40 | 43.95 | 117535626 | |
5-Sep-13 | 41.79 | 42.76 | 41.77 | 42.66 | 50035380 | |
4-Sep-13 | 42.01 | 42.17 | 41.44 | 41.78 | 42581854 | |
3-Sep-13 | 41.84 | 42.16 | 41.51 | 41.87 | 48774896 | |
30-Aug-13 | 42.02 | 42.26 | 41.06 | 41.29 | 67735053 | |
29-Aug-13 | 40.89 | 41.78 | 40.80 | 41.28 | 58303395 | |
28-Aug-13 | 39.96 | 40.85 | 39.88 | 40.55 | 57918194 | |
27-Aug-13 | 40.68 | 41.20 | 39.42 | 39.64 | 72695050 | |
26-Aug-13 | 40.90 | 41.94 | 40.62 | 41.34 | 94162358 | |
23-Aug-13 | 39.00 | 40.63 | 38.93 | 40.55 | 86442283 | |
22-Aug-13 | 38.37 | 38.75 | 38.34 | 38.55 | 21931163 | |
21-Aug-13 | 38.38 | 38.85 | 38.14 | 38.32 | 46116868 | |
20-Aug-13 | 38.35 | 38.58 | 37.69 | 38.41 | 57995140 | |
19-Aug-13 | 37.43 | 38.28 | 37.14 | 37.81 | 57609591 | |
16-Aug-13 | 36.97 | 37.49 | 36.90 | 37.08 | 45840714 | |
15-Aug-13 | 36.36 | 37.07 | 36.02 | 36.56 | 56521095 | |
14-Aug-13 | 36.83 | 37.55 | 36.62 | 36.65 | 48423890 | |
13-Aug-13 | 38.24 | 38.32 | 36.77 | 37.02 | 65379198 | |
12-Aug-13 | 38.20 | 38.50 | 38.10 | 38.22 | 31160951 | |
9-Aug-13 | 38.59 | 38.74 | 38.01 | 38.50 | 43620024 | |
8-Aug-13 | 39.13 | 39.19 | 38.43 | 38.54 | 41300906 | |
7-Aug-13 | 38.61 | 38.94 | 37.70 | 38.87 | 68854764 | |
6-Aug-13 | 39.11 | 39.25 | 37.94 | 38.55 | 63950791 | |
5-Aug-13 | 38.43 | 39.32 | 38.25 | 39.19 | 79994774 | |
2-Aug-13 | 37.66 | 38.49 | 37.50 | 38.05 | 73058424 | |
1-Aug-13 | 37.30 | 38.29 | 36.92 | 37.49 | 106066472 | |
31-Jul-13 | 37.96 | 38.31 | 36.33 | 36.80 | 154828679 | |
30-Jul-13 | 35.65 | 37.96 | 35.32 | 37.63 | 173582710 | |
29-Jul-13 | 34.07 | 35.63 | 34.01 | 35.43 | 124884870 | |
26-Jul-13 | 33.77 | 34.73 | 33.56 | 34.01 | 136028897 | |
25-Jul-13 | 33.54 | 34.88 | 32.75 | 34.36 | 365935212 | |
24-Jul-13 | 26.32 | 26.53 | 26.05 | 26.51 | 82635587 | |
23-Jul-13 | 26.10 | 26.30 | 25.97 | 26.13 | 28221534 | |
22-Jul-13 | 25.99 | 26.13 | 25.72 | 26.04 | 27526213 | |
19-Jul-13 | 25.82 | 26.11 | 25.60 | 25.88 | 46544938 | |
18-Jul-13 | 26.75 | 26.77 | 26.12 | 26.18 | 24806825 | |
17-Jul-13 | 26.37 | 26.78 | 26.30 | 26.65 | 21518463 | |
16-Jul-13 | 26.39 | 26.75 | 26.01 | 26.32 | 30817554 | |
15-Jul-13 | 25.93 | 26.43 | 25.65 | 26.28 | 24233957 | |
12-Jul-13 | 25.74 | 25.93 | 25.55 | 25.91 | 16537840 | |
11-Jul-13 | 25.96 | 26.00 | 25.45 | 25.81 | 26777354 | |
10-Jul-13 | 25.58 | 25.83 | 25.47 | 25.80 | 26721794 | |
9-Jul-13 | 25.07 | 25.49 | 25.03 | 25.48 | 30387889 | |
8-Jul-13 | 24.47 | 25.04 | 24.42 | 24.71 | 27073983 | |
5-Jul-13 | 24.65 | 24.66 | 24.20 | 24.37 | 20229451 | |
3-Jul-13 | 24.22 | 24.71 | 24.15 | 24.52 | 10404332 | |
2-Jul-13 | 24.70 | 24.77 | 24.30 | 24.41 | 18394008 | |
1-Jul-13 | 24.97 | 25.06 | 24.62 | 24.81 | 20582195 | |
28-Jun-13 | 24.68 | 24.98 | 24.42 | 24.88 | 96778879 | |
27-Jun-13 | 24.24 | 24.84 | 24.21 | 24.66 | 34694013 | |
26-Jun-13 | 24.51 | 24.65 | 23.99 | 24.16 | 29890205 | |
25-Jun-13 | 24.14 | 24.43 | 24.04 | 24.25 | 24719988 | |
24-Jun-13 | 23.95 | 24.11 | 23.38 | 23.94 | 40625948 | |
21-Jun-13 | 24.59 | 24.70 | 24.05 | 24.53 | 45826173 | |
20-Jun-13 | 24.28 | 24.74 | 23.65 | 23.90 | 42765586 | |
19-Jun-13 | 24.20 | 25.19 | 24.10 | 24.31 | 31790525 | |
18-Jun-13 | 24.09 | 24.69 | 24.08 | 24.21 | 36709004 | |
17-Jun-13 | 23.91 | 24.25 | 23.75 | 24.02 | 33664419 | |
14-Jun-13 | 23.56 | 23.89 | 23.26 | 23.63 | 30561387 | |
13-Jun-13 | 23.72 | 23.83 | 23.26 | 23.73 | 31189247 | |
12-Jun-13 | 24.16 | 24.26 | 23.58 | 23.77 | 26445790 | |
11-Jun-13 | 24.03 | 24.35 | 24.00 | 24.03 | 29676383 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.williams path { | |
fill: none; | |
stroke-width: 1; | |
} | |
.williams.up { | |
stroke: #006600; | |
stroke-width: 1.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 williams = techan.plot.williams() | |
.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 williamsPeriod = 14; | |
d3.csv("data.csv", function(error, data) { | |
var accessor = williams.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 williamsData = techan.indicator.williams()(data.slice(0, williamsPeriod+200)); | |
x.domain(williamsData.map(accessor.d)); | |
y.domain(techan.scale.plot.williams(williamsData.slice(williamsPeriod,williamsPeriod+200), accessor).domain()); | |
svg.append("g") | |
.datum(williamsData) | |
.attr("class", "williams") | |
.call(williams); | |
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("williams"); | |
}); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
TechanJS v0.3.0-4 | |
(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.3.0-4'; | |
},{}],2:[function(_dereq_,module,exports){ | |
'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(); | |
}; | |
},{}],3:[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(); | |
}; | |
},{}],4:[function(_dereq_,module,exports){ | |
'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(); | |
}; | |
},{}],5:[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'), | |
adx: _dereq_('./adx'), | |
aroon: _dereq_('./aroon'), | |
stochastic: _dereq_('./stochastic'), | |
williams: _dereq_('./williams'), | |
bollinger: _dereq_('./bollinger'), | |
trendline: _dereq_('./trendline'), | |
value: _dereq_('./value') | |
}; | |
}; | |
},{"./adx":2,"./aroon":3,"./bollinger":4,"./macd":6,"./ohlc":7,"./rsi":8,"./stochastic":9,"./trendline":10,"./value":11,"./volume":12,"./williams":13}],6:[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(); | |
}; | |
},{}],7:[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(); | |
}; | |
},{}],8:[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(); | |
}; | |
},{}],9:[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(); | |
}; | |
},{}],10:[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(); | |
}; | |
},{}],11:[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(); | |
}; | |
},{}],12:[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(); | |
}; | |
},{}],13:[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(); | |
}; | |
},{}],14:[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; | |
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(); | |
return data.map(function(d, i) { | |
if(i < 1) return datum(p.accessor.d(d)); | |
var upMove = data[i].high - data[i-1].high; | |
var downMove = data[i-1].low - data[i].low; | |
var plusDM = 0; | |
if(upMove > downMove && upMove>0){ | |
plusDM = upMove; | |
} | |
var minusDM = 0; | |
if(downMove > upMove && downMove > 0){ | |
minusDM = downMove; | |
} | |
var TR = d3.max([(d.high- d.low),Math.abs(d.high - data[i-1].close),Math.abs(d.low - data[i-1].close) ]); | |
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; | |
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_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 }; | |
} | |
} | |
},{}],15:[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 = 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 }; | |
} | |
},{}],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 | |
period = 20, | |
sdMultiplication = 2; | |
var sd; | |
function indicator(data) { | |
var signalLine = indicator_ema().accessor(indicator.accessor()).period(period).init(); | |
var j; | |
return data.map(function(d, i) { | |
var middleBand = signalLine.average(p.accessor(d)); | |
if(i >= period) { | |
var sum = 0; | |
for(j = 0;j<period;j++){ | |
sum += (Math.pow( (data[i-j].close - middleBand) ,2 ) ); | |
} | |
sd = Math.sqrt( sum/period ); | |
var upperBand = middleBand+sdMultiplication*sd, | |
lowerBand = middleBand-sdMultiplication*sd; | |
return datum(p.accessor.d(d), middleBand, upperBand, lowerBand); | |
} | |
else return datum(p.accessor.d(d)); | |
}).filter(function(d) { return d.middleBand; }); | |
} | |
indicator.period = function(_) { | |
if (!arguments.length) return period; | |
period = _; | |
return indicator; | |
}; | |
indicator.sdMultiplication = function(_) { | |
if (!arguments.length) return sdMultiplication; | |
sdMultiplication = _; | |
return indicator; | |
}; | |
// Mixin 'superclass' methods and variables | |
indicatorMixin(indicator, p, accessor_ohlc()); | |
return indicator; | |
}; | |
}; | |
function datum(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}; | |
} | |
},{}],17:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(indicatorMixin, accessor_ohlc, alpha_init) { // 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 = alpha_init(period); | |
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; | |
}; | |
}; | |
},{}],18:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var indicatorMixin = _dereq_('./indicatormixin')(), | |
accessor = _dereq_('../accessor')(), | |
sma = _dereq_('./sma')(indicatorMixin, accessor.ohlc), | |
ema_init = _dereq_('./ema'), | |
ema = ema_init(indicatorMixin, accessor.ohlc, ema_alpha_init); | |
return { | |
ema: ema, | |
macd: _dereq_('./macd')(indicatorMixin, accessor.ohlc, ema), | |
rsi: _dereq_('./rsi')(indicatorMixin, accessor.ohlc, ema), | |
aroon: _dereq_('./aroon')(indicatorMixin, accessor.ohlc), | |
stochastic: _dereq_('./stochastic')(indicatorMixin, accessor.ohlc, ema), | |
williams: _dereq_('./williams')(indicatorMixin, accessor.ohlc, ema), | |
adx: _dereq_('./adx')(indicatorMixin, accessor.ohlc, ema), | |
bollinger: _dereq_('./bollinger')(indicatorMixin, accessor.ohlc, sma), | |
sma: _dereq_('./sma')(indicatorMixin, accessor.ohlc), | |
wilderma: ema_init(indicatorMixin, accessor.ohlc, wilder_alpha_init) | |
}; | |
}; | |
function ema_alpha_init(period) { | |
return 2/(period+1); | |
} | |
function wilder_alpha_init(period) { | |
return 1/period; | |
} | |
},{"../accessor":5,"./adx":14,"./aroon":15,"./bollinger":16,"./ema":17,"./indicatormixin":19,"./macd":20,"./rsi":21,"./sma":22,"./stochastic":23,"./williams":24}],19:[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(); | |
}; | |
}; | |
},{}],20:[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 }; | |
} | |
},{}],21:[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 }; | |
} | |
},{}],22:[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; | |
}; | |
}; | |
},{}],23:[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; | |
} | |
} | |
var diff = (max[k]-min[k]); | |
if(diff > 0) { | |
stochasticKBuffer[k] = ((data[i - k].close - 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(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 }; | |
} | |
},{}],24:[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 }; | |
} | |
},{}],25:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_adx, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function adx(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d); | |
group.entry.append('path').attr('class', 'adx'); | |
group.entry.append('path').attr('class', 'plusDi'); | |
group.entry.append('path').attr('class', 'minusDi'); | |
adx.refresh(g); | |
} | |
adx.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale, plot); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(adx, p, accessor_adx()); | |
return adx; | |
}; | |
}; | |
function refresh(g, accessor, x, y, plot) { | |
g.selectAll('path.adx').attr('d', plot.pathLine(accessor.d, x, accessor.adx, y)); | |
g.selectAll('path.plusDi').attr('d', plot.pathLine(accessor.d, x, accessor.plusDi, y)); | |
g.selectAll('path.minusDi').attr('d', plot.pathLine(accessor.d, x, accessor.minusDi, y)); | |
} | |
},{}],26:[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)); | |
} | |
},{}],27:[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(); | |
} | |
}; | |
} | |
},{}],28:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_bollinger, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function bollinger(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d); | |
group.entry.append('path').attr('class', 'upper'); | |
group.entry.append('path').attr('class', 'middle'); | |
group.entry.append('path').attr('class', 'lower'); | |
bollinger.refresh(g); | |
} | |
bollinger.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale, plot); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(bollinger, p, accessor_bollinger()); | |
return bollinger; | |
}; | |
}; | |
function refresh(g, accessor, x, y, plot) { | |
g.selectAll('path.upper').attr('d', plot.pathLine(accessor.d, x, accessor.upper, y)); | |
g.selectAll('path.middle').attr('d', plot.pathLine(accessor.d, x, accessor.middle, y)); | |
g.selectAll('path.lower').attr('d', plot.pathLine(accessor.d, x, accessor.lower, y)); | |
} | |
},{}],29:[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); | |
}); | |
} | |
},{}],30:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_select, d3_event, d3_mouse, d3_dispatch, axisannotation, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var dispatch = d3_dispatch('enter', 'out', 'move'), | |
xAnnotation = [axisannotation()], | |
yAnnotation = [axisannotation()], | |
verticalWireRange, | |
horizontalWireRange, | |
change = 0; // Track changes to this object, to know when to redraw | |
function crosshair(g) { | |
var group = g.selectAll('g.data.top').data([change], function(d) { return d; }), | |
groupEnter = group.enter(), | |
dataEnter = groupEnter.append('g').attr('class', 'data top').style('display', 'none'); | |
group.exit().remove(); | |
dataEnter.append('path').attr('class', 'horizontal wire'); | |
dataEnter.append('path').attr('class', 'vertical wire'); | |
appendAnnotation(dataEnter, d3_select, 'x', xAnnotation); | |
appendAnnotation(dataEnter, d3_select, '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'), | |
pathVerticalSelection = group.selectAll('path.vertical'), | |
pathHorizontalSelection = group.selectAll('path.horizontal'), | |
xAnnotationSelection = group.selectAll('g.axisannotation.x > g'), | |
yAnnotationSelection = group.selectAll('g.axisannotation.y > g'); | |
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', function() { | |
display(g, 'inline'); | |
dispatch.enter(); | |
}) | |
.on('mouseout', function() { | |
display(g, 'none'); | |
dispatch.out(); | |
}) | |
.on('mousemove', mousemoveRefresh(d3_select, d3_mouse, dispatch, xAnnotation, yAnnotation, | |
pathVerticalSelection, pathHorizontalSelection, xAnnotationSelection, yAnnotationSelection, | |
verticalWireRange, horizontalWireRange) | |
); | |
refresh(d3_select, xAnnotation, yAnnotation, pathVerticalSelection, pathHorizontalSelection, | |
xAnnotationSelection, yAnnotationSelection, verticalWireRange, horizontalWireRange | |
); | |
}; | |
crosshair.xAnnotation = function(_) { | |
if(!arguments.length) return xAnnotation; | |
xAnnotation = _ instanceof Array ? _ : [_]; | |
change++; // Annotations have changed, increment to trigger a redraw | |
return crosshair; | |
}; | |
crosshair.yAnnotation = function(_) { | |
if(!arguments.length) return yAnnotation; | |
yAnnotation = _ instanceof Array ? _ : [_]; | |
change++; // Annotations have changed, increment to trigger a redraw | |
return crosshair; | |
}; | |
crosshair.verticalWireRange = function(_) { | |
if(!arguments.length) return verticalWireRange; | |
verticalWireRange = _; | |
return crosshair; | |
}; | |
crosshair.horizontalWireRange = function(_) { | |
if(!arguments.length) return horizontalWireRange; | |
horizontalWireRange = _; | |
return crosshair; | |
}; | |
// Mixin event listening | |
plotMixin.on(crosshair, dispatch); | |
return crosshair; | |
}; | |
}; | |
function display(g, style) { | |
g.select('g.data.top').style('display', style); | |
} | |
function mousemoveRefresh(d3_select, d3_mouse, dispatch, xAnnotation, yAnnotation, pathVerticalSelection, pathHorizontalSelection, | |
xAnnotationSelection, yAnnotationSelection, verticalWireRange, horizontalWireRange) { | |
var event = [new Array(xAnnotation.length), new Array(yAnnotation.length)]; | |
return function() { | |
var coords = d3_mouse(this), | |
x = xAnnotation[0].axis().scale(), | |
y = yAnnotation[0].axis().scale(); | |
refresh(d3_select, xAnnotation, yAnnotation, | |
pathVerticalSelection.datum(x.invert(coords[0])), | |
pathHorizontalSelection.datum(y.invert(coords[1])), | |
xAnnotationSelection.each(updateAnnotationValue(xAnnotation, coords[0], event[0])), | |
yAnnotationSelection.each(updateAnnotationValue(yAnnotation, coords[1], event[1])), | |
verticalWireRange, horizontalWireRange | |
); | |
dispatch.move(event); | |
}; | |
} | |
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, event) { | |
return function(d, i) { | |
event[i] = annotations[i].axis().scale().invert(value); | |
// d[0] because only ever 1 value for crosshairs | |
d[0].value = event[i]; | |
}; | |
} | |
function appendAnnotation(selection, d3_select, clazz, annotation) { | |
var annotationSelection = selection.append('g').attr('class', 'axisannotation ' + clazz) | |
.selectAll('g').data(annotation.map(function() { return [{ value: null }]; })); | |
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)); | |
}; | |
} | |
},{}],31:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3) { | |
var scale = _dereq_('../scale')(d3), | |
accessor = _dereq_('../accessor')(), | |
plot = _dereq_('./plot')(d3.svg.line, d3.select), | |
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, d3.dispatch, axisannotation, plotMixin), | |
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), | |
adx: _dereq_('./adx')(accessor.adx, plot, plotMixin), | |
aroon: _dereq_('./aroon')(accessor.aroon, plot, plotMixin), | |
stochastic: _dereq_('./stochastic')(accessor.stochastic, plot, plotMixin), | |
williams: _dereq_('./williams')(accessor.williams, plot, plotMixin), | |
bollinger: _dereq_('./bollinger')(accessor.bollinger, 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, d3.dispatch, accessor.value, plot, plotMixin), | |
trendline: _dereq_('./trendline')(d3.behavior.drag, d3_event, d3.select, d3.dispatch, accessor.trendline, plot, plotMixin), | |
wilderma: line(accessor.value, plot, plotMixin) | |
}; | |
}; | |
function d3_event() { | |
return d3.event; | |
} | |
},{"../accessor":5,"../scale":44,"./adx":25,"./aroon":26,"./axisannotation":27,"./bollinger":28,"./candlestick":29,"./crosshair":30,"./line":32,"./macd":33,"./ohlc":34,"./plot":35,"./plotmixin":36,"./rsi":37,"./stochastic":38,"./supstance":39,"./trendline":40,"./volume":41,"./williams":42}],32:[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)); | |
} | |
} | |
},{}],33:[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(' '); | |
}; | |
} | |
},{}],34:[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(' '); | |
}; | |
} | |
},{}],35:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_svg_line, d3_select) { | |
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)); } ); | |
}, | |
interaction: { | |
mousedispatch: function(dispatch) { | |
return function(selection) { | |
return selection.on('mouseenter', function(d) { | |
d3_select(this.parentNode).classed('mouseover', true); | |
dispatch.mouseenter(d); | |
}) | |
.on('mouseleave', function(d) { | |
var parentElement = d3_select(this.parentNode); | |
if(!parentElement.classed('dragging')) { | |
parentElement.classed('mouseover', false); | |
dispatch.mouseout(d); | |
} | |
}) | |
.on('mousemove', function(d) { dispatch.mousemove(d); }); | |
}; | |
}, | |
dragStartEndDispatch: function(drag, dispatch) { | |
return drag.on('dragstart', function(d) { | |
d3_select(this.parentNode.parentNode).classed('dragging', true); | |
dispatch.dragstart(d); | |
}) | |
.on('dragend', function(d) { | |
d3_select(this.parentNode.parentNode).classed('dragging', false); | |
dispatch.dragend(d); | |
}); | |
} | |
} | |
}; | |
}; | |
},{}],36:[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; | |
} | |
plotMixin.on = function(source, dispatch) { | |
source.on = function(type, listener) { | |
dispatch.on(type, listener); | |
return source; | |
}; | |
return plotMixin; | |
}; | |
return plotMixin; | |
}; | |
},{}],37:[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)); | |
} | |
},{}],38:[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)); | |
} | |
},{}],39:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_behavior_drag, d3_event, d3_select, d3_dispatch, accessor_value, plot, plotMixin) { // Injected dependencies | |
function Supstance() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
dispatch = d3_dispatch('mouseenter', 'mouseout', 'mousemove', 'drag', 'dragstart', 'dragend'); | |
function supstance(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.unity); | |
group.entry.append('g').attr('class', 'supstance') | |
.append('path'); | |
var interaction = group.entry.append('g').attr('class', 'interaction').style({ opacity: 0, fill: 'none' }) | |
.call(plot.interaction.mousedispatch(dispatch)); | |
interaction.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(dispatch, p.accessor, p.xScale, p.yScale)); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(supstance, p, accessor_value()) | |
.on(supstance, dispatch); | |
return supstance; | |
} | |
function dragBody(dispatch, accessor, x, y) { | |
var drag = d3_behavior_drag().origin(function(d) { | |
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); | |
dispatch.drag(d); | |
}); | |
return plot.interaction.dragStartEndDispatch(drag, dispatch); | |
} | |
return Supstance; | |
}; | |
function refresh(g, accessor, x, y) { | |
g.selectAll('.supstance path').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(' '); | |
}; | |
} | |
},{}],40:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_behavior_drag, d3_event, d3_select, d3_dispatch, accessor_trendline, plot, plotMixin) { // Injected dependencies | |
function Trendline() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
dispatch = d3_dispatch('mouseenter', 'mouseout', 'mousemove', 'drag', 'dragstart', 'dragend'); | |
function trendline(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.unity), | |
trendlineGroup = group.entry.append('g').attr('class', 'trendline'); | |
trendlineGroup.append('path').attr('class', 'body'); | |
trendlineGroup.append('circle').attr({ class: 'start', r: 1 }); | |
trendlineGroup.append('circle').attr({ class: 'end', r: 1 }); | |
var interaction = group.entry.append('g').attr('class', 'interaction').style({ opacity: 0, fill: 'none' }) | |
.call(plot.interaction.mousedispatch(dispatch)); | |
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) { | |
g.selectAll('.interaction circle.start') | |
.call(dragEnd(dispatch, p.accessor, p.accessor.sd, p.xScale, p.accessor.sv, p.yScale)); | |
g.selectAll('.interaction circle.end') | |
.call(dragEnd(dispatch, p.accessor, p.accessor.ed, p.xScale, p.accessor.ev, p.yScale)); | |
g.selectAll('.interaction path.body') | |
.call(dragBody(dispatch, p.accessor, p.xScale, p.yScale)); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(trendline, p, accessor_trendline()) | |
.on(trendline, dispatch); | |
return trendline; | |
} | |
function dragEnd(dispatch, accessor, accessor_x, x, accessor_y, y) { | |
var drag = d3_behavior_drag(); | |
drag.origin(function(d) { | |
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); | |
dispatch.drag(d); | |
}); | |
return plot.interaction.dragStartEndDispatch(drag, dispatch); | |
} | |
function dragBody(dispatch, accessor, x, y) { | |
var dragStart = {}, // State information, grabs the start coords of the line | |
drag = d3_behavior_drag(); | |
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)) }; | |
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); | |
dispatch.drag(d); | |
}); | |
return plot.interaction.dragStartEndDispatch(drag, dispatch); | |
} | |
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)); | |
} | |
return Trendline; | |
}; | |
function refresh(g, accessor, x, y) { | |
g.selectAll('.trendline path.body').attr('d', trendlinePath(accessor, x, y)); | |
g.selectAll('.trendline circle.start').attr(trendlineEnd(accessor.sd, x, accessor.sv, y)); | |
g.selectAll('.trendline circle.end').attr(trendlineEnd(accessor.ed, x, accessor.ev, y)); | |
g.selectAll('.interaction path.body').attr('d', trendlinePath(accessor, x, y)); | |
g.selectAll('.interaction circle.start').attr(trendlineEnd(accessor.sd, x, accessor.sv, y)); | |
g.selectAll('.interaction circle.end').attr(trendlineEnd(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 trendlineEnd(accessor_x, x, accessor_y, y) { | |
return { | |
cx: function(d) { return x(accessor_x(d)); }, | |
cy: function(d) { return y(accessor_y(d)); } | |
}; | |
} | |
},{}],41:[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(' '); | |
}; | |
} | |
},{}],42:[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)); | |
} | |
},{}],43:[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, scale_widen, zoomable) { // Injected dependencies | |
function financetime(index, domain, padding, outerPadding) { | |
var dateIndexMap, | |
tickState = { tickFormat: dailyTickMethod[dailyTickMethod.length-1][2] }, | |
band = 3; | |
index = index || d3_scale_linear(); | |
domain = domain || [new Date(0), new Date(1)]; | |
padding = padding === undefined ? 0.2 : padding; | |
outerPadding = outerPadding === undefined ? 0.65 : outerPadding; | |
/** | |
* 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 = _; | |
return applyDomain(); | |
}; | |
function zoomed() { | |
band = rangeBand(index, domain, padding); | |
return scale; | |
} | |
function domainMap() { | |
dateIndexMap = lookupIndex(domain); | |
} | |
function applyDomain() { | |
domainMap(); | |
index.domain([0, domain.length-1]); | |
zoomed(); | |
// Apply outerPadding and widen the outer edges by pulling the domain in to ensure start and end bands are fully visible | |
index.domain(index.range().map(scale_widen(outerPadding, band)).map(index.invert)); | |
return zoomed(); | |
} | |
scale.copy = function() { | |
return financetime(index.copy(), domain, padding, outerPadding); | |
}; | |
/** | |
* 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.outerPadding = function(_) { | |
if(!arguments.length) return outerPadding; | |
outerPadding = _; | |
return applyDomain(); | |
}; | |
scale.padding = function(_) { | |
if(!arguments.length) return padding; | |
padding = _; | |
return applyDomain(); | |
}; | |
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]; | |
} | |
if(typeof steps === "undefined"){ | |
return []; | |
} | |
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, padding) { | |
return (Math.abs(linear(domain.length-1) - linear(0))/Math.max(1, domain.length-1))*(1-padding); | |
} | |
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; | |
}; | |
},{}],44:[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]); | |
}, | |
adx: 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]); | |
}, | |
bollinger: function (data, accessor) { | |
accessor = accessor || accessors.bollinger(); | |
return d3.scale.linear() | |
.domain([ | |
d3.min(data.map(function(d){return accessor.lower(d);})), | |
d3.max(data.map(function(d){return accessor.upper(d);})) | |
].map(widen(0.02))) | |
.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":5,"../util":47,"./financetime":43,"./zoomable":45}],45:[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; | |
}; | |
},{}],46:[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":5,"./indicator":18,"./plot":31,"./scale":44}],47:[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; | |
}; | |
} | |
},{}]},{},[46]) | |
(46) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment