MFIIndicator.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /* *
  2. *
  3. * Money Flow Index indicator for Highstock
  4. *
  5. * (c) 2010-2020 Grzegorz Blachliński
  6. *
  7. * License: www.highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. import U from '../../Core/Utilities.js';
  14. var error = U.error, isArray = U.isArray, seriesType = U.seriesType;
  15. /* eslint-disable require-jsdoc */
  16. // Utils:
  17. function sumArray(array) {
  18. return array.reduce(function (prev, cur) {
  19. return prev + cur;
  20. });
  21. }
  22. function toFixed(a, n) {
  23. return parseFloat(a.toFixed(n));
  24. }
  25. function calculateTypicalPrice(point) {
  26. return (point[1] + point[2] + point[3]) / 3;
  27. }
  28. function calculateRawMoneyFlow(typicalPrice, volume) {
  29. return typicalPrice * volume;
  30. }
  31. /* eslint-enable require-jsdoc */
  32. /**
  33. * The MFI series type.
  34. *
  35. * @private
  36. * @class
  37. * @name Highcharts.seriesTypes.mfi
  38. *
  39. * @augments Highcharts.Series
  40. */
  41. seriesType('mfi', 'sma',
  42. /**
  43. * Money Flow Index. This series requires `linkedTo` option to be set and
  44. * should be loaded after the `stock/indicators/indicators.js` file.
  45. *
  46. * @sample stock/indicators/mfi
  47. * Money Flow Index Indicator
  48. *
  49. * @extends plotOptions.sma
  50. * @since 6.0.0
  51. * @product highstock
  52. * @requires stock/indicators/indicators
  53. * @requires stock/indicators/mfi
  54. * @optionparent plotOptions.mfi
  55. */
  56. {
  57. /**
  58. * @excluding index
  59. */
  60. params: {
  61. period: 14,
  62. /**
  63. * The id of volume series which is mandatory.
  64. * For example using OHLC data, volumeSeriesID='volume' means
  65. * the indicator will be calculated using OHLC and volume values.
  66. */
  67. volumeSeriesID: 'volume',
  68. /**
  69. * Number of maximum decimals that are used in MFI calculations.
  70. */
  71. decimals: 4
  72. }
  73. },
  74. /**
  75. * @lends Highcharts.Series#
  76. */
  77. {
  78. nameBase: 'Money Flow Index',
  79. getValues: function (series, params) {
  80. var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, decimals = params.decimals,
  81. // MFI starts calculations from the second point
  82. // Cause we need to calculate change between two points
  83. range = 1, volumeSeries = series.chart.get(params.volumeSeriesID), yValVolume = (volumeSeries && volumeSeries.yData), MFI = [], isUp = false, xData = [], yData = [], positiveMoneyFlow = [], negativeMoneyFlow = [], newTypicalPrice, oldTypicalPrice, rawMoneyFlow, negativeMoneyFlowSum, positiveMoneyFlowSum, moneyFlowRatio, MFIPoint, i;
  84. if (!volumeSeries) {
  85. error('Series ' +
  86. params.volumeSeriesID +
  87. ' not found! Check `volumeSeriesID`.', true, series.chart);
  88. return;
  89. }
  90. // MFI requires high low and close values
  91. if ((xVal.length <= period) || !isArray(yVal[0]) ||
  92. yVal[0].length !== 4 ||
  93. !yValVolume) {
  94. return;
  95. }
  96. // Calculate first typical price
  97. newTypicalPrice = calculateTypicalPrice(yVal[range]);
  98. // Accumulate first N-points
  99. while (range < period + 1) {
  100. // Calculate if up or down
  101. oldTypicalPrice = newTypicalPrice;
  102. newTypicalPrice = calculateTypicalPrice(yVal[range]);
  103. isUp = newTypicalPrice >= oldTypicalPrice;
  104. // Calculate raw money flow
  105. rawMoneyFlow = calculateRawMoneyFlow(newTypicalPrice, yValVolume[range]);
  106. // Add to array
  107. positiveMoneyFlow.push(isUp ? rawMoneyFlow : 0);
  108. negativeMoneyFlow.push(isUp ? 0 : rawMoneyFlow);
  109. range++;
  110. }
  111. for (i = range - 1; i < yValLen; i++) {
  112. if (i > range - 1) {
  113. // Remove first point from array
  114. positiveMoneyFlow.shift();
  115. negativeMoneyFlow.shift();
  116. // Calculate if up or down
  117. oldTypicalPrice = newTypicalPrice;
  118. newTypicalPrice = calculateTypicalPrice(yVal[i]);
  119. isUp = newTypicalPrice > oldTypicalPrice;
  120. // Calculate raw money flow
  121. rawMoneyFlow = calculateRawMoneyFlow(newTypicalPrice, yValVolume[i]);
  122. // Add to array
  123. positiveMoneyFlow.push(isUp ? rawMoneyFlow : 0);
  124. negativeMoneyFlow.push(isUp ? 0 : rawMoneyFlow);
  125. }
  126. // Calculate sum of negative and positive money flow:
  127. negativeMoneyFlowSum = sumArray(negativeMoneyFlow);
  128. positiveMoneyFlowSum = sumArray(positiveMoneyFlow);
  129. moneyFlowRatio = positiveMoneyFlowSum / negativeMoneyFlowSum;
  130. MFIPoint = toFixed(100 - (100 / (1 + moneyFlowRatio)), decimals);
  131. MFI.push([xVal[i], MFIPoint]);
  132. xData.push(xVal[i]);
  133. yData.push(MFIPoint);
  134. }
  135. return {
  136. values: MFI,
  137. xData: xData,
  138. yData: yData
  139. };
  140. }
  141. });
  142. /**
  143. * A `MFI` series. If the [type](#series.mfi.type) option is not specified, it
  144. * is inherited from [chart.type](#chart.type).
  145. *
  146. * @extends series,plotOptions.mfi
  147. * @since 6.0.0
  148. * @excluding dataParser, dataURL
  149. * @product highstock
  150. * @requires stock/indicators/indicators
  151. * @requires stock/indicators/mfi
  152. * @apioption series.mfi
  153. */
  154. ''; // to include the above in the js output