hyperprop-charting-library 0.1.25 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/hyperprop-charting-library.cjs +17 -3
- package/dist/hyperprop-charting-library.js +17 -3
- package/dist/index.cjs +17 -3
- package/dist/index.js +17 -3
- package/docs/API.md +2 -0
- package/docs/RECIPES.md +9 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -136,6 +136,17 @@ chart.updateIndicator(emaId, { inputs: { length: 55 } });
|
|
|
136
136
|
chart.updateIndicator(volumeId, { paneHeightRatio: 0.1 });
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
Volume scaling tip (for large spikes):
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
chart.updateIndicator(volumeId, {
|
|
143
|
+
inputs: {
|
|
144
|
+
scaleMode: "visible", // default; scales using current viewport only
|
|
145
|
+
clampPercentile: 0.95 // optional outlier clamp
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
139
150
|
Autoscale controls per indicator:
|
|
140
151
|
|
|
141
152
|
```ts
|
|
@@ -509,6 +509,15 @@ var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride,
|
|
|
509
509
|
}
|
|
510
510
|
ctx.restore();
|
|
511
511
|
};
|
|
512
|
+
var getPercentileValue = (values, percentile) => {
|
|
513
|
+
if (values.length === 0) {
|
|
514
|
+
return 1;
|
|
515
|
+
}
|
|
516
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
517
|
+
const clamped = Math.min(1, Math.max(0, percentile));
|
|
518
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(clamped * sorted.length) - 1));
|
|
519
|
+
return sorted[index] ?? 1;
|
|
520
|
+
};
|
|
512
521
|
var BUILTIN_VOLUME_INDICATOR = {
|
|
513
522
|
id: "volume",
|
|
514
523
|
name: "Volume",
|
|
@@ -520,7 +529,9 @@ var BUILTIN_VOLUME_INDICATOR = {
|
|
|
520
529
|
upColor: "",
|
|
521
530
|
downColor: "",
|
|
522
531
|
minBarWidth: 1,
|
|
523
|
-
overlayHeightRatio: 0.22
|
|
532
|
+
overlayHeightRatio: 0.22,
|
|
533
|
+
scaleMode: "visible",
|
|
534
|
+
clampPercentile: 1
|
|
524
535
|
},
|
|
525
536
|
draw: (ctx, renderContext, inputs) => {
|
|
526
537
|
const { data, startIndex, endIndex, chartTop, chartBottom, candleSpacing, xFromIndex, upColor, downColor } = renderContext;
|
|
@@ -529,8 +540,11 @@ var BUILTIN_VOLUME_INDICATOR = {
|
|
|
529
540
|
const overlayHeightRatio = Math.min(0.45, Math.max(0.08, Number(inputs.overlayHeightRatio) || 0.22));
|
|
530
541
|
const paneHeight = overlayMode ? Math.max(20, Math.round(fullPaneHeight * overlayHeightRatio)) : fullPaneHeight;
|
|
531
542
|
const paneBottom = chartBottom;
|
|
532
|
-
const
|
|
533
|
-
const
|
|
543
|
+
const scaleMode = inputs.scaleMode === "full" ? "full" : "visible";
|
|
544
|
+
const scalingPoints = scaleMode === "full" ? data : data.slice(startIndex, endIndex + 1);
|
|
545
|
+
const scalingVolumes = scalingPoints.map((point) => Math.max(0, point.v ?? 0)).filter((value) => value > 0);
|
|
546
|
+
const clampPercentile = Number.isFinite(inputs.clampPercentile) ? Number(inputs.clampPercentile) : 1;
|
|
547
|
+
const maxVolume = Math.max(1, getPercentileValue(scalingVolumes, clampPercentile));
|
|
534
548
|
const barWidth = Math.max(
|
|
535
549
|
Math.max(1, Number(inputs.minBarWidth) || 1),
|
|
536
550
|
Math.min(Math.max(1, candleSpacing - 1), Math.floor(candleSpacing * 0.7))
|
|
@@ -485,6 +485,15 @@ var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride,
|
|
|
485
485
|
}
|
|
486
486
|
ctx.restore();
|
|
487
487
|
};
|
|
488
|
+
var getPercentileValue = (values, percentile) => {
|
|
489
|
+
if (values.length === 0) {
|
|
490
|
+
return 1;
|
|
491
|
+
}
|
|
492
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
493
|
+
const clamped = Math.min(1, Math.max(0, percentile));
|
|
494
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(clamped * sorted.length) - 1));
|
|
495
|
+
return sorted[index] ?? 1;
|
|
496
|
+
};
|
|
488
497
|
var BUILTIN_VOLUME_INDICATOR = {
|
|
489
498
|
id: "volume",
|
|
490
499
|
name: "Volume",
|
|
@@ -496,7 +505,9 @@ var BUILTIN_VOLUME_INDICATOR = {
|
|
|
496
505
|
upColor: "",
|
|
497
506
|
downColor: "",
|
|
498
507
|
minBarWidth: 1,
|
|
499
|
-
overlayHeightRatio: 0.22
|
|
508
|
+
overlayHeightRatio: 0.22,
|
|
509
|
+
scaleMode: "visible",
|
|
510
|
+
clampPercentile: 1
|
|
500
511
|
},
|
|
501
512
|
draw: (ctx, renderContext, inputs) => {
|
|
502
513
|
const { data, startIndex, endIndex, chartTop, chartBottom, candleSpacing, xFromIndex, upColor, downColor } = renderContext;
|
|
@@ -505,8 +516,11 @@ var BUILTIN_VOLUME_INDICATOR = {
|
|
|
505
516
|
const overlayHeightRatio = Math.min(0.45, Math.max(0.08, Number(inputs.overlayHeightRatio) || 0.22));
|
|
506
517
|
const paneHeight = overlayMode ? Math.max(20, Math.round(fullPaneHeight * overlayHeightRatio)) : fullPaneHeight;
|
|
507
518
|
const paneBottom = chartBottom;
|
|
508
|
-
const
|
|
509
|
-
const
|
|
519
|
+
const scaleMode = inputs.scaleMode === "full" ? "full" : "visible";
|
|
520
|
+
const scalingPoints = scaleMode === "full" ? data : data.slice(startIndex, endIndex + 1);
|
|
521
|
+
const scalingVolumes = scalingPoints.map((point) => Math.max(0, point.v ?? 0)).filter((value) => value > 0);
|
|
522
|
+
const clampPercentile = Number.isFinite(inputs.clampPercentile) ? Number(inputs.clampPercentile) : 1;
|
|
523
|
+
const maxVolume = Math.max(1, getPercentileValue(scalingVolumes, clampPercentile));
|
|
510
524
|
const barWidth = Math.max(
|
|
511
525
|
Math.max(1, Number(inputs.minBarWidth) || 1),
|
|
512
526
|
Math.min(Math.max(1, candleSpacing - 1), Math.floor(candleSpacing * 0.7))
|
package/dist/index.cjs
CHANGED
|
@@ -509,6 +509,15 @@ var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride,
|
|
|
509
509
|
}
|
|
510
510
|
ctx.restore();
|
|
511
511
|
};
|
|
512
|
+
var getPercentileValue = (values, percentile) => {
|
|
513
|
+
if (values.length === 0) {
|
|
514
|
+
return 1;
|
|
515
|
+
}
|
|
516
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
517
|
+
const clamped = Math.min(1, Math.max(0, percentile));
|
|
518
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(clamped * sorted.length) - 1));
|
|
519
|
+
return sorted[index] ?? 1;
|
|
520
|
+
};
|
|
512
521
|
var BUILTIN_VOLUME_INDICATOR = {
|
|
513
522
|
id: "volume",
|
|
514
523
|
name: "Volume",
|
|
@@ -520,7 +529,9 @@ var BUILTIN_VOLUME_INDICATOR = {
|
|
|
520
529
|
upColor: "",
|
|
521
530
|
downColor: "",
|
|
522
531
|
minBarWidth: 1,
|
|
523
|
-
overlayHeightRatio: 0.22
|
|
532
|
+
overlayHeightRatio: 0.22,
|
|
533
|
+
scaleMode: "visible",
|
|
534
|
+
clampPercentile: 1
|
|
524
535
|
},
|
|
525
536
|
draw: (ctx, renderContext, inputs) => {
|
|
526
537
|
const { data, startIndex, endIndex, chartTop, chartBottom, candleSpacing, xFromIndex, upColor, downColor } = renderContext;
|
|
@@ -529,8 +540,11 @@ var BUILTIN_VOLUME_INDICATOR = {
|
|
|
529
540
|
const overlayHeightRatio = Math.min(0.45, Math.max(0.08, Number(inputs.overlayHeightRatio) || 0.22));
|
|
530
541
|
const paneHeight = overlayMode ? Math.max(20, Math.round(fullPaneHeight * overlayHeightRatio)) : fullPaneHeight;
|
|
531
542
|
const paneBottom = chartBottom;
|
|
532
|
-
const
|
|
533
|
-
const
|
|
543
|
+
const scaleMode = inputs.scaleMode === "full" ? "full" : "visible";
|
|
544
|
+
const scalingPoints = scaleMode === "full" ? data : data.slice(startIndex, endIndex + 1);
|
|
545
|
+
const scalingVolumes = scalingPoints.map((point) => Math.max(0, point.v ?? 0)).filter((value) => value > 0);
|
|
546
|
+
const clampPercentile = Number.isFinite(inputs.clampPercentile) ? Number(inputs.clampPercentile) : 1;
|
|
547
|
+
const maxVolume = Math.max(1, getPercentileValue(scalingVolumes, clampPercentile));
|
|
534
548
|
const barWidth = Math.max(
|
|
535
549
|
Math.max(1, Number(inputs.minBarWidth) || 1),
|
|
536
550
|
Math.min(Math.max(1, candleSpacing - 1), Math.floor(candleSpacing * 0.7))
|
package/dist/index.js
CHANGED
|
@@ -485,6 +485,15 @@ var drawSeparateSeries = (ctx, renderContext, values, color, width, minOverride,
|
|
|
485
485
|
}
|
|
486
486
|
ctx.restore();
|
|
487
487
|
};
|
|
488
|
+
var getPercentileValue = (values, percentile) => {
|
|
489
|
+
if (values.length === 0) {
|
|
490
|
+
return 1;
|
|
491
|
+
}
|
|
492
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
493
|
+
const clamped = Math.min(1, Math.max(0, percentile));
|
|
494
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(clamped * sorted.length) - 1));
|
|
495
|
+
return sorted[index] ?? 1;
|
|
496
|
+
};
|
|
488
497
|
var BUILTIN_VOLUME_INDICATOR = {
|
|
489
498
|
id: "volume",
|
|
490
499
|
name: "Volume",
|
|
@@ -496,7 +505,9 @@ var BUILTIN_VOLUME_INDICATOR = {
|
|
|
496
505
|
upColor: "",
|
|
497
506
|
downColor: "",
|
|
498
507
|
minBarWidth: 1,
|
|
499
|
-
overlayHeightRatio: 0.22
|
|
508
|
+
overlayHeightRatio: 0.22,
|
|
509
|
+
scaleMode: "visible",
|
|
510
|
+
clampPercentile: 1
|
|
500
511
|
},
|
|
501
512
|
draw: (ctx, renderContext, inputs) => {
|
|
502
513
|
const { data, startIndex, endIndex, chartTop, chartBottom, candleSpacing, xFromIndex, upColor, downColor } = renderContext;
|
|
@@ -505,8 +516,11 @@ var BUILTIN_VOLUME_INDICATOR = {
|
|
|
505
516
|
const overlayHeightRatio = Math.min(0.45, Math.max(0.08, Number(inputs.overlayHeightRatio) || 0.22));
|
|
506
517
|
const paneHeight = overlayMode ? Math.max(20, Math.round(fullPaneHeight * overlayHeightRatio)) : fullPaneHeight;
|
|
507
518
|
const paneBottom = chartBottom;
|
|
508
|
-
const
|
|
509
|
-
const
|
|
519
|
+
const scaleMode = inputs.scaleMode === "full" ? "full" : "visible";
|
|
520
|
+
const scalingPoints = scaleMode === "full" ? data : data.slice(startIndex, endIndex + 1);
|
|
521
|
+
const scalingVolumes = scalingPoints.map((point) => Math.max(0, point.v ?? 0)).filter((value) => value > 0);
|
|
522
|
+
const clampPercentile = Number.isFinite(inputs.clampPercentile) ? Number(inputs.clampPercentile) : 1;
|
|
523
|
+
const maxVolume = Math.max(1, getPercentileValue(scalingVolumes, clampPercentile));
|
|
510
524
|
const barWidth = Math.max(
|
|
511
525
|
Math.max(1, Number(inputs.minBarWidth) || 1),
|
|
512
526
|
Math.min(Math.max(1, candleSpacing - 1), Math.floor(candleSpacing * 0.7))
|
package/docs/API.md
CHANGED
|
@@ -309,6 +309,8 @@ Volume style inputs:
|
|
|
309
309
|
- `upColor`, `downColor`
|
|
310
310
|
- `minBarWidth`
|
|
311
311
|
- `overlayHeightRatio` (when used as overlay)
|
|
312
|
+
- `scaleMode` (`"visible"` default, or `"full"`)
|
|
313
|
+
- `clampPercentile` (`0..1`, default `1`; e.g. `0.95` to reduce outlier crush)
|
|
312
314
|
|
|
313
315
|
---
|
|
314
316
|
|
package/docs/RECIPES.md
CHANGED
|
@@ -138,6 +138,15 @@ const rsiId = chart.addIndicator("rsi", { length: 14 }, { pane: "separate", pane
|
|
|
138
138
|
const volumeId = chart.addIndicator("volume", { upOpacity: 0.72, downOpacity: 0.72 });
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
## Prevent one volume spike from crushing all bars
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const volumeId = chart.addIndicator("volume", {
|
|
145
|
+
scaleMode: "visible", // default behavior
|
|
146
|
+
clampPercentile: 0.95 // clamp scaling reference to p95 of visible bars
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
141
150
|
Available built-ins:
|
|
142
151
|
|
|
143
152
|
- `volume`, `sma`, `ema`, `rsi`, `wma`, `vwma`, `rma`, `hma`, `stddev`, `atr`
|