hyperprop-charting-library 0.1.136 → 0.1.137
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/dist/hyperprop-charting-library.cjs +203 -42
- package/dist/hyperprop-charting-library.d.ts +56 -1
- package/dist/hyperprop-charting-library.js +201 -42
- package/dist/index.cjs +203 -42
- package/dist/index.d.cts +56 -1
- package/dist/index.d.ts +56 -1
- package/dist/index.js +201 -42
- package/docs/API.md +39 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -22,8 +22,10 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
FIB_DEFAULT_PALETTE: () => FIB_DEFAULT_PALETTE,
|
|
24
24
|
POSITION_DEFAULT_COLORS: () => POSITION_DEFAULT_COLORS,
|
|
25
|
+
SCRIPT_SOURCE_INPUT_VALUES: () => SCRIPT_SOURCE_INPUT_VALUES,
|
|
25
26
|
compileScriptIndicator: () => compileScriptIndicator,
|
|
26
27
|
createChart: () => createChart,
|
|
28
|
+
extractScriptInputs: () => extractScriptInputs,
|
|
27
29
|
validateScriptSource: () => validateScriptSource
|
|
28
30
|
});
|
|
29
31
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -2151,6 +2153,82 @@ var hashScriptSource = (source) => {
|
|
|
2151
2153
|
}
|
|
2152
2154
|
return (hash >>> 0).toString(36);
|
|
2153
2155
|
};
|
|
2156
|
+
var SCRIPT_SOURCE_INPUT_VALUES = ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4"];
|
|
2157
|
+
var slugifyInputLabel = (label) => {
|
|
2158
|
+
const slug = String(label).trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
2159
|
+
return slug || "input";
|
|
2160
|
+
};
|
|
2161
|
+
var createScriptInputHelper = (values, registry) => {
|
|
2162
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
2163
|
+
const resolveKey = (label, options) => {
|
|
2164
|
+
let key = options?.key ?? slugifyInputLabel(label);
|
|
2165
|
+
if (usedKeys.has(key)) {
|
|
2166
|
+
let suffix = 2;
|
|
2167
|
+
while (usedKeys.has(`${key}_${suffix}`)) suffix += 1;
|
|
2168
|
+
key = `${key}_${suffix}`;
|
|
2169
|
+
}
|
|
2170
|
+
usedKeys.add(key);
|
|
2171
|
+
return key;
|
|
2172
|
+
};
|
|
2173
|
+
const resolve = (key, fallback, coerce) => {
|
|
2174
|
+
if (!values) return fallback;
|
|
2175
|
+
const raw = values[key];
|
|
2176
|
+
if (raw === void 0 || raw === null) return fallback;
|
|
2177
|
+
const coerced = coerce(raw);
|
|
2178
|
+
return coerced === void 0 ? fallback : coerced;
|
|
2179
|
+
};
|
|
2180
|
+
const coerceNumber = (raw) => {
|
|
2181
|
+
const num = typeof raw === "number" ? raw : Number(raw);
|
|
2182
|
+
return Number.isFinite(num) ? num : void 0;
|
|
2183
|
+
};
|
|
2184
|
+
const numberInput = (integer) => (label, defaultValue, options) => {
|
|
2185
|
+
const key = resolveKey(label, options);
|
|
2186
|
+
registry?.push({
|
|
2187
|
+
key,
|
|
2188
|
+
label,
|
|
2189
|
+
type: "number",
|
|
2190
|
+
default: defaultValue,
|
|
2191
|
+
...options?.min !== void 0 ? { min: options.min } : {},
|
|
2192
|
+
...options?.max !== void 0 ? { max: options.max } : {},
|
|
2193
|
+
...options?.step !== void 0 ? { step: options.step } : integer ? { step: 1 } : {}
|
|
2194
|
+
});
|
|
2195
|
+
const value = resolve(key, defaultValue, coerceNumber);
|
|
2196
|
+
return integer ? Math.round(value) : value;
|
|
2197
|
+
};
|
|
2198
|
+
const selectInput = (label, defaultValue, choices, options) => {
|
|
2199
|
+
const key = resolveKey(label, options);
|
|
2200
|
+
const normalized = (Array.isArray(choices) ? choices : []).map(
|
|
2201
|
+
(choice) => typeof choice === "string" ? { value: choice, label: choice } : { value: String(choice.value), label: String(choice.label ?? choice.value) }
|
|
2202
|
+
);
|
|
2203
|
+
registry?.push({ key, label, type: "select", default: defaultValue, options: normalized });
|
|
2204
|
+
return resolve(key, defaultValue, (raw) => typeof raw === "string" ? raw : String(raw));
|
|
2205
|
+
};
|
|
2206
|
+
return Object.freeze({
|
|
2207
|
+
int: numberInput(true),
|
|
2208
|
+
float: numberInput(false),
|
|
2209
|
+
bool: (label, defaultValue = false, options) => {
|
|
2210
|
+
const key = resolveKey(label, options);
|
|
2211
|
+
registry?.push({ key, label, type: "boolean", default: defaultValue });
|
|
2212
|
+
return resolve(
|
|
2213
|
+
key,
|
|
2214
|
+
defaultValue,
|
|
2215
|
+
(raw) => typeof raw === "boolean" ? raw : raw === "true" ? true : raw === "false" ? false : void 0
|
|
2216
|
+
);
|
|
2217
|
+
},
|
|
2218
|
+
color: (label, defaultValue, options) => {
|
|
2219
|
+
const key = resolveKey(label, options);
|
|
2220
|
+
registry?.push({ key, label, type: "color", default: defaultValue });
|
|
2221
|
+
return resolve(key, defaultValue, (raw) => typeof raw === "string" ? raw : void 0);
|
|
2222
|
+
},
|
|
2223
|
+
string: (label, defaultValue = "", options) => {
|
|
2224
|
+
const key = resolveKey(label, options);
|
|
2225
|
+
registry?.push({ key, label, type: "string", default: defaultValue });
|
|
2226
|
+
return resolve(key, defaultValue, (raw) => typeof raw === "string" ? raw : String(raw));
|
|
2227
|
+
},
|
|
2228
|
+
select: selectInput,
|
|
2229
|
+
source: (label = "Source", defaultValue = "close", options) => selectInput(label, defaultValue, SCRIPT_SOURCE_INPUT_VALUES, options)
|
|
2230
|
+
});
|
|
2231
|
+
};
|
|
2154
2232
|
var SCRIPT_GUARD_BUDGET_MS = 1e3;
|
|
2155
2233
|
var ScriptBudgetError = class extends Error {
|
|
2156
2234
|
constructor() {
|
|
@@ -2425,31 +2503,34 @@ var injectLoopGuards = (source, guardName) => {
|
|
|
2425
2503
|
}
|
|
2426
2504
|
return out;
|
|
2427
2505
|
};
|
|
2428
|
-
var
|
|
2506
|
+
var compileScriptFactory = (source) => {
|
|
2429
2507
|
const makeFactory = (body) => new Function(
|
|
2430
2508
|
"__hpGuard",
|
|
2509
|
+
"input",
|
|
2431
2510
|
`"use strict";
|
|
2432
2511
|
${body}
|
|
2433
2512
|
;if (typeof compute !== "function") { throw new Error("Script must define function compute(bars, inputs, hp)"); }
|
|
2434
2513
|
return compute;`
|
|
2435
2514
|
);
|
|
2436
|
-
let factory;
|
|
2437
2515
|
try {
|
|
2438
|
-
|
|
2516
|
+
return makeFactory(injectLoopGuards(source, "__hpGuard"));
|
|
2439
2517
|
} catch {
|
|
2440
|
-
|
|
2518
|
+
return makeFactory(source);
|
|
2441
2519
|
}
|
|
2442
|
-
guard.reset();
|
|
2443
|
-
return factory(guard);
|
|
2444
2520
|
};
|
|
2445
|
-
var
|
|
2521
|
+
var extractScriptInputs = (source) => {
|
|
2522
|
+
const registry = [];
|
|
2446
2523
|
try {
|
|
2447
|
-
|
|
2448
|
-
|
|
2524
|
+
const factory = compileScriptFactory(source);
|
|
2525
|
+
const guard = createScriptGuard();
|
|
2526
|
+
guard.reset();
|
|
2527
|
+
factory(guard, createScriptInputHelper(null, registry));
|
|
2528
|
+
return { inputs: registry, error: null };
|
|
2449
2529
|
} catch (error) {
|
|
2450
|
-
return error instanceof Error ? error.message : String(error);
|
|
2530
|
+
return { inputs: registry, error: error instanceof Error ? error.message : String(error) };
|
|
2451
2531
|
}
|
|
2452
2532
|
};
|
|
2533
|
+
var validateScriptSource = (source) => extractScriptInputs(source).error;
|
|
2453
2534
|
var drawScriptError = (ctx, renderContext, name, message) => {
|
|
2454
2535
|
ctx.save();
|
|
2455
2536
|
ctx.font = "11px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
|
|
@@ -2466,18 +2547,24 @@ var compileScriptIndicator = (definition) => {
|
|
|
2466
2547
|
for (const input of definition.inputs ?? []) {
|
|
2467
2548
|
defaultInputs[input.key] = input.default;
|
|
2468
2549
|
}
|
|
2469
|
-
let
|
|
2550
|
+
let factory = null;
|
|
2470
2551
|
let compileError = null;
|
|
2471
2552
|
const guard = createScriptGuard();
|
|
2472
2553
|
try {
|
|
2473
|
-
|
|
2554
|
+
factory = compileScriptFactory(definition.source);
|
|
2555
|
+
const declared = [];
|
|
2556
|
+
guard.reset();
|
|
2557
|
+
factory(guard, createScriptInputHelper(null, declared));
|
|
2558
|
+
for (const input of declared) {
|
|
2559
|
+
defaultInputs[input.key] = input.default;
|
|
2560
|
+
}
|
|
2474
2561
|
} catch (error) {
|
|
2475
2562
|
compileError = error instanceof Error ? error.message : String(error);
|
|
2476
2563
|
}
|
|
2477
2564
|
const sourceKey = hashScriptSource(definition.source);
|
|
2478
2565
|
let trippedError = null;
|
|
2479
2566
|
const runCompute = (data, inputs) => {
|
|
2480
|
-
if (!
|
|
2567
|
+
if (!factory || compileError) {
|
|
2481
2568
|
return { error: compileError ?? "Script failed to compile" };
|
|
2482
2569
|
}
|
|
2483
2570
|
if (trippedError) {
|
|
@@ -2485,6 +2572,7 @@ var compileScriptIndicator = (definition) => {
|
|
|
2485
2572
|
}
|
|
2486
2573
|
try {
|
|
2487
2574
|
guard.reset();
|
|
2575
|
+
const compiled = factory(guard, createScriptInputHelper(inputs, null));
|
|
2488
2576
|
const result = compiled(data, inputs, SCRIPT_HELPERS);
|
|
2489
2577
|
if (!result || !Array.isArray(result.plots)) {
|
|
2490
2578
|
return { error: "compute() must return { plots: [...] }" };
|
|
@@ -2979,6 +3067,24 @@ function createChart(element, options = {}) {
|
|
|
2979
3067
|
let drawingDoubleClickHandler = null;
|
|
2980
3068
|
let drawingEditTextHandler = null;
|
|
2981
3069
|
let magnetMode = "none";
|
|
3070
|
+
let priceScaleMode = "linear";
|
|
3071
|
+
let priceScaleInverted = false;
|
|
3072
|
+
const PRICE_SCALE_LOG_FLOOR = 1e-9;
|
|
3073
|
+
const priceScaleTransform = (price) => priceScaleMode === "log" ? Math.log10(Math.max(PRICE_SCALE_LOG_FLOOR, price)) : price;
|
|
3074
|
+
const priceScaleUntransform = (value) => priceScaleMode === "log" ? Math.pow(10, value) : value;
|
|
3075
|
+
const percentBaselineForXStart = (xStartValue) => {
|
|
3076
|
+
if (priceScaleMode !== "percent") return null;
|
|
3077
|
+
const bar = data[clamp(Math.round(xStartValue), 0, Math.max(0, data.length - 1))];
|
|
3078
|
+
return bar && bar.c > 0 ? bar.c : null;
|
|
3079
|
+
};
|
|
3080
|
+
const formatPriceForScale = (price, percentBaseline) => {
|
|
3081
|
+
if (percentBaseline !== null) {
|
|
3082
|
+
const pct = (price / percentBaseline - 1) * 100;
|
|
3083
|
+
const rounded = Math.abs(pct) < 5e-3 ? 0 : pct;
|
|
3084
|
+
return `${rounded > 0 ? "+" : ""}${rounded.toFixed(2)}%`;
|
|
3085
|
+
}
|
|
3086
|
+
return formatPrice(price);
|
|
3087
|
+
};
|
|
2982
3088
|
let magnetModifierActive = false;
|
|
2983
3089
|
let shiftKeyActive = false;
|
|
2984
3090
|
const MAGNET_WEAK_THRESHOLD_PX = 16;
|
|
@@ -4312,9 +4418,14 @@ function createChart(element, options = {}) {
|
|
|
4312
4418
|
yMax
|
|
4313
4419
|
};
|
|
4314
4420
|
plotBottomForHit = fullChartBottom;
|
|
4421
|
+
const scaledYMin = priceScaleTransform(yMin);
|
|
4422
|
+
const scaledYRange = priceScaleTransform(yMax) - scaledYMin || 1;
|
|
4315
4423
|
const yFromPrice = (price) => {
|
|
4316
|
-
|
|
4424
|
+
const ratio = (priceScaleTransform(price) - scaledYMin) / scaledYRange;
|
|
4425
|
+
return priceScaleInverted ? chartTop + ratio * chartHeight : chartBottom - ratio * chartHeight;
|
|
4317
4426
|
};
|
|
4427
|
+
const percentBaseline = percentBaselineForXStart(xStart);
|
|
4428
|
+
const formatAxisPrice = (price) => formatPriceForScale(price, percentBaseline);
|
|
4318
4429
|
const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
|
|
4319
4430
|
const FIB_RATIOS = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
|
|
4320
4431
|
const FIB_EXT_RATIOS = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
|
|
@@ -5221,19 +5332,40 @@ function createChart(element, options = {}) {
|
|
|
5221
5332
|
const yTicks = Math.max(1, Math.floor(yTickCountInput));
|
|
5222
5333
|
const gridColor = grid.color ?? mergedOptions.gridColor;
|
|
5223
5334
|
const priceTicks = [];
|
|
5224
|
-
{
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5335
|
+
const pickNiceStep = (targetStep) => {
|
|
5336
|
+
if (!(targetStep > 0) || !Number.isFinite(targetStep)) return 1;
|
|
5337
|
+
const base = Math.pow(10, Math.floor(Math.log10(targetStep)));
|
|
5338
|
+
const frac = targetStep / base;
|
|
5339
|
+
return (frac <= 1 ? 1 : frac <= 2 ? 2 : frac <= 2.5 ? 2.5 : frac <= 5 ? 5 : 10) * base;
|
|
5340
|
+
};
|
|
5341
|
+
if (percentBaseline !== null) {
|
|
5342
|
+
const pctMin = (yMin / percentBaseline - 1) * 100;
|
|
5343
|
+
const pctMax = (yMax / percentBaseline - 1) * 100;
|
|
5344
|
+
const step = pickNiceStep((pctMax - pctMin) / Math.max(1, yTicks));
|
|
5345
|
+
const firstTick = Math.ceil(pctMin / step) * step;
|
|
5346
|
+
for (let i = 0; ; i += 1) {
|
|
5347
|
+
const pct = firstTick + i * step;
|
|
5348
|
+
if (pct > pctMax + step * 1e-6) break;
|
|
5349
|
+
priceTicks.push(percentBaseline * (1 + pct / 100));
|
|
5350
|
+
if (priceTicks.length > 200) break;
|
|
5351
|
+
}
|
|
5352
|
+
} else if (priceScaleMode === "log" && yMin > 0 && yMax / yMin > 4) {
|
|
5353
|
+
const mantissas = Math.log10(yMax / yMin) > 8 ? [1] : [1, 2, 5];
|
|
5354
|
+
const firstDecade = Math.floor(Math.log10(Math.max(PRICE_SCALE_LOG_FLOOR, yMin)));
|
|
5355
|
+
const lastDecade = Math.ceil(Math.log10(yMax));
|
|
5356
|
+
for (let decade = firstDecade; decade <= lastDecade; decade += 1) {
|
|
5357
|
+
for (const mantissa of mantissas) {
|
|
5358
|
+
const price = mantissa * Math.pow(10, decade);
|
|
5359
|
+
if (price >= yMin && price <= yMax) priceTicks.push(price);
|
|
5360
|
+
if (priceTicks.length > 200) break;
|
|
5234
5361
|
}
|
|
5235
|
-
}
|
|
5236
|
-
|
|
5362
|
+
}
|
|
5363
|
+
} else {
|
|
5364
|
+
const targetStep = yRange / Math.max(1, yTicks);
|
|
5365
|
+
let step = pickNiceStep(targetStep);
|
|
5366
|
+
const instrumentTick = getConfiguredTickSize();
|
|
5367
|
+
if (targetStep > 0 && Number.isFinite(targetStep) && instrumentTick > 0 && step < instrumentTick * 1e6) {
|
|
5368
|
+
step = Math.max(instrumentTick, Math.round(step / instrumentTick) * instrumentTick);
|
|
5237
5369
|
}
|
|
5238
5370
|
const firstTick = Math.ceil(yMin / step) * step;
|
|
5239
5371
|
for (let i = 0; ; i += 1) {
|
|
@@ -5996,7 +6128,7 @@ function createChart(element, options = {}) {
|
|
|
5996
6128
|
ctx.font = `${yAxisFontSize}px ${mergedOptions.fontFamily}`;
|
|
5997
6129
|
for (const price of priceTicks) {
|
|
5998
6130
|
const y = clamp(yFromPrice(price), chartTop + priceScaleTickLabelInset, chartBottom - priceScaleTickLabelInset);
|
|
5999
|
-
drawText(
|
|
6131
|
+
drawText(formatAxisPrice(price), chartRight + 6, y, "left", "middle", yAxis.textColor);
|
|
6000
6132
|
}
|
|
6001
6133
|
ctx.font = prevFont;
|
|
6002
6134
|
}
|
|
@@ -6071,7 +6203,7 @@ function createChart(element, options = {}) {
|
|
|
6071
6203
|
...ticker.showCountdownInLabel ? [getCountdownText()] : []
|
|
6072
6204
|
].map((value) => value === null || value === void 0 ? "" : String(value).trim()).filter((value) => value.length > 0);
|
|
6073
6205
|
addPriceAxisLabel({
|
|
6074
|
-
text:
|
|
6206
|
+
text: formatAxisPrice(tickerPrice),
|
|
6075
6207
|
...tickerSubtexts.length > 0 ? { subtexts: tickerSubtexts } : {},
|
|
6076
6208
|
subtextColor: ticker.labelSubtextColor ?? ticker.labelTextColor ?? "#0b1220",
|
|
6077
6209
|
...ticker.labelSubtextFontSize === void 0 ? {} : { subtextFontSize: ticker.labelSubtextFontSize },
|
|
@@ -6098,7 +6230,7 @@ function createChart(element, options = {}) {
|
|
|
6098
6230
|
const previousClose = previousCloseCandidate;
|
|
6099
6231
|
drawReferenceLine(previousClose, labels.previousCloseColor, "dashed");
|
|
6100
6232
|
addPriceAxisLabel({
|
|
6101
|
-
text: `PDC ${
|
|
6233
|
+
text: `PDC ${formatAxisPrice(previousClose)}`,
|
|
6102
6234
|
price: previousClose,
|
|
6103
6235
|
backgroundColor: labels.backgroundColor,
|
|
6104
6236
|
textColor: labels.mutedTextColor,
|
|
@@ -6117,7 +6249,7 @@ function createChart(element, options = {}) {
|
|
|
6117
6249
|
if (point.l < visibleLow) visibleLow = point.l;
|
|
6118
6250
|
}
|
|
6119
6251
|
addPriceAxisLabel({
|
|
6120
|
-
text: `H ${
|
|
6252
|
+
text: `H ${formatAxisPrice(visibleHigh)}`,
|
|
6121
6253
|
price: visibleHigh,
|
|
6122
6254
|
backgroundColor: labels.backgroundColor,
|
|
6123
6255
|
textColor: labels.textColor,
|
|
@@ -6125,7 +6257,7 @@ function createChart(element, options = {}) {
|
|
|
6125
6257
|
priority: 40
|
|
6126
6258
|
});
|
|
6127
6259
|
addPriceAxisLabel({
|
|
6128
|
-
text: `L ${
|
|
6260
|
+
text: `L ${formatAxisPrice(visibleLow)}`,
|
|
6129
6261
|
price: visibleLow,
|
|
6130
6262
|
backgroundColor: labels.backgroundColor,
|
|
6131
6263
|
textColor: labels.textColor,
|
|
@@ -6137,7 +6269,7 @@ function createChart(element, options = {}) {
|
|
|
6137
6269
|
if (Number.isFinite(labels.bidPrice)) {
|
|
6138
6270
|
drawReferenceLine(labels.bidPrice, labels.bidColor, "dotted");
|
|
6139
6271
|
addPriceAxisLabel({
|
|
6140
|
-
text: `B ${
|
|
6272
|
+
text: `B ${formatAxisPrice(labels.bidPrice)}`,
|
|
6141
6273
|
price: labels.bidPrice,
|
|
6142
6274
|
backgroundColor: labels.bidColor,
|
|
6143
6275
|
textColor: "#0b1220",
|
|
@@ -6148,7 +6280,7 @@ function createChart(element, options = {}) {
|
|
|
6148
6280
|
if (Number.isFinite(labels.askPrice)) {
|
|
6149
6281
|
drawReferenceLine(labels.askPrice, labels.askColor, "dotted");
|
|
6150
6282
|
addPriceAxisLabel({
|
|
6151
|
-
text: `A ${
|
|
6283
|
+
text: `A ${formatAxisPrice(labels.askPrice)}`,
|
|
6152
6284
|
price: labels.askPrice,
|
|
6153
6285
|
backgroundColor: labels.askColor,
|
|
6154
6286
|
textColor: "#0b1220",
|
|
@@ -6409,8 +6541,11 @@ function createChart(element, options = {}) {
|
|
|
6409
6541
|
ctx.restore();
|
|
6410
6542
|
};
|
|
6411
6543
|
if (crosshair.showPriceLabel) {
|
|
6412
|
-
const
|
|
6413
|
-
const
|
|
6544
|
+
const scaledYMin = priceScaleTransform(yMin);
|
|
6545
|
+
const scaledYRange = priceScaleTransform(yMin + yRange) - scaledYMin || 1;
|
|
6546
|
+
const hoverRatio = priceScaleInverted ? (cy - chartTop) / chartHeight : (chartBottom - cy) / chartHeight;
|
|
6547
|
+
const hoverPrice = priceScaleUntransform(scaledYMin + hoverRatio * scaledYRange);
|
|
6548
|
+
const priceText = formatPriceForScale(hoverPrice, percentBaselineForXStart(xStart));
|
|
6414
6549
|
const priceWidth = getRightAxisLabelWidth(chartRight, getPriceLabelWidth(priceText, labelPaddingX));
|
|
6415
6550
|
const priceX = getRightAxisLabelX(chartRight);
|
|
6416
6551
|
const priceY = clamp(cy - labelHeight / 2, chartTop, chartBottom - labelHeight);
|
|
@@ -6578,8 +6713,8 @@ function createChart(element, options = {}) {
|
|
|
6578
6713
|
const maxRange = bounds.hardSpan;
|
|
6579
6714
|
const nextRange = clamp(currentRange * factor, minRange, maxRange);
|
|
6580
6715
|
const anchorRatio = clamp((anchorY - drawState.chartTop) / drawState.chartHeight, 0, 1);
|
|
6581
|
-
const anchorPrice = currentMax - anchorRatio * currentRange;
|
|
6582
|
-
const nextMax = anchorPrice + anchorRatio * nextRange;
|
|
6716
|
+
const anchorPrice = priceScaleInverted ? currentMin + anchorRatio * currentRange : currentMax - anchorRatio * currentRange;
|
|
6717
|
+
const nextMax = priceScaleInverted ? anchorPrice + (1 - anchorRatio) * nextRange : anchorPrice + anchorRatio * nextRange;
|
|
6583
6718
|
const nextMin = nextMax - nextRange;
|
|
6584
6719
|
const clamped = clampYRange(nextMin, nextMax);
|
|
6585
6720
|
yMinOverride = clamped.min;
|
|
@@ -6603,7 +6738,7 @@ function createChart(element, options = {}) {
|
|
|
6603
6738
|
const currentMin = yMinOverride ?? drawState.yMin;
|
|
6604
6739
|
const currentMax = yMaxOverride ?? drawState.yMax;
|
|
6605
6740
|
const range = currentMax - currentMin || 1;
|
|
6606
|
-
const priceShift = deltaY / drawState.chartHeight * range;
|
|
6741
|
+
const priceShift = deltaY / drawState.chartHeight * range * (priceScaleInverted ? -1 : 1);
|
|
6607
6742
|
const clamped = clampYRange(currentMin + priceShift, currentMax + priceShift);
|
|
6608
6743
|
yMinOverride = clamped.min;
|
|
6609
6744
|
yMaxOverride = clamped.max;
|
|
@@ -6836,8 +6971,14 @@ function createChart(element, options = {}) {
|
|
|
6836
6971
|
if (!drawState) {
|
|
6837
6972
|
return 0;
|
|
6838
6973
|
}
|
|
6839
|
-
const ratio = clamp(
|
|
6840
|
-
|
|
6974
|
+
const ratio = clamp(
|
|
6975
|
+
priceScaleInverted ? (y - drawState.chartTop) / drawState.chartHeight : (drawState.chartBottom - y) / drawState.chartHeight,
|
|
6976
|
+
0,
|
|
6977
|
+
1
|
|
6978
|
+
);
|
|
6979
|
+
const scaledMin = priceScaleTransform(drawState.yMin);
|
|
6980
|
+
const scaledMax = priceScaleTransform(drawState.yMax);
|
|
6981
|
+
return priceScaleUntransform(scaledMin + ratio * (scaledMax - scaledMin));
|
|
6841
6982
|
};
|
|
6842
6983
|
const applyMagnet = (y, index, price) => {
|
|
6843
6984
|
const effective = magnetModifierActive ? "strong" : magnetMode;
|
|
@@ -6909,8 +7050,10 @@ function createChart(element, options = {}) {
|
|
|
6909
7050
|
};
|
|
6910
7051
|
const canvasYFromDrawingPrice = (price) => {
|
|
6911
7052
|
if (!drawState) return 0;
|
|
6912
|
-
const
|
|
6913
|
-
|
|
7053
|
+
const scaledMin = priceScaleTransform(drawState.yMin);
|
|
7054
|
+
const scaledRange = priceScaleTransform(drawState.yMax) - scaledMin || 1;
|
|
7055
|
+
const ratio = (priceScaleTransform(price) - scaledMin) / scaledRange;
|
|
7056
|
+
return priceScaleInverted ? drawState.chartTop + ratio * drawState.chartHeight : drawState.chartBottom - ratio * drawState.chartHeight;
|
|
6914
7057
|
};
|
|
6915
7058
|
const distanceToSegment = (x, y, x1, y1, x2, y2) => {
|
|
6916
7059
|
const dx = x2 - x1;
|
|
@@ -8924,6 +9067,16 @@ function createChart(element, options = {}) {
|
|
|
8924
9067
|
magnetMode = mode;
|
|
8925
9068
|
};
|
|
8926
9069
|
const getMagnetMode = () => magnetMode;
|
|
9070
|
+
const setPriceScale = (options2) => {
|
|
9071
|
+
if (options2.mode === "linear" || options2.mode === "log" || options2.mode === "percent") {
|
|
9072
|
+
priceScaleMode = options2.mode;
|
|
9073
|
+
}
|
|
9074
|
+
if (typeof options2.inverted === "boolean") {
|
|
9075
|
+
priceScaleInverted = options2.inverted;
|
|
9076
|
+
}
|
|
9077
|
+
scheduleDraw();
|
|
9078
|
+
};
|
|
9079
|
+
const getPriceScale = () => ({ mode: priceScaleMode, inverted: priceScaleInverted });
|
|
8927
9080
|
const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
|
|
8928
9081
|
const setDrawings = (nextDrawings) => {
|
|
8929
9082
|
drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
|
|
@@ -8993,6 +9146,7 @@ function createChart(element, options = {}) {
|
|
|
8993
9146
|
drawings: getDrawings(),
|
|
8994
9147
|
indicators: getIndicators(),
|
|
8995
9148
|
magnetMode: getMagnetMode(),
|
|
9149
|
+
priceScale: getPriceScale(),
|
|
8996
9150
|
drawingDefaults
|
|
8997
9151
|
};
|
|
8998
9152
|
};
|
|
@@ -9012,6 +9166,9 @@ function createChart(element, options = {}) {
|
|
|
9012
9166
|
if (state.magnetMode === "none" || state.magnetMode === "weak" || state.magnetMode === "strong") {
|
|
9013
9167
|
setMagnetMode(state.magnetMode);
|
|
9014
9168
|
}
|
|
9169
|
+
if (state.priceScale && typeof state.priceScale === "object") {
|
|
9170
|
+
setPriceScale(state.priceScale);
|
|
9171
|
+
}
|
|
9015
9172
|
if (state.drawingDefaults && typeof state.drawingDefaults === "object") {
|
|
9016
9173
|
for (const [tool, defaults] of Object.entries(state.drawingDefaults)) {
|
|
9017
9174
|
if (defaults && typeof defaults === "object") {
|
|
@@ -9106,6 +9263,8 @@ function createChart(element, options = {}) {
|
|
|
9106
9263
|
onDrawingEditText,
|
|
9107
9264
|
setMagnetMode,
|
|
9108
9265
|
getMagnetMode,
|
|
9266
|
+
setPriceScale,
|
|
9267
|
+
getPriceScale,
|
|
9109
9268
|
cancelDrawing,
|
|
9110
9269
|
setTradeMarkers,
|
|
9111
9270
|
setSelectedDrawing,
|
|
@@ -9132,7 +9291,9 @@ function createChart(element, options = {}) {
|
|
|
9132
9291
|
0 && (module.exports = {
|
|
9133
9292
|
FIB_DEFAULT_PALETTE,
|
|
9134
9293
|
POSITION_DEFAULT_COLORS,
|
|
9294
|
+
SCRIPT_SOURCE_INPUT_VALUES,
|
|
9135
9295
|
compileScriptIndicator,
|
|
9136
9296
|
createChart,
|
|
9297
|
+
extractScriptInputs,
|
|
9137
9298
|
validateScriptSource
|
|
9138
9299
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -583,6 +583,15 @@ interface ChartInstance {
|
|
|
583
583
|
onDrawingEditText: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
584
584
|
setMagnetMode: (mode: "none" | "weak" | "strong") => void;
|
|
585
585
|
getMagnetMode: () => "none" | "weak" | "strong";
|
|
586
|
+
/**
|
|
587
|
+
* Change the price-scale mode ("linear" | "log" | "percent") and/or invert
|
|
588
|
+
* the axis. Log maps prices through log10, percent relabels the axis
|
|
589
|
+
* relative to the first visible bar's close (TradingView behavior), and
|
|
590
|
+
* invert flips the axis top-to-bottom. Only the main price pane is
|
|
591
|
+
* affected; indicator panes keep their own scales.
|
|
592
|
+
*/
|
|
593
|
+
setPriceScale: (options: Partial<PriceScaleOptions>) => void;
|
|
594
|
+
getPriceScale: () => PriceScaleOptions;
|
|
586
595
|
cancelDrawing: () => boolean;
|
|
587
596
|
setTradeMarkers: (markers: TradeMarkerOptions[]) => void;
|
|
588
597
|
onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
|
|
@@ -657,8 +666,15 @@ interface ChartSavedState {
|
|
|
657
666
|
drawings: DrawingObjectOptions[];
|
|
658
667
|
indicators: IndicatorInstanceOptions[];
|
|
659
668
|
magnetMode: "none" | "weak" | "strong";
|
|
669
|
+
/** Optional for backward compatibility with pre-0.1.137 blobs. */
|
|
670
|
+
priceScale?: PriceScaleOptions;
|
|
660
671
|
drawingDefaults: Partial<Record<DrawingToolType, DrawingDefaults>>;
|
|
661
672
|
}
|
|
673
|
+
type PriceScaleMode = "linear" | "log" | "percent";
|
|
674
|
+
interface PriceScaleOptions {
|
|
675
|
+
mode: PriceScaleMode;
|
|
676
|
+
inverted: boolean;
|
|
677
|
+
}
|
|
662
678
|
|
|
663
679
|
interface ScriptIndicatorInputDef {
|
|
664
680
|
key: string;
|
|
@@ -706,6 +722,45 @@ interface ScriptComputeResult {
|
|
|
706
722
|
guides?: number[];
|
|
707
723
|
decimals?: number;
|
|
708
724
|
}
|
|
725
|
+
interface ScriptInputOptions {
|
|
726
|
+
/** Stable settings key; defaults to a slug of the label. */
|
|
727
|
+
key?: string;
|
|
728
|
+
min?: number;
|
|
729
|
+
max?: number;
|
|
730
|
+
step?: number;
|
|
731
|
+
}
|
|
732
|
+
declare const SCRIPT_SOURCE_INPUT_VALUES: readonly ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4"];
|
|
733
|
+
/**
|
|
734
|
+
* The `input` object passed to script top level. In value mode it resolves
|
|
735
|
+
* each declaration against the current settings; in recording mode it
|
|
736
|
+
* additionally appends every declaration to `registry`. Keys are assigned
|
|
737
|
+
* deterministically (same order both modes), so values always line up with
|
|
738
|
+
* the extracted schema.
|
|
739
|
+
*/
|
|
740
|
+
declare const createScriptInputHelper: (values: Record<string, unknown> | null, registry: ScriptIndicatorInputDef[] | null) => Readonly<{
|
|
741
|
+
int: (label: string, defaultValue: number, options?: ScriptInputOptions) => number;
|
|
742
|
+
float: (label: string, defaultValue: number, options?: ScriptInputOptions) => number;
|
|
743
|
+
bool: (label: string, defaultValue?: boolean, options?: ScriptInputOptions) => boolean;
|
|
744
|
+
color: (label: string, defaultValue: string, options?: ScriptInputOptions) => string;
|
|
745
|
+
string: (label: string, defaultValue?: string, options?: ScriptInputOptions) => string;
|
|
746
|
+
select: (label: string, defaultValue: string, choices: ReadonlyArray<string | {
|
|
747
|
+
value: string;
|
|
748
|
+
label: string;
|
|
749
|
+
}>, options?: ScriptInputOptions) => string;
|
|
750
|
+
source: (label?: string, defaultValue?: string, options?: ScriptInputOptions) => string;
|
|
751
|
+
}>;
|
|
752
|
+
type ScriptInputHelper = ReturnType<typeof createScriptInputHelper>;
|
|
753
|
+
/**
|
|
754
|
+
* Discover a script's Pine-style `input.*` declarations by running its top
|
|
755
|
+
* level with a recording helper. Returns the declared schema (empty when the
|
|
756
|
+
* script declares nothing) plus a compile/runtime error message when the
|
|
757
|
+
* script is broken. Runs under the standard execution guard, so an infinite
|
|
758
|
+
* top-level loop returns a budget error instead of hanging the caller.
|
|
759
|
+
*/
|
|
760
|
+
declare const extractScriptInputs: (source: string) => {
|
|
761
|
+
inputs: ScriptIndicatorInputDef[];
|
|
762
|
+
error: string | null;
|
|
763
|
+
};
|
|
709
764
|
/**
|
|
710
765
|
* Compile-check a script without registering it: returns an error message, or
|
|
711
766
|
* `null` when the script compiles. Runs with the same execution guard as the
|
|
@@ -726,4 +781,4 @@ declare const compileScriptIndicator: (definition: ScriptIndicatorDefinition) =>
|
|
|
726
781
|
|
|
727
782
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
728
783
|
|
|
729
|
-
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type ChartSavedState, type ChartType, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, FIB_DEFAULT_PALETTE, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneActionEvent, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneHeightChangeEvent, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, POSITION_DEFAULT_COLORS, type PriceLineOptions, type ScriptComputeResult, type ScriptIndicatorDefinition, type ScriptIndicatorInputDef, type ScriptPlotSpec, type TickerLineOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, compileScriptIndicator, createChart, validateScriptSource };
|
|
784
|
+
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type ChartSavedState, type ChartType, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, FIB_DEFAULT_PALETTE, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneActionEvent, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneHeightChangeEvent, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, POSITION_DEFAULT_COLORS, type PriceLineOptions, type PriceScaleMode, type PriceScaleOptions, SCRIPT_SOURCE_INPUT_VALUES, type ScriptComputeResult, type ScriptIndicatorDefinition, type ScriptIndicatorInputDef, type ScriptInputHelper, type ScriptInputOptions, type ScriptPlotSpec, type TickerLineOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, compileScriptIndicator, createChart, extractScriptInputs, validateScriptSource };
|
package/dist/index.d.ts
CHANGED
|
@@ -583,6 +583,15 @@ interface ChartInstance {
|
|
|
583
583
|
onDrawingEditText: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
|
|
584
584
|
setMagnetMode: (mode: "none" | "weak" | "strong") => void;
|
|
585
585
|
getMagnetMode: () => "none" | "weak" | "strong";
|
|
586
|
+
/**
|
|
587
|
+
* Change the price-scale mode ("linear" | "log" | "percent") and/or invert
|
|
588
|
+
* the axis. Log maps prices through log10, percent relabels the axis
|
|
589
|
+
* relative to the first visible bar's close (TradingView behavior), and
|
|
590
|
+
* invert flips the axis top-to-bottom. Only the main price pane is
|
|
591
|
+
* affected; indicator panes keep their own scales.
|
|
592
|
+
*/
|
|
593
|
+
setPriceScale: (options: Partial<PriceScaleOptions>) => void;
|
|
594
|
+
getPriceScale: () => PriceScaleOptions;
|
|
586
595
|
cancelDrawing: () => boolean;
|
|
587
596
|
setTradeMarkers: (markers: TradeMarkerOptions[]) => void;
|
|
588
597
|
onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
|
|
@@ -657,8 +666,15 @@ interface ChartSavedState {
|
|
|
657
666
|
drawings: DrawingObjectOptions[];
|
|
658
667
|
indicators: IndicatorInstanceOptions[];
|
|
659
668
|
magnetMode: "none" | "weak" | "strong";
|
|
669
|
+
/** Optional for backward compatibility with pre-0.1.137 blobs. */
|
|
670
|
+
priceScale?: PriceScaleOptions;
|
|
660
671
|
drawingDefaults: Partial<Record<DrawingToolType, DrawingDefaults>>;
|
|
661
672
|
}
|
|
673
|
+
type PriceScaleMode = "linear" | "log" | "percent";
|
|
674
|
+
interface PriceScaleOptions {
|
|
675
|
+
mode: PriceScaleMode;
|
|
676
|
+
inverted: boolean;
|
|
677
|
+
}
|
|
662
678
|
|
|
663
679
|
interface ScriptIndicatorInputDef {
|
|
664
680
|
key: string;
|
|
@@ -706,6 +722,45 @@ interface ScriptComputeResult {
|
|
|
706
722
|
guides?: number[];
|
|
707
723
|
decimals?: number;
|
|
708
724
|
}
|
|
725
|
+
interface ScriptInputOptions {
|
|
726
|
+
/** Stable settings key; defaults to a slug of the label. */
|
|
727
|
+
key?: string;
|
|
728
|
+
min?: number;
|
|
729
|
+
max?: number;
|
|
730
|
+
step?: number;
|
|
731
|
+
}
|
|
732
|
+
declare const SCRIPT_SOURCE_INPUT_VALUES: readonly ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4"];
|
|
733
|
+
/**
|
|
734
|
+
* The `input` object passed to script top level. In value mode it resolves
|
|
735
|
+
* each declaration against the current settings; in recording mode it
|
|
736
|
+
* additionally appends every declaration to `registry`. Keys are assigned
|
|
737
|
+
* deterministically (same order both modes), so values always line up with
|
|
738
|
+
* the extracted schema.
|
|
739
|
+
*/
|
|
740
|
+
declare const createScriptInputHelper: (values: Record<string, unknown> | null, registry: ScriptIndicatorInputDef[] | null) => Readonly<{
|
|
741
|
+
int: (label: string, defaultValue: number, options?: ScriptInputOptions) => number;
|
|
742
|
+
float: (label: string, defaultValue: number, options?: ScriptInputOptions) => number;
|
|
743
|
+
bool: (label: string, defaultValue?: boolean, options?: ScriptInputOptions) => boolean;
|
|
744
|
+
color: (label: string, defaultValue: string, options?: ScriptInputOptions) => string;
|
|
745
|
+
string: (label: string, defaultValue?: string, options?: ScriptInputOptions) => string;
|
|
746
|
+
select: (label: string, defaultValue: string, choices: ReadonlyArray<string | {
|
|
747
|
+
value: string;
|
|
748
|
+
label: string;
|
|
749
|
+
}>, options?: ScriptInputOptions) => string;
|
|
750
|
+
source: (label?: string, defaultValue?: string, options?: ScriptInputOptions) => string;
|
|
751
|
+
}>;
|
|
752
|
+
type ScriptInputHelper = ReturnType<typeof createScriptInputHelper>;
|
|
753
|
+
/**
|
|
754
|
+
* Discover a script's Pine-style `input.*` declarations by running its top
|
|
755
|
+
* level with a recording helper. Returns the declared schema (empty when the
|
|
756
|
+
* script declares nothing) plus a compile/runtime error message when the
|
|
757
|
+
* script is broken. Runs under the standard execution guard, so an infinite
|
|
758
|
+
* top-level loop returns a budget error instead of hanging the caller.
|
|
759
|
+
*/
|
|
760
|
+
declare const extractScriptInputs: (source: string) => {
|
|
761
|
+
inputs: ScriptIndicatorInputDef[];
|
|
762
|
+
error: string | null;
|
|
763
|
+
};
|
|
709
764
|
/**
|
|
710
765
|
* Compile-check a script without registering it: returns an error message, or
|
|
711
766
|
* `null` when the script compiles. Runs with the same execution guard as the
|
|
@@ -726,4 +781,4 @@ declare const compileScriptIndicator: (definition: ScriptIndicatorDefinition) =>
|
|
|
726
781
|
|
|
727
782
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
728
783
|
|
|
729
|
-
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type ChartSavedState, type ChartType, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, FIB_DEFAULT_PALETTE, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneActionEvent, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneHeightChangeEvent, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, POSITION_DEFAULT_COLORS, type PriceLineOptions, type ScriptComputeResult, type ScriptIndicatorDefinition, type ScriptIndicatorInputDef, type ScriptPlotSpec, type TickerLineOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, compileScriptIndicator, createChart, validateScriptSource };
|
|
784
|
+
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type ChartSavedState, type ChartType, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingToolType, FIB_DEFAULT_PALETTE, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneActionEvent, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneHeightChangeEvent, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, POSITION_DEFAULT_COLORS, type PriceLineOptions, type PriceScaleMode, type PriceScaleOptions, SCRIPT_SOURCE_INPUT_VALUES, type ScriptComputeResult, type ScriptIndicatorDefinition, type ScriptIndicatorInputDef, type ScriptInputHelper, type ScriptInputOptions, type ScriptPlotSpec, type TickerLineOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, compileScriptIndicator, createChart, extractScriptInputs, validateScriptSource };
|