SupertrendIndicator.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /* *
  2. *
  3. * License: www.highcharts.com/license
  4. *
  5. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6. *
  7. * */
  8. 'use strict';
  9. import H from '../../Core/Globals.js';
  10. import U from '../../Core/Utilities.js';
  11. var correctFloat = U.correctFloat, merge = U.merge, seriesType = U.seriesType;
  12. var isArray = U.isArray, objectEach = U.objectEach;
  13. var ATR = H.seriesTypes.atr, SMA = H.seriesTypes.sma;
  14. /* eslint-disable require-jsdoc */
  15. // Utils:
  16. function createPointObj(mainSeries, index, close) {
  17. return {
  18. index: index,
  19. close: mainSeries.yData[index][close],
  20. x: mainSeries.xData[index]
  21. };
  22. }
  23. /* eslint-enable require-jsdoc */
  24. /**
  25. * The Supertrend series type.
  26. *
  27. * @private
  28. * @class
  29. * @name Highcharts.seriesTypes.supertrend
  30. *
  31. * @augments Highcharts.Series
  32. */
  33. seriesType('supertrend', 'sma',
  34. /**
  35. * Supertrend indicator. This series requires the `linkedTo` option to be
  36. * set and should be loaded after the `stock/indicators/indicators.js` and
  37. * `stock/indicators/sma.js`.
  38. *
  39. * @sample {highstock} stock/indicators/supertrend
  40. * Supertrend indicator
  41. *
  42. * @extends plotOptions.sma
  43. * @since 7.0.0
  44. * @product highstock
  45. * @excluding allAreas, cropThreshold, negativeColor, colorAxis, joinBy,
  46. * keys, navigatorOptions, pointInterval, pointIntervalUnit,
  47. * pointPlacement, pointRange, pointStart, showInNavigator,
  48. * stacking, threshold
  49. * @requires stock/indicators/indicators
  50. * @requires stock/indicators/supertrend
  51. * @optionparent plotOptions.supertrend
  52. */
  53. {
  54. /**
  55. * Paramters used in calculation of Supertrend indicator series points.
  56. *
  57. * @excluding index
  58. */
  59. params: {
  60. /**
  61. * Multiplier for Supertrend Indicator.
  62. */
  63. multiplier: 3,
  64. /**
  65. * The base period for indicator Supertrend Indicator calculations.
  66. * This is the number of data points which are taken into account
  67. * for the indicator calculations.
  68. */
  69. period: 10
  70. },
  71. /**
  72. * Color of the Supertrend series line that is beneath the main series.
  73. *
  74. * @sample {highstock} stock/indicators/supertrend/
  75. * Example with risingTrendColor
  76. *
  77. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  78. */
  79. risingTrendColor: '#06B535',
  80. /**
  81. * Color of the Supertrend series line that is above the main series.
  82. *
  83. * @sample {highstock} stock/indicators/supertrend/
  84. * Example with fallingTrendColor
  85. *
  86. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  87. */
  88. fallingTrendColor: '#F21313',
  89. /**
  90. * The styles for the Supertrend line that intersect main series.
  91. *
  92. * @sample {highstock} stock/indicators/supertrend/
  93. * Example with changeTrendLine
  94. */
  95. changeTrendLine: {
  96. styles: {
  97. /**
  98. * Pixel width of the line.
  99. */
  100. lineWidth: 1,
  101. /**
  102. * Color of the line.
  103. *
  104. * @type {Highcharts.ColorString}
  105. */
  106. lineColor: '#333333',
  107. /**
  108. * The dash or dot style of the grid lines. For possible
  109. * values, see
  110. * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  111. *
  112. * @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
  113. * Long dashes
  114. * @sample {highstock} stock/xaxis/gridlinedashstyle/
  115. * Long dashes
  116. *
  117. * @type {Highcharts.DashStyleValue}
  118. * @since 7.0.0
  119. */
  120. dashStyle: 'LongDash'
  121. }
  122. }
  123. },
  124. /**
  125. * @lends Highcharts.Series.prototype
  126. */
  127. {
  128. nameBase: 'Supertrend',
  129. nameComponents: ['multiplier', 'period'],
  130. requiredIndicators: ['atr'],
  131. init: function () {
  132. var options, parentOptions;
  133. SMA.prototype.init.apply(this, arguments);
  134. options = this.options;
  135. parentOptions = this.linkedParent.options;
  136. // Indicator cropThreshold has to be equal linked series one
  137. // reduced by period due to points comparison in drawGraph method
  138. // (#9787)
  139. options.cropThreshold = (parentOptions.cropThreshold -
  140. (options.params.period - 1));
  141. },
  142. drawGraph: function () {
  143. var indicator = this, indicOptions = indicator.options,
  144. // Series that indicator is linked to
  145. mainSeries = indicator.linkedParent, mainLinePoints = (mainSeries ? mainSeries.points : []), indicPoints = indicator.points, indicPath = indicator.graph, indicPointsLen = indicPoints.length,
  146. // Points offset between lines
  147. tempOffset = mainLinePoints.length - indicPointsLen, offset = tempOffset > 0 ? tempOffset : 0,
  148. // @todo: fix when ichi-moku indicator is merged to master.
  149. gappedExtend = {
  150. options: {
  151. gapSize: indicOptions.gapSize
  152. }
  153. },
  154. // Sorted supertrend points array
  155. groupedPoitns = {
  156. top: [],
  157. bottom: [],
  158. intersect: [] // Change trend line points
  159. },
  160. // Options for trend lines
  161. supertrendLineOptions = {
  162. top: {
  163. styles: {
  164. lineWidth: indicOptions.lineWidth,
  165. lineColor: (indicOptions.fallingTrendColor ||
  166. indicOptions.color),
  167. dashStyle: indicOptions.dashStyle
  168. }
  169. },
  170. bottom: {
  171. styles: {
  172. lineWidth: indicOptions.lineWidth,
  173. lineColor: (indicOptions.risingTrendColor ||
  174. indicOptions.color),
  175. dashStyle: indicOptions.dashStyle
  176. }
  177. },
  178. intersect: indicOptions.changeTrendLine
  179. }, close = 3,
  180. // Supertrend line point
  181. point,
  182. // Supertrend line next point (has smaller x pos than point)
  183. nextPoint,
  184. // Main series points
  185. mainPoint, nextMainPoint,
  186. // Used when supertrend and main points are shifted
  187. // relative to each other
  188. prevMainPoint, prevPrevMainPoint,
  189. // Used when particular point color is set
  190. pointColor,
  191. // Temporary points that fill groupedPoitns array
  192. newPoint, newNextPoint;
  193. // Loop which sort supertrend points
  194. while (indicPointsLen--) {
  195. point = indicPoints[indicPointsLen];
  196. nextPoint = indicPoints[indicPointsLen - 1];
  197. mainPoint = mainLinePoints[indicPointsLen - 1 + offset];
  198. nextMainPoint = mainLinePoints[indicPointsLen - 2 + offset];
  199. prevMainPoint = mainLinePoints[indicPointsLen + offset];
  200. prevPrevMainPoint = mainLinePoints[indicPointsLen + offset + 1];
  201. pointColor = point.options.color;
  202. newPoint = {
  203. x: point.x,
  204. plotX: point.plotX,
  205. plotY: point.plotY,
  206. isNull: false
  207. };
  208. // When mainPoint is the last one (left plot area edge)
  209. // but supertrend has additional one
  210. if (!nextMainPoint &&
  211. mainPoint && mainSeries.yData[mainPoint.index - 1]) {
  212. nextMainPoint = createPointObj(mainSeries, mainPoint.index - 1, close);
  213. }
  214. // When prevMainPoint is the last one (right plot area edge)
  215. // but supertrend has additional one (and points are shifted)
  216. if (!prevPrevMainPoint &&
  217. prevMainPoint && mainSeries.yData[prevMainPoint.index + 1]) {
  218. prevPrevMainPoint = createPointObj(mainSeries, prevMainPoint.index + 1, close);
  219. }
  220. // When points are shifted (right or left plot area edge)
  221. if (!mainPoint &&
  222. nextMainPoint && mainSeries.yData[nextMainPoint.index + 1]) {
  223. mainPoint = createPointObj(mainSeries, nextMainPoint.index + 1, close);
  224. }
  225. else if (!mainPoint &&
  226. prevMainPoint && mainSeries.yData[prevMainPoint.index - 1]) {
  227. mainPoint = createPointObj(mainSeries, prevMainPoint.index - 1, close);
  228. }
  229. // Check if points are shifted relative to each other
  230. if (point &&
  231. mainPoint &&
  232. prevMainPoint &&
  233. nextMainPoint &&
  234. point.x !== mainPoint.x) {
  235. if (point.x === prevMainPoint.x) {
  236. nextMainPoint = mainPoint;
  237. mainPoint = prevMainPoint;
  238. }
  239. else if (point.x === nextMainPoint.x) {
  240. mainPoint = nextMainPoint;
  241. nextMainPoint = {
  242. close: mainSeries.yData[mainPoint.index - 1][close],
  243. x: mainSeries.xData[mainPoint.index - 1]
  244. };
  245. }
  246. else if (prevPrevMainPoint && point.x === prevPrevMainPoint.x) {
  247. mainPoint = prevPrevMainPoint;
  248. nextMainPoint = prevMainPoint;
  249. }
  250. }
  251. if (nextPoint && nextMainPoint && mainPoint) {
  252. newNextPoint = {
  253. x: nextPoint.x,
  254. plotX: nextPoint.plotX,
  255. plotY: nextPoint.plotY,
  256. isNull: false
  257. };
  258. if (point.y >= mainPoint.close &&
  259. nextPoint.y >= nextMainPoint.close) {
  260. point.color = (pointColor || indicOptions.fallingTrendColor ||
  261. indicOptions.color);
  262. groupedPoitns.top.push(newPoint);
  263. }
  264. else if (point.y < mainPoint.close &&
  265. nextPoint.y < nextMainPoint.close) {
  266. point.color = (pointColor || indicOptions.risingTrendColor ||
  267. indicOptions.color);
  268. groupedPoitns.bottom.push(newPoint);
  269. }
  270. else {
  271. groupedPoitns.intersect.push(newPoint);
  272. groupedPoitns.intersect.push(newNextPoint);
  273. // Additional null point to make a gap in line
  274. groupedPoitns.intersect.push(merge(newNextPoint, {
  275. isNull: true
  276. }));
  277. if (point.y >= mainPoint.close &&
  278. nextPoint.y < nextMainPoint.close) {
  279. point.color = (pointColor || indicOptions.fallingTrendColor ||
  280. indicOptions.color);
  281. nextPoint.color = (pointColor || indicOptions.risingTrendColor ||
  282. indicOptions.color);
  283. groupedPoitns.top.push(newPoint);
  284. groupedPoitns.top.push(merge(newNextPoint, {
  285. isNull: true
  286. }));
  287. }
  288. else if (point.y < mainPoint.close &&
  289. nextPoint.y >= nextMainPoint.close) {
  290. point.color = (pointColor || indicOptions.risingTrendColor ||
  291. indicOptions.color);
  292. nextPoint.color = (pointColor || indicOptions.fallingTrendColor ||
  293. indicOptions.color);
  294. groupedPoitns.bottom.push(newPoint);
  295. groupedPoitns.bottom.push(merge(newNextPoint, {
  296. isNull: true
  297. }));
  298. }
  299. }
  300. }
  301. else if (mainPoint) {
  302. if (point.y >= mainPoint.close) {
  303. point.color = (pointColor || indicOptions.fallingTrendColor ||
  304. indicOptions.color);
  305. groupedPoitns.top.push(newPoint);
  306. }
  307. else {
  308. point.color = (pointColor || indicOptions.risingTrendColor ||
  309. indicOptions.color);
  310. groupedPoitns.bottom.push(newPoint);
  311. }
  312. }
  313. }
  314. // Generate lines:
  315. objectEach(groupedPoitns, function (values, lineName) {
  316. indicator.points = values;
  317. indicator.options = merge(supertrendLineOptions[lineName].styles, gappedExtend);
  318. indicator.graph = indicator['graph' + lineName + 'Line'];
  319. SMA.prototype.drawGraph.call(indicator);
  320. // Now save line
  321. indicator['graph' + lineName + 'Line'] = indicator.graph;
  322. });
  323. // Restore options:
  324. indicator.points = indicPoints;
  325. indicator.options = indicOptions;
  326. indicator.graph = indicPath;
  327. },
  328. // Supertrend (Multiplier, Period) Formula:
  329. // BASIC UPPERBAND = (HIGH + LOW) / 2 + Multiplier * ATR(Period)
  330. // BASIC LOWERBAND = (HIGH + LOW) / 2 - Multiplier * ATR(Period)
  331. // FINAL UPPERBAND =
  332. // IF(
  333. // Current BASICUPPERBAND < Previous FINAL UPPERBAND AND
  334. // Previous Close > Previous FINAL UPPERBAND
  335. // ) THEN (Current BASIC UPPERBAND)
  336. // ELSE (Previous FINALUPPERBAND)
  337. // FINAL LOWERBAND =
  338. // IF(
  339. // Current BASIC LOWERBAND > Previous FINAL LOWERBAND AND
  340. // Previous Close < Previous FINAL LOWERBAND
  341. // ) THEN (Current BASIC LOWERBAND)
  342. // ELSE (Previous FINAL LOWERBAND)
  343. // SUPERTREND =
  344. // IF(
  345. // Previous Supertrend == Previous FINAL UPPERBAND AND
  346. // Current Close < Current FINAL UPPERBAND
  347. // ) THAN Current FINAL UPPERBAND
  348. // ELSE IF(
  349. // Previous Supertrend == Previous FINAL LOWERBAND AND
  350. // Current Close < Current FINAL LOWERBAND
  351. // ) THAN Current FINAL UPPERBAND
  352. // ELSE IF(
  353. // Previous Supertrend == Previous FINAL UPPERBAND AND
  354. // Current Close > Current FINAL UPPERBAND
  355. // ) THAN Current FINAL LOWERBAND
  356. // ELSE IF(
  357. // Previous Supertrend == Previous FINAL LOWERBAND AND
  358. // Current Close > Current FINAL LOWERBAND
  359. // ) THAN Current FINAL LOWERBAND
  360. getValues: function (series, params) {
  361. var period = params.period, multiplier = params.multiplier, xVal = series.xData, yVal = series.yData, ATRData = [],
  362. // 0- date, 1- Supertrend indicator
  363. ST = [], xData = [], yData = [], close = 3, low = 2, high = 1, periodsOffset = (period === 0) ? 0 : period - 1, basicUp, basicDown, finalUp = [], finalDown = [], supertrend, prevFinalUp, prevFinalDown, prevST, // previous Supertrend
  364. prevY, y, i;
  365. if ((xVal.length <= period) || !isArray(yVal[0]) ||
  366. yVal[0].length !== 4 || period < 0) {
  367. return;
  368. }
  369. ATRData = ATR.prototype.getValues.call(this, series, {
  370. period: period
  371. }).yData;
  372. for (i = 0; i < ATRData.length; i++) {
  373. y = yVal[periodsOffset + i];
  374. prevY = yVal[periodsOffset + i - 1] || [];
  375. prevFinalUp = finalUp[i - 1];
  376. prevFinalDown = finalDown[i - 1];
  377. prevST = yData[i - 1];
  378. if (i === 0) {
  379. prevFinalUp = prevFinalDown = prevST = 0;
  380. }
  381. basicUp = correctFloat((y[high] + y[low]) / 2 + multiplier * ATRData[i]);
  382. basicDown = correctFloat((y[high] + y[low]) / 2 - multiplier * ATRData[i]);
  383. if ((basicUp < prevFinalUp) ||
  384. (prevY[close] > prevFinalUp)) {
  385. finalUp[i] = basicUp;
  386. }
  387. else {
  388. finalUp[i] = prevFinalUp;
  389. }
  390. if ((basicDown > prevFinalDown) ||
  391. (prevY[close] < prevFinalDown)) {
  392. finalDown[i] = basicDown;
  393. }
  394. else {
  395. finalDown[i] = prevFinalDown;
  396. }
  397. if (prevST === prevFinalUp && y[close] < finalUp[i] ||
  398. prevST === prevFinalDown && y[close] < finalDown[i]) {
  399. supertrend = finalUp[i];
  400. }
  401. else if (prevST === prevFinalUp && y[close] > finalUp[i] ||
  402. prevST === prevFinalDown && y[close] > finalDown[i]) {
  403. supertrend = finalDown[i];
  404. }
  405. ST.push([xVal[periodsOffset + i], supertrend]);
  406. xData.push(xVal[periodsOffset + i]);
  407. yData.push(supertrend);
  408. }
  409. return {
  410. values: ST,
  411. xData: xData,
  412. yData: yData
  413. };
  414. }
  415. });
  416. /**
  417. * A `Supertrend indicator` series. If the [type](#series.supertrend.type)
  418. * option is not specified, it is inherited from [chart.type](#chart.type).
  419. *
  420. * @extends series,plotOptions.supertrend
  421. * @since 7.0.0
  422. * @product highstock
  423. * @excluding allAreas, colorAxis, cropThreshold, data, dataParser, dataURL,
  424. * joinBy, keys, navigatorOptions, negativeColor, pointInterval,
  425. * pointIntervalUnit, pointPlacement, pointRange, pointStart,
  426. * showInNavigator, stacking, threshold
  427. * @requires stock/indicators/indicators
  428. * @requires stock/indicators/supertrend
  429. * @apioption series.supertrend
  430. */
  431. ''; // to include the above in the js output