forked from kimboqi/stock-indicators
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindicator.js
220 lines (209 loc) · 6.39 KB
/
indicator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
var Indicator = (function(){
/**
* 计算obv指标
*
* @method OBV
* @param {Array} ticks
* ticks为二维数组类型,其中内层数组第一个值为收盘价,第二个值为成交量
* @return {Array} obvs
*/
var obv = function (ticks) {
var lastTick, obvs = [], length = ticks.length;
for (var i = 0; i < length; i++) {
var value = 0, curTick = ticks[i];
if (i != 0) {
var lastObvValue = obvs[i-1];
if (curTick[0] >= lastTick[0]) {
value = lastObvValue + curTick[1];
} else {
value = lastObvValue - curTick[1];
}
}
obvs.push(value);
lastTick = curTick;
}
return obvs;
};
var ema = function (lastEma, closePrice, units) {
return (lastEma * (units - 1) + closePrice * 2) / (units + 1);
};
var dea = function (lastDea, curDiff) {
return (lastDea * 8 + curDiff * 2) / 10;
};
/**
*
* 计算macd指标,快速和慢速移动平均线的周期分别取12和26
*
* @method MACD
* @param {Array} ticks
* 一维数组类型,每个元素为tick的收盘价格
* @return {Object} 返回一个包含diffs deas bars属性的对象,每个属性对应的类型为{Array[Number]}
*/
var macd = function (ticks) {
var ema12 = [], ema26 = [], diffs = [], deas = [], bars = [];
for(var i = 0; i < ticks.length; i++) {
var c = ticks[i];
if (i == 0) {
ema12.push(c);
ema26.push(c);
deas.push(0);
} else {
ema12.push(ema(ema12[i-1], c, 12));
ema26.push(ema(ema26[i-1], c, 26));
}
diffs.push(ema12[i] - ema26[i]);
if (i != 0) {
deas.push(dea(deas[i-1], diffs[i]));
}
bars.push((diffs[i]-deas[i]) * 2);
}
return {diffs: diffs, deas: deas, bars: bars};
};
var getMaxHighAndMinLow = function (ticks) {
var maxHigh = ticks[0][0], minLow = ticks[0][1];
for (var i = 0; i < ticks.length; i++) {
var t = ticks[i], high = t[0], low = t[1];
if (high > maxHigh) {
maxHigh = high;
}
if (low < minLow) {
minLow = low;
}
}
return [maxHigh, minLow];
};
/**
*
* 计算kdj指标,rsv的周期为9日
*
* @method KDJ
* @param {Array} ticks
* 二维数组类型,其中内层数组包含三个元素值,第一个值表示当前Tick的最高价格,第二个表示当前Tick的最低价格,第三个表示当前Tick的收盘价格
* @return {Object} 返回一个包含k d j属性的对象,每个属性对应的类型为{Array[Number]}
*/
var kdj = function (ticks) {
var nineDaysTicks = [], days = 9, rsvs = [];
var ks = [], ds = [], js = [];
var lastK, lastD, curK, curD;
var maxAndMin, max, min;
for (var i = 0; i < ticks.length; i++) {
var t = ticks[i], close = t[2];
nineDaysTicks.push(t);
maxAndMin = getMaxHighAndMinLow(nineDaysTicks);
max = maxAndMin[0];
min = maxAndMin[1];
if (max == min) {
rsvs.push(0);
} else {
rsvs.push((close - min) / (max - min) * 100);
}
if (nineDaysTicks.length == days) {
nineDaysTicks.shift();
}
if (i == 0) {
lastK = lastD = rsvs[i];
}
curK = 2 / 3 * lastK + 1 / 3 * rsvs[i];
ks.push(curK);
lastK = curK;
curD = 2 / 3 * lastD + 1 / 3 * curK;
ds.push(curD);
lastD = curD;
js.push(3 * curK - 2 * curD);
}
return {"k": ks, "d": ds, "j": js};
};
/**
*
* 计算移动平均线指标, ma的周期为days
*
* @method MA
* @param {Array} ticks
* @param Number days
* 一维数组类型,每个元素为当前Tick的收盘价格
* @return {Array} mas
*/
var ma = function(ticks, days) {
var maSum = 0, p = 0;
var mas = [];
for (var i = 0; i < ticks.length; i++) {
maSum += ticks[i];
ma = maSum / days;
mas.push(ma);
}
return mas;
};
/**
*
* 计算boll指标,ma的周期为20日
*
* @method BOLL
* @param {Array} ticks
* 一维数组类型,每个元素为当前Tick的收盘价格
* @return {Object} 返回一个包含upper mid lower属性的对象,每个属性对应的类型为{Array[Number]}
*/
var boll = function(ticks) {
//移动平均线周期为20
var maDays = 20, tickBegin = maDays - 1, maSum = 0, p = 0;
var ups = [], mas = [], lows = [];
for (var i = 0; i < ticks.length; i ++) {
var c = ticks[i], ma, md, bstart, mdSum;
maSum += c;
if (i >= tickBegin) {
maSum = maSum - p;
ma = maSum / maDays;
mas.push(ma);
bstart = i - tickBegin;
p = ticks[bstart];
mdSum = ticks.slice(bstart, bstart+maDays).reduce(function(a, b) {return a + Math.pow(b-ma, 2);}, 0);
md = Math.sqrt(mdSum/maDays);
ups.push(ma + 2 * md);
lows.push(ma - 2 * md);
} else {
//ugly constant, just keep the same type for client
ups.push(-1);
mas.push(-1);
lows.push(-1);
}
}
return {"upper": ups, "mid": mas, "lower": lows};
};
/**
*
* 计算rsi指标,分别返回以6日,12日,24日为参考基期的RSI值
*
* @method RSI
* @param {Array} ticks
* 一维数组类型,每个元素为当前Tick的收盘价格
* @return {Object} 返回一个包含rsi6 rsi12 rsi24属性的对象,每个属性对应的类型为{Array[Number]}
*/
var rsi = function(ticks) {
var lastClosePx = ticks[0];
var days = [6, 12, 24], result = {};
for (var i = 0 ; i < ticks.length; i ++) {
var c = ticks[i];
var m = Math.max(c-lastClosePx, 0), a = Math.abs(c-lastClosePx);
for (var di = 0; di < days.length; di++) {
var d = days[di];
if (!result.hasOwnProperty("rsi"+d)) {
result["lastSm"+d] = result["lastSa"+d] = 0;
result["rsi"+d] = [0];
} else {
result["lastSm"+d] = (m + (d - 1) * result["lastSm"+d]) / d;
result["lastSa"+d] = (a + (d - 1) * result["lastSa"+d]) / d;
if (result["lastSa"+d] != 0) {
result["rsi"+d].push(result["lastSm"+d] / result["lastSa"+d] * 100);
} else {
result["rsi"+d].push(0);
}
}
}
lastClosePx = c;
}
return {"rsi6": result["rsi6"], "rsi12": result["rsi12"], "rsi24": result["rsi24"]};
};
return {"OBV": obv, "MACD": macd, "KDJ": kdj, "BOLL": boll, "RSI": rsi, "MA": ma};
})();
if (module) {
module.exports = Indicator;
}