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