mfi.src.js 5.2 KB

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