volume-by-price.src.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /**
  2. * @license Highstock JS v9.1.1 (2021-06-04)
  3. *
  4. * Indicator series type for Highcharts Stock
  5. *
  6. * (c) 2010-2021 Paweł Dalek
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/indicators/volume-by-price', ['highcharts', 'highcharts/modules/stock'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Stock/Indicators/VBP/VBPIndicator.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Globals.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (A, H, SeriesRegistry, U) {
  32. /* *
  33. *
  34. * (c) 2010-2021 Paweł Dalek
  35. *
  36. * Volume By Price (VBP) indicator for Highcharts Stock
  37. *
  38. * License: www.highcharts.com/license
  39. *
  40. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41. *
  42. * */
  43. var __extends = (this && this.__extends) || (function () {
  44. var extendStatics = function (d,
  45. b) {
  46. extendStatics = Object.setPrototypeOf ||
  47. ({ __proto__: [] } instanceof Array && function (d,
  48. b) { d.__proto__ = b; }) ||
  49. function (d,
  50. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  51. return extendStatics(d, b);
  52. };
  53. return function (d, b) {
  54. extendStatics(d, b);
  55. function __() { this.constructor = d; }
  56. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  57. };
  58. })();
  59. var animObject = A.animObject;
  60. var noop = H.noop;
  61. var SMAIndicator = SeriesRegistry.seriesTypes.sma;
  62. var addEvent = U.addEvent,
  63. arrayMax = U.arrayMax,
  64. arrayMin = U.arrayMin,
  65. correctFloat = U.correctFloat,
  66. error = U.error,
  67. extend = U.extend,
  68. isArray = U.isArray,
  69. merge = U.merge;
  70. /* eslint-disable require-jsdoc */
  71. // Utils
  72. function arrayExtremesOHLC(data) {
  73. var dataLength = data.length,
  74. min = data[0][3],
  75. max = min,
  76. i = 1,
  77. currentPoint;
  78. for (; i < dataLength; i++) {
  79. currentPoint = data[i][3];
  80. if (currentPoint < min) {
  81. min = currentPoint;
  82. }
  83. if (currentPoint > max) {
  84. max = currentPoint;
  85. }
  86. }
  87. return {
  88. min: min,
  89. max: max
  90. };
  91. }
  92. /* eslint-enable require-jsdoc */
  93. var abs = Math.abs,
  94. columnPrototype = SeriesRegistry.seriesTypes.column.prototype;
  95. /**
  96. * The Volume By Price (VBP) series type.
  97. *
  98. * @private
  99. * @class
  100. * @name Highcharts.seriesTypes.vbp
  101. *
  102. * @augments Highcharts.Series
  103. */
  104. var VBPIndicator = /** @class */ (function (_super) {
  105. __extends(VBPIndicator, _super);
  106. function VBPIndicator() {
  107. var _this = _super !== null && _super.apply(this,
  108. arguments) || this;
  109. _this.data = void 0;
  110. _this.negWidths = void 0;
  111. _this.options = void 0;
  112. _this.points = void 0;
  113. _this.posWidths = void 0;
  114. _this.priceZones = void 0;
  115. _this.rangeStep = void 0;
  116. _this.volumeDataArray = void 0;
  117. _this.zoneStarts = void 0;
  118. _this.zoneLinesSVG = void 0;
  119. return _this;
  120. }
  121. VBPIndicator.prototype.init = function (chart) {
  122. var indicator = this,
  123. params,
  124. baseSeries,
  125. volumeSeries;
  126. H.seriesTypes.sma.prototype.init.apply(indicator, arguments);
  127. params = indicator.options.params;
  128. baseSeries = indicator.linkedParent;
  129. volumeSeries = chart.get(params.volumeSeriesID);
  130. indicator.addCustomEvents(baseSeries, volumeSeries);
  131. return indicator;
  132. };
  133. // Adds events related with removing series
  134. VBPIndicator.prototype.addCustomEvents = function (baseSeries, volumeSeries) {
  135. var indicator = this;
  136. /* eslint-disable require-jsdoc */
  137. function toEmptyIndicator() {
  138. indicator.chart.redraw();
  139. indicator.setData([]);
  140. indicator.zoneStarts = [];
  141. if (indicator.zoneLinesSVG) {
  142. indicator.zoneLinesSVG = indicator.zoneLinesSVG.destroy();
  143. }
  144. }
  145. /* eslint-enable require-jsdoc */
  146. // If base series is deleted, indicator series data is filled with
  147. // an empty array
  148. indicator.dataEventsToUnbind.push(addEvent(baseSeries, 'remove', function () {
  149. toEmptyIndicator();
  150. }));
  151. // If volume series is deleted, indicator series data is filled with
  152. // an empty array
  153. if (volumeSeries) {
  154. indicator.dataEventsToUnbind.push(addEvent(volumeSeries, 'remove', function () {
  155. toEmptyIndicator();
  156. }));
  157. }
  158. return indicator;
  159. };
  160. // Initial animation
  161. VBPIndicator.prototype.animate = function (init) {
  162. var series = this,
  163. inverted = series.chart.inverted,
  164. group = series.group,
  165. attr = {},
  166. position;
  167. if (!init && group) {
  168. position = inverted ? series.yAxis.top : series.xAxis.left;
  169. if (inverted) {
  170. group['forceAnimate:translateY'] = true;
  171. attr.translateY = position;
  172. }
  173. else {
  174. group['forceAnimate:translateX'] = true;
  175. attr.translateX = position;
  176. }
  177. group.animate(attr, extend(animObject(series.options.animation), {
  178. step: function (val, fx) {
  179. series.group.attr({
  180. scaleX: Math.max(0.001, fx.pos)
  181. });
  182. }
  183. }));
  184. }
  185. };
  186. VBPIndicator.prototype.drawPoints = function () {
  187. var indicator = this;
  188. if (indicator.options.volumeDivision.enabled) {
  189. indicator.posNegVolume(true, true);
  190. columnPrototype.drawPoints.apply(indicator, arguments);
  191. indicator.posNegVolume(false, false);
  192. }
  193. columnPrototype.drawPoints.apply(indicator, arguments);
  194. };
  195. // Function responsible for dividing volume into positive and negative
  196. VBPIndicator.prototype.posNegVolume = function (initVol, pos) {
  197. var indicator = this, signOrder = pos ?
  198. ['positive', 'negative'] :
  199. ['negative', 'positive'], volumeDivision = indicator.options.volumeDivision, pointLength = indicator.points.length, posWidths = [], negWidths = [], i = 0, pointWidth, priceZone, wholeVol, point;
  200. if (initVol) {
  201. indicator.posWidths = posWidths;
  202. indicator.negWidths = negWidths;
  203. }
  204. else {
  205. posWidths = indicator.posWidths;
  206. negWidths = indicator.negWidths;
  207. }
  208. for (; i < pointLength; i++) {
  209. point = indicator.points[i];
  210. point[signOrder[0] + 'Graphic'] = point.graphic;
  211. point.graphic = point[signOrder[1] + 'Graphic'];
  212. if (initVol) {
  213. pointWidth = point.shapeArgs.width;
  214. priceZone = indicator.priceZones[i];
  215. wholeVol = priceZone.wholeVolumeData;
  216. if (wholeVol) {
  217. posWidths.push(pointWidth / wholeVol * priceZone.positiveVolumeData);
  218. negWidths.push(pointWidth / wholeVol * priceZone.negativeVolumeData);
  219. }
  220. else {
  221. posWidths.push(0);
  222. negWidths.push(0);
  223. }
  224. }
  225. point.color = pos ?
  226. volumeDivision.styles.positiveColor :
  227. volumeDivision.styles.negativeColor;
  228. point.shapeArgs.width = pos ?
  229. indicator.posWidths[i] :
  230. indicator.negWidths[i];
  231. point.shapeArgs.x = pos ?
  232. point.shapeArgs.x :
  233. indicator.posWidths[i];
  234. }
  235. };
  236. VBPIndicator.prototype.translate = function () {
  237. var indicator = this,
  238. options = indicator.options,
  239. chart = indicator.chart,
  240. yAxis = indicator.yAxis,
  241. yAxisMin = yAxis.min,
  242. zoneLinesOptions = indicator.options.zoneLines,
  243. priceZones = (indicator.priceZones),
  244. yBarOffset = 0,
  245. indicatorPoints,
  246. volumeDataArray,
  247. maxVolume,
  248. primalBarWidth,
  249. barHeight,
  250. barHeightP,
  251. oldBarHeight,
  252. barWidth,
  253. pointPadding,
  254. chartPlotTop,
  255. barX,
  256. barY;
  257. columnPrototype.translate.apply(indicator);
  258. indicatorPoints = indicator.points;
  259. // Do translate operation when points exist
  260. if (indicatorPoints.length) {
  261. pointPadding = options.pointPadding < 0.5 ?
  262. options.pointPadding :
  263. 0.1;
  264. volumeDataArray = indicator.volumeDataArray;
  265. maxVolume = arrayMax(volumeDataArray);
  266. primalBarWidth = chart.plotWidth / 2;
  267. chartPlotTop = chart.plotTop;
  268. barHeight = abs(yAxis.toPixels(yAxisMin) -
  269. yAxis.toPixels(yAxisMin + indicator.rangeStep));
  270. oldBarHeight = abs(yAxis.toPixels(yAxisMin) -
  271. yAxis.toPixels(yAxisMin + indicator.rangeStep));
  272. if (pointPadding) {
  273. barHeightP = abs(barHeight * (1 - 2 * pointPadding));
  274. yBarOffset = abs((barHeight - barHeightP) / 2);
  275. barHeight = abs(barHeightP);
  276. }
  277. indicatorPoints.forEach(function (point, index) {
  278. barX = point.barX = point.plotX = 0;
  279. barY = point.plotY = (yAxis.toPixels(priceZones[index].start) -
  280. chartPlotTop -
  281. (yAxis.reversed ?
  282. (barHeight - oldBarHeight) :
  283. barHeight) -
  284. yBarOffset);
  285. barWidth = correctFloat(primalBarWidth *
  286. priceZones[index].wholeVolumeData / maxVolume);
  287. point.pointWidth = barWidth;
  288. point.shapeArgs = indicator.crispCol.apply(// eslint-disable-line no-useless-call
  289. indicator, [barX, barY, barWidth, barHeight]);
  290. point.volumeNeg = priceZones[index].negativeVolumeData;
  291. point.volumePos = priceZones[index].positiveVolumeData;
  292. point.volumeAll = priceZones[index].wholeVolumeData;
  293. });
  294. if (zoneLinesOptions.enabled) {
  295. indicator.drawZones(chart, yAxis, indicator.zoneStarts, zoneLinesOptions.styles);
  296. }
  297. }
  298. };
  299. VBPIndicator.prototype.getValues = function (series, params) {
  300. var indicator = this,
  301. xValues = series.processedXData,
  302. yValues = series.processedYData,
  303. chart = indicator.chart,
  304. ranges = params.ranges,
  305. VBP = [],
  306. xData = [],
  307. yData = [],
  308. isOHLC,
  309. volumeSeries,
  310. priceZones;
  311. // Checks if base series exists
  312. if (!series.chart) {
  313. error('Base series not found! In case it has been removed, add ' +
  314. 'a new one.', true, chart);
  315. return;
  316. }
  317. // Checks if volume series exists
  318. if (!(volumeSeries = (chart.get(params.volumeSeriesID)))) {
  319. error('Series ' +
  320. params.volumeSeriesID +
  321. ' not found! Check `volumeSeriesID`.', true, chart);
  322. return;
  323. }
  324. // Checks if series data fits the OHLC format
  325. isOHLC = isArray(yValues[0]);
  326. if (isOHLC && yValues[0].length !== 4) {
  327. error('Type of ' +
  328. series.name +
  329. ' series is different than line, OHLC or candlestick.', true, chart);
  330. return;
  331. }
  332. // Price zones contains all the information about the zones (index,
  333. // start, end, volumes, etc.)
  334. priceZones = indicator.priceZones = indicator.specifyZones(isOHLC, xValues, yValues, ranges, volumeSeries);
  335. priceZones.forEach(function (zone, index) {
  336. VBP.push([zone.x, zone.end]);
  337. xData.push(VBP[index][0]);
  338. yData.push(VBP[index][1]);
  339. });
  340. return {
  341. values: VBP,
  342. xData: xData,
  343. yData: yData
  344. };
  345. };
  346. // Specifing where each zone should start ans end
  347. VBPIndicator.prototype.specifyZones = function (isOHLC, xValues, yValues, ranges, volumeSeries) {
  348. var indicator = this,
  349. rangeExtremes = (isOHLC ? arrayExtremesOHLC(yValues) : false),
  350. lowRange = rangeExtremes ?
  351. rangeExtremes.min :
  352. arrayMin(yValues),
  353. highRange = rangeExtremes ?
  354. rangeExtremes.max :
  355. arrayMax(yValues),
  356. zoneStarts = indicator.zoneStarts = [],
  357. priceZones = [],
  358. i = 0,
  359. j = 1,
  360. rangeStep,
  361. zoneStartsLength;
  362. if (!lowRange || !highRange) {
  363. if (this.points.length) {
  364. this.setData([]);
  365. this.zoneStarts = [];
  366. if (this.zoneLinesSVG) {
  367. this.zoneLinesSVG = this.zoneLinesSVG.destroy();
  368. }
  369. }
  370. return [];
  371. }
  372. rangeStep = indicator.rangeStep =
  373. correctFloat(highRange - lowRange) / ranges;
  374. zoneStarts.push(lowRange);
  375. for (; i < ranges - 1; i++) {
  376. zoneStarts.push(correctFloat(zoneStarts[i] + rangeStep));
  377. }
  378. zoneStarts.push(highRange);
  379. zoneStartsLength = zoneStarts.length;
  380. // Creating zones
  381. for (; j < zoneStartsLength; j++) {
  382. priceZones.push({
  383. index: j - 1,
  384. x: xValues[0],
  385. start: zoneStarts[j - 1],
  386. end: zoneStarts[j]
  387. });
  388. }
  389. return indicator.volumePerZone(isOHLC, priceZones, volumeSeries, xValues, yValues);
  390. };
  391. // Calculating sum of volume values for a specific zone
  392. VBPIndicator.prototype.volumePerZone = function (isOHLC, priceZones, volumeSeries, xValues, yValues) {
  393. var indicator = this,
  394. volumeXData = volumeSeries.processedXData,
  395. volumeYData = volumeSeries.processedYData,
  396. lastZoneIndex = priceZones.length - 1,
  397. baseSeriesLength = yValues.length,
  398. volumeSeriesLength = volumeYData.length,
  399. previousValue,
  400. startFlag,
  401. endFlag,
  402. value,
  403. i;
  404. // Checks if each point has a corresponding volume value
  405. if (abs(baseSeriesLength - volumeSeriesLength)) {
  406. // If the first point don't have volume, add 0 value at the
  407. // beggining of the volume array
  408. if (xValues[0] !== volumeXData[0]) {
  409. volumeYData.unshift(0);
  410. }
  411. // If the last point don't have volume, add 0 value at the end
  412. // of the volume array
  413. if (xValues[baseSeriesLength - 1] !==
  414. volumeXData[volumeSeriesLength - 1]) {
  415. volumeYData.push(0);
  416. }
  417. }
  418. indicator.volumeDataArray = [];
  419. priceZones.forEach(function (zone) {
  420. zone.wholeVolumeData = 0;
  421. zone.positiveVolumeData = 0;
  422. zone.negativeVolumeData = 0;
  423. for (i = 0; i < baseSeriesLength; i++) {
  424. startFlag = false;
  425. endFlag = false;
  426. value = isOHLC ? yValues[i][3] : yValues[i];
  427. previousValue = i ?
  428. (isOHLC ?
  429. yValues[i - 1][3] :
  430. yValues[i - 1]) :
  431. value;
  432. // Checks if this is the point with the
  433. // lowest close value and if so, adds it calculations
  434. if (value <= zone.start && zone.index === 0) {
  435. startFlag = true;
  436. }
  437. // Checks if this is the point with the highest
  438. // close value and if so, adds it calculations
  439. if (value >= zone.end && zone.index === lastZoneIndex) {
  440. endFlag = true;
  441. }
  442. if ((value > zone.start || startFlag) &&
  443. (value < zone.end || endFlag)) {
  444. zone.wholeVolumeData += volumeYData[i];
  445. if (previousValue > value) {
  446. zone.negativeVolumeData += volumeYData[i];
  447. }
  448. else {
  449. zone.positiveVolumeData += volumeYData[i];
  450. }
  451. }
  452. }
  453. indicator.volumeDataArray.push(zone.wholeVolumeData);
  454. });
  455. return priceZones;
  456. };
  457. // Function responsoble for drawing additional lines indicating zones
  458. VBPIndicator.prototype.drawZones = function (chart, yAxis, zonesValues, zonesStyles) {
  459. var indicator = this,
  460. renderer = chart.renderer,
  461. zoneLinesSVG = indicator.zoneLinesSVG,
  462. zoneLinesPath = [],
  463. leftLinePos = 0,
  464. rightLinePos = chart.plotWidth,
  465. verticalOffset = chart.plotTop,
  466. verticalLinePos;
  467. zonesValues.forEach(function (value) {
  468. verticalLinePos = yAxis.toPixels(value) - verticalOffset;
  469. zoneLinesPath = zoneLinesPath.concat(chart.renderer.crispLine([[
  470. 'M',
  471. leftLinePos,
  472. verticalLinePos
  473. ], [
  474. 'L',
  475. rightLinePos,
  476. verticalLinePos
  477. ]], zonesStyles.lineWidth));
  478. });
  479. // Create zone lines one path or update it while animating
  480. if (zoneLinesSVG) {
  481. zoneLinesSVG.animate({
  482. d: zoneLinesPath
  483. });
  484. }
  485. else {
  486. zoneLinesSVG = indicator.zoneLinesSVG =
  487. renderer.path(zoneLinesPath).attr({
  488. 'stroke-width': zonesStyles.lineWidth,
  489. 'stroke': zonesStyles.color,
  490. 'dashstyle': zonesStyles.dashStyle,
  491. 'zIndex': indicator.group.zIndex + 0.1
  492. })
  493. .add(indicator.group);
  494. }
  495. };
  496. /**
  497. * Volume By Price indicator.
  498. *
  499. * This series requires `linkedTo` option to be set.
  500. *
  501. * @sample stock/indicators/volume-by-price
  502. * Volume By Price indicator
  503. *
  504. * @extends plotOptions.sma
  505. * @since 6.0.0
  506. * @product highstock
  507. * @requires stock/indicators/indicators
  508. * @requires stock/indicators/volume-by-price
  509. * @optionparent plotOptions.vbp
  510. */
  511. VBPIndicator.defaultOptions = merge(SMAIndicator.defaultOptions, {
  512. /**
  513. * @excluding index, period
  514. */
  515. params: {
  516. // Index and period are unchangeable, do not inherit (#15362)
  517. index: void 0,
  518. period: void 0,
  519. /**
  520. * The number of price zones.
  521. */
  522. ranges: 12,
  523. /**
  524. * The id of volume series which is mandatory. For example using
  525. * OHLC data, volumeSeriesID='volume' means the indicator will be
  526. * calculated using OHLC and volume values.
  527. */
  528. volumeSeriesID: 'volume'
  529. },
  530. /**
  531. * The styles for lines which determine price zones.
  532. */
  533. zoneLines: {
  534. /**
  535. * Enable/disable zone lines.
  536. */
  537. enabled: true,
  538. /**
  539. * Specify the style of zone lines.
  540. *
  541. * @type {Highcharts.CSSObject}
  542. * @default {"color": "#0A9AC9", "dashStyle": "LongDash", "lineWidth": 1}
  543. */
  544. styles: {
  545. /** @ignore-options */
  546. color: '#0A9AC9',
  547. /** @ignore-options */
  548. dashStyle: 'LongDash',
  549. /** @ignore-options */
  550. lineWidth: 1
  551. }
  552. },
  553. /**
  554. * The styles for bars when volume is divided into positive/negative.
  555. */
  556. volumeDivision: {
  557. /**
  558. * Option to control if volume is divided.
  559. */
  560. enabled: true,
  561. styles: {
  562. /**
  563. * Color of positive volume bars.
  564. *
  565. * @type {Highcharts.ColorString}
  566. */
  567. positiveColor: 'rgba(144, 237, 125, 0.8)',
  568. /**
  569. * Color of negative volume bars.
  570. *
  571. * @type {Highcharts.ColorString}
  572. */
  573. negativeColor: 'rgba(244, 91, 91, 0.8)'
  574. }
  575. },
  576. // To enable series animation; must be animationLimit > pointCount
  577. animationLimit: 1000,
  578. enableMouseTracking: false,
  579. pointPadding: 0,
  580. zIndex: -1,
  581. crisp: true,
  582. dataGrouping: {
  583. enabled: false
  584. },
  585. dataLabels: {
  586. allowOverlap: true,
  587. enabled: true,
  588. format: 'P: {point.volumePos:.2f} | N: {point.volumeNeg:.2f}',
  589. padding: 0,
  590. style: {
  591. /** @internal */
  592. fontSize: '7px'
  593. },
  594. verticalAlign: 'top'
  595. }
  596. });
  597. return VBPIndicator;
  598. }(SMAIndicator));
  599. extend(VBPIndicator.prototype, {
  600. nameBase: 'Volume by Price',
  601. nameComponents: ['ranges'],
  602. bindTo: {
  603. series: false,
  604. eventName: 'afterSetExtremes'
  605. },
  606. calculateOn: 'render',
  607. markerAttribs: noop,
  608. drawGraph: noop,
  609. getColumnMetrics: columnPrototype.getColumnMetrics,
  610. crispCol: columnPrototype.crispCol
  611. });
  612. SeriesRegistry.registerSeriesType('vbp', VBPIndicator);
  613. /* *
  614. *
  615. * Default Export
  616. *
  617. * */
  618. /**
  619. * A `Volume By Price (VBP)` series. If the [type](#series.vbp.type) option is
  620. * not specified, it is inherited from [chart.type](#chart.type).
  621. *
  622. * @extends series,plotOptions.vbp
  623. * @since 6.0.0
  624. * @product highstock
  625. * @excluding dataParser, dataURL
  626. * @requires stock/indicators/indicators
  627. * @requires stock/indicators/volume-by-price
  628. * @apioption series.vbp
  629. */
  630. ''; // to include the above in the js output
  631. return VBPIndicator;
  632. });
  633. _registerModule(_modules, 'masters/indicators/volume-by-price.src.js', [], function () {
  634. });
  635. }));