Last active
August 29, 2015 14:06
-
-
Save fojt/4b1efcfcf92b2deaee20 to your computer and use it in GitHub Desktop.
TechanJS Aroon
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; | |
} | |
.aroon path { | |
fill: none; | |
stroke-width: 1; | |
} | |
.aroon { | |
stroke: #000000; | |
} | |
.aroon.up { | |
stroke: #00AA00; | |
stroke-width: 1.5; | |
} | |
.aroon.down { | |
stroke: #FF0000; | |
stroke-width: 1.5; | |
} | |
.aroon.oscillator { | |
stroke: #0000FF; | |
stroke-width: 0.5; | |
} | |
.aroon.oscillatorArea{ | |
fill: #0000FF; | |
opacity:0.025; | |
} | |
.aroon path.overbought, .aroon path.oversold { | |
stroke: #FF9999; | |
stroke-width: 1; | |
stroke-dasharray: 2, 2; | |
} | |
.aroon path.middle, path.zero { | |
stroke: #BBBBBB; | |
stroke-dasharray: 5, 5; | |
} | |
</style> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="techan.js"></script> | |
<script> | |
var margin = {top: 20, right: 20, bottom: 30, left: 50}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
var parseDate = d3.time.format("%d-%b-%y").parse; | |
var x = techan.scale.financetime() | |
.range([0, width]); | |
var y = d3.scale.linear() | |
.range([height, 0]); | |
var aroon = techan.plot.aroon() | |
.xScale(x) | |
.yScale(y); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom"); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient("left") | |
.tickFormat(d3.format(",.3s")); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var aroonPeriod = 14; | |
d3.csv("data.csv", function(error, data) { | |
var accessor = aroon.accessor(); | |
data = data.map(function(d) { | |
// Open, high, low, close generally not required, is being used here to demonstrate colored volume | |
// bars | |
return { | |
date: parseDate(d.Date), | |
volume: +d.Volume, | |
open: +d.Open, | |
high: +d.High, | |
low: +d.Low, | |
close: +d.Close | |
}; | |
}).sort(function(a, b) { return d3.ascending(accessor.d(a), accessor.d(b)); }); | |
var aroonData = techan.indicator.aroon()(data.slice(0, aroonPeriod+200)); | |
x.domain(aroonData.map(accessor.d)); | |
y.domain(techan.scale.plot.aroon(aroonData.slice(aroonPeriod,aroonPeriod+200), accessor).domain()); | |
svg.append("g") | |
.datum(aroonData) | |
.attr("class", "aroon") | |
.call(aroon); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis) | |
.append("text") | |
.attr("transform", "rotate(-90)") | |
.attr("y", 6) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end") | |
.text("aroon"); | |
}); | |
</script> |
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.2.0 | |
(c) 2014 - 2014 Andre Dumas | https://github.com/andredumas/techan.js | |
*/ | |
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.techan=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ | |
'use strict';module.exports='0.2.0'; | |
},{}],2:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var date = function(d) { return d.date; }, | |
up = function(d) { return d.up; }, | |
down = function(d) { return d.down; }, | |
oscillator = function(d) { return d.oscillator; }, | |
overbought = function(d) { return d.overbought; }, | |
oversold = function(d) { return d.oversold; }, | |
middle = function(d) { return d.middle; }; | |
function accessor(d) { | |
return accessor.r(d); | |
} | |
// TODO use d3.rebind to obtain this from 'super class' | |
accessor.date = function(_) { | |
if (!arguments.length) return date; | |
date = _; | |
return bind(); | |
}; | |
accessor.up = function(_) { | |
if (!arguments.length) return up; | |
up = _; | |
return bind(); | |
}; | |
accessor.down = function(_) { | |
if (!arguments.length) return down; | |
down = _; | |
return bind(); | |
}; | |
accessor.oscillator = function(_) { | |
if (!arguments.length) return oscillator; | |
oscillator = _; | |
return bind(); | |
}; | |
accessor.overbought = function(_) { | |
if (!arguments.length) return overbought; | |
overbought = _; | |
return bind(); | |
}; | |
accessor.oversold = function(_) { | |
if (!arguments.length) return oversold; | |
oversold = _; | |
return bind(); | |
}; | |
accessor.middle = function(_) { | |
if (!arguments.length) return middle; | |
middle = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.d = date; | |
accessor.up = up; | |
accessor.down = down; | |
accessor.oscillator = oscillator; | |
accessor.ob = overbought; | |
accessor.os = oversold; | |
accessor.m = middle; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],3:[function(_dereq_,module,exports){ | |
'use strict'; | |
// TODO Could these be singletons? Generally will be accessing the same data and data structures at the same time | |
module.exports = function() { | |
return { | |
ohlc: _dereq_('./ohlc'), | |
volume: _dereq_('./volume'), | |
macd: _dereq_('./macd'), | |
rsi: _dereq_('./rsi'), | |
aroon: _dereq_('./aroon'), | |
stochastic: _dereq_('./stochastic'), | |
williams: _dereq_('./williams'), | |
trendline: _dereq_('./trendline'), | |
value: _dereq_('./value') | |
}; | |
}; | |
},{"./aroon":2,"./macd":4,"./ohlc":5,"./rsi":6,"./stochastic":7,"./trendline":8,"./value":9,"./volume":10,"./williams":11}],4:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var date = function(d) { return d.date; }, | |
macd = function(d) { return d.macd; }, | |
zero = function(d) { return d.zero; }, | |
signal = function(d) { return d.signal;}, | |
difference = function(d) { return d.difference;}; | |
function accessor(d) { | |
return accessor.m(d); | |
} | |
// TODO use d3.rebind to obtain this from 'super class' | |
accessor.date = function(_) { | |
if (!arguments.length) return date; | |
date = _; | |
return bind(); | |
}; | |
accessor.macd = function(_) { | |
if (!arguments.length) return macd; | |
macd = _; | |
return bind(); | |
}; | |
accessor.signal = function(_) { | |
if (!arguments.length) return signal; | |
signal = _; | |
return bind(); | |
}; | |
accessor.difference = function(_) { | |
if (!arguments.length) return difference; | |
difference = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.d = date; | |
accessor.m = macd; | |
accessor.s = signal; | |
accessor.dif = difference; | |
accessor.z = zero; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],5:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var date = function(d) { return d.date; }, | |
open = function(d) { return d.open; }, | |
high = function(d) { return d.high; }, | |
low = function(d) { return d.low; }, | |
close = function(d) { return d.close;}, | |
volume = function(d) { return d.volume; }; | |
function accessor(d) { | |
return accessor.c(d); | |
} | |
// TODO use d3.rebind to obtain this from 'super class' | |
accessor.date = function(_) { | |
if (!arguments.length) return date; | |
date = _; | |
return bind(); | |
}; | |
accessor.open = function(_) { | |
if (!arguments.length) return open; | |
open = _; | |
return bind(); | |
}; | |
accessor.high = function(_) { | |
if (!arguments.length) return high; | |
high = _; | |
return bind(); | |
}; | |
accessor.low = function(_) { | |
if (!arguments.length) return low; | |
low = _; | |
return bind(); | |
}; | |
accessor.close = function(_) { | |
if (!arguments.length) return close; | |
close = _; | |
return bind(); | |
}; | |
accessor.volume = function(_) { | |
if (!arguments.length) return volume; | |
volume = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.d = date; | |
accessor.o = open; | |
accessor.h = high; | |
accessor.l = low; | |
accessor.c = close; | |
accessor.v = volume; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],6:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var date = function(d) { return d.date; }, | |
rsi = function(d) { return d.rsi; }, | |
overbought = function(d) { return d.overbought; }, | |
oversold = function(d) { return d.oversold; }, | |
middle = function(d) { return d.middle; }; | |
function accessor(d) { | |
return accessor.r(d); | |
} | |
// TODO use d3.rebind to obtain this from 'super class' | |
accessor.date = function(_) { | |
if (!arguments.length) return date; | |
date = _; | |
return bind(); | |
}; | |
accessor.rsi = function(_) { | |
if (!arguments.length) return rsi; | |
rsi = _; | |
return bind(); | |
}; | |
accessor.overbought = function(_) { | |
if (!arguments.length) return overbought; | |
overbought = _; | |
return bind(); | |
}; | |
accessor.oversold = function(_) { | |
if (!arguments.length) return oversold; | |
oversold = _; | |
return bind(); | |
}; | |
accessor.middle = function(_) { | |
if (!arguments.length) return middle; | |
middle = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.d = date; | |
accessor.r = rsi; | |
accessor.ob = overbought; | |
accessor.os = oversold; | |
accessor.m = middle; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],7:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var date = function(d) { return d.date; }, | |
stochasticK = function(d) { return d.stochasticK; }, | |
stochasticD = function(d) { return d.stochasticD; }, | |
overbought = function(d) { return d.overbought; }, | |
oversold = function(d) { return d.oversold; }, | |
middle = function(d) { return d.middle; }; | |
function accessor(d) { | |
return accessor.r(d); | |
} | |
// TODO use d3.rebind to obtain this from 'super class' | |
accessor.date = function(_) { | |
if (!arguments.length) return date; | |
date = _; | |
return bind(); | |
}; | |
accessor.stochasticK = function(_) { | |
if (!arguments.length) return stochasticK; | |
stochasticK = _; | |
return bind(); | |
}; | |
accessor.stochasticD = function(_) { | |
if (!arguments.length) return stochasticD; | |
stochasticD = _; | |
return bind(); | |
}; | |
accessor.overbought = function(_) { | |
if (!arguments.length) return overbought; | |
overbought = _; | |
return bind(); | |
}; | |
accessor.oversold = function(_) { | |
if (!arguments.length) return oversold; | |
oversold = _; | |
return bind(); | |
}; | |
accessor.middle = function(_) { | |
if (!arguments.length) return middle; | |
middle = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.d = date; | |
accessor.k = stochasticK; | |
accessor.sd = stochasticD; | |
accessor.ob = overbought; | |
accessor.os = oversold; | |
accessor.m = middle; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],8:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var startDate = function(d, _) { | |
if(arguments.length < 2) return d.start.date; | |
d.start.date = _; | |
}, | |
startValue = function(d, _) { | |
if(arguments.length < 2) return d.start.value; | |
d.start.value = _; | |
}, | |
endDate = function(d, _) { | |
if(arguments.length < 2) return d.end.date; | |
d.end.date = _; | |
}, | |
endValue = function(d, _) { | |
if(arguments.length < 2) return d.end.value; | |
d.end.value = _; | |
}; | |
function accessor(d) { | |
return accessor.sv(d); | |
} | |
accessor.startDate = function(_) { | |
if (!arguments.length) return startDate; | |
startDate = _; | |
return bind(); | |
}; | |
accessor.startValue = function(_) { | |
if (!arguments.length) return startValue; | |
startValue = _; | |
return bind(); | |
}; | |
accessor.endDate = function(_) { | |
if (!arguments.length) return endDate; | |
endDate = _; | |
return bind(); | |
}; | |
accessor.endValue = function(_) { | |
if (!arguments.length) return endValue; | |
endValue = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.sd = startDate; | |
accessor.sv = startValue; | |
accessor.ed = endDate; | |
accessor.ev = endValue; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],9:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var date = function(d) { return d.date; }, | |
value = function(d, _) { | |
if(arguments.length < 2) return d.value; | |
d.value = _; | |
}, | |
zero = function(d) { return d.zero; }; | |
function accessor(d) { | |
return accessor.v(d); | |
} | |
// TODO use d3.rebind to obtain this from 'super class' | |
accessor.date = function(_) { | |
if (!arguments.length) return date; | |
date = _; | |
return bind(); | |
}; | |
accessor.value = function(_) { | |
if (!arguments.length) return value; | |
value = _; | |
return bind(); | |
}; | |
accessor.zero = function(_) { | |
if (!arguments.length) return zero; | |
zero = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.d = date; | |
accessor.v = value; | |
accessor.z = zero; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],10:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var date = function(d) { return d.date; }, | |
volume = function(d) { return d.volume; }; | |
function accessor(d) { | |
return accessor.v(d); | |
} | |
// TODO use d3.rebind to obtain this from 'super class' | |
accessor.date = function(_) { | |
if (!arguments.length) return date; | |
date = _; | |
return bind(); | |
}; | |
accessor.volume = function(_) { | |
if (!arguments.length) return volume; | |
volume = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.d = date; | |
accessor.v = volume; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],11:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var date = function(d) { return d.date; }, | |
williams = function(d) { return d.williams; }; | |
function accessor(d) { | |
return accessor.r(d); | |
} | |
// TODO use d3.rebind to obtain this from 'super class' | |
accessor.date = function(_) { | |
if (!arguments.length) return date; | |
date = _; | |
return bind(); | |
}; | |
accessor.williams = function(_) { | |
if (!arguments.length) return williams; | |
williams = _; | |
return bind(); | |
}; | |
function bind() { | |
// TODO These methods will need to know if the variables are functions or values and execute as such | |
accessor.d = date; | |
accessor.w = williams; | |
return accessor; | |
} | |
return bind(); | |
}; | |
},{}],12:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(indicatorMixin, accessor_ohlc, indicator_ema) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
period = 20, | |
overbought = 70, | |
middle = 0, | |
oversold = 30; | |
function indicator(data) { | |
return data.map(function(d, i) { | |
if(i >= (period-1)){ | |
var max = 0; | |
var maxi = 0; | |
var min = 10000; | |
var mini = 0; | |
for (var j = 0; j < period; j++) { | |
if(data[i-j].high > max){ | |
max = data[i-j].high; | |
maxi = j; | |
} | |
if(data[i-j].low < min){ | |
min = data[i-j].low; | |
mini = j; | |
} | |
} | |
var up = ((period-maxi)/period)*100; | |
var down = ((period-mini)/period)*100; | |
var oscillator = up - down; | |
return datum(p.accessor.d(d), up,down, oscillator, middle, overbought, oversold); | |
} | |
else return datum(p.accessor.d(d)); | |
}).filter(function(d) { return d.up; }); | |
} | |
indicator.period = function(_) { | |
if (!arguments.length) return period; | |
period = _; | |
return indicator; | |
}; | |
indicator.overbought = function(_) { | |
if (!arguments.length) return overbought; | |
overbought = _; | |
return indicator; | |
}; | |
indicator.middle = function(_) { | |
if (!arguments.length) return middle; | |
middle = _; | |
return indicator; | |
}; | |
indicator.oversold = function(_) { | |
if (!arguments.length) return oversold; | |
oversold = _; | |
return indicator; | |
}; | |
// Mixin 'superclass' methods and variables | |
indicatorMixin(indicator, p, accessor_ohlc()); | |
return indicator; | |
}; | |
}; | |
function datum(date, up,down,oscillator, middle, overbought, oversold) { | |
if(up) return { date: date, up: up,down:down,oscillator:oscillator, middle: middle, overbought: overbought, oversold: oversold }; | |
else return { date: date, up: null,down:null,oscillator:null, middle: null, overbought: null, oversold: null }; | |
} | |
},{}],13:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(indicatorMixin, accessor_ohlc) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
period = 10, | |
previous, | |
alpha, | |
initialTotal, | |
initialCount; | |
function indicator(data) { | |
indicator.init(); | |
return data.map(ma).filter(function(d) { return d.value; }); | |
} | |
indicator.init = function() { | |
previous = null; | |
alpha = 2/(period+1); | |
initialTotal = 0; | |
initialCount = 0; | |
return indicator; | |
}; | |
function ma(d, i) { | |
var value = indicator.average(p.accessor(d)); | |
if (i+1 < period) { | |
value = null; | |
} | |
return { date: p.accessor.d(d), value: value }; | |
} | |
indicator.average = function(value) { | |
if(initialCount < period) return (initialTotal += value)/++initialCount; | |
else { | |
if(initialCount === period) { | |
previous = initialTotal/initialCount++; | |
} | |
return (previous = previous + alpha*(value-previous)); | |
} | |
}; | |
indicator.period = function(_) { | |
if (!arguments.length) return period; | |
period = _; | |
return indicator; | |
}; | |
// Mixin 'superclass' methods and variables | |
indicatorMixin(indicator, p, accessor_ohlc()); | |
return indicator; | |
}; | |
}; | |
},{}],14:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
var indicatorMixin = _dereq_('./indicatormixin')(), | |
accessor = _dereq_('../accessor')(), | |
ema = _dereq_('./ema')(indicatorMixin, accessor.ohlc); | |
return { | |
ema: ema, | |
macd: _dereq_('./macd')(indicatorMixin, accessor.ohlc, ema), | |
rsi: _dereq_('./rsi')(indicatorMixin, accessor.ohlc, ema), | |
aroon: _dereq_('./aroon')(indicatorMixin, accessor.ohlc, ema), | |
stochastic: _dereq_('./stochastic')(indicatorMixin, accessor.ohlc, ema), | |
williams: _dereq_('./williams')(indicatorMixin, accessor.ohlc, ema), | |
sma: _dereq_('./sma')(indicatorMixin, accessor.ohlc) | |
}; | |
}; | |
},{"../accessor":3,"./aroon":12,"./ema":13,"./indicatormixin":15,"./macd":16,"./rsi":17,"./sma":18,"./stochastic":19,"./williams":20}],15:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
return function(source, priv, accessor) { | |
// Mixin the functions to the source | |
source.accessor = function(_) { | |
if (!arguments.length) return accessor; | |
accessor = _; | |
return bind(); | |
}; | |
// Add in the private, direct access variables | |
function bind() { | |
priv.accessor = accessor; | |
return source; | |
} | |
bind(); | |
}; | |
}; | |
},{}],16:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(indicatorMixin, accessor_ohlc, indicator_ema) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
fast = 12, | |
slow = 26, | |
signal = 9; | |
function indicator(data) { | |
var minFastSlow = Math.max(fast, slow) - 1, | |
minCount = minFastSlow + signal - 1, | |
signalLine = indicator_ema().accessor(indicator.accessor()).period(signal).init(), | |
fastAverage = indicator_ema().accessor(indicator.accessor()).period(fast).init(), | |
slowAverage = indicator_ema().accessor(indicator.accessor()).period(slow).init(); | |
return data.map(function(d, i) { | |
slow = fastAverage.average(p.accessor(d)); | |
fast = slowAverage.average(p.accessor(d)); | |
var macd = slow - fast, | |
signalValue = i >= minFastSlow ? signalLine.average(macd) : null; | |
if(i >= minCount) return datum(p.accessor.d(d), macd, signalValue, macd - signalValue, 0); | |
else return datum(p.accessor.d(d)); | |
}).filter(function(d) { return d.macd; }); | |
} | |
indicator.fast = function(_) { | |
if (!arguments.length) return fast; | |
fast = _; | |
return indicator; | |
}; | |
indicator.slow = function(_) { | |
if (!arguments.length) return slow; | |
slow = _; | |
return indicator; | |
}; | |
indicator.signal = function(_) { | |
if (!arguments.length) return signal; | |
signal = _; | |
return indicator; | |
}; | |
// Mixin 'superclass' methods and variables | |
indicatorMixin(indicator, p, accessor_ohlc()); | |
return indicator; | |
}; | |
}; | |
function datum(date, macd, signal, difference, zero) { | |
if(macd) return { date: date, macd: macd, signal: signal, difference: difference, zero: zero }; | |
else return { date: date, macd: null, signal: null, difference: null, zero: null }; | |
} | |
},{}],17:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(indicatorMixin, accessor_ohlc, indicator_ema) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
period = 14, | |
overbought = 70, | |
middle = 50, | |
oversold = 30; | |
function indicator(data) { | |
var lossAverage = indicator_ema().accessor(indicator.accessor()).period(period).init(), | |
gainAverage = indicator_ema().accessor(indicator.accessor()).period(period).init(); | |
return data.map(function(d, i) { | |
if(i < 1) return datum(p.accessor.d(d)); | |
var difference = p.accessor(d) - p.accessor(data[i-1]), | |
averageGain = gainAverage.average(Math.max(difference, 0)), | |
averageLoss = Math.abs(lossAverage.average(Math.min(difference, 0))); | |
if(i >= period) { | |
var rsi = 100 - (100/(1+(averageGain/averageLoss))); | |
return datum(p.accessor.d(d), rsi, middle, overbought, oversold); | |
} | |
else return datum(p.accessor.d(d)); | |
}).filter(function(d) { return d.rsi; }); | |
} | |
indicator.period = function(_) { | |
if (!arguments.length) return period; | |
period = _; | |
return indicator; | |
}; | |
indicator.overbought = function(_) { | |
if (!arguments.length) return overbought; | |
overbought = _; | |
return indicator; | |
}; | |
indicator.middle = function(_) { | |
if (!arguments.length) return middle; | |
middle = _; | |
return indicator; | |
}; | |
indicator.oversold = function(_) { | |
if (!arguments.length) return oversold; | |
oversold = _; | |
return indicator; | |
}; | |
// Mixin 'superclass' methods and variables | |
indicatorMixin(indicator, p, accessor_ohlc()); | |
return indicator; | |
}; | |
}; | |
function datum(date, rsi, middle, overbought, oversold) { | |
if(rsi) return { date: date, rsi: rsi, middle: middle, overbought: overbought, oversold: oversold }; | |
else return { date: date, rsi: null, middle: null, overbought: null, oversold: null }; | |
} | |
},{}],18:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(indicatorMixin, accessor_ohlc) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
period = 10, | |
samples, | |
currentIndex, | |
total; | |
function indicator(data) { | |
indicator.init(); | |
return data.map(ma).filter(function(d) { return d.value; }); | |
} | |
indicator.init = function() { | |
total = 0; | |
samples = []; | |
currentIndex = 0; | |
return indicator; | |
}; | |
function ma(d, i) { | |
var value = indicator.average(p.accessor(d)); | |
if (i+1 < period) value = null; | |
return { date: p.accessor.d(d), value: value }; | |
} | |
indicator.average = function(value) { | |
total += value; | |
if(samples.length+1 < period) { | |
samples.push(value); | |
return total/++currentIndex; | |
} | |
else { | |
if(samples.length < period) { | |
samples.push(value); | |
total += value; | |
} | |
total -= samples[currentIndex]; | |
samples[currentIndex] = value; | |
if(++currentIndex === period) { | |
currentIndex = 0; | |
} | |
return total/period; | |
} | |
}; | |
indicator.period = function(_) { | |
if (!arguments.length) return period; | |
period = _; | |
return indicator; | |
}; | |
// Mixin 'superclass' methods and variables | |
indicatorMixin(indicator, p, accessor_ohlc()); | |
return indicator; | |
}; | |
}; | |
},{}],19:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(indicatorMixin, accessor_ohlc) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
period = 20, | |
periodD = 3, | |
overbought = 80, | |
middle = 50, | |
oversold = 20; | |
function indicator(data) { | |
return data.map(function(d, i) { | |
if(i >= period+periodD){ | |
var max = []; | |
var min = []; | |
var stochasticKBuffer = []; | |
for (var per = 0; per < periodD; per++) { | |
max.push(0); | |
min.push(10000); | |
stochasticKBuffer.push(0); | |
} | |
var stochasticD = 0; | |
for (var k = 0; k < periodD; k++) { | |
for (var j = 0; j < period; j++) { | |
if(data[i-j-k].high > max[k]){ | |
max[k] = data[i-j-k].high; | |
} | |
if(data[i-j-k].low < min[k]){ | |
min[k] = data[i-j-k].low; | |
} | |
} | |
stochasticKBuffer[k] = ((data[i-k].close-min[k])/(max[k]-min[k]))*100; | |
stochasticD +=stochasticKBuffer[k]; | |
} | |
var stochasticK =stochasticKBuffer[0];// ((d.close-min)/(max-min))*100; | |
stochasticD /= periodD; | |
return datum(p.accessor.d(d), stochasticK,stochasticD, middle, overbought, oversold); | |
} | |
else return datum(p.accessor.d(d)); | |
}).filter(function(d) { return d.stochasticK; }); | |
} | |
indicator.period = function(_) { | |
if (!arguments.length) return period; | |
period = _; | |
return indicator; | |
}; | |
indicator.periodD = function(_) { | |
if (!arguments.length) return periodD; | |
periodD = _; | |
return indicator; | |
}; | |
indicator.overbought = function(_) { | |
if (!arguments.length) return overbought; | |
overbought = _; | |
return indicator; | |
}; | |
indicator.middle = function(_) { | |
if (!arguments.length) return middle; | |
middle = _; | |
return indicator; | |
}; | |
indicator.oversold = function(_) { | |
if (!arguments.length) return oversold; | |
oversold = _; | |
return indicator; | |
}; | |
// Mixin 'superclass' methods and variables | |
indicatorMixin(indicator, p, accessor_ohlc()); | |
return indicator; | |
}; | |
}; | |
function datum(date, stochasticK,stochasticD, middle, overbought, oversold) { | |
if(stochasticK) return { date: date, stochasticK: stochasticK,stochasticD:stochasticD, middle: middle, overbought: overbought, oversold: oversold }; | |
else return { date: date, stochasticK: null,stochasticD:null, middle: null, overbought: null, oversold: null }; | |
} | |
},{}],20:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(indicatorMixin, accessor_ohlc) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}, // Container for private, direct access mixed in variables | |
period = 20, | |
overbought = 80, | |
middle = 50, | |
oversold = 20; | |
function indicator(data) { | |
return data.map(function(d, i) { | |
if(i >= period){ | |
var max = 0; | |
var maxi = 0; | |
var min = 10000; | |
var mini = 0; | |
for (var j = 0; j < period; j++) { | |
if(data[i-j].high > max){ | |
max = data[i-j].high; | |
maxi = j; | |
} | |
if(data[i-j].low < min){ | |
min = data[i-j].low; | |
mini = j; | |
} | |
} | |
var williams = ((data[i].close-min)/(max-min))*100; | |
return datum(p.accessor.d(d), williams, middle, overbought, oversold); | |
} | |
else return datum(p.accessor.d(d)); | |
}).filter(function(d) { return d.williams; }); | |
} | |
indicator.period = function(_) { | |
if (!arguments.length) return period; | |
period = _; | |
return indicator; | |
}; | |
indicator.overbought = function(_) { | |
if (!arguments.length) return overbought; | |
overbought = _; | |
return indicator; | |
}; | |
indicator.middle = function(_) { | |
if (!arguments.length) return middle; | |
middle = _; | |
return indicator; | |
}; | |
indicator.oversold = function(_) { | |
if (!arguments.length) return oversold; | |
oversold = _; | |
return indicator; | |
}; | |
// Mixin 'superclass' methods and variables | |
indicatorMixin(indicator, p, accessor_ohlc()); | |
return indicator; | |
}; | |
}; | |
function datum(date, williams, middle, overbought, oversold) { | |
if(williams) return { date: date, williams: williams, middle: middle, overbought: overbought, oversold: oversold }; | |
else return { date: date, williams: null, middle: null, overbought: null, oversold: null }; | |
} | |
},{}],21:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_aroon, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function aroon(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d); | |
group.entry.append('path').attr('class', 'overbought'); | |
group.entry.append('path').attr('class', 'oversold'); | |
group.entry.append('path').attr('class', 'aroon oscillator'); | |
group.entry.append('path').attr('class', 'aroon oscillatorArea'); | |
group.entry.append('path').attr('class', 'aroon middle'); | |
group.entry.append('path').attr('class', 'aroon up'); | |
group.entry.append('path').attr('class', 'aroon down'); | |
aroon.refresh(g); | |
} | |
aroon.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale, plot); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(aroon, p, accessor_aroon()); | |
return aroon; | |
}; | |
}; | |
function refresh(g, accessor, x, y, plot) { | |
g.selectAll('path.overbought').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.ob, y)); | |
g.selectAll('path.oversold').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.os, y)); | |
g.selectAll('path.aroon.oscillator').attr('d', plot.pathLine(accessor.d, x, accessor.oscillator, y)); | |
g.selectAll('path.aroon.oscillatorArea').attr('d', plot.pathArea(accessor.d, x, accessor.oscillator, y,0)); | |
g.selectAll('path.aroon.middle').attr('d', plot.pathLine(accessor.d, x, accessor.m, y)); | |
g.selectAll('path.aroon.up').attr('d', plot.pathLine(accessor.d, x, accessor.up, y)); | |
g.selectAll('path.aroon.down').attr('d', plot.pathLine(accessor.d, x, accessor.down, y)); | |
} | |
},{}],22:[function(_dereq_,module,exports){ | |
'use strict'; | |
/** | |
* TODO Refactor this to techan.plot.annotation.axis()? | |
*/ | |
module.exports = function(d3_svg_axis, plot) { // Injected dependencies | |
return function() { // Closure function | |
var axis = d3_svg_axis(), | |
format, | |
point = 4, | |
height = 14, | |
width = 50, | |
translate = [0, 0]; | |
function annotation(g) { | |
g.selectAll('g.translate').data(plot.dataMapper.array).enter() | |
.append('g').attr('class', 'translate'); | |
annotation.refresh(g); | |
} | |
annotation.refresh = function(g) { | |
var fmt = format ? format : (axis.tickFormat() ? axis.tickFormat() : axis.scale().tickFormat()); | |
refresh(g, plot, axis, fmt, height, width, point, translate); | |
}; | |
annotation.axis = function(_) { | |
if(!arguments.length) return axis; | |
axis = _; | |
return annotation; | |
}; | |
annotation.format = function(_) { | |
if(!arguments.length) return format; | |
format = _; | |
return annotation; | |
}; | |
annotation.height = function(_) { | |
if(!arguments.length) return height; | |
height = _; | |
return annotation; | |
}; | |
annotation.width = function(_) { | |
if(!arguments.length) return width; | |
width = _; | |
return annotation; | |
}; | |
annotation.translate = function(_) { | |
if(!arguments.length) return translate; | |
translate = _; | |
return annotation; | |
}; | |
return annotation; | |
}; | |
}; | |
function refresh(g, plot, axis, format, height, width, point, translate) { | |
var neg = axis.orient() === 'left' || axis.orient() === 'top' ? -1 : 1, | |
translateSelection = g.select('g.translate'), | |
dataGroup = plot.groupSelect(translateSelection, filterInvalidValues(axis.scale())); | |
dataGroup.entry.append('path'); | |
dataGroup.entry.append('text'); | |
translateSelection.attr('transform', 'translate(' + translate[0] + ',' + translate[1] + ')'); | |
dataGroup.selection.selectAll('path').attr('d', backgroundPath(axis, height, width, point, neg)); | |
dataGroup.selection.selectAll('text').text(textValue(format)).call(textAttributes, axis, neg); | |
} | |
function filterInvalidValues(scale) { | |
return function(data) { | |
var range = scale.range(), | |
start = range[0], | |
end = range[range.length - 1]; | |
range = start < end ? [start, end] : [end, start]; | |
return data.filter(function (d) { | |
if (!d.value) return false; | |
var value = scale(d.value); | |
return value && !isNaN(value) && range[0] <= value && value <= range[1]; | |
}); | |
}; | |
} | |
function textAttributes(text, axis, neg) { | |
var scale = axis.scale(); | |
switch(axis.orient()) { | |
case 'left': | |
case 'right': | |
text.attr({ | |
x: neg*(Math.max(axis.innerTickSize(), 0) + axis.tickPadding()), | |
y: textPosition(scale), | |
dy: '.32em' | |
}).style('text-anchor', neg < 0 ? 'end' : 'start'); | |
break; | |
case 'top': | |
case 'bottom': | |
text.attr({ | |
x: textPosition(scale), | |
y: neg*(Math.max(axis.innerTickSize(), 0) + axis.tickPadding()), | |
dy: neg < 0 ? '0em' : '.72em' | |
}).style('text-anchor', 'middle'); | |
break; | |
} | |
} | |
function textPosition(scale) { | |
return function(d) { | |
return scale(d.value); | |
}; | |
} | |
function textValue(format) { | |
return function(d) { | |
return format(d.value); | |
}; | |
} | |
function backgroundPath(axis, height, width, point, neg) { | |
return function(d) { | |
var scale = axis.scale(), | |
value = scale(d.value), | |
pt = point; | |
switch(axis.orient()) { | |
case 'left': | |
case 'right': | |
var h = 0; | |
if(height/2 < point) pt = height/2; | |
else h = height/2-point; | |
return [ | |
'M', 0, value, | |
'l', neg*axis.innerTickSize(), -pt, | |
'l', 0, -h, | |
'l', neg*width, 0, | |
'l', 0, height, | |
'l', neg*-width, 0, | |
'l', 0, -h | |
].join(' '); | |
case 'top': | |
case 'bottom': | |
var w = 0; | |
if(width/2 < point) pt = width/2; | |
else w = width/2-point; | |
return [ | |
'M', value, 0, | |
'l', -pt, neg*axis.innerTickSize(), | |
'l', -w, 0, | |
'l', 0, neg*height, | |
'l', width, 0, | |
'l', 0, neg*-height, | |
'l', -w, 0 | |
].join(' '); | |
default: throw "Unsupported axis.orient() = " + axis.orient(); | |
} | |
}; | |
} | |
},{}],23:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_scale_linear, d3_extent, accessor_ohlc, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure constructor | |
var p = {}, // Container for private, direct access mixed in variables | |
volumeOpacity = false; | |
function candlestick(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d); | |
// Two path's as wick and body can be styled slightly differently (stroke and fills) | |
group.entry.append('path').attr('class', 'candle body'); | |
group.entry.append('path').attr('class', 'candle wick'); | |
candlestick.refresh(g); | |
} | |
candlestick.refresh = function(g) { | |
if(volumeOpacity) opacity(g, d3_scale_linear, d3_extent, p.accessor.v); | |
refresh(g, plot, p.accessor, p.xScale, p.yScale); | |
}; | |
candlestick.volumeOpacity = function(_) { | |
if (!arguments.length) return volumeOpacity; | |
volumeOpacity = _; | |
return candlestick; | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(candlestick, p, accessor_ohlc()); | |
return candlestick; | |
}; | |
}; | |
function refresh(g, plot, accessor, x, y) { | |
g.selectAll('path.candle.body').attr('d', bodyPath(accessor, x, y)).classed(plot.classedUpDown(accessor)); | |
g.selectAll('path.candle.wick').attr('d', wickPath(accessor, x, y)).classed(plot.classedUpDown(accessor)); | |
} | |
function bodyPath(accessor, x, y) { | |
return function(d) { | |
var path = [], | |
open = y(accessor.o(d)), | |
close = y(accessor.c(d)), | |
rangeBand = x.band(), | |
xValue = x(accessor.d(d)) - rangeBand/2; | |
path.push( | |
'M', xValue, open, | |
'l', rangeBand, 0 | |
); | |
// Draw body only if there is a body (there is no stroke, so will not appear anyway) | |
if(open != close) { | |
path.push( | |
'L', xValue + rangeBand, close, | |
'l', -rangeBand, 0, | |
'L', xValue, open | |
); | |
} | |
return path.join(' '); | |
}; | |
} | |
function wickPath(accessor, x, y) { | |
return function(d) { | |
var path = [], | |
open = y(accessor.o(d)), | |
close = y(accessor.c(d)), | |
rangeBand = x.band(), | |
xPoint = x(accessor.d(d)), | |
xValue = xPoint - rangeBand/2; | |
// Top | |
path.push( | |
'M', xPoint, y(accessor.h(d)), | |
'L', xPoint, Math.min(open, close) | |
); | |
// Draw another cross wick if there is no body | |
if(open == close) { | |
path.push( | |
'M', xValue, open, | |
'l', rangeBand, 0 | |
); | |
} | |
// Bottom | |
path.push( | |
'M', xPoint, Math.max(open, close), | |
'L', xPoint, y(accessor.l(d)) | |
); | |
return path.join(' '); | |
}; | |
} | |
function opacity(g, d3_scale_linear, d3_extent, accessor_volume) { | |
var selection = g.selectAll('g.data'), | |
volumeOpacityScale = d3_scale_linear() | |
.domain(d3_extent(selection.data().map(accessor_volume).filter(function(d) { return !isNaN(d); }))) | |
.range([0.2, 1]); | |
selection.selectAll('path.candle').style('opacity', function(d) { | |
var volume = accessor_volume(d); | |
return isNaN(volume) ? null : volumeOpacityScale(volume); | |
}); | |
} | |
},{}],24:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_select, d3_event, d3_mouse, axisannotation) { // Injected dependencies | |
return function() { // Closure function | |
var xAnnotation = [axisannotation()], | |
yAnnotation = [axisannotation()], | |
verticalWireRange, | |
horizontalWireRange; | |
function crosshair(g) { | |
var group = g.selectAll('g.data').data([0]), | |
groupEnter = group.enter().append('g').attr('class', 'data').call(display, 'none'); | |
groupEnter.append('path').attr('class', 'horizontal wire'); | |
groupEnter.append('path').attr('class', 'vertical wire'); | |
appendAnnotation(group, groupEnter, d3_select, ['axisannotation', 'x'], xAnnotation); | |
appendAnnotation(group, groupEnter, d3_select, ['axisannotation', 'y'], yAnnotation); | |
g.selectAll('rect').data([0]).enter() | |
.append('rect').style({ fill: 'none', 'pointer-events': 'all'}); | |
crosshair.refresh(g); | |
} | |
crosshair.refresh = function(g) { | |
var xRange = xAnnotation[0].axis().scale().range(), | |
yRange = yAnnotation[0].axis().scale().range(), | |
group = g.selectAll('g.data'), | |
mouseSelection = g.selectAll('rect'); | |
mouseSelection.attr({ | |
x: Math.min(xRange[0], xRange[xRange.length-1]), | |
y: Math.min(yRange[0], yRange[yRange.length-1]), | |
height: Math.abs(yRange[yRange.length-1] - yRange[0]), | |
width: Math.abs(xRange[xRange.length-1] - xRange[0]) | |
}) | |
.on('mouseenter', display(g, 'inline')) | |
.on('mouseout', display(g, 'none')) | |
.on('mousemove', mousemoveRefresh(group, d3_select, d3_mouse, xAnnotation, yAnnotation, verticalWireRange, horizontalWireRange)); | |
refresh(d3_select, xAnnotation, yAnnotation, | |
g.select('path.vertical'), g.select('path.horizontal'), | |
g.selectAll('g.axisannotation.x > g'), g.selectAll('g.axisannotation.y > g'), | |
verticalWireRange, horizontalWireRange | |
); | |
}; | |
crosshair.xAnnotation = function(_) { | |
if(!arguments.length) return xAnnotation; | |
xAnnotation = _ instanceof Array ? _ : [_]; | |
return crosshair; | |
}; | |
crosshair.yAnnotation = function(_) { | |
if(!arguments.length) return yAnnotation; | |
yAnnotation = _ instanceof Array ? _ : [_]; | |
return crosshair; | |
}; | |
crosshair.verticalWireRange = function(_) { | |
if(!arguments.length) return verticalWireRange; | |
verticalWireRange = _; | |
return crosshair; | |
}; | |
crosshair.horizontalWireRange = function(_) { | |
if(!arguments.length) return horizontalWireRange; | |
horizontalWireRange = _; | |
return crosshair; | |
}; | |
return crosshair; | |
}; | |
}; | |
function display(g, style) { | |
return function() { | |
g.selectAll('g.data').style('display', style); | |
}; | |
} | |
function mousemoveRefresh(group, d3_select, d3_mouse, xAnnotation, yAnnotation, verticalWireRange, horizontalWireRange) { | |
return function() { | |
var coords = d3_mouse(this), | |
x = xAnnotation[0].axis().scale(), | |
y = yAnnotation[0].axis().scale(); | |
refresh(d3_select, xAnnotation, yAnnotation, | |
group.select('path.vertical').datum(x.invert(coords[0])), | |
group.select('path.horizontal').datum(y.invert(coords[1])), | |
group.selectAll('g.axisannotation.x > g').each(updateAnnotationValue(xAnnotation, coords[0])), | |
group.selectAll('g.axisannotation.y > g').each(updateAnnotationValue(yAnnotation, coords[1])), | |
verticalWireRange, horizontalWireRange | |
); | |
}; | |
} | |
function refresh(d3_select, xAnnotation, yAnnotation, xPath, yPath, | |
xAnnotationSelection, yAnnotationSelection, | |
verticalWireRange, horizontalWireRange) { | |
var x = xAnnotation[0].axis().scale(), | |
y = yAnnotation[0].axis().scale(); | |
xPath.attr('d', verticalPathLine(x, verticalWireRange || y.range())); | |
yPath.attr('d', horizontalPathLine(y, horizontalWireRange || x.range())); | |
xAnnotationSelection.each(refreshAnnotation(d3_select, xAnnotation)); | |
yAnnotationSelection.each(refreshAnnotation(d3_select, yAnnotation)); | |
} | |
function horizontalPathLine(y, range) { | |
return function(d) { | |
if(!d) return "M 0 0"; | |
var value = y(d); | |
return ['M', range[0], value, 'L', range[range.length-1], value].join(' '); | |
}; | |
} | |
function verticalPathLine(x, range) { | |
return function(d) { | |
if(!d) return "M 0 0"; | |
var value = x(d); | |
return ['M', value, range[0], 'L', value, range[range.length-1]].join(' '); | |
}; | |
} | |
function updateAnnotationValue(annotations, value) { | |
return function(d, i) { | |
// d[0] because only ever 1 value for crosshairs | |
d[0].value = annotations[i].axis().scale().invert(value); | |
}; | |
} | |
function appendAnnotation(selection, selectionEnter, d3_select, classes, annotation) { | |
selectionEnter.append('g').attr('class', classes.join(' ')); | |
var annotationSelection = selection.select('g.' + classes.join('.')).selectAll('g') | |
.data(annotation.map(function() { return [{ value: null }]; })); | |
annotationSelection.exit().remove(); | |
annotationSelection.enter().append('g').attr('class', function(d, i) { return i; }) | |
.each(function(d, i) { annotation[i](d3_select(this)); }); | |
} | |
function refreshAnnotation(d3_select, annotation) { | |
return function(d, i) { | |
annotation[i].refresh(d3_select(this)); | |
}; | |
} | |
},{}],25:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3) { | |
var scale = _dereq_('../scale')(d3), | |
accessor = _dereq_('../accessor')(), | |
plot = _dereq_('./plot')(d3), | |
plotMixin = _dereq_('./plotmixin')(d3.scale.linear, scale.financetime), | |
line = _dereq_('./line'), | |
axisannotation = _dereq_('./axisannotation')(d3.svg.axis, plot); | |
return { | |
axisannotation: axisannotation, | |
candlestick: _dereq_('./candlestick')(d3.scale.linear, d3.extent, accessor.ohlc, plot, plotMixin), | |
crosshair: _dereq_('./crosshair')(d3.select, d3_event, d3.mouse, axisannotation), | |
ema: line(accessor.value, plot, plotMixin), | |
ohlc: _dereq_('./ohlc')(d3.scale.linear, d3.extent, accessor.ohlc, plot, plotMixin), | |
close: line(accessor.ohlc, plot, plotMixin), | |
volume: _dereq_('./volume')(accessor.volume, plot, plotMixin), | |
rsi: _dereq_('./rsi')(accessor.rsi, plot, plotMixin), | |
aroon: _dereq_('./aroon')(accessor.aroon, plot, plotMixin), | |
stochastic: _dereq_('./stochastic')(accessor.stochastic, plot, plotMixin), | |
williams: _dereq_('./williams')(accessor.williams, plot, plotMixin), | |
macd: _dereq_('./macd')(accessor.macd, plot, plotMixin), | |
momentum: line(accessor.value, plot, plotMixin, true), | |
moneyflow: line(accessor.value, plot, plotMixin, true), | |
sma: line(accessor.value, plot, plotMixin), | |
supstance: _dereq_('./supstance')(d3.behavior.drag, d3_event, d3.select, accessor.value, plot, plotMixin), | |
trendline: _dereq_('./trendline')(d3.behavior.drag, d3_event, d3.select, accessor.trendline, plot, plotMixin) | |
}; | |
}; | |
function d3_event() { | |
return d3.event; | |
} | |
},{"../accessor":3,"../scale":38,"./aroon":21,"./axisannotation":22,"./candlestick":23,"./crosshair":24,"./line":26,"./macd":27,"./ohlc":28,"./plot":29,"./plotmixin":30,"./rsi":31,"./stochastic":32,"./supstance":33,"./trendline":34,"./volume":35,"./williams":36}],26:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_value, plot, plotMixin, showZero) { // Injected dependencies | |
showZero = showZero || false; | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function line(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.array); | |
group.entry.append('path').attr('class', 'line'); | |
if(showZero) { | |
group.selection.append('path').attr('class', 'zero'); | |
} | |
line.refresh(g); | |
} | |
line.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale, plot, showZero); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(line, p, accessor_value()); | |
return line; | |
}; | |
}; | |
function refresh(g, accessor, x, y, plot, showZero) { | |
g.selectAll('path.line').attr('d', plot.pathLine(accessor.d, x, accessor, y)); | |
if(showZero) { | |
g.selectAll('path.zero').attr('d', plot.horizontalPathLine(x, accessor.z, y)); | |
} | |
} | |
},{}],27:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_macd, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function macd(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d); | |
var histogramSelection = group.selection | |
.append('g').attr('class', 'difference') | |
.selectAll('g.difference').data(function(data) { return data; }); | |
histogramSelection.exit().remove(); | |
histogramSelection.enter().append('path').attr('class', 'difference'); | |
group.selection.append('path').attr('class', 'zero'); | |
group.selection.append('path').attr('class', 'macd'); | |
group.selection.append('path').attr('class', 'signal'); | |
macd.refresh(g); | |
} | |
macd.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale, plot); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(macd, p, accessor_macd()); | |
return macd; | |
}; | |
}; | |
function refresh(g, accessor, x, y, plot) { | |
g.selectAll('path.difference').attr('d', differencePath(accessor, x, y)); | |
g.selectAll('path.zero').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.z, y)); | |
g.selectAll('path.macd').attr('d', plot.pathLine(accessor.d, x, accessor.m, y)); | |
g.selectAll('path.signal').attr('d', plot.pathLine(accessor.d, x, accessor.s, y)); | |
} | |
function differencePath(accessor, x, y) { | |
return function(d) { | |
var zero = y(0), | |
height = y(accessor.dif(d)) - zero, | |
rangeBand = x.band(), | |
xValue = x(accessor.d(d)) - rangeBand/2; | |
return [ | |
'M', xValue, zero, | |
'l', 0, height, | |
'l', rangeBand, 0, | |
'l', 0, -height | |
].join(' '); | |
}; | |
} | |
},{}],28:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_scale_linear, d3_extent, accessor_ohlc, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure constructor | |
var p = {}; // Container for private, direct access mixed in variables | |
function ohlc(g) { | |
plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d) | |
.entry.append('path').attr({ class: 'ohlc' }); | |
ohlc.refresh(g); | |
} | |
ohlc.refresh = function(g) { | |
refresh(g, plot, p.accessor, p.xScale, p.yScale); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(ohlc, p, accessor_ohlc()); | |
return ohlc; | |
}; | |
}; | |
function refresh(g, plot, accessor, x, y) { | |
g.selectAll('path.ohlc').attr({ d: ohlcPath(accessor, x, y) }).classed(plot.classedUpDown(accessor)); | |
} | |
function ohlcPath(accessor, x, y) { | |
return function(d) { | |
var open = y(accessor.o(d)), | |
close = y(accessor.c(d)), | |
rangeBand = x.band(), | |
xPoint = x(accessor.d(d)), | |
xValue = xPoint - rangeBand/2; | |
return [ | |
'M', xValue, open, | |
'l', rangeBand/2, 0, | |
'M', xPoint, y(accessor.h(d)), | |
'L', xPoint, y(accessor.l(d)), | |
'M', xPoint, close, | |
'l', rangeBand/2, 0 | |
].join(' '); | |
}; | |
} | |
},{}],29:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3) { | |
function dataSelection(g, dataMapper, accessor_date) { | |
var selection = g.selectAll('g.data').data(dataMapper, accessor_date); | |
selection.exit().remove(); | |
return selection; | |
} | |
function dataEntry(dataSelection) { | |
return dataSelection.enter().append('g').attr('class', 'data'); | |
} | |
return { | |
dataMapper: { | |
unity: function(d) { return d; }, | |
array: function(d) { return [d]; } | |
}, | |
dataSelection: dataSelection, | |
dataEntry: dataEntry, | |
groupSelect: function(g, dataMapper, accessor_date) { | |
var selection = dataSelection(g, dataMapper, accessor_date), | |
entry = dataEntry(selection); | |
return { | |
selection: selection, | |
entry: entry | |
}; | |
}, | |
classedUpDown: function(accessor) { | |
return { | |
up: function(d) { return accessor.o(d) < accessor.c(d); }, | |
down: function(d) { return accessor.o(d) > accessor.c(d); } | |
}; | |
}, | |
horizontalPathLine: function(accessor_date, x, accessor_value, y) { | |
return function(d) { | |
var firstDatum = d[0], | |
lastDatum = d[d.length-1]; | |
return [ | |
'M', x(accessor_date(firstDatum)), y(accessor_value(firstDatum)), | |
'L', x(accessor_date(lastDatum)), y(accessor_value(lastDatum)) | |
].join(' '); | |
}; | |
}, | |
pathLine: function(accessor_date, x, accessor_value, y) { | |
return d3.svg.line().interpolate('monotone') | |
.x(function(d) { return x(accessor_date(d)); } ) | |
.y(function(d) { return y(accessor_value(d)); } ); | |
}, | |
pathArea: function(accessor_date, x, accessor_value, y, yBase) { | |
return d3.svg.area().interpolate('monotone') | |
.x(function(d) { return x(accessor_date(d)); } ) | |
.y0(function(d) { return y(yBase);}) | |
.y1(function(d) { return y(accessor_value(d)); } ); | |
} | |
}; | |
}; | |
},{}],30:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_scale_linear, techan_scale_financetime) { | |
function plotMixin(source, priv, accessor) { | |
var xScale = techan_scale_financetime(), | |
yScale = d3_scale_linear(); | |
// Mixin the functions to the source | |
source.accessor = function(_) { | |
if (!arguments.length) return accessor; | |
accessor = _; | |
return bind(); | |
}; | |
source.xScale = function(_) { | |
if (!arguments.length) return xScale; | |
xScale = _; | |
return bind(); | |
}; | |
source.yScale = function(_) { | |
if (!arguments.length) return yScale; | |
yScale = _; | |
return bind(); | |
}; | |
// Add in the private, direct access variables | |
function bind() { | |
priv.xScale = xScale; | |
priv.yScale = yScale; | |
priv.accessor = accessor; | |
return source; | |
} | |
bind(); | |
} | |
return plotMixin; | |
}; | |
},{}],31:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_rsi, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function rsi(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d); | |
group.entry.append('path').attr('class', 'overbought'); | |
group.entry.append('path').attr('class', 'middle'); | |
group.entry.append('path').attr('class', 'oversold'); | |
group.entry.append('path').attr('class', 'rsi'); | |
rsi.refresh(g); | |
} | |
rsi.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale, plot); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(rsi, p, accessor_rsi()); | |
return rsi; | |
}; | |
}; | |
function refresh(g, accessor, x, y, plot) { | |
g.selectAll('path.overbought').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.ob, y)); | |
g.selectAll('path.middle').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.m, y)); | |
g.selectAll('path.oversold').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.os, y)); | |
g.selectAll('path.rsi').attr('d', plot.pathLine(accessor.d, x, accessor.r, y)); | |
} | |
},{}],32:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_stochastic, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function stochastic(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d); | |
group.entry.append('path').attr('class', 'overbought'); | |
group.entry.append('path').attr('class', 'oversold'); | |
group.entry.append('path').attr('class', 'stochastic up'); | |
group.entry.append('path').attr('class', 'stochastic down'); | |
stochastic.refresh(g); | |
} | |
stochastic.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale, plot); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(stochastic, p, accessor_stochastic()); | |
return stochastic; | |
}; | |
}; | |
function refresh(g, accessor, x, y, plot) { | |
g.selectAll('path.overbought').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.ob, y)); | |
g.selectAll('path.oversold').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.os, y)); | |
g.selectAll('path.stochastic.up').attr('d', plot.pathLine(accessor.d, x, accessor.k, y)); | |
g.selectAll('path.stochastic.down').attr('d', plot.pathLine(accessor.d, x, accessor.sd, y)); | |
} | |
},{}],33:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_behavior_drag, d3_event, d3_select, accessor_value, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function supstance(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.unity); | |
group.entry.append('path').attr('class', 'supstance'); | |
group.entry.append('g').attr('class', 'interaction').style({ opacity: 0, fill: 'none' }) | |
.append('path').style('stroke-width', 16); | |
supstance.refresh(g); | |
} | |
supstance.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale); | |
}; | |
supstance.drag = function(g) { | |
g.selectAll('.interaction path') | |
.call(dragBody(d3_behavior_drag, d3_event, d3_select, p.accessor, p.xScale, p.yScale)); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(supstance, p, accessor_value()); | |
return supstance; | |
}; | |
}; | |
function refresh(g, accessor, x, y) { | |
g.selectAll('path.supstance').attr('d', supstancePath(accessor, x, y)); | |
g.selectAll('.interaction path').attr('d', supstancePath(accessor, x, y)); | |
} | |
function supstancePath(accessor, x, y) { | |
return function(d) { | |
var path = [], | |
range = x.range(); | |
path.push('M', range[0], y(accessor.v(d))); | |
path.push('L', range[range.length-1], y(accessor.v(d))); | |
return path.join(' '); | |
}; | |
} | |
function dragBody(d3_behavior_drag, d3_event, d3_select, accessor, x, y) { | |
return d3_behavior_drag() | |
.origin(function(d) { | |
// TODO Fire listeners | |
return { x: 0, y: y(accessor.v(d)) }; | |
}) | |
.on('drag', function(d) { | |
accessor.v(d, y.invert(d3_event().y)); | |
refresh(d3_select(this.parentNode.parentNode), accessor, x, y); | |
// TODO Fire listeners | |
}); | |
} | |
},{}],34:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3_behavior_drag, d3_event, d3_select, accessor_trendline, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function trendline(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.unity); | |
group.entry.append('path').attr('class', 'trendline'); | |
var interaction = group.entry.append('g').attr('class', 'interaction').style({ opacity: 0, fill: 'none' }); | |
interaction.append('path').attr('class', 'body').style('stroke-width', 16); | |
interaction.append('circle').attr({ class: 'start', r: 8 }); | |
interaction.append('circle').attr({ class: 'end', r: 8 }); | |
trendline.refresh(g); | |
} | |
trendline.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale); | |
}; | |
trendline.drag = function(g) { | |
// TODO Emit events | |
g.selectAll('.interaction circle.start') | |
.call(dragEnd(d3_behavior_drag, d3_event, d3_select, p.accessor, p.accessor.sd, p.xScale, p.accessor.sv, p.yScale)); | |
g.selectAll('.interaction circle.end') | |
.call(dragEnd(d3_behavior_drag, d3_event, d3_select, p.accessor, p.accessor.ed, p.xScale, p.accessor.ev, p.yScale)); | |
g.selectAll('.interaction path.body') | |
.call(dragBody(d3_behavior_drag, d3_event, d3_select, p.accessor, p.xScale, p.yScale)); | |
}; | |
// TODO Add mouseover, mouseout listener support for drag/user update events | |
// Mixin 'superclass' methods and variables | |
plotMixin(trendline, p, accessor_trendline()); | |
return trendline; | |
}; | |
}; | |
function refresh(g, accessor, x, y) { | |
g.selectAll('path.trendline').attr('d', trendlinePath(accessor, x, y)); | |
g.selectAll('.interaction path.body').attr('d', trendlinePath(accessor, x, y)); | |
g.selectAll('.interaction circle.start').attr(interactionEnds(accessor.sd, x, accessor.sv, y)); | |
g.selectAll('.interaction circle.end').attr(interactionEnds(accessor.ed, x, accessor.ev, y)); | |
} | |
function trendlinePath(accessor, x, y) { | |
return function(d) { | |
var path = []; | |
path.push('M', x(accessor.sd(d)), y(accessor.sv(d))); | |
path.push('L', x(accessor.ed(d)), y(accessor.ev(d))); | |
return path.join(' '); | |
}; | |
} | |
function interactionEnds(accessor_x, x, accessor_y, y) { | |
return { | |
cx: function(d) { return x(accessor_x(d)); }, | |
cy: function(d) { return y(accessor_y(d)); } | |
}; | |
} | |
function dragEnd(d3_behavior_drag, d3_event, d3_select, accessor, accessor_x, x, accessor_y, y) { | |
return d3_behavior_drag() | |
.origin(function(d) { | |
// TODO Fire listeners dragstart | |
return { x: x(accessor_x(d)), y: y(accessor_y(d)) }; | |
}) | |
.on('drag', function(d) { | |
updateEnd(accessor_x, x, d3_event().x, accessor_y, y, d3_event().y, d); | |
refresh(d3_select(this.parentNode.parentNode), accessor, x, y); | |
// TODO Fire listeners dragging | |
}); | |
} | |
function dragBody(d3_behavior_drag, d3_event, d3_select, accessor, x, y) { | |
var dragStart = {}; // State information, grabs the start coords of the line | |
return d3_behavior_drag() | |
.origin(function(d) { | |
dragStart.start = { date: x(accessor.sd(d)), value: y(accessor.sv(d)) }; | |
dragStart.end = { date: x(accessor.ed(d)), value: y(accessor.ev(d)) }; | |
// TODO Fire listeners | |
return { x: 0, y: 0 }; | |
}) | |
.on('drag', function(d) { | |
updateEnd(accessor.sd, x, d3_event().x + dragStart.start.date, | |
accessor.sv, y, d3_event().y + dragStart.start.value, | |
d); | |
updateEnd(accessor.ed, x, d3_event().x + dragStart.end.date, | |
accessor.ev, y, d3_event().y + dragStart.end.value, | |
d); | |
refresh(d3_select(this.parentNode.parentNode), accessor, x, y); | |
// TODO Fire listeners | |
}); | |
} | |
function updateEnd(accessor_x, x, xValue, accessor_y, y, yValue, d) { | |
var date = x.invert(xValue); | |
if(date) accessor_x(d, date); | |
accessor_y(d, y.invert(yValue)); | |
} | |
},{}],35:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_volume, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function volume(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d) | |
.entry.append('path') | |
.attr('class', 'volume'); | |
if(p.accessor.o && p.accessor.c) { | |
group.classed(plot.classedUpDown(p.accessor)); | |
} | |
volume.refresh(g); | |
} | |
volume.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(volume, p, accessor_volume()); | |
return volume; | |
}; | |
}; | |
function refresh(g, accessor, x, y) { | |
g.selectAll('path.volume').attr('d', volumePath(accessor, x, y)); | |
} | |
function volumePath(accessor, x, y) { | |
return function(d) { | |
var vol = accessor.v(d); | |
if(isNaN(vol)) return null; | |
var zero = y(0), | |
height = y(vol) - zero, | |
rangeBand = x.band(), | |
xValue = x(accessor.d(d)) - rangeBand/2; | |
return [ | |
'M', xValue, zero, | |
'l', 0, height, | |
'l', rangeBand, 0, | |
'l', 0, -height | |
].join(' '); | |
}; | |
} | |
},{}],36:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(accessor_williams, plot, plotMixin) { // Injected dependencies | |
return function() { // Closure function | |
var p = {}; // Container for private, direct access mixed in variables | |
function williams(g) { | |
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d); | |
group.entry.append('path').attr('class', 'williams up'); | |
williams.refresh(g); | |
} | |
williams.refresh = function(g) { | |
refresh(g, p.accessor, p.xScale, p.yScale, plot); | |
}; | |
// Mixin 'superclass' methods and variables | |
plotMixin(williams, p, accessor_williams()); | |
return williams; | |
}; | |
}; | |
function refresh(g, accessor, x, y, plot) { | |
g.selectAll('path.williams.up').attr('d', plot.pathLine(accessor.d, x, accessor.w, y)); | |
} | |
},{}],37:[function(_dereq_,module,exports){ | |
'use strict'; | |
/* | |
Finance time scale which is not necessarily continuous, is required to be plot continuous. Finance scale | |
generally contains data points on days where a market is open but no points when closed, such as weekday | |
and weekends respectively. When plot, is done so without weekend gaps. | |
*/ | |
module.exports = function(d3_scale_linear, d3_time, d3_bisect, techan_util_rebindCallback, widen, zoomable) { // Injected dependencies | |
function financetime(index, domain) { | |
var dateIndexMap, | |
tickState = { tickFormat: dailyTickMethod[dailyTickMethod.length-1][2] }, | |
band = 3; | |
index = index || d3_scale_linear(); | |
domain = domain || [new Date(0), new Date(1)]; | |
/** | |
* Scales the value to domain. If the value is not within the domain, will currently brutally round the data: | |
* - If before min domain, will round to 1 index value before min domain | |
* - If after max domain, will round to 1 index value after min domain | |
* - If within domain, but not mapped to domain value, uses d3.bisect to find nearest domain index | |
* | |
* This logic was not required until the domain was being updated and scales re-rendered and this line | |
* https://github.com/mbostock/d3/blob/abbe1c75c16c3e9cb08b1d0872f4a19890d3bb58/src/svg/axis.js#L107 was causing error. | |
* New scale generated ticks that old scale did not have, causing error during transform. To avoid error this logic | |
* was added. | |
* | |
* @param x The value to scale | |
* @returns {*} | |
*/ | |
function scale(x) { | |
var mappedIndex = dateIndexMap[+x]; | |
// Make sure the value has been mapped, if not, determine if it's just before, round in, or just after domain | |
if(mappedIndex === undefined) { | |
if(domain[0] > x) mappedIndex = -1; // Less than min, round just out of domain | |
else mappedIndex = d3_bisect(domain, x); // else let bisect determine where in or just after than domain it is | |
} | |
return index(mappedIndex); | |
} | |
scale.invert = function(y) { | |
var i = scale.invertToIndex(y); | |
return i === null ? null : domain[i]; | |
}; | |
scale.invertToIndex = function(y) { | |
var i = Math.round(index.invert(y)); | |
return domain[i] ? Math.abs(i) : null; | |
}; | |
/** | |
* As the underlying structure relies on a full array, ensure the full domain is passed here, | |
* not just min and max values. | |
* | |
* @param _ The full domain array | |
* @returns {*} | |
*/ | |
scale.domain = function(_) { | |
if (!arguments.length) { | |
var visible = index.domain(); | |
visible = [ | |
Math.ceil(visible[0]), // If min is fraction, it is partially out of view, round up (ceil) | |
Math.floor(visible[visible.length-1]) // If max is fraction, is partially out of view, round down (floor) | |
]; | |
return domain.slice(visible[0], visible[visible.length-1]+1); // Grab visible domain, inclusive | |
} | |
domain = _; | |
domainMap(); | |
index.domain([0, domain.length-1]); | |
zoomed(); | |
// Widen the outer edges by pulling the domain in to ensure start and end bands are fully visible | |
index.domain(index.range().map(widen(0.65, band)).map(index.invert)); | |
return zoomed(); | |
}; | |
function zoomed() { | |
band = rangeBand(index, domain); | |
return scale; | |
} | |
function domainMap() { | |
dateIndexMap = lookupIndex(domain); | |
} | |
scale.copy = function() { | |
return financetime(index.copy(), domain); | |
}; | |
/** | |
* Equivalent to d3's ordinal.rangeBand(). It could not be named rangeBand as d3 uses the method | |
* to determine how axis ticks should be rendered. This scale is a hybrid ordinal and linear scale, | |
* such that scale(x) returns y at center of the band as does d3.scale.linear()(x) does, whereas | |
* d3.scale.ordinal()(x) returns y at the beginning of the band. When rendering svg axis, d3 | |
* compensates for this checking if rangeBand is defined and compensates as such. | |
* @returns {number} | |
*/ | |
scale.band = function() { | |
return band; | |
}; | |
scale.zoomable = function() { | |
return zoomable(index, zoomed); | |
}; | |
/* | |
* Ticks based heavily on d3 implementation. Attempted to implement this using composition with d3.time.scale, | |
* but in the end there were sufficient differences to 'roll my own'. | |
* - Different base tick steps: millis not required (yet!) | |
* - State based tick formatting given the non continuous, even steps of ticks | |
* - Supporting daily and intraday continuous (no gaps) plotting | |
* https://github.com/mbostock/d3/blob/e03b6454294e1c0bbe3125f787df56c468658d4e/src/time/scale.js#L67 | |
*/ | |
/** | |
* Generates ticks as continuous as possible against the underlying domain. Where continuous time ticks | |
* fall on where there is no matching domain (such as weekend or holiday day), it will be replaced with | |
* the nearest domain datum ahead of the tick to keep close to continuous. | |
* @param interval | |
* @param steps | |
* @returns {*} | |
*/ | |
scale.ticks = function(interval, steps) { | |
var visibleDomain = scale.domain(); | |
if(!visibleDomain.length) return []; // Nothing is visible, no ticks to show | |
var method = interval === undefined ? tickMethod(visibleDomain, 10) : | |
typeof interval === 'number' ? tickMethod(visibleDomain, interval) : null; | |
tickState.tickFormat = method ? method[2] : tickMethod(visibleDomain, 10)[2]; | |
if(method) { | |
interval = method[0]; | |
steps = method[1]; | |
} | |
var intervalRange = interval.range(visibleDomain[0], +visibleDomain[visibleDomain.length-1]+1, steps); | |
return intervalRange // Interval, possibly contains values not in domain | |
.map(domainTicks(visibleDomain)) // Line up interval ticks with domain, possibly adding duplicates | |
.reduce(sequentialDuplicates, []); // Filter out duplicates, produce new 'reduced' array | |
}; | |
/** | |
* NOTE: The type of tick format returned is dependant on ticks that were generated. To obtain the correct | |
* format for ticks, ensure ticks function is called first, otherwise a default tickFormat will be returned | |
* which may not be the optimal representation of the current domain state. | |
* @returns {Function} | |
*/ | |
scale.tickFormat = function() { | |
return function(date) { | |
return tickState.tickFormat(date); | |
}; | |
}; | |
techan_util_rebindCallback(scale, index, zoomed, 'range', 'interpolate', 'clamp', 'nice'); | |
domainMap(); | |
return zoomed(); | |
} | |
function rangeBand(linear, domain) { | |
return (Math.abs(linear(domain.length-1) - linear(0))/Math.max(1, domain.length-1))*0.8; | |
} | |
var dayFormat = d3_time.format('%b %e'), | |
yearFormat = d3_time.format.multi([ | |
['%b %Y', function(d) { return d.getMonth(); }], | |
['%Y', function() { return true; }] | |
]), | |
intraDayFormat = d3_time.format.multi([ | |
[":%S", function(d) { return d.getSeconds(); }], | |
["%I:%M", function(d) { return d.getMinutes(); }], | |
["%I %p", function(d) { return d.getHours(); }] | |
]), | |
genericTickMethod = [d3_time.second, 1, d3_time.format.multi([ | |
[":%S", function(d) { return d.getSeconds(); }], | |
["%I:%M", function(d) { return d.getMinutes(); }], | |
["%I %p", function(d) { return d.getHours(); }], | |
['%b %e', function() { return true; }] | |
]) | |
]; | |
var dailyStep = 864e5, | |
dailyTickSteps = [ | |
dailyStep, // 1-day | |
6048e5, // 1-week | |
2592e6, // 1-month | |
7776e6, // 3-month | |
31536e6 // 1-year | |
]; | |
var dailyTickMethod = [ | |
[d3_time.day, 1, dayFormat], | |
[d3_time.monday, 1, dayFormat], | |
[d3_time.month, 1, yearFormat], | |
[d3_time.month, 3, yearFormat], | |
[d3_time.year, 1, yearFormat] | |
]; | |
var intraDayTickSteps = [ | |
1e3, // 1-second | |
5e3, // 5-second | |
15e3, // 15-second | |
3e4, // 30-second | |
6e4, // 1-minute | |
3e5, // 5-minute | |
9e5, // 15-minute | |
18e5, // 30-minute | |
36e5, // 1-hour | |
108e5, // 3-hour | |
216e5, // 6-hour | |
432e5, // 12-hour | |
864e5 // 1-day | |
]; | |
var intraDayTickMethod = [ | |
[d3_time.second, 1, intraDayFormat], | |
[d3_time.second, 5, intraDayFormat], | |
[d3_time.second, 15, intraDayFormat], | |
[d3_time.second, 30, intraDayFormat], | |
[d3_time.minute, 1, intraDayFormat], | |
[d3_time.minute, 5, intraDayFormat], | |
[d3_time.minute, 15, intraDayFormat], | |
[d3_time.minute, 30, intraDayFormat], | |
[d3_time.hour, 1, intraDayFormat], | |
[d3_time.hour, 3, intraDayFormat], | |
[d3_time.hour, 6, intraDayFormat], | |
[d3_time.hour, 12, intraDayFormat], | |
[d3_time.day, 1, dayFormat] | |
]; | |
function tickMethod(visibleDomain, count) { | |
if(visibleDomain.length == 1) return genericTickMethod; // If we only have 1 to display, show the generic tick method | |
// Determine whether we're showing daily or intraday data | |
var intraDay = (visibleDomain[visibleDomain.length-1] - visibleDomain[0])/dailyStep < 1, | |
tickMethods = intraDay ? intraDayTickMethod : dailyTickMethod, | |
tickSteps = intraDay ? intraDayTickSteps : dailyTickSteps, | |
target = (visibleDomain[visibleDomain.length-1] - visibleDomain[0])/count, | |
i = d3_bisect(tickSteps, target); | |
return i ? tickMethods[target/tickSteps[i-1] < tickSteps[i]/target ? i-1 : i] : tickMethods[i]; | |
} | |
function lookupIndex(array) { | |
var lookup = {}; | |
array.forEach(function(d, i) { lookup[+d] = i; }); | |
return lookup; | |
} | |
function domainTicks(visibleDomain) { | |
var visibleDomainLookup = lookupIndex(visibleDomain); // Quickly lookup index of the domain | |
return function(d) { | |
var value = visibleDomainLookup[+d]; | |
if (value !== undefined) return visibleDomain[value]; | |
return visibleDomain[d3_bisect(visibleDomain, d)]; | |
}; | |
} | |
function sequentialDuplicates(previous, current) { | |
if(previous.length === 0 || previous[previous.length-1] !== current) previous.push(current); | |
return previous; | |
} | |
return financetime; | |
}; | |
},{}],38:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function(d3) { | |
var zoomable = _dereq_('./zoomable')(), | |
util = _dereq_('../util')(), | |
accessors = _dereq_('../accessor')(), | |
financetime = _dereq_('./financetime')(d3.scale.linear, d3.time, d3.bisect, util.rebindCallback, widen, zoomable); | |
return { | |
financetime: financetime, | |
analysis: { | |
supstance: function(data, accessor) { | |
return d3.scale.linear(); | |
}, | |
trendline: function(data, accessor) { | |
return d3.scale.linear(); | |
} | |
}, | |
plot: { | |
time: function(data, accessor) { | |
accessor = accessor || accessors.value(); | |
return financetime().domain(data.map(accessor.d)); | |
}, | |
percent: function (scale, reference) { | |
var domain = scale.domain(); | |
reference = reference || domain[0]; | |
return scale.copy().domain([domain[0], domain[domain.length-1]].map(function(d) { return (d-reference)/reference; })); | |
}, | |
ohlc: function (data, accessor) { | |
accessor = accessor || accessors.ohlc(); | |
return d3.scale.linear() | |
.domain([d3.min(data.map(accessor.low())), d3.max(data.map(accessor.high()))].map(widen(0.02))) | |
.range([1, 0]); | |
}, | |
volume: function (data, accessor) { | |
accessor = accessor || accessors.ohlc().v; | |
return d3.scale.linear() | |
.domain([0, d3.max(data.map(accessor))*1.15]) | |
.range([1, 0]); | |
}, | |
rsi: function () { | |
return d3.scale.linear().domain([0, 100]) | |
.range([1, 0]); | |
}, | |
aroon: function () { | |
return d3.scale.linear().domain([-100, 100]) | |
.range([1, 0]); | |
}, | |
stochastic: function () { | |
return d3.scale.linear().domain([0, 100]) | |
.range([1, 0]); | |
}, | |
williams: function () { | |
return d3.scale.linear().domain([0, 100]) | |
.range([1, 0]); | |
}, | |
momentum: function(data, accessor) { | |
accessor = accessor || accessors.value(); | |
return pathScale(d3, data, accessor, 0.04); | |
}, | |
moneyflow: function(data, accessor) { | |
accessor = accessor || accessors.value(); | |
return pathScale(d3, data, accessor, 0.04); | |
}, | |
macd: function(data, accessor) { | |
accessor = accessor || accessors.macd(); | |
return pathScale(d3, data, accessor, 0.04); | |
}, | |
movingaverage: function(data, accessor) { | |
accessor = accessor || accessors.value(); | |
return pathScale(d3, data, accessor); | |
} | |
}, | |
position: { | |
} | |
}; | |
}; | |
function pathDomain(d3, data, accessor, widening) { | |
return data.length > 0 ? d3.extent(data, accessor).map(widen(widening)) : null; | |
} | |
function pathScale(d3, data, accessor, widening) { | |
return d3.scale.linear().domain(pathDomain(d3, data, accessor, widening)) | |
.range([1, 0]); | |
} | |
/** | |
* Only to be used on an array of 2 elements [min, max] | |
* @param padding | |
* @param width | |
* @returns {Function} | |
*/ | |
function widen(widening, width) { | |
widening = widening || 0; | |
return function(d, i, array) { | |
if(array.length > 2) throw "array.length > 2 unsupported. array.length = " + array.length; | |
width = width || (array[array.length-1] - array[0]); | |
return d + (i*2-1)*width*widening; | |
}; | |
} | |
},{"../accessor":3,"../util":41,"./financetime":37,"./zoomable":39}],39:[function(_dereq_,module,exports){ | |
'use strict'; | |
/** | |
* Creates a decorated zoomable view of the passed scale. As the finance scale deals with an array and integer positions within the | |
* array, it does not support the d3 zoom behaviour. d3 zoom behaviour rescales the input domain. | |
* Finance scale is composed of an array of dates which is fixed in length and position and a linear scale mapping index | |
* to range. The linear scale can be zoomed. This object decorates the scale with only the methods required by zoom | |
* (invert, domain, copy). On zoom, calls the based zoomed callback. | |
* | |
* NOTE: This is not a complete scale, it will throw errors if it is used for anything else but zooming | |
*/ | |
module.exports = function() { | |
function zoomable(linear, zoomed) { | |
var scale = {}, | |
domainLimit = linear.domain(); | |
scale.invert = linear.invert; | |
scale.domain = function(_) { | |
if(!arguments.length) throw "zoomable is a write only domain. Use this scale for zooming only"; | |
linear.domain([Math.max(domainLimit[0], _[0]), Math.min(domainLimit[1], _[1])]); | |
if(zoomed) zoomed(); // Callback to that we have been zoomed | |
return scale; | |
}; | |
scale.range = function(_) { | |
if(!arguments.length) return linear.range(); | |
throw "zoomable is a read only range. Use this scale for zooming only"; | |
}; | |
scale.copy = function() { | |
return zoomable(linear.copy(), zoomed); | |
}; | |
return scale; | |
} | |
return zoomable; | |
}; | |
},{}],40:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = (function(d3) { | |
return { | |
version: _dereq_('../build/version'), | |
accessor: _dereq_('./accessor')(), | |
indicator: _dereq_('./indicator')(), | |
plot: _dereq_('./plot')(d3), | |
scale: _dereq_('./scale')(d3) | |
}; | |
})(d3); | |
},{"../build/version":1,"./accessor":3,"./indicator":14,"./plot":25,"./scale":38}],41:[function(_dereq_,module,exports){ | |
'use strict'; | |
module.exports = function() { | |
return { | |
rebindCallback: rebindCallback, | |
rebind: function(target, source) { | |
var newArgs = Array.prototype.slice.call(arguments, 0); | |
newArgs.splice(2, 0, undefined); | |
return rebindCallback.apply(this, newArgs); | |
} | |
}; | |
}; | |
/* | |
Slight modification to d3.rebind taking a post set callback | |
https://github.com/mbostock/d3/blob/master/src/core/rebind.js | |
*/ | |
function rebindCallback(target, source, postSetCallback) { | |
var i = 2, n = arguments.length, method; | |
while (++i < n) target[method = arguments[i]] = doRebind(target, source, source[method], postSetCallback); | |
return target; | |
} | |
function doRebind(target, source, method, postSetCallback) { | |
return function() { | |
var value = method.apply(source, arguments); | |
if(postSetCallback && value === source) postSetCallback(); | |
return value === source ? target : value; | |
}; | |
} | |
},{}]},{},[40]) | |
(40) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment