1 
  2 /**
  3  * @description Max value in a series
  4  * @param{array} values array of numerical values
  5  * @returns {value} the max element in the series
  6  */
  7 max = function (values) {
  8   var ret = Number.MIN_VALUE
  9   for (var i = 0; i < values.length; i++) {
 10     if (values[i] > ret) {
 11       ret = values[i];
 12     }
 13   }
 14   return ret;
 15 }
 16 
 17 //////////////////////////////////////////////////////////
 18 
 19 /**
 20  * @description Min value in a series
 21  * @param {array} values array of numerical values
 22  * @returns {value} min value in the series
 23  */
 24 min = function (values) {
 25   var ret = Number.MAX_VALUE
 26   for (var i = 0; i < values.length; i++) {
 27     if (values[i] < ret) {
 28       ret = values[i];
 29     }
 30   }
 31   return ret;
 32 }
 33 
 34 //////////////////////////////////////////////////////////
 35 
 36 /**
 37  * @description Mean of values in a serie
 38  * @param {array} values array of numerical values
 39  * @return {value} mean of the series
 40  */
 41 mean = function (values, targetAttr) {
 42   var mean = 0;
 43   if (values.length == 0)
 44     return mean;
 45   for (var i = 0; i < values.length; i++) {
 46       mean += isUndef(targetAttr) ? values[i] : values[i][targetAttr]
 47   }
 48   return mean/values.length;
 49 }
 50 
 51 //////////////////////////////////////////////////////////
 52 
 53 /**
 54  * @description Standar deviation of values in a serie.
 55  * @param {array} values array of numerical values
 56  * @return {value} standard deviation of the series values.
 57  */
 58 sd = function (values, targetAttr) {
 59   var meanVal = mean(values, targetAttr);
 60   var sqrSum = 0;
 61   for (var i = 0; i < values.length; i++) {
 62     var value = isUndef(targetAttr) ? values[i] : values[i][targetAttr]
 63     sqrSum += Math.pow(value-meanVal, 2);
 64   }
 65   return Math.sqrt (sqrSum/values.length);
 66 }
 67 /**
 68  * @description This is an internal function and is not supposed to be used directly. This function moves the window of size value along the values, applying the defined function on each chunk.
 69  * @param {array} values values array
 70  * @param {value} value size of the window
 71  * @param {function} fun function to apply on each chunk
 72  * @return {array} values returned by the given function in each chunck
 73  */
 74 windowOp = function (values, value, fun, targetAttr) {
 75   var result = new Array();
 76   for (var i = value; i <= values.length; i++)
 77   {
 78     var windowVal = fun (values.slice(i-value, i), targetAttr);
 79     result.push (windowVal);
 80   }
 81   return result;
 82 }
 83 /**
 84  * @description This is an internal function and is not supposed to be used directly. This function moves the window of size value along the values, applying the defined function on each chunk.
 85  * @param {object} objects list
 86  * @param {attrs} list of attributes to look for
 87  * @return {value} object attribute
 88  */
 89 resolveParam = function (obj, attrs) {
 90   for (var i = 0; i < attrs.length; i++) {
 91     var field = attrs[i]
 92     if (obj[field] != undefined)
 93       return obj[field]
 94   }
 95   throw new Error( "No valid (" + attrs + ") found in obj");
 96 }
 97 
 98 /**
 99  * @description returns the given value if the object is undefined
100  * @param {obj} object to check
101  * @param {val} value to return
102  */
103 valueIfUndef = function (obj, val) {
104   return isUndef(obj) ? val : obj;
105 }
106 
107 isUndef = function (obj) {
108   return typeof obj == "undefined";
109 }
110 
111 reverseAppend = function (refList, addList, field) {
112   if (isUndef(field))
113     throw new Error ("Unable to append values, no field given")
114   addList.forEach (function (add, i) {
115     refList[refList.length-addList.length+i][field] = add[field] ? add[field] : add;
116   })
117   return refList;
118 }
119 
120 flat = function (list, attr) {
121   return list.map (function (i) {
122     return isUndef(i[attr]) ? 0 : i[attr];
123   });
124 }
125 
126 fill = function (list, attr, defaultValue) {
127   list.forEach(function(l) {
128     if (isUndef(l[attr]))
129       l[attr] = defaultValue;
130   });
131 }
132 
133 /**
134  * @description Alternative forEach for all those browsers like IE8 and below
135  * @param {function} function to apply to each element
136  * @param {scope} scope
137  */
138 if ( !Array.prototype.forEach ) {
139   Array.prototype.forEach = function(fn, scope)
140   {
141     for(var i = 0, len = this.length; i < len; ++i)
142     {
143       if (i in this)
144       {
145         fn.call(scope, this[i], i, this);
146       }
147     }
148   };
149 }
150 
151 ////////////////////////////////////////////////////////
152 
153 /**
154  * @description Returns a vector containing the difference of the parameters.
155  * @param {array} series1 first values array
156  * @param {array} series2 second values array
157  * @return {array} series1 - series2
158  */
159 diffVectors = function (series1, series2, targetAttr)
160 {
161   var size = max([series1.length, series2.length])
162   var result = [];
163   var s1Size = series1.length;
164   var s2Size = series2.length;
165   for (var i = 0; i < size; i++)
166   {
167     var itemS1 = 0;
168     var itemS2 = 0;
169     if (s1Size > i)
170     {
171       itemS1 = isUndef(targetAttr) ? series1[i] : series1[i][targetAttr];
172     }
173     if (s2Size > i)
174     {
175       itemS2 = isUndef(targetAttr) ? series2[i] : series2[i][targetAttr];
176     }
177     result.push (itemS1 - itemS2);
178   }
179   return result;
180 }
181 
182 ////////////////////////////////////////////////////////
183 
184 /**
185  * @description Returns a vector to the 2nd power
186  * @param {array} serie values array
187  * @return {array} values array ^ 2
188  */
189 powVector = function (serie)
190 {
191   var result = [];
192   pow = function (x) {
193     result.push (Math.pow(x, 2));
194   };
195   serie.forEach (pow);
196   return result;
197 }
198 
199 ////////////////////////////////////////////////////////
200 
201 /**
202  * @description Returns the sum of all elements in a vector
203  * @param {array} vector values array
204  * @returns {value} the sum of all elements
205  */
206 sumVector = function (values, targetAttr)
207 {
208   var result = 0;
209   sum = function (x) {
210     if (isUndef(x[targetAttr]))
211       result += x
212     else
213       result += x[targetAttr]
214   }
215   values.forEach (sum);
216   return result;
217 }
218 
219 ////////////////////////////////////////////////////////
220 
221 /**
222  * @description Returns the average of the sum of all vector elements
223  * @param {array} vector values array
224  * @returns {value} the average of the all elements
225  */
226 avgVector = function (vector, targetAttr)
227 {
228   var result = sumVector (vector, targetAttr);
229   if (!vector.length)
230     return 0;
231   else
232     return result / vector.length;
233 }
234 
235 ////////////////////////////////////////////////////////
236 
237 /**
238  * @description Returns the vector containing absolutes values of the input
239  * @param {array} vector values array
240  * @return {array} the absolute values of the given array
241  */
242 absVector = function (vector)
243 {
244   var result = [];
245   vector.forEach (function ab(x)
246   {
247     result.push(Math.abs(x));
248   });
249   return result;
250 }
251 
252 ////////////////////////////////////////////////////////
253 
254 /**
255  * @description Returns the values of the first vector divided by the second
256  * @param {array} v1 values array
257  * @param {array} v2 values array
258  * @return {array} v1 / v2
259  */
260 divVector = function (v1, v2)
261 {
262   var result = [];
263   for (var i = 0; i < v1.length; i++)
264   {
265     result.push (v1[i] / v2[i]);
266   }
267   return result;
268 }
269 
270 ////////////////////////////////////////////////////////
271 
272 /**
273  * @description Combine two vectors using the provided function.
274  * Both series must have the same length.
275  * @param {array} serie1
276  * @param {array} serie2
277  * @param {function} fun
278  * @return {array} values fun(serie1, serie2)
279  */
280 combineVectors = function (serie1, serie2, fun)
281 {
282   if (serie1.length != serie2.length || serie1.length + serie2.length < 2)
283   {
284     return [-1];
285   }
286   else
287   {
288     var result = [];
289     for (var i = 0; i < serie1.length; i++)
290     {
291       result.push (fun(serie1[i], serie2[i]));
292     }
293     return result;
294   }
295 }
296 /**
297  * @description Returns the MSE error of two series
298  * @param{array} series1 values array
299  * @param{array} series2 values array
300  * @return{value} the mse error
301  */
302 mse = function (series1, series2)
303 {
304   return avgVector (powVector (diffVectors(series1, series2)));
305 }
306 
307 ////////////////////////////////////////////////////////
308 
309 /**
310  * @description Returns the RMSE error (squared MSE)
311  * @param{array} series1 values array
312  * @param{array} series2 values array
313  * @return{value} the RMSE error
314  */
315 rmse = function (series1, series2)
316 {
317   return Math.sqrt (mse(series1, series2));
318 }
319 
320 ////////////////////////////////////////////////////////
321 
322 /**
323  * @description Returns the MAE erro (mean absolute error)
324  * @param{array} series1 values array
325  * @param{array} series2 values array
326  * @return{value} the mae error
327  */
328 mae = function (series1, series2)
329 {
330   return avgVector(absVector(diffVectors(series1, series2)));
331 }
332 
333 /*
334  * Returns the Bollinger Band values as an object
335  * containing three arrays:
336  *         - the upper values (upperBand),
337  *         - central moving average values (ma),
338  *         - lower band values (lowerBand).
339  *         
340  * Params: list - values
341  *         n - size of the sample window
342  *         k - height of the band (sd multiplier)
343  * Usual values are n = 20 and k = 2 (i.e. the base
344  * moving average is calculated on the previous 20 
345  * elems for a elem and upper and lower bands are
346  * located +2*sd and -2*sd from the central moving average.
347  */
348 bollinger = function (list, n, k, targetAttr) {
349   targetAttr = valueIfUndef(targetAttr, ["c"])
350   var movingAvg = ma (list, n, targetAttr);
351   var movingSd = windowOp (list, n, sd, targetAttr);
352   var upperBand = new Array();
353   var lowerBand = new Array();
354   var movingAvgElem = 0;
355   var movingSdElem = 0;
356   for (var index = 0; index < movingSd.length; index++) {
357     movingAvgElem = movingAvg[index].ma;
358     movingSdElem = movingSd[index] * k;
359     upperBand.push (movingAvgElem + movingSdElem);
360     lowerBand.push (movingAvgElem - movingSdElem);
361   }
362   return {
363       upperBand: upperBand,
364       ma: movingAvg,
365       lowerBand: lowerBand
366   };
367 }
368 
369 /*
370  * Moving Average: 
371  * also known as simple moving average, rolling average, moving mean
372  * and a million of similar combinations
373  */
374 ma = function (values, order, targetAttr) {
375   targetAttr = valueIfUndef(targetAttr, ["c"])
376   // Sums the content of a window
377   sumWindow = function (serie) {
378     var sum = 0;
379     for (var init = 0; init < serie.length; init++) {
380       sum += resolveParam(serie[init], targetAttr);
381     }
382     return (sum/serie.length);
383   }
384   newVal = windowOp (values, order, sumWindow);
385   return reverseAppend(values, newVal, "ma")
386 }
387 
388 ///////////////////////////////////////////////////////
389 
390 /**
391  * Exponential moving average
392  */
393 ema = function (serie, period, targetAttr, newAttr) 
394 {
395   if (typeof serie[0] == "object" && !targetAttr)
396     throw new Error("targetAttr not provided")
397   newAttr = valueIfUndef (newAttr, "ema")
398   var emaValues = new Array();
399   var k = (2/(period+1));
400   var initSlice = serie.slice (0, period);
401   var previousDay = avgVector (initSlice, targetAttr);
402   emaValues.push(previousDay)
403   var emaSlice = serie.slice (period);
404   emaSlice.forEach (function (elem)
405   {
406     var value = isUndef(targetAttr) ? elem : elem[targetAttr]
407     previousDay = value * k + previousDay * (1-k)
408     emaValues.push (previousDay);
409   });
410   var newSerie = serie.slice()
411   return reverseAppend(newSerie, emaValues, newAttr)
412 }
413 
414 ///////////////////////////////////////////////////////
415 
416 /**
417  * Weighted moving average.
418  * The order of the mean (the number of elements to sum) 
419  * is based on the weight's length.
420  * The sum of weights should be 1.
421  */
422 wma = function (series, weights, targetAttr)
423 {
424   targetAttr = valueIfUndef(targetAttr, ["c"])
425   sumWindow = function (elems) {
426     var sum = 0;
427     elems.forEach(function(elem,i) {
428       sum = sum + (elem[targetAttr] * weights[i]);
429     });
430     return (sum/elems.length);
431   }
432   var wmaValues = windowOp (series, weights.length, sumWindow);
433   return reverseAppend(series, wmaValues, "wma")
434 }
435 
436 ///////////////////////////////////////////////////////
437 
438 
439 /**
440  * @description On-Balance Volume (obv).
441  * @param {array} closeList list of closing prices
442  * @param {array} volumeList list of volumes
443  * @return {array} the OBV values list
444  */
445 obv = function (closeList, volumeList)
446 {
447   var result = [];
448   var prevObv = volumeList[0];
449   result.push (prevObv);
450   for (var i = 1; i < closeList.length; i++)
451   {
452     if (closeList[i] > closeList[i-1])
453     {
454       // bullish
455       result.push (prevObv + volumeList[i]);
456       prevObv += volumeList[i];
457     }
458     else if (closeList[i] < closeList[i-1])
459     {
460       // bearish
461       result.push (prevObv - volumeList[i]);
462       prevObv -= volumeList[i];
463     }
464     else 
465     {
466       result.push (prevObv);
467     }
468   }
469   return result;
470 }
471 /**
472  * @description Returns the VPT (Volume-price Trend)
473  * @param {array} closeList list of closing prices
474  * @param {array} volumeList list of volume
475  * @return {array} vpt values array
476  */
477 vpt = function (closeList, volumeList)
478 {
479   var result = [];
480   var vpt = volumeList[0]
481   result.push (vpt);
482   for (var i = 1; i < closeList.length; i++)
483   {
484     var newVpt = vpt + volumeList[i] * ((closeList[i] - closeList[i-1])/closeList[i-1])
485     result.push (newVpt);
486     vpt = newVpt;
487   }
488   return result;
489 }
490 
491 /**
492  * @description Returns the Money-flow Index
493  * @param {array} highPrices list of high prices
494  * @param {array} lowPrices list of low prices
495  * @param {array} closePrices list of closing prices
496  * @param {array} volumes list of volumes
497  * @return {value} the money-flow index
498  */
499 mfi = function (values)
500 {
501   var typicalMoney = [];
502   var moneyFlow = [];
503   for (var i = 0; i < values.length; i++)
504   {
505     var tpMoney = (values[i].h + values[i].l + values[i].c) / 3;
506     typicalMoney.push(tpMoney);
507     moneyFlow.push (tpMoney * values[i].v);
508   }
509 
510   var posMoneyFlow = [];
511   var negMoneyFlow = [];
512   for (var i = 0; i < typicalMoney.length-1; i++)
513   {
514     if (typicalMoney[i] <= typicalMoney[i+1])
515     {
516       posMoneyFlow.push (moneyFlow[i+1]);
517       negMoneyFlow.push(0);
518     }
519     else if (typicalMoney[i] > typicalMoney[i+1])
520     {
521       posMoneyFlow.push (0);
522       negMoneyFlow.push (moneyFlow[i+1]);
523     }
524     else // typical money unchanged implies day is discharged
525     {
526     	posMoneyFlow.push(0);
527     	negMoneyFlow.push(0);
528     }
529   }
530 
531   var sumPosFlow = windowOp (posMoneyFlow, 14, sumVector);
532   var sumNegFlow = windowOp (negMoneyFlow, 14, sumVector);
533   var moneyRatio = divVector (sumPosFlow, sumNegFlow);
534 
535   var mfi = [];
536   moneyRatio.forEach (function (value)
537   {
538     mfi.push (100 - (100/(1+value)));
539   });
540   return mfi;
541 }
542 
543 ////////////////////////////////////////////
544 
545 /**
546  * @description Returns the MACD
547  * @param {array} closePrices list of closing prices
548  * @return {object} object containing the macd, signal
549  *                  and hist series.
550  */
551 macd = function (closeValues, targetAttr)
552 {
553   targetAttr = valueIfUndef(targetAttr, ["c"])
554   slow = 26;
555   fast = 12;
556   signal = 9;
557   slowEMA = ema (closeValues, slow, targetAttr, "slowema");
558   fastEMA = ema (closeValues, fast, targetAttr, "fastema");
559   macdLine = combineVectors (slowEMA, fastEMA, function (slow,fast) {
560     if (slow.slowema == 0 || isUndef(slow.slowema))
561     {
562       return ({macd:0}); // avoid div by 0
563     };
564     return ({macd:100 * ((fast.fastema/slow.slowema) - 1)});
565   });
566   signalLine = ema (macdLine.slice(25), signal, "macd"); // avoid first 25 (padding)
567   for (var i = 0; i < 25; i++)
568   {
569     signalLine.unshift({macd:0}); // append again 25 zeros
570   }
571   histLine = diffVectors(macdLine, signalLine, "macd");
572   fill(signalLine, "ema", 0);
573   macdItems = [];
574   for (var i = 0; i < macdLine.length; i++) {
575     macdItems.push({macd:{line:macdLine[i].macd, signal:signalLine[i].ema, hist:histLine[i]}});
576   }
577   var returnList = closeValues.slice()
578   return reverseAppend (returnList, macdItems, "macd");
579 }
580 
581 ////////////////////////////////////////////
582 
583 /**
584  * @description Returns the Momentum
585  * @param {array} closePrices list of closing prices
586  * @param {value} order order of the momentum 
587  * @returns {array} list containing the momentum series
588  * @example 
589  * var m = momemtum ([12,34,23, 81], 1) 
590  * console.log(m)  // [22, -11, 58]
591  */
592 momentum = function(values, order)
593 {
594   momentumN = function (chunk)
595   {
596     return chunk[chunk.length-1].c - chunk[0].c
597   };
598   var returnValues = values.slice()
599   var newValues = windowOp (values, order+1, momentumN);
600   return reverseAppend(returnValues, newValues, "mom")
601 }
602 
603 ////////////////////////////////////////////
604 
605 /**
606  * @description Returns the Rate of Change value (ROC)
607  * @param {array} closePrices list of closing prices
608  * @param {value} order order of the ROC
609  * @returns {array} list containing the ROC series
610  * @example 
611  * var roc = roc ([12, 11, 15, 10], 1) 
612  * console.log(roc)  // [-0.09, 0.36, -0.33]
613  */
614 roc = function(values, order, targetAttr)
615 {
616   rocN = function (chunk)
617   {
618     return (chunk[chunk.length-1].c - chunk[0].c) / chunk[0].c;
619   };
620   var returnValues = values.slice()
621   var rocValues = windowOp (values, order+1, rocN);
622   return reverseAppend(returnValues, rocValues, "roc");
623 }
624 
625 
626 ////////////////////////////////////////////
627 /**
628  * @description Returns the RSI (Relative Strength Index)
629  * @param {array} closePrices list of closing prices
630  * @param {value} order RSI order (typically 14)
631  * @returns {array} list containing the RSI for each period
632  * @example 
633  * var rsi = rsi ([45.34, 44, ..., 42,9, 45.23], 14) 
634  * console.log(rsi)  // [70.53, 66.32, ..., 56.05]
635  */
636 rsi = function (values, order)
637 {
638   if (values.length < order+1)
639   {
640     return [-1]; // not enough params
641   }
642   gains = [];
643   losses = [];
644   for (var i = 0; i < values.length-1; i++)
645   {
646     diff = values[i+1].c - values[i].c;
647     if (diff > 0) 
648     {
649       gains.push(diff);
650       losses.push(0);
651     }
652     else if (diff < 0)
653     {
654       gains.push(0);
655       losses.push(Math.abs(diff));
656     }
657     else
658     {
659       gains.push(0);
660       losses.push(0);
661     }
662   }
663   result = [];
664   avgGain = avgVector (gains.slice(0, order));
665   avgLoss = avgVector (losses.slice (0, order));
666   firstRS = avgGain / avgLoss;
667   result.push (100 - (100 / (1 + firstRS)));
668   for (var i = order; i < values.length-1; i++)
669   {
670     partialCurrentGain = ((avgGain * (order-1)) + gains[i]) / order;
671     partialCurrentLoss = ((avgLoss * (order-1)) + losses[i]) / order;
672     smoothedRS = partialCurrentGain / partialCurrentLoss;
673     currentRSI = 100 - (100 / (1 + smoothedRS))
674     result.push(currentRSI);
675     avgGain = partialCurrentGain;
676     avgLoss = partialCurrentLoss;
677   }
678   var newValues = values.slice()
679   return reverseAppend(newValues, result, "rsi");
680 }
681 
682 //////////////////////////////
683 /**
684  * @description Returns the ATR (Average True Value). ATR is provided after 14th element.
685  * @param {array} values containing {high,low,close}
686  * @returns {array} list containing {tr,atr} values for each period.
687  * @example 
688  * var atr = atr ([{high:48.7, low:45.3, close:46}, ...])
689  * console.log(atr)  // [{tr:2.4, atr:0}, ... 13 empty atr's, ... ,{atr:_value_, tr:_value_} ]
690  */
691 
692 atr = function (values) {
693   var results = [];
694   for (var i = 0; i < values.length; i++) {
695     if (i == 0) {
696       results.push({tr:values[i].high - values[i].low, atr:0})
697     }
698     else {
699       var hl = values[i].high - values[i].low;
700       var hcp = Math.abs(values[i].high - values[i-1].close);
701       var lcp = Math.abs(values[i].low - values[i-1].close);
702       var tr = Math.max(hl,hcp,lcp);
703       var atr = 0;
704       if (i == 13) {
705         atr = tr;
706         for (var j = 0; j < results.length; j++) {
707           atr += results[j].tr;
708         }
709         atr = atr / 14.0;
710       }
711       else if (i > 13) {
712         atr = ((results[i-1].atr * 13) + tr) / 14;
713       }
714       results.push({tr:tr, atr:atr});
715     }
716   }
717   var newValues = values.slice()
718   return reverseAppend(newValues, results, "at");
719 }
720 /**
721  * Returns the Floor pivot level, three support levels (s1,s2 and s3)
722  * and three resistance levels (r1, r2 and r3) of the
723  * given data series.
724  * These values for a given day are calculated based on the day before
725  * so expect n values as output for a given list of n days.
726  * Note that all three must have the same length.
727  * Params: - higList: list of high values
728  *         - lowList: list of low values
729  *         - cliseList: list of closing values
730  * The result is a list of elements with fields:
731  *         - r3: resistence third level
732  *         - r2: resistance second level
733  *         - r1: resistance first level
734  *         - pl: pivot level
735  *         - s3: support third level
736  *         - s2: support second level
737  *         - s1: support first level
738  */
739 floorPivots = function (values) {
740   var result = new Array();
741   for (var i = 0; i < values.length; i++)
742   {
743     pivotLevel = (values[i].h + values[i].l + values[i].c) / 3;
744     r1 = 2 * pivotLevel - values[i].l;
745     r2 = pivotLevel + values[i].h - values[i].l;
746     r3 = r1 + values[i].h - values[i].l;
747     s1 = 2 * pivotLevel - values[i].h;
748     s2 = pivotLevel - values[i].h + values[i].l;
749     s3 = s1 - values[i].h + values[i].l;
750     elem = {r3:r3, r2:r2, r1:r1, pl: pivotLevel, s1:s1, s2:s2, s3:s3};
751     result.push(elem);
752   }
753   return result;
754 }
755 
756 ////////////////////////////////////////////////////////
757 
758 /**
759  * Returns the Tom Demark points, the predicted low and highs
760  * of the period.
761  * These values for a given day are calculated based on the day before
762  * so expect n values as output for a given list of n days.
763  * Note that three lists must have the same length.
764  * Params: - higList: list of high values
765  *         - lowList: list of low values
766  *         - cliseList: list of closing values
767  * The result is a list of elements with fields:
768  *         - low: predicted low value.
769  *         - high: predicted high value.
770  */
771 tomDemarksPoints = function (values) {
772   var result = new Array();
773   for (var i = 0; i < values.length; i++)
774   {
775     var x = 0;
776     if (values[i].c < values[i].o)
777     {
778       x = values[i].h + (2 * (values[i].l) + values[i].c);
779     }
780     if (values[i].c > values[i].o)
781     {
782       x = (2 * values[i].h) +  values[i].l + values[i].c;
783     }
784     if (values[i].c == values[i].o)
785     {
786       x = values[i].h + values[i].l + (2 * values[i].c);
787     }
788     newHigh = (x/2) - values[i].l;
789     newLow = (x/2) - values[i].h;
790     elem = {low: newLow, high: newHigh};
791     result.push(elem);
792   }
793   return result;
794 }
795 
796 ////////////////////////////////////////////////////////
797 
798 /**
799  * Returns the Woodies points: pivot, supports (s1 and s2) and
800  * resistance values (r1 and r2).
801  * These values for a given day are calculated based on the day before
802  * so expect n values as output for a given list of n days.
803  * Note that the three lists must have the same length.
804  * Params: - higList: list of high values
805  *         - lowList: list of low values
806  *         - closeList: list of closing values
807  * The result is a list of elements with fields:
808  *         - pivot: predicted pivot value.
809  *         - s1: predicted support (s1).
810  *         - r1: predicted resistance (r1).
811  *         - r2: predicted secondary resistance (r2).
812  *         - s2: predicted secondary support (s2).
813  */
814 woodiesPoints = function (values) {
815   var result = new Array();
816   for (var i = 0; i < values.length; i++)
817   {
818     var x = 0;
819     var pivot = (values[i].h + values[i].l + 2 * values[i].c) / 4;
820     var r1 = (2 * pivot) - values[i].l;
821     var r2 = pivot + values[i].h - values[i].l;
822     var s1 = (2 * pivot) - values[i].h;
823     var s2 = pivot - values[i].h + values[i].l; 
824     elem = {pivot: pivot, r1: r1, 
825             s1: s1, s2: s2, r2: r2};
826     result.push(elem);
827   }
828   return result;
829 }
830 
831 ////////////////////////////////////////////////////////
832 
833 /**
834  * Returns the Camarilla points: supports (s1,s2,3 and s4)) and
835  * resistance values (r1, r2, r3 and r4).
836  * These values for a given day are calculated based on the day before
837  * so expect n values as output for a given list of n days.
838  * Note that the three lists must have the same length.
839  * Params: - higList: list of high values
840  *         - lowList: list of low values
841  *         - closeList: list of closing values
842  * The result is a list of elements with fields:
843  *         - s1: predicted s1 support.
844  *         - s2: predicted s2 support.
845  *         - s3: predicted s3 support.
846  *         - s4: predicted s4 support.
847  *         - r1: predicted r1 resistance.
848  *         - r2: predicted r2 resistance.
849  *         - r3: predicted r3 resistance.
850  *         - r4: predicted r4 resistance.
851  */
852 camarillaPoints = function (values) {
853   var result = new Array();
854   for (var i = 0; i < values.length; i++)
855   {
856     var diff = values[i].h - values[i].l;
857     var r4 = (diff * 1.1) / 2 + values[i].c;
858     var r3 = (diff *1.1) / 4 + values[i].c;
859     var r2 = (diff * 1.1) / 6 + values[i].c;
860     var r1 = (diff * 1.1) / 12 + values[i].c;
861     var s1 = values[i].c - (diff * 1.1 / 12);
862     var s2 = values[i].c - (diff *1.1 /6);
863     var s3 = values[i].c - (diff * 1.1 / 4);
864     var s4 = values[i].c - (diff *1.1 / 2);
865     elem = {r4: r4, r3: r3, r2: r2, r1: r1, s1: s1, s2: s2, s3: s3,
866             s4: s4};
867     result.push(elem);
868   }
869   return result;
870 }
871 
872 
873 ////////////////////////////////////////////////////////
874 
875 fibonacciRetrs = function (values, trend)
876 {
877   var result = new Array();
878   var retracements = [1, 0.618, 0.5, 0.382, 0.236, 0];
879   if (trend == 'DOWNTREND') 
880   {
881     for (var i = 0; i < values.length; i++)
882     {
883       var diff = values[i].h - values[i].l;
884       var elem = new Array();
885       for (var r = 0; r < retracements.length; r++)
886       {
887         var level = values[i].h - diff * retracements[r];
888         elem.push(level);
889       }
890       result.push(elem);
891     }
892   }
893   else  // UPTREND
894   {
895     for (var i = 0; i < values.length; i++)
896     {
897       var diff = values[i].h - values[i].l;
898       var elem = new Array();
899       for (var r = 0; r < retracements.length; r++)
900       {
901         var level = values[i].l + diff * retracements[r];
902         elem.push (level);
903       }
904       result.push(elem);
905     }
906   }
907   return result;
908 }
909