volume-by-price.src.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. /**
  2. * @license Highstock JS v9.0.1 (2021-02-16)
  3. *
  4. * Indicator series type for Highstock
  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 Highstock
  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.destroy();
  143. delete indicator.zoneLinesSVG;
  144. }
  145. }
  146. /* eslint-enable require-jsdoc */
  147. // If base series is deleted, indicator series data is filled with
  148. // an empty array
  149. indicator.dataEventsToUnbind.push(addEvent(baseSeries, 'remove', function () {
  150. toEmptyIndicator();
  151. }));
  152. // If volume series is deleted, indicator series data is filled with
  153. // an empty array
  154. if (volumeSeries) {
  155. indicator.dataEventsToUnbind.push(addEvent(volumeSeries, 'remove', function () {
  156. toEmptyIndicator();
  157. }));
  158. }
  159. return indicator;
  160. };
  161. // Initial animation
  162. VBPIndicator.prototype.animate = function (init) {
  163. var series = this,
  164. inverted = series.chart.inverted,
  165. group = series.group,
  166. attr = {},
  167. translate,
  168. position;
  169. if (!init && group) {
  170. translate = inverted ? 'translateY' : 'translateX';
  171. position = inverted ? series.yAxis.top : series.xAxis.left;
  172. group['forceAnimate:' + translate] = true;
  173. attr[translate] = position;
  174. group.animate(attr, extend(animObject(series.options.animation), {
  175. step: function (val, fx) {
  176. series.group.attr({
  177. scaleX: Math.max(0.001, fx.pos)
  178. });
  179. }
  180. }));
  181. }
  182. };
  183. VBPIndicator.prototype.drawPoints = function () {
  184. var indicator = this;
  185. if (indicator.options.volumeDivision.enabled) {
  186. indicator.posNegVolume(true, true);
  187. columnPrototype.drawPoints.apply(indicator, arguments);
  188. indicator.posNegVolume(false, false);
  189. }
  190. columnPrototype.drawPoints.apply(indicator, arguments);
  191. };
  192. // Function responsible for dividing volume into positive and negative
  193. VBPIndicator.prototype.posNegVolume = function (initVol, pos) {
  194. var indicator = this, signOrder = pos ?
  195. ['positive', 'negative'] :
  196. ['negative', 'positive'], volumeDivision = indicator.options.volumeDivision, pointLength = indicator.points.length, posWidths = [], negWidths = [], i = 0, pointWidth, priceZone, wholeVol, point;
  197. if (initVol) {
  198. indicator.posWidths = posWidths;
  199. indicator.negWidths = negWidths;
  200. }
  201. else {
  202. posWidths = indicator.posWidths;
  203. negWidths = indicator.negWidths;
  204. }
  205. for (; i < pointLength; i++) {
  206. point = indicator.points[i];
  207. point[signOrder[0] + 'Graphic'] = point.graphic;
  208. point.graphic = point[signOrder[1] + 'Graphic'];
  209. if (initVol) {
  210. pointWidth = point.shapeArgs.width;
  211. priceZone = indicator.priceZones[i];
  212. wholeVol = priceZone.wholeVolumeData;
  213. if (wholeVol) {
  214. posWidths.push(pointWidth / wholeVol * priceZone.positiveVolumeData);
  215. negWidths.push(pointWidth / wholeVol * priceZone.negativeVolumeData);
  216. }
  217. else {
  218. posWidths.push(0);
  219. negWidths.push(0);
  220. }
  221. }
  222. point.color = pos ?
  223. volumeDivision.styles.positiveColor :
  224. volumeDivision.styles.negativeColor;
  225. point.shapeArgs.width = pos ?
  226. indicator.posWidths[i] :
  227. indicator.negWidths[i];
  228. point.shapeArgs.x = pos ?
  229. point.shapeArgs.x :
  230. indicator.posWidths[i];
  231. }
  232. };
  233. VBPIndicator.prototype.translate = function () {
  234. var indicator = this,
  235. options = indicator.options,
  236. chart = indicator.chart,
  237. yAxis = indicator.yAxis,
  238. yAxisMin = yAxis.min,
  239. zoneLinesOptions = indicator.options.zoneLines,
  240. priceZones = (indicator.priceZones),
  241. yBarOffset = 0,
  242. indicatorPoints,
  243. volumeDataArray,
  244. maxVolume,
  245. primalBarWidth,
  246. barHeight,
  247. barHeightP,
  248. oldBarHeight,
  249. barWidth,
  250. pointPadding,
  251. chartPlotTop,
  252. barX,
  253. barY;
  254. columnPrototype.translate.apply(indicator);
  255. indicatorPoints = indicator.points;
  256. // Do translate operation when points exist
  257. if (indicatorPoints.length) {
  258. pointPadding = options.pointPadding < 0.5 ?
  259. options.pointPadding :
  260. 0.1;
  261. volumeDataArray = indicator.volumeDataArray;
  262. maxVolume = arrayMax(volumeDataArray);
  263. primalBarWidth = chart.plotWidth / 2;
  264. chartPlotTop = chart.plotTop;
  265. barHeight = abs(yAxis.toPixels(yAxisMin) -
  266. yAxis.toPixels(yAxisMin + indicator.rangeStep));
  267. oldBarHeight = abs(yAxis.toPixels(yAxisMin) -
  268. yAxis.toPixels(yAxisMin + indicator.rangeStep));
  269. if (pointPadding) {
  270. barHeightP = abs(barHeight * (1 - 2 * pointPadding));
  271. yBarOffset = abs((barHeight - barHeightP) / 2);
  272. barHeight = abs(barHeightP);
  273. }
  274. indicatorPoints.forEach(function (point, index) {
  275. barX = point.barX = point.plotX = 0;
  276. barY = point.plotY = (yAxis.toPixels(priceZones[index].start) -
  277. chartPlotTop -
  278. (yAxis.reversed ?
  279. (barHeight - oldBarHeight) :
  280. barHeight) -
  281. yBarOffset);
  282. barWidth = correctFloat(primalBarWidth *
  283. priceZones[index].wholeVolumeData / maxVolume);
  284. point.pointWidth = barWidth;
  285. point.shapeArgs = indicator.crispCol.apply(// eslint-disable-line no-useless-call
  286. indicator, [barX, barY, barWidth, barHeight]);
  287. point.volumeNeg = priceZones[index].negativeVolumeData;
  288. point.volumePos = priceZones[index].positiveVolumeData;
  289. point.volumeAll = priceZones[index].wholeVolumeData;
  290. });
  291. if (zoneLinesOptions.enabled) {
  292. indicator.drawZones(chart, yAxis, indicator.zoneStarts, zoneLinesOptions.styles);
  293. }
  294. }
  295. };
  296. VBPIndicator.prototype.getValues = function (series, params) {
  297. var indicator = this,
  298. xValues = series.processedXData,
  299. yValues = series.processedYData,
  300. chart = indicator.chart,
  301. ranges = params.ranges,
  302. VBP = [],
  303. xData = [],
  304. yData = [],
  305. isOHLC,
  306. volumeSeries,
  307. priceZones;
  308. // Checks if base series exists
  309. if (!series.chart) {
  310. error('Base series not found! In case it has been removed, add ' +
  311. 'a new one.', true, chart);
  312. return;
  313. }
  314. // Checks if volume series exists
  315. if (!(volumeSeries = (chart.get(params.volumeSeriesID)))) {
  316. error('Series ' +
  317. params.volumeSeriesID +
  318. ' not found! Check `volumeSeriesID`.', true, chart);
  319. return;
  320. }
  321. // Checks if series data fits the OHLC format
  322. isOHLC = isArray(yValues[0]);
  323. if (isOHLC && yValues[0].length !== 4) {
  324. error('Type of ' +
  325. series.name +
  326. ' series is different than line, OHLC or candlestick.', true, chart);
  327. return;
  328. }
  329. // Price zones contains all the information about the zones (index,
  330. // start, end, volumes, etc.)
  331. priceZones = indicator.priceZones = indicator.specifyZones(isOHLC, xValues, yValues, ranges, volumeSeries);
  332. priceZones.forEach(function (zone, index) {
  333. VBP.push([zone.x, zone.end]);
  334. xData.push(VBP[index][0]);
  335. yData.push(VBP[index][1]);
  336. });
  337. return {
  338. values: VBP,
  339. xData: xData,
  340. yData: yData
  341. };
  342. };
  343. // Specifing where each zone should start ans end
  344. VBPIndicator.prototype.specifyZones = function (isOHLC, xValues, yValues, ranges, volumeSeries) {
  345. var indicator = this,
  346. rangeExtremes = (isOHLC ? arrayExtremesOHLC(yValues) : false),
  347. lowRange = rangeExtremes ?
  348. rangeExtremes.min :
  349. arrayMin(yValues),
  350. highRange = rangeExtremes ?
  351. rangeExtremes.max :
  352. arrayMax(yValues),
  353. zoneStarts = indicator.zoneStarts = [],
  354. priceZones = [],
  355. i = 0,
  356. j = 1,
  357. rangeStep,
  358. zoneStartsLength;
  359. if (!lowRange || !highRange) {
  360. if (this.points.length) {
  361. this.setData([]);
  362. this.zoneStarts = [];
  363. this.zoneLinesSVG.destroy();
  364. }
  365. return [];
  366. }
  367. rangeStep = indicator.rangeStep =
  368. correctFloat(highRange - lowRange) / ranges;
  369. zoneStarts.push(lowRange);
  370. for (; i < ranges - 1; i++) {
  371. zoneStarts.push(correctFloat(zoneStarts[i] + rangeStep));
  372. }
  373. zoneStarts.push(highRange);
  374. zoneStartsLength = zoneStarts.length;
  375. // Creating zones
  376. for (; j < zoneStartsLength; j++) {
  377. priceZones.push({
  378. index: j - 1,
  379. x: xValues[0],
  380. start: zoneStarts[j - 1],
  381. end: zoneStarts[j]
  382. });
  383. }
  384. return indicator.volumePerZone(isOHLC, priceZones, volumeSeries, xValues, yValues);
  385. };
  386. // Calculating sum of volume values for a specific zone
  387. VBPIndicator.prototype.volumePerZone = function (isOHLC, priceZones, volumeSeries, xValues, yValues) {
  388. var indicator = this,
  389. volumeXData = volumeSeries.processedXData,
  390. volumeYData = volumeSeries.processedYData,
  391. lastZoneIndex = priceZones.length - 1,
  392. baseSeriesLength = yValues.length,
  393. volumeSeriesLength = volumeYData.length,
  394. previousValue,
  395. startFlag,
  396. endFlag,
  397. value,
  398. i;
  399. // Checks if each point has a corresponding volume value
  400. if (abs(baseSeriesLength - volumeSeriesLength)) {
  401. // If the first point don't have volume, add 0 value at the
  402. // beggining of the volume array
  403. if (xValues[0] !== volumeXData[0]) {
  404. volumeYData.unshift(0);
  405. }
  406. // If the last point don't have volume, add 0 value at the end
  407. // of the volume array
  408. if (xValues[baseSeriesLength - 1] !==
  409. volumeXData[volumeSeriesLength - 1]) {
  410. volumeYData.push(0);
  411. }
  412. }
  413. indicator.volumeDataArray = [];
  414. priceZones.forEach(function (zone) {
  415. zone.wholeVolumeData = 0;
  416. zone.positiveVolumeData = 0;
  417. zone.negativeVolumeData = 0;
  418. for (i = 0; i < baseSeriesLength; i++) {
  419. startFlag = false;
  420. endFlag = false;
  421. value = isOHLC ? yValues[i][3] : yValues[i];
  422. previousValue = i ?
  423. (isOHLC ?
  424. yValues[i - 1][3] :
  425. yValues[i - 1]) :
  426. value;
  427. // Checks if this is the point with the
  428. // lowest close value and if so, adds it calculations
  429. if (value <= zone.start && zone.index === 0) {
  430. startFlag = true;
  431. }
  432. // Checks if this is the point with the highest
  433. // close value and if so, adds it calculations
  434. if (value >= zone.end && zone.index === lastZoneIndex) {
  435. endFlag = true;
  436. }
  437. if ((value > zone.start || startFlag) &&
  438. (value < zone.end || endFlag)) {
  439. zone.wholeVolumeData += volumeYData[i];
  440. if (previousValue > value) {
  441. zone.negativeVolumeData += volumeYData[i];
  442. }
  443. else {
  444. zone.positiveVolumeData += volumeYData[i];
  445. }
  446. }
  447. }
  448. indicator.volumeDataArray.push(zone.wholeVolumeData);
  449. });
  450. return priceZones;
  451. };
  452. // Function responsoble for drawing additional lines indicating zones
  453. VBPIndicator.prototype.drawZones = function (chart, yAxis, zonesValues, zonesStyles) {
  454. var indicator = this,
  455. renderer = chart.renderer,
  456. zoneLinesSVG = indicator.zoneLinesSVG,
  457. zoneLinesPath = [],
  458. leftLinePos = 0,
  459. rightLinePos = chart.plotWidth,
  460. verticalOffset = chart.plotTop,
  461. verticalLinePos;
  462. zonesValues.forEach(function (value) {
  463. verticalLinePos = yAxis.toPixels(value) - verticalOffset;
  464. zoneLinesPath = zoneLinesPath.concat(chart.renderer.crispLine([[
  465. 'M',
  466. leftLinePos,
  467. verticalLinePos
  468. ], [
  469. 'L',
  470. rightLinePos,
  471. verticalLinePos
  472. ]], zonesStyles.lineWidth));
  473. });
  474. // Create zone lines one path or update it while animating
  475. if (zoneLinesSVG) {
  476. zoneLinesSVG.animate({
  477. d: zoneLinesPath
  478. });
  479. }
  480. else {
  481. zoneLinesSVG = indicator.zoneLinesSVG =
  482. renderer.path(zoneLinesPath).attr({
  483. 'stroke-width': zonesStyles.lineWidth,
  484. 'stroke': zonesStyles.color,
  485. 'dashstyle': zonesStyles.dashStyle,
  486. 'zIndex': indicator.group.zIndex + 0.1
  487. })
  488. .add(indicator.group);
  489. }
  490. };
  491. /**
  492. * Volume By Price indicator.
  493. *
  494. * This series requires `linkedTo` option to be set.
  495. *
  496. * @sample stock/indicators/volume-by-price
  497. * Volume By Price indicator
  498. *
  499. * @extends plotOptions.sma
  500. * @since 6.0.0
  501. * @product highstock
  502. * @requires stock/indicators/indicators
  503. * @requires stock/indicators/volume-by-price
  504. * @optionparent plotOptions.vbp
  505. */
  506. VBPIndicator.defaultOptions = merge(SMAIndicator.defaultOptions, {
  507. /**
  508. * @excluding index, period
  509. */
  510. params: {
  511. /**
  512. * The number of price zones.
  513. */
  514. ranges: 12,
  515. /**
  516. * The id of volume series which is mandatory. For example using
  517. * OHLC data, volumeSeriesID='volume' means the indicator will be
  518. * calculated using OHLC and volume values.
  519. */
  520. volumeSeriesID: 'volume'
  521. },
  522. /**
  523. * The styles for lines which determine price zones.
  524. */
  525. zoneLines: {
  526. /**
  527. * Enable/disable zone lines.
  528. */
  529. enabled: true,
  530. /**
  531. * Specify the style of zone lines.
  532. *
  533. * @type {Highcharts.CSSObject}
  534. * @default {"color": "#0A9AC9", "dashStyle": "LongDash", "lineWidth": 1}
  535. */
  536. styles: {
  537. /** @ignore-options */
  538. color: '#0A9AC9',
  539. /** @ignore-options */
  540. dashStyle: 'LongDash',
  541. /** @ignore-options */
  542. lineWidth: 1
  543. }
  544. },
  545. /**
  546. * The styles for bars when volume is divided into positive/negative.
  547. */
  548. volumeDivision: {
  549. /**
  550. * Option to control if volume is divided.
  551. */
  552. enabled: true,
  553. styles: {
  554. /**
  555. * Color of positive volume bars.
  556. *
  557. * @type {Highcharts.ColorString}
  558. */
  559. positiveColor: 'rgba(144, 237, 125, 0.8)',
  560. /**
  561. * Color of negative volume bars.
  562. *
  563. * @type {Highcharts.ColorString}
  564. */
  565. negativeColor: 'rgba(244, 91, 91, 0.8)'
  566. }
  567. },
  568. // To enable series animation; must be animationLimit > pointCount
  569. animationLimit: 1000,
  570. enableMouseTracking: false,
  571. pointPadding: 0,
  572. zIndex: -1,
  573. crisp: true,
  574. dataGrouping: {
  575. enabled: false
  576. },
  577. dataLabels: {
  578. allowOverlap: true,
  579. enabled: true,
  580. format: 'P: {point.volumePos:.2f} | N: {point.volumeNeg:.2f}',
  581. padding: 0,
  582. style: {
  583. /** @internal */
  584. fontSize: '7px'
  585. },
  586. verticalAlign: 'top'
  587. }
  588. });
  589. return VBPIndicator;
  590. }(SMAIndicator));
  591. extend(VBPIndicator.prototype, {
  592. nameBase: 'Volume by Price',
  593. bindTo: {
  594. series: false,
  595. eventName: 'afterSetExtremes'
  596. },
  597. calculateOn: 'render',
  598. markerAttribs: noop,
  599. drawGraph: noop,
  600. getColumnMetrics: columnPrototype.getColumnMetrics,
  601. crispCol: columnPrototype.crispCol
  602. });
  603. SeriesRegistry.registerSeriesType('vbp', VBPIndicator);
  604. /* *
  605. *
  606. * Default Export
  607. *
  608. * */
  609. /**
  610. * A `Volume By Price (VBP)` series. If the [type](#series.vbp.type) option is
  611. * not specified, it is inherited from [chart.type](#chart.type).
  612. *
  613. * @extends series,plotOptions.vbp
  614. * @since 6.0.0
  615. * @product highstock
  616. * @excluding dataParser, dataURL
  617. * @requires stock/indicators/indicators
  618. * @requires stock/indicators/volume-by-price
  619. * @apioption series.vbp
  620. */
  621. ''; // to include the above in the js output
  622. return VBPIndicator;
  623. });
  624. _registerModule(_modules, 'masters/indicators/volume-by-price.src.js', [], function () {
  625. });
  626. }));