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