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