@supersolutions/super-chart-superscript 0.0.0 → 0.1.0
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/LICENSE +674 -0
- package/README.md +11 -1
- package/dist/chunk-PAWYIS77.js +1214 -0
- package/dist/index.d.ts +354 -0
- package/dist/index.js +429 -0
- package/dist/scripts.d.ts +119 -0
- package/dist/scripts.js +443 -0
- package/package.json +43 -6
package/dist/index.js
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SuperScriptError,
|
|
3
|
+
builtinReferenceLines,
|
|
4
|
+
check,
|
|
5
|
+
diag,
|
|
6
|
+
evaluate,
|
|
7
|
+
formatDiagnostics,
|
|
8
|
+
isSuperScriptError,
|
|
9
|
+
parse
|
|
10
|
+
} from "./chunk-PAWYIS77.js";
|
|
11
|
+
|
|
12
|
+
// packages/superscript/src/runtime/index.ts
|
|
13
|
+
import { ta } from "@supersolutions/super-chart-core";
|
|
14
|
+
var { sma, ema, rsi, highest, lowest, crossover, crossunder, wma, stdev, change, roc, trueRange, atr, sum, dev, mom, linreg, rma, hma, vwma } = ta;
|
|
15
|
+
var SUPERLIB = {
|
|
16
|
+
sma,
|
|
17
|
+
ema,
|
|
18
|
+
rsi,
|
|
19
|
+
highest,
|
|
20
|
+
lowest,
|
|
21
|
+
crossover,
|
|
22
|
+
crossunder,
|
|
23
|
+
wma,
|
|
24
|
+
stdev,
|
|
25
|
+
change: (src) => change(src),
|
|
26
|
+
roc,
|
|
27
|
+
tr: trueRange,
|
|
28
|
+
atr,
|
|
29
|
+
sum,
|
|
30
|
+
dev,
|
|
31
|
+
mom,
|
|
32
|
+
linreg,
|
|
33
|
+
rma,
|
|
34
|
+
hma,
|
|
35
|
+
vwma
|
|
36
|
+
};
|
|
37
|
+
function tooDeep(e) {
|
|
38
|
+
if (!(e instanceof RangeError))
|
|
39
|
+
return void 0;
|
|
40
|
+
const d = diag("error", "UNSUPPORTED", "this script is too large or too deeply nested to process", { line: 1, col: 1, endCol: 1 }, "split long expressions into several named steps");
|
|
41
|
+
return new SuperScriptError(d);
|
|
42
|
+
}
|
|
43
|
+
function compileSuperScript(source, options = {}) {
|
|
44
|
+
let program;
|
|
45
|
+
let checked;
|
|
46
|
+
try {
|
|
47
|
+
program = parse(source);
|
|
48
|
+
checked = check(program);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
throw tooDeep(e) ?? e;
|
|
51
|
+
}
|
|
52
|
+
const diagnostics = checked.diagnostics;
|
|
53
|
+
const firstError = diagnostics.find((d) => d.severity === "error");
|
|
54
|
+
if (firstError !== void 0)
|
|
55
|
+
throw new SuperScriptError(firstError, diagnostics);
|
|
56
|
+
if (options.strict === true && diagnostics.length > 0)
|
|
57
|
+
throw new SuperScriptError(diagnostics[0], diagnostics);
|
|
58
|
+
const meta = () => ({ title: program.title, inputs: checked.inputs.map((i) => ({ ...i })), overlay: program.overlay });
|
|
59
|
+
return {
|
|
60
|
+
meta: meta(),
|
|
61
|
+
diagnostics,
|
|
62
|
+
run(bars, params) {
|
|
63
|
+
let r;
|
|
64
|
+
try {
|
|
65
|
+
r = evaluate(program, bars, params);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
throw tooDeep(e) ?? e;
|
|
68
|
+
}
|
|
69
|
+
return { meta: meta(), plots: r.plots, diagnostics };
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function checkSuperScript(source) {
|
|
74
|
+
try {
|
|
75
|
+
return check(parse(source)).diagnostics;
|
|
76
|
+
} catch (e) {
|
|
77
|
+
const deep = tooDeep(e);
|
|
78
|
+
if (deep !== void 0)
|
|
79
|
+
return deep.diagnostics;
|
|
80
|
+
return [diag("error", "UNSUPPORTED", `internal error: ${String(e)}`, { line: 1, col: 1, endCol: 1 })];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
var SuperScriptLanguage = {
|
|
84
|
+
id: "superscript",
|
|
85
|
+
name: "SuperScript",
|
|
86
|
+
compile: compileSuperScript
|
|
87
|
+
};
|
|
88
|
+
var JavaScriptLanguage = {
|
|
89
|
+
id: "javascript",
|
|
90
|
+
name: "JavaScript",
|
|
91
|
+
compile(source) {
|
|
92
|
+
const fn = new Function("ctx", `"use strict"; ${source}`);
|
|
93
|
+
return { meta: { title: "JavaScript" }, run: (bars) => fn({ bars, lib: SUPERLIB }) };
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var LANGUAGES = /* @__PURE__ */ new Map();
|
|
97
|
+
function registerLanguage(lang) {
|
|
98
|
+
LANGUAGES.set(lang.id, lang);
|
|
99
|
+
}
|
|
100
|
+
function getLanguage(id) {
|
|
101
|
+
return LANGUAGES.get(id);
|
|
102
|
+
}
|
|
103
|
+
function listLanguages() {
|
|
104
|
+
return [...LANGUAGES.values()];
|
|
105
|
+
}
|
|
106
|
+
registerLanguage(SuperScriptLanguage);
|
|
107
|
+
function superscriptReference() {
|
|
108
|
+
return [
|
|
109
|
+
"Series: open, high, low, close, volume, hl2, hlc3, ohlc4, bar_index, time. History: close[1], close[len] (a constant whole number >= 0). na is the missing value; x/0, log(0) and other non-finite results are na.",
|
|
110
|
+
"Operators: + - * / %, comparison < <= > >= == !=, logic and/or/not (also && || !), ternary cond ? a : b.",
|
|
111
|
+
'Bind variables: name = expr, one statement per line (a line continues inside brackets or after an operator). Declare params: len = input(14, title="Length", minval=1, maxval=200, step=1); input(14, "Length") also works. Lengths and offsets must be constants: numbers, inputs, or arithmetic on them.',
|
|
112
|
+
...builtinReferenceLines(),
|
|
113
|
+
'Draw: plot(expr, title="...", color="#rrggbb", type="line"|"histogram"|"circle", lineWidth=1, histbase=0); histogram bars grow from histbase. hline(price, title="...", color="#rrggbb") draws a dashed level.',
|
|
114
|
+
"Start the script with // @title <name>. Add // @overlay when it plots on the price scale (drawn over the candles).",
|
|
115
|
+
"Not supported (an error names the line): := and += (bindings are single-assignment series; bind a new name), if/for/while (use cond ? a : b), function definitions, request.*, strings or colours as values. Ignored with a warning: var, strategy.*, plotshape, plotchar, bgcolor, fill, alertcondition.",
|
|
116
|
+
'Pine names are accepted where the arguments match: ta.sma \u2026 ta.pivotlow, math.abs \u2026 math.sum, input.int, input.float, color.red \u2026, plot.style_*, indicator("Name", overlay=true). ta.cci and ta.mfi only over hlc3; ta.stoch only as ta.stoch(close, high, low, len); ta.vwap is session-anchored and has no equivalent.'
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
var superlib = SUPERLIB;
|
|
120
|
+
|
|
121
|
+
// packages/superscript/src/ai/index.ts
|
|
122
|
+
import { resolvePricePrecision } from "@supersolutions/super-chart-core";
|
|
123
|
+
function createHttpAIClient(opts) {
|
|
124
|
+
return {
|
|
125
|
+
async complete({ system, prompt, signal }) {
|
|
126
|
+
const abort = combineSignals(signal, opts.timeoutMs);
|
|
127
|
+
try {
|
|
128
|
+
const res = await fetch(opts.endpoint, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: { "content-type": "application/json", ...opts.headers ?? {} },
|
|
131
|
+
// Exactly these two fields: never the whole input (it holds the signal).
|
|
132
|
+
body: JSON.stringify({ system, prompt }),
|
|
133
|
+
...abort.signal !== void 0 ? { signal: abort.signal } : {}
|
|
134
|
+
});
|
|
135
|
+
if (!res.ok) {
|
|
136
|
+
const body = await res.text().catch(() => "");
|
|
137
|
+
throw new Error(`AI endpoint responded ${res.status}${body.trim() !== "" ? `: ${body.slice(0, 200)}` : ""}`);
|
|
138
|
+
}
|
|
139
|
+
const data = await res.json();
|
|
140
|
+
const text = opts.extract !== void 0 ? opts.extract(data) : textOf(data);
|
|
141
|
+
if (typeof text !== "string" || text.trim() === "")
|
|
142
|
+
throw new Error("AI endpoint returned no text");
|
|
143
|
+
return text;
|
|
144
|
+
} finally {
|
|
145
|
+
abort.dispose();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function textOf(data) {
|
|
151
|
+
if (typeof data === "string")
|
|
152
|
+
return data;
|
|
153
|
+
if (typeof data !== "object" || data === null)
|
|
154
|
+
throw new Error(`AI endpoint returned ${JSON.stringify(data)}, not text`);
|
|
155
|
+
const o = data;
|
|
156
|
+
if (o.error !== void 0 && o.error !== null) {
|
|
157
|
+
throw new Error(`AI endpoint returned an error: ${(typeof o.error === "string" ? o.error : JSON.stringify(o.error)).slice(0, 200)}`);
|
|
158
|
+
}
|
|
159
|
+
if (typeof o.text === "string")
|
|
160
|
+
return o.text;
|
|
161
|
+
if (typeof o.content === "string")
|
|
162
|
+
return o.content;
|
|
163
|
+
if (Array.isArray(o.content)) {
|
|
164
|
+
return o.content.filter((b) => typeof b === "object" && b !== null && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
|
|
165
|
+
}
|
|
166
|
+
const keys = Object.keys(o);
|
|
167
|
+
throw new Error(`unrecognised AI response (keys: ${keys.length > 0 ? keys.join(", ") : "none"}); pass createHttpAIClient({ extract }) to read it`);
|
|
168
|
+
}
|
|
169
|
+
function combineSignals(signal, timeoutMs) {
|
|
170
|
+
if (signal === void 0 && timeoutMs === void 0)
|
|
171
|
+
return { dispose: () => {
|
|
172
|
+
} };
|
|
173
|
+
const c = new AbortController();
|
|
174
|
+
const onAbort = () => {
|
|
175
|
+
c.abort(signal?.reason);
|
|
176
|
+
};
|
|
177
|
+
if (signal !== void 0) {
|
|
178
|
+
if (signal.aborted)
|
|
179
|
+
c.abort(signal.reason);
|
|
180
|
+
else
|
|
181
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
182
|
+
}
|
|
183
|
+
const timer = timeoutMs !== void 0 ? setTimeout(() => {
|
|
184
|
+
c.abort(new DOMException(`AI request timed out after ${timeoutMs} ms`, "TimeoutError"));
|
|
185
|
+
}, timeoutMs) : void 0;
|
|
186
|
+
return {
|
|
187
|
+
signal: c.signal,
|
|
188
|
+
dispose: () => {
|
|
189
|
+
if (timer !== void 0)
|
|
190
|
+
clearTimeout(timer);
|
|
191
|
+
signal?.removeEventListener("abort", onAbort);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
var SUPERSCRIPT_SYSTEM_PROMPT = `You write SuperScript, the indicator/strategy language for SUPer-Chart.
|
|
196
|
+
SuperScript is line-based and Pine-flavoured, but it is not Pine: use only what this reference lists.
|
|
197
|
+
${superscriptReference()}
|
|
198
|
+
Return ONLY the script, no prose, no code fences.`;
|
|
199
|
+
async function nlToSuperScript(client, request, opts = {}) {
|
|
200
|
+
const out = await client.complete({ system: SUPERSCRIPT_SYSTEM_PROMPT, prompt: request, ...opts.signal !== void 0 ? { signal: opts.signal } : {} });
|
|
201
|
+
const source = stripCodeFence(out);
|
|
202
|
+
const plots = SuperScriptLanguage.compile(source).run([]).plots.length;
|
|
203
|
+
if (plots === 0)
|
|
204
|
+
throw new Error(`the model's reply draws nothing, so it is not an indicator: ${JSON.stringify(out.slice(0, 200))}`);
|
|
205
|
+
return source;
|
|
206
|
+
}
|
|
207
|
+
async function nlToSuperScriptChecked(client, request, opts = {}) {
|
|
208
|
+
const maxRepairs = Math.max(0, Math.floor(opts.maxRepairs ?? 2));
|
|
209
|
+
let prompt = request;
|
|
210
|
+
for (let attempts = 1; ; attempts++) {
|
|
211
|
+
const out = await client.complete({ system: SUPERSCRIPT_SYSTEM_PROMPT, prompt, ...opts.signal !== void 0 ? { signal: opts.signal } : {} });
|
|
212
|
+
const source = stripCodeFence(out);
|
|
213
|
+
const diagnostics = checkSuperScript(source);
|
|
214
|
+
const failing = diagnostics.some((d) => d.severity === "error" || d.code === "NO_PLOTS");
|
|
215
|
+
if (!failing || attempts > maxRepairs)
|
|
216
|
+
return { ok: !failing, source, diagnostics, attempts };
|
|
217
|
+
prompt = [
|
|
218
|
+
`The request was: ${request}`,
|
|
219
|
+
"Your SuperScript was:",
|
|
220
|
+
source,
|
|
221
|
+
"It has these problems:",
|
|
222
|
+
formatDiagnostics(diagnostics, source),
|
|
223
|
+
"Return the corrected script only."
|
|
224
|
+
].join("\n");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function buildChartContext(source, opts = {}) {
|
|
228
|
+
const parts = "getChart" in source ? { chart: source.getChart(), symbol: source.getSymbol(), period: source.getPeriod(), timezone: source.getTimezone() } : "getBars" in source ? workspaceParts(source) : source;
|
|
229
|
+
const chart = parts.chart;
|
|
230
|
+
if (chart === null)
|
|
231
|
+
throw new Error("buildChartContext: the chart has not mounted yet (getChart() is null)");
|
|
232
|
+
const dl = chart.getDataList();
|
|
233
|
+
const n = dl.length;
|
|
234
|
+
const clamp = (x) => Math.max(0, Math.min(n, Math.round(x)));
|
|
235
|
+
let from;
|
|
236
|
+
let to;
|
|
237
|
+
const range = opts.range ?? "visible";
|
|
238
|
+
if (range === "all") {
|
|
239
|
+
from = 0;
|
|
240
|
+
to = n;
|
|
241
|
+
} else if (range === "visible") {
|
|
242
|
+
const vr = chart.getVisibleRange();
|
|
243
|
+
from = clamp(vr.from);
|
|
244
|
+
to = clamp(vr.to);
|
|
245
|
+
} else {
|
|
246
|
+
from = clamp(range.from);
|
|
247
|
+
to = clamp(range.to);
|
|
248
|
+
}
|
|
249
|
+
if (to < from)
|
|
250
|
+
to = from;
|
|
251
|
+
const visibleBars = to - from;
|
|
252
|
+
const maxBars = Math.max(1, Math.floor(opts.maxBars ?? 200));
|
|
253
|
+
const lookback = visibleBars > 0 && maxBars < 2 ? 0 : Math.max(0, Math.min(Math.floor(opts.lookback ?? 0), from));
|
|
254
|
+
const fit = (ctx2) => {
|
|
255
|
+
let b = Math.max(1, Math.ceil((ctx2 + visibleBars) / maxBars));
|
|
256
|
+
while (Math.ceil(ctx2 / b) + Math.ceil(visibleBars / b) > maxBars)
|
|
257
|
+
b++;
|
|
258
|
+
return b;
|
|
259
|
+
};
|
|
260
|
+
let k = fit(lookback);
|
|
261
|
+
const contextBars = Math.min(Math.ceil(lookback / k), Math.floor(from / k)) * k;
|
|
262
|
+
if (contextBars === 0 && lookback > 0)
|
|
263
|
+
k = fit(0);
|
|
264
|
+
const rows = aggregate(dl.slice(from - contextBars, to), k);
|
|
265
|
+
const period = typeof parts.period === "string" ? parts.period : parts.period.text ?? `${parts.period.multiplier}${parts.period.timespan}`;
|
|
266
|
+
const format = {
|
|
267
|
+
timezone: parts.timezone ?? "UTC",
|
|
268
|
+
visibleFrom: from,
|
|
269
|
+
visibleTo: to,
|
|
270
|
+
bucket: k,
|
|
271
|
+
sourceBars: contextBars + visibleBars,
|
|
272
|
+
contextRows: contextBars / k
|
|
273
|
+
};
|
|
274
|
+
if (parts.symbol.pricePrecision === "auto") {
|
|
275
|
+
format.pricePrecision = resolvePricePrecision("auto", dl.slice(from - contextBars, to));
|
|
276
|
+
} else if (parts.symbol.pricePrecision !== void 0) {
|
|
277
|
+
format.pricePrecision = parts.symbol.pricePrecision;
|
|
278
|
+
}
|
|
279
|
+
if (parts.symbol.volumePrecision !== void 0)
|
|
280
|
+
format.volumePrecision = parts.symbol.volumePrecision;
|
|
281
|
+
const ctx = { symbol: parts.symbol.ticker, period, bars: rows, format };
|
|
282
|
+
if (opts.indicators !== void 0)
|
|
283
|
+
ctx.indicators = opts.indicators;
|
|
284
|
+
return ctx;
|
|
285
|
+
}
|
|
286
|
+
function workspaceParts(workspace) {
|
|
287
|
+
const timezone = workspace.chart.getRenderOptions?.().timeZone;
|
|
288
|
+
return {
|
|
289
|
+
chart: {
|
|
290
|
+
getDataList: () => workspace.getBars(),
|
|
291
|
+
getVisibleRange: () => {
|
|
292
|
+
const { i0, i1 } = workspace.chart.view.getSnapshot().viewport;
|
|
293
|
+
return { from: Math.floor(i0), to: Math.ceil(i1) + 1 };
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
symbol: workspace.getSymbol(),
|
|
297
|
+
period: workspace.getPeriod(),
|
|
298
|
+
...timezone !== void 0 ? { timezone } : {}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function aggregate(bars, k) {
|
|
302
|
+
if (k <= 1)
|
|
303
|
+
return bars.map((b) => ({ ...b }));
|
|
304
|
+
const out = [];
|
|
305
|
+
for (let start = 0; start < bars.length; start += k) {
|
|
306
|
+
const chunk = bars.slice(start, start + k);
|
|
307
|
+
let high = -Infinity;
|
|
308
|
+
let low = Infinity;
|
|
309
|
+
let volume;
|
|
310
|
+
for (const b of chunk) {
|
|
311
|
+
high = Math.max(high, b.high);
|
|
312
|
+
low = Math.min(low, b.low);
|
|
313
|
+
if (b.volume !== void 0)
|
|
314
|
+
volume = (volume ?? 0) + b.volume;
|
|
315
|
+
}
|
|
316
|
+
const row = { timestamp: chunk[0].timestamp, open: chunk[0].open, high, low, close: chunk[chunk.length - 1].close };
|
|
317
|
+
if (volume !== void 0)
|
|
318
|
+
row.volume = volume;
|
|
319
|
+
out.push(row);
|
|
320
|
+
}
|
|
321
|
+
return out;
|
|
322
|
+
}
|
|
323
|
+
var ANALYST_SYSTEM_PROMPT = `You are a professional markets analyst embedded in a trading chart.
|
|
324
|
+
Be concise and concrete. Reference price structure, key support/resistance, momentum,
|
|
325
|
+
and lay out the most likely scenarios with invalidation levels. Never give financial
|
|
326
|
+
advice or position sizing \u2014 describe the chart, not what the user should do.`;
|
|
327
|
+
async function analyzeChart(client, ctx, question, opts = {}) {
|
|
328
|
+
const indicators = ctx.indicators !== void 0 && ctx.indicators.length > 0 ? `
|
|
329
|
+
Indicators on chart: ${ctx.indicators.join(", ")}.` : "";
|
|
330
|
+
const data = ctx.format === void 0 ? v1Bars(ctx) : compactBars(ctx, ctx.format);
|
|
331
|
+
const prompt = `Symbol ${ctx.symbol}, period ${ctx.period}.${indicators}
|
|
332
|
+
${data}
|
|
333
|
+
|
|
334
|
+
` + (question ?? "Analyze the current structure, key levels, momentum, and likely scenarios.");
|
|
335
|
+
return await client.complete({ system: ANALYST_SYSTEM_PROMPT, prompt, ...opts.signal !== void 0 ? { signal: opts.signal } : {} });
|
|
336
|
+
}
|
|
337
|
+
function v1Bars(ctx) {
|
|
338
|
+
const recent = ctx.bars.slice(-160).map((b) => `${b.timestamp},${b.open},${b.high},${b.low},${b.close},${b.volume ?? ""}`).join("\n");
|
|
339
|
+
return `Recent OHLCV (timestamp,open,high,low,close,volume):
|
|
340
|
+
${recent}`;
|
|
341
|
+
}
|
|
342
|
+
function compactBars(ctx, f) {
|
|
343
|
+
const bars = ctx.bars;
|
|
344
|
+
if (bars.length === 0)
|
|
345
|
+
return "No bars in view.";
|
|
346
|
+
const tz = f.timezone ?? "UTC";
|
|
347
|
+
const k = f.bucket ?? 1;
|
|
348
|
+
const header = [
|
|
349
|
+
`Bars (i,open,high,low,close,volume): start=${isoInZone(bars[0].timestamp, tz)} end=${isoInZone(bars[bars.length - 1].timestamp, tz)} step=${k > 1 ? `${k}x${ctx.period}` : ctx.period} tz=${tz} rows=${bars.length}.`,
|
|
350
|
+
"Row i starts i steps after start; times skip market closures."
|
|
351
|
+
];
|
|
352
|
+
if (k > 1) {
|
|
353
|
+
const lastRow = f.sourceBars !== void 0 ? f.sourceBars - (bars.length - 1) * k : k;
|
|
354
|
+
header.push(`${f.sourceBars ?? bars.length * k} bars aggregated into ${bars.length} rows of ${k} bars (first open, highest high, lowest low, last close, summed volume).` + (lastRow > 0 && lastRow < k ? ` The last row holds ${lastRow} bars.` : ""));
|
|
355
|
+
}
|
|
356
|
+
const c = f.contextRows ?? 0;
|
|
357
|
+
if (c > 0)
|
|
358
|
+
header.push(`Rows 0-${c - 1} are context before the visible window; the user sees rows ${c}-${bars.length - 1}.`);
|
|
359
|
+
const p = (x) => num(x, f.pricePrecision);
|
|
360
|
+
const rows = bars.map((b, i) => `${i},${p(b.open)},${p(b.high)},${p(b.low)},${p(b.close)},${b.volume === void 0 ? "" : num(b.volume, f.volumePrecision)}`);
|
|
361
|
+
return `${header.join("\n")}
|
|
362
|
+
${rows.join("\n")}`;
|
|
363
|
+
}
|
|
364
|
+
function num(x, precision) {
|
|
365
|
+
if (!Number.isFinite(x))
|
|
366
|
+
return "";
|
|
367
|
+
const r = precision !== void 0 && precision >= 0 && precision <= 20 ? Number(x.toFixed(precision)) : Number(x.toPrecision(8));
|
|
368
|
+
return String(r);
|
|
369
|
+
}
|
|
370
|
+
function isoInZone(ts, tz) {
|
|
371
|
+
try {
|
|
372
|
+
const f = new Intl.DateTimeFormat("en-US", { timeZone: tz, hourCycle: "h23", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
373
|
+
const p = {};
|
|
374
|
+
for (const part of f.formatToParts(new Date(ts)))
|
|
375
|
+
p[part.type] = part.value;
|
|
376
|
+
const wall = Date.UTC(Number(p.year), Number(p.month) - 1, Number(p.day), Number(p.hour) % 24, Number(p.minute), Number(p.second));
|
|
377
|
+
const off = Math.round((wall - Math.floor(ts / 1e3) * 1e3) / 6e4);
|
|
378
|
+
const two = (v) => String(v).padStart(2, "0");
|
|
379
|
+
const zone = off === 0 ? "Z" : `${off < 0 ? "-" : "+"}${two(Math.floor(Math.abs(off) / 60))}:${two(Math.abs(off) % 60)}`;
|
|
380
|
+
return `${p.year}-${p.month}-${p.day}T${two(Number(p.hour) % 24)}:${p.minute}:${p.second}${zone}`;
|
|
381
|
+
} catch {
|
|
382
|
+
return new Date(ts).toISOString();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function stripCodeFence(s) {
|
|
386
|
+
const m = /```[a-z]*\n([\s\S]*?)```/i.exec(s);
|
|
387
|
+
return (m !== null ? m[1] : s).trim();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// packages/superscript/src/index.ts
|
|
391
|
+
var enabled = /* @__PURE__ */ new Set([SuperScriptLanguage.id]);
|
|
392
|
+
function registerLanguage2(lang) {
|
|
393
|
+
registerLanguage(lang);
|
|
394
|
+
enabled.add(lang.id);
|
|
395
|
+
}
|
|
396
|
+
function getLanguage2(id) {
|
|
397
|
+
return enabled.has(id) ? getLanguage(id) : void 0;
|
|
398
|
+
}
|
|
399
|
+
function listLanguages2() {
|
|
400
|
+
return listLanguages().filter((lang) => enabled.has(lang.id));
|
|
401
|
+
}
|
|
402
|
+
function runScript(source, bars, languageId = SuperScriptLanguage.id, params) {
|
|
403
|
+
const lang = getLanguage2(languageId);
|
|
404
|
+
if (lang === void 0) {
|
|
405
|
+
throw new Error(`language "${languageId}" is not enabled on @supersolutions/super-chart-superscript; only "superscript" runs by default. Register another language with registerLanguage() from this entry first.`);
|
|
406
|
+
}
|
|
407
|
+
return lang.compile(source).run(bars, params);
|
|
408
|
+
}
|
|
409
|
+
export {
|
|
410
|
+
ANALYST_SYSTEM_PROMPT,
|
|
411
|
+
JavaScriptLanguage,
|
|
412
|
+
SUPERSCRIPT_SYSTEM_PROMPT,
|
|
413
|
+
SuperScriptError,
|
|
414
|
+
SuperScriptLanguage,
|
|
415
|
+
analyzeChart,
|
|
416
|
+
buildChartContext,
|
|
417
|
+
checkSuperScript,
|
|
418
|
+
createHttpAIClient,
|
|
419
|
+
formatDiagnostics,
|
|
420
|
+
getLanguage2 as getLanguage,
|
|
421
|
+
isSuperScriptError,
|
|
422
|
+
listLanguages2 as listLanguages,
|
|
423
|
+
nlToSuperScript,
|
|
424
|
+
nlToSuperScriptChecked,
|
|
425
|
+
registerLanguage2 as registerLanguage,
|
|
426
|
+
runScript,
|
|
427
|
+
superlib,
|
|
428
|
+
superscriptReference
|
|
429
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { EngineIndicatorInstance, EngineIndicatorSpec, IndicatorDefinition } from '@supersolutions/super-chart-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SuperScript diagnostics: every problem has a severity, a code, a message and
|
|
5
|
+
* a 1-based position, so a person, an editor or an AI repair loop can act on
|
|
6
|
+
* it. Part of Super Chart.
|
|
7
|
+
*/
|
|
8
|
+
export type DiagnosticSeverity = "error" | "warning";
|
|
9
|
+
/**
|
|
10
|
+
* Codes are stable; messages may be reworded.
|
|
11
|
+
*
|
|
12
|
+
* Errors (the script does not compile):
|
|
13
|
+
* UNEXPECTED_CHAR, UNTERMINATED_STRING, UNEXPECTED_TOKEN, TRAILING_TOKENS,
|
|
14
|
+
* UNBALANCED, REASSIGNMENT (`:=`, `+=`, …), CONTROL_FLOW (`if`, `for`, …),
|
|
15
|
+
* FUNCTION_DEF (`f(x) => …`), BUILTIN_REASSIGNED, UNKNOWN_IDENTIFIER,
|
|
16
|
+
* UNKNOWN_FUNCTION, ARITY, UNKNOWN_ARGUMENT, DUPLICATE_ARGUMENT,
|
|
17
|
+
* EXPECTED_CONSTANT, BAD_LENGTH, BAD_OFFSET, TYPE, PINE_MISMATCH,
|
|
18
|
+
* UNSUPPORTED, STATEMENT (`plot()` inside an expression).
|
|
19
|
+
*
|
|
20
|
+
* Warnings (the script compiles; the part named is ignored):
|
|
21
|
+
* IGNORED_STATEMENT (plotshape, bgcolor, strategy.*, …), UNUSED_EXPRESSION,
|
|
22
|
+
* IGNORED_KEYWORD (var, float, …), UNKNOWN_KEYWORD (an unknown plot()
|
|
23
|
+
* keyword), NO_PLOTS.
|
|
24
|
+
*/
|
|
25
|
+
export type DiagnosticCode = "UNEXPECTED_CHAR" | "UNTERMINATED_STRING" | "UNEXPECTED_TOKEN" | "TRAILING_TOKENS" | "UNBALANCED" | "REASSIGNMENT" | "CONTROL_FLOW" | "FUNCTION_DEF" | "BUILTIN_REASSIGNED" | "UNKNOWN_IDENTIFIER" | "UNKNOWN_FUNCTION" | "ARITY" | "UNKNOWN_ARGUMENT" | "DUPLICATE_ARGUMENT" | "EXPECTED_CONSTANT" | "BAD_LENGTH" | "BAD_OFFSET" | "TYPE" | "PINE_MISMATCH" | "UNSUPPORTED" | "STATEMENT" | "IGNORED_STATEMENT" | "UNUSED_EXPRESSION" | "IGNORED_KEYWORD" | "UNKNOWN_KEYWORD" | "NO_PLOTS";
|
|
26
|
+
export interface Diagnostic {
|
|
27
|
+
severity: DiagnosticSeverity;
|
|
28
|
+
code: DiagnosticCode;
|
|
29
|
+
message: string;
|
|
30
|
+
/** 1-based line. */
|
|
31
|
+
line: number;
|
|
32
|
+
/** 1-based column of the first character. */
|
|
33
|
+
col: number;
|
|
34
|
+
/** 1-based column just past the last character. */
|
|
35
|
+
endCol: number;
|
|
36
|
+
/** A suggested fix, when there is one. */
|
|
37
|
+
hint?: string;
|
|
38
|
+
}
|
|
39
|
+
/** One `input()` of a script: what a settings form or an agent can change. */
|
|
40
|
+
export interface ScriptInput {
|
|
41
|
+
/** The name it is bound to (`len = input(14)` → "len"), else "in<index>". */
|
|
42
|
+
id: string;
|
|
43
|
+
title: string;
|
|
44
|
+
defval: number;
|
|
45
|
+
min?: number;
|
|
46
|
+
max?: number;
|
|
47
|
+
step?: number;
|
|
48
|
+
type: "int" | "float";
|
|
49
|
+
}
|
|
50
|
+
/** Input values for `run()`: by position (the order of `meta.inputs`) or by input id. */
|
|
51
|
+
export type ScriptParams = ReadonlyArray<number | null | undefined> | Readonly<Record<string, number | null | undefined>>;
|
|
52
|
+
/** Per-script ceilings, not a wall-clock timeout or an isolation sandbox. */
|
|
53
|
+
export interface EngineScriptLimits {
|
|
54
|
+
readonly sourceLength: number;
|
|
55
|
+
readonly nodes: number;
|
|
56
|
+
readonly depth: number;
|
|
57
|
+
readonly plots: number;
|
|
58
|
+
readonly inputs: number;
|
|
59
|
+
readonly bars: number;
|
|
60
|
+
readonly window: number;
|
|
61
|
+
readonly seriesCells: number;
|
|
62
|
+
readonly workUnits: number;
|
|
63
|
+
}
|
|
64
|
+
export declare const ENGINE_SCRIPT_LIMITS: Readonly<EngineScriptLimits>;
|
|
65
|
+
export interface EngineScriptOptions {
|
|
66
|
+
readonly name: string;
|
|
67
|
+
readonly source: string;
|
|
68
|
+
readonly params?: ScriptParams;
|
|
69
|
+
/** Optional tighter ceilings. Raising the built-in ceilings is not supported. */
|
|
70
|
+
readonly limits?: Partial<EngineScriptLimits>;
|
|
71
|
+
}
|
|
72
|
+
export type EngineScriptErrorCode = "LIMIT" | "UNSUPPORTED" | "PARAMS" | "DATA" | "RESULT" | "DISPOSED";
|
|
73
|
+
export declare class EngineScriptError extends Error {
|
|
74
|
+
readonly code: EngineScriptErrorCode;
|
|
75
|
+
constructor(code: EngineScriptErrorCode, message: string);
|
|
76
|
+
}
|
|
77
|
+
/** Structural host interface: no EngineChart, DOM, or upstream-engine import. */
|
|
78
|
+
export interface EngineScriptHost {
|
|
79
|
+
registerIndicator: (definition: IndicatorDefinition) => void;
|
|
80
|
+
unregisterIndicator: (name: string, expectedCalculate?: IndicatorDefinition["calculate"]) => boolean;
|
|
81
|
+
addIndicator: (spec: EngineIndicatorSpec) => string;
|
|
82
|
+
listIndicators: () => readonly EngineIndicatorInstance[];
|
|
83
|
+
removeIndicator: (id: string) => boolean;
|
|
84
|
+
updateIndicator: (id: string, changes: Pick<EngineIndicatorSpec, "params">) => boolean;
|
|
85
|
+
}
|
|
86
|
+
export interface EngineScriptAttachment {
|
|
87
|
+
readonly id: string;
|
|
88
|
+
/** Validate before changing the instance. Unspecified inputs keep their current values. */
|
|
89
|
+
setParams: (params: ScriptParams) => readonly number[];
|
|
90
|
+
/** Remove only this instance, then unregister only this exact calculation if unused. */
|
|
91
|
+
dispose: () => void;
|
|
92
|
+
}
|
|
93
|
+
export interface EngineScriptIndicator {
|
|
94
|
+
readonly title: string;
|
|
95
|
+
readonly definition: IndicatorDefinition;
|
|
96
|
+
readonly inputs: readonly Readonly<ScriptInput>[];
|
|
97
|
+
readonly diagnostics: readonly Readonly<Diagnostic>[];
|
|
98
|
+
readonly limits: Readonly<EngineScriptLimits>;
|
|
99
|
+
attach: (host: EngineScriptHost, options?: Omit<EngineIndicatorSpec, "name" | "params"> & {
|
|
100
|
+
params?: ScriptParams;
|
|
101
|
+
}) => EngineScriptAttachment;
|
|
102
|
+
/** Detach all owned instances. Borrowed definitions remain valid until their host releases them. */
|
|
103
|
+
dispose: () => void;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Optional owned-engine bridge. Import this module on demand; it never imports
|
|
107
|
+
* klinecharts or the arbitrary-JavaScript language registry. The own parser and
|
|
108
|
+
* evaluator run SuperScript only; warnings about ignored constructs are errors.
|
|
109
|
+
*
|
|
110
|
+
* Calculations see every revealed raw bar, not HA values or a viewport slice.
|
|
111
|
+
* `time` is the feed's epoch milliseconds and `bar_index` starts at the first
|
|
112
|
+
* loaded bar. Prepending history therefore intentionally changes cumulative and
|
|
113
|
+
* indexed formulas. Replay must supply only its revealed prefix, as EngineChart
|
|
114
|
+
* does. Resource ceilings reject work; they never truncate or invent results.
|
|
115
|
+
* The synchronous runtime has no interruptible wall-clock deadline.
|
|
116
|
+
*/
|
|
117
|
+
export declare function createEngineScriptIndicator(options: EngineScriptOptions): EngineScriptIndicator;
|
|
118
|
+
|
|
119
|
+
export {};
|