oakscriptjs 0.10.1 → 0.11.1
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/cjs/array/index.d.ts +8 -2
- package/dist/cjs/array/index.d.ts.map +1 -1
- package/dist/cjs/array/index.js +2 -1
- package/dist/cjs/array/index.js.map +2 -2
- package/dist/cjs/box/index.d.ts.map +1 -1
- package/dist/cjs/box/index.js +7 -1
- package/dist/cjs/box/index.js.map +2 -2
- package/dist/cjs/callsite/index.d.ts +73 -32
- package/dist/cjs/callsite/index.d.ts.map +1 -1
- package/dist/cjs/callsite/index.js +69 -65
- package/dist/cjs/callsite/index.js.map +2 -2
- package/dist/cjs/drawing/registry.d.ts +84 -0
- package/dist/cjs/drawing/registry.d.ts.map +1 -1
- package/dist/cjs/drawing/registry.js +153 -6
- package/dist/cjs/drawing/registry.js.map +2 -2
- package/dist/cjs/index.d.ts +8 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +33 -1
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/input.d.ts +9 -1
- package/dist/cjs/input.d.ts.map +1 -1
- package/dist/cjs/input.js +16 -0
- package/dist/cjs/input.js.map +2 -2
- package/dist/cjs/label/index.d.ts.map +1 -1
- package/dist/cjs/label/index.js +3 -0
- package/dist/cjs/label/index.js.map +2 -2
- package/dist/cjs/line/index.d.ts +10 -27
- package/dist/cjs/line/index.d.ts.map +1 -1
- package/dist/cjs/line/index.js +11 -22
- package/dist/cjs/line/index.js.map +2 -2
- package/dist/cjs/linefill/index.d.ts +3 -0
- package/dist/cjs/linefill/index.d.ts.map +1 -1
- package/dist/cjs/linefill/index.js +15 -1
- package/dist/cjs/linefill/index.js.map +3 -3
- package/dist/cjs/math/index.d.ts.map +1 -1
- package/dist/cjs/math/index.js +2 -0
- package/dist/cjs/math/index.js.map +2 -2
- package/dist/cjs/polyline/index.d.ts.map +1 -1
- package/dist/cjs/polyline/index.js +3 -0
- package/dist/cjs/polyline/index.js.map +2 -2
- package/dist/cjs/runtime/adapters/SimpleInputAdapter.js +1 -1
- package/dist/cjs/runtime/adapters/SimpleInputAdapter.js.map +2 -2
- package/dist/cjs/runtime/index.d.ts +1 -1
- package/dist/cjs/runtime/index.d.ts.map +1 -1
- package/dist/cjs/runtime/index.js +1 -0
- package/dist/cjs/runtime/index.js.map +2 -2
- package/dist/cjs/runtime/inputs.d.ts +9 -0
- package/dist/cjs/runtime/inputs.d.ts.map +1 -1
- package/dist/cjs/runtime/inputs.js +8 -0
- package/dist/cjs/runtime/inputs.js.map +2 -2
- package/dist/cjs/runtime/types.d.ts +1 -1
- package/dist/cjs/runtime/types.d.ts.map +1 -1
- package/dist/cjs/runtime/types.js.map +1 -1
- package/dist/cjs/script/index.d.ts +91 -21
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +207 -19
- package/dist/cjs/script/index.js.map +3 -3
- package/dist/cjs/security/other.d.ts +92 -0
- package/dist/cjs/security/other.d.ts.map +1 -0
- package/dist/cjs/security/other.js +164 -0
- package/dist/cjs/security/other.js.map +7 -0
- package/dist/cjs/session/bars.d.ts +24 -1
- package/dist/cjs/session/bars.d.ts.map +1 -1
- package/dist/cjs/session/bars.js +56 -7
- package/dist/cjs/session/bars.js.map +3 -3
- package/dist/cjs/session/calendar.d.ts +13 -2
- package/dist/cjs/session/calendar.d.ts.map +1 -1
- package/dist/cjs/session/calendar.js +39 -9
- package/dist/cjs/session/calendar.js.map +2 -2
- package/dist/cjs/session/context.d.ts +76 -0
- package/dist/cjs/session/context.d.ts.map +1 -0
- package/dist/cjs/session/context.js +114 -0
- package/dist/cjs/session/context.js.map +7 -0
- package/dist/cjs/str/floatingdecimal.d.ts +31 -0
- package/dist/cjs/str/floatingdecimal.d.ts.map +1 -0
- package/dist/cjs/str/floatingdecimal.js +298 -0
- package/dist/cjs/str/floatingdecimal.js.map +7 -0
- package/dist/cjs/str/index.d.ts +4 -1
- package/dist/cjs/str/index.d.ts.map +1 -1
- package/dist/cjs/str/index.js +2 -1
- package/dist/cjs/str/index.js.map +2 -2
- package/dist/cjs/str/numberformat.d.ts +7 -3
- package/dist/cjs/str/numberformat.d.ts.map +1 -1
- package/dist/cjs/str/numberformat.js +4 -3
- package/dist/cjs/str/numberformat.js.map +2 -2
- package/dist/cjs/ta/index.d.ts +67 -34
- package/dist/cjs/ta/index.d.ts.map +1 -1
- package/dist/cjs/ta/index.js +202 -145
- package/dist/cjs/ta/index.js.map +2 -2
- package/dist/cjs/ta/limits.d.ts +35 -0
- package/dist/cjs/ta/limits.d.ts.map +1 -0
- package/dist/cjs/ta/limits.js +64 -0
- package/dist/cjs/ta/limits.js.map +7 -0
- package/dist/cjs/ta/running-sum.d.ts +0 -16
- package/dist/cjs/ta/running-sum.d.ts.map +1 -1
- package/dist/cjs/ta/running-sum.js +2 -0
- package/dist/cjs/ta/running-sum.js.map +2 -2
- package/dist/cjs/ta-series.d.ts +8 -8
- package/dist/cjs/ta-series.d.ts.map +1 -1
- package/dist/cjs/ta-series.js +10 -8
- package/dist/cjs/ta-series.js.map +2 -2
- package/dist/cjs/table/index.d.ts +3 -1
- package/dist/cjs/table/index.d.ts.map +1 -1
- package/dist/cjs/table/index.js +1 -0
- package/dist/cjs/table/index.js.map +3 -3
- package/dist/cjs/time/index.d.ts +5 -2
- package/dist/cjs/time/index.d.ts.map +1 -1
- package/dist/cjs/time/index.js +2 -2
- package/dist/cjs/time/index.js.map +2 -2
- package/dist/cjs/time/session.d.ts +26 -1
- package/dist/cjs/time/session.d.ts.map +1 -1
- package/dist/cjs/time/session.js +94 -12
- package/dist/cjs/time/session.js.map +3 -3
- package/dist/cjs/time/timezone.d.ts +6 -1
- package/dist/cjs/time/timezone.d.ts.map +1 -1
- package/dist/cjs/time/timezone.js +2 -2
- package/dist/cjs/time/timezone.js.map +2 -2
- package/dist/cjs/types/metadata.d.ts +28 -4
- package/dist/cjs/types/metadata.d.ts.map +1 -1
- package/dist/cjs/types/metadata.js.map +1 -1
- package/dist/esm/array/index.d.ts +8 -2
- package/dist/esm/array/index.d.ts.map +1 -1
- package/dist/esm/array/index.js +12 -3
- package/dist/esm/array/index.js.map +1 -1
- package/dist/esm/box/index.d.ts.map +1 -1
- package/dist/esm/box/index.js +8 -2
- package/dist/esm/box/index.js.map +1 -1
- package/dist/esm/callsite/index.d.ts +73 -32
- package/dist/esm/callsite/index.d.ts.map +1 -1
- package/dist/esm/callsite/index.js +143 -106
- package/dist/esm/callsite/index.js.map +1 -1
- package/dist/esm/drawing/registry.d.ts +84 -0
- package/dist/esm/drawing/registry.d.ts.map +1 -1
- package/dist/esm/drawing/registry.js +260 -6
- package/dist/esm/drawing/registry.js.map +1 -1
- package/dist/esm/index.d.ts +8 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +13 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/input.d.ts +9 -1
- package/dist/esm/input.d.ts.map +1 -1
- package/dist/esm/input.js +16 -0
- package/dist/esm/input.js.map +1 -1
- package/dist/esm/label/index.d.ts.map +1 -1
- package/dist/esm/label/index.js +4 -1
- package/dist/esm/label/index.js.map +1 -1
- package/dist/esm/line/index.d.ts +10 -27
- package/dist/esm/line/index.d.ts.map +1 -1
- package/dist/esm/line/index.js +22 -60
- package/dist/esm/line/index.js.map +1 -1
- package/dist/esm/linefill/index.d.ts +3 -0
- package/dist/esm/linefill/index.d.ts.map +1 -1
- package/dist/esm/linefill/index.js +20 -1
- package/dist/esm/linefill/index.js.map +1 -1
- package/dist/esm/math/index.d.ts.map +1 -1
- package/dist/esm/math/index.js +3 -0
- package/dist/esm/math/index.js.map +1 -1
- package/dist/esm/polyline/index.d.ts.map +1 -1
- package/dist/esm/polyline/index.js +5 -1
- package/dist/esm/polyline/index.js.map +1 -1
- package/dist/esm/runtime/adapters/SimpleInputAdapter.js +1 -1
- package/dist/esm/runtime/adapters/SimpleInputAdapter.js.map +1 -1
- package/dist/esm/runtime/index.d.ts +1 -1
- package/dist/esm/runtime/index.d.ts.map +1 -1
- package/dist/esm/runtime/index.js +1 -1
- package/dist/esm/runtime/index.js.map +1 -1
- package/dist/esm/runtime/inputs.d.ts +9 -0
- package/dist/esm/runtime/inputs.d.ts.map +1 -1
- package/dist/esm/runtime/inputs.js +15 -0
- package/dist/esm/runtime/inputs.js.map +1 -1
- package/dist/esm/runtime/types.d.ts +1 -1
- package/dist/esm/runtime/types.d.ts.map +1 -1
- package/dist/esm/script/index.d.ts +91 -21
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +259 -28
- package/dist/esm/script/index.js.map +1 -1
- package/dist/esm/security/other.d.ts +92 -0
- package/dist/esm/security/other.d.ts.map +1 -0
- package/dist/esm/security/other.js +211 -0
- package/dist/esm/security/other.js.map +1 -0
- package/dist/esm/session/bars.d.ts +24 -1
- package/dist/esm/session/bars.d.ts.map +1 -1
- package/dist/esm/session/bars.js +75 -6
- package/dist/esm/session/bars.js.map +1 -1
- package/dist/esm/session/calendar.d.ts +13 -2
- package/dist/esm/session/calendar.d.ts.map +1 -1
- package/dist/esm/session/calendar.js +54 -10
- package/dist/esm/session/calendar.js.map +1 -1
- package/dist/esm/session/context.d.ts +76 -0
- package/dist/esm/session/context.d.ts.map +1 -0
- package/dist/esm/session/context.js +124 -0
- package/dist/esm/session/context.js.map +1 -0
- package/dist/esm/str/floatingdecimal.d.ts +31 -0
- package/dist/esm/str/floatingdecimal.d.ts.map +1 -0
- package/dist/esm/str/floatingdecimal.js +272 -0
- package/dist/esm/str/floatingdecimal.js.map +1 -0
- package/dist/esm/str/index.d.ts +4 -1
- package/dist/esm/str/index.d.ts.map +1 -1
- package/dist/esm/str/index.js +9 -2
- package/dist/esm/str/index.js.map +1 -1
- package/dist/esm/str/numberformat.d.ts +7 -3
- package/dist/esm/str/numberformat.d.ts.map +1 -1
- package/dist/esm/str/numberformat.js +11 -5
- package/dist/esm/str/numberformat.js.map +1 -1
- package/dist/esm/ta/index.d.ts +67 -34
- package/dist/esm/ta/index.d.ts.map +1 -1
- package/dist/esm/ta/index.js +284 -240
- package/dist/esm/ta/index.js.map +1 -1
- package/dist/esm/ta/limits.d.ts +35 -0
- package/dist/esm/ta/limits.d.ts.map +1 -0
- package/dist/esm/ta/limits.js +67 -0
- package/dist/esm/ta/limits.js.map +1 -0
- package/dist/esm/ta/running-sum.d.ts +0 -16
- package/dist/esm/ta/running-sum.d.ts.map +1 -1
- package/dist/esm/ta/running-sum.js +2 -0
- package/dist/esm/ta/running-sum.js.map +1 -1
- package/dist/esm/ta-series.d.ts +8 -8
- package/dist/esm/ta-series.d.ts.map +1 -1
- package/dist/esm/ta-series.js +11 -8
- package/dist/esm/ta-series.js.map +1 -1
- package/dist/esm/table/index.d.ts +3 -1
- package/dist/esm/table/index.d.ts.map +1 -1
- package/dist/esm/table/index.js +6 -1
- package/dist/esm/table/index.js.map +1 -1
- package/dist/esm/time/index.d.ts +5 -2
- package/dist/esm/time/index.d.ts.map +1 -1
- package/dist/esm/time/index.js +6 -3
- package/dist/esm/time/index.js.map +1 -1
- package/dist/esm/time/session.d.ts +26 -1
- package/dist/esm/time/session.d.ts.map +1 -1
- package/dist/esm/time/session.js +141 -16
- package/dist/esm/time/session.js.map +1 -1
- package/dist/esm/time/timezone.d.ts +6 -1
- package/dist/esm/time/timezone.d.ts.map +1 -1
- package/dist/esm/time/timezone.js +8 -2
- package/dist/esm/time/timezone.js.map +1 -1
- package/dist/esm/types/metadata.d.ts +28 -4
- package/dist/esm/types/metadata.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/array/index.ts +11 -3
- package/src/box/index.ts +8 -2
- package/src/callsite/index.ts +150 -100
- package/src/drawing/registry.ts +278 -5
- package/src/index.ts +44 -1
- package/src/input.ts +18 -1
- package/src/label/index.ts +4 -1
- package/src/line/index.ts +22 -64
- package/src/linefill/index.ts +20 -1
- package/src/math/index.ts +2 -0
- package/src/polyline/index.ts +3 -1
- package/src/runtime/adapters/SimpleInputAdapter.ts +1 -1
- package/src/runtime/index.ts +1 -0
- package/src/runtime/inputs.ts +16 -0
- package/src/runtime/types.ts +1 -1
- package/src/script/index.ts +355 -34
- package/src/security/other.ts +269 -0
- package/src/session/bars.ts +70 -6
- package/src/session/calendar.ts +60 -10
- package/src/session/context.ts +162 -0
- package/src/str/floatingdecimal.ts +268 -0
- package/src/str/index.ts +8 -2
- package/src/str/numberformat.ts +13 -6
- package/src/ta/index.ts +304 -248
- package/src/ta/limits.ts +74 -0
- package/src/ta/running-sum.ts +2 -0
- package/src/ta-series.ts +19 -16
- package/src/table/index.ts +4 -1
- package/src/time/index.ts +6 -3
- package/src/time/session.ts +137 -15
- package/src/time/timezone.ts +9 -2
- package/src/types/metadata.ts +28 -4
package/dist/esm/script/index.js
CHANGED
|
@@ -46,7 +46,8 @@ import * as tableCore from '../table/index.js';
|
|
|
46
46
|
import * as chartPointCore from '../chartpoint/index.js';
|
|
47
47
|
import * as textCore from '../text/index.js';
|
|
48
48
|
import * as mathCore from '../math/index.js';
|
|
49
|
-
import { resetDrawings, setDrawingLimits } from '../drawing/registry.js';
|
|
49
|
+
import { resetDrawings, setDrawingBar, setDrawingLimits } from '../drawing/registry.js';
|
|
50
|
+
export { holdDrawing, releaseDrawing, varDrawing } from '../drawing/registry.js';
|
|
50
51
|
import { drawingOutputs } from '../drawing/output.js';
|
|
51
52
|
import * as timeframeCore from '../timeframe/index.js';
|
|
52
53
|
import * as timeCore from '../time/index.js';
|
|
@@ -54,11 +55,12 @@ import * as strCore from '../str/index.js';
|
|
|
54
55
|
import { formatMessage } from '../str/messageformat.js';
|
|
55
56
|
import { tickScale } from '../str/numberformat.js';
|
|
56
57
|
import { TradingCalendar } from '../session/calendar.js';
|
|
57
|
-
import { SessionBars, sessionFlags } from '../session/bars.js';
|
|
58
|
+
import { SessionBars, sessionFlags, strayPeriods } from '../session/bars.js';
|
|
58
59
|
import { afterCompletion, heikinAshi, mapToChart, periodsOf, resample } from '../security/resample.js';
|
|
60
|
+
import { securityLowerTf, securityOther } from '../security/other.js';
|
|
59
61
|
import { position } from '../types/index.js';
|
|
60
62
|
import { STRATEGY_CONSTANTS, STRATEGY_DEFAULTS, STRATEGY_NUMBER_VARIABLES, STRATEGY_TEXT_VARIABLES, } from '../strategy/index.js';
|
|
61
|
-
import { parsePineSession } from '../time/session.js';
|
|
63
|
+
import { parsePineSession, sessionRun } from '../time/session.js';
|
|
62
64
|
/** Bars for the current run. Module-level and REUSED across runs so builtins
|
|
63
65
|
* and every Series derived from them keep their version-keyed memoization. */
|
|
64
66
|
const ctxBars = new BarData([]);
|
|
@@ -120,12 +122,16 @@ let scriptOptions = {};
|
|
|
120
122
|
export function executeScript(body, barsInput, inputs = {}, chart = {}, options = {}) {
|
|
121
123
|
ctxBars.setAll(barsInput);
|
|
122
124
|
chartCtx = { ...chart };
|
|
125
|
+
runNow = chart.now ?? Date.now();
|
|
123
126
|
scriptOptions = options;
|
|
124
127
|
ctx = freshCollector(inputs);
|
|
125
128
|
resetDrawings();
|
|
126
129
|
mathCore.resetRandom();
|
|
130
|
+
missingSymbols = [];
|
|
127
131
|
try {
|
|
128
132
|
body();
|
|
133
|
+
if (missingSymbols.length)
|
|
134
|
+
throw new MissingSymbolDataError(missingSymbols);
|
|
129
135
|
const c = ctx;
|
|
130
136
|
return {
|
|
131
137
|
metadata: {
|
|
@@ -252,6 +258,10 @@ export const input = {
|
|
|
252
258
|
session(defval, title, opts = {}) {
|
|
253
259
|
return registerInput({ type: 'session', defval, title, options: opts.options, ...common(opts) }, 'input_session');
|
|
254
260
|
},
|
|
261
|
+
/** Ticker id ("NASDAQ:MSFT"), for `request.security` of another symbol (#180). */
|
|
262
|
+
symbol(defval, title, opts = {}) {
|
|
263
|
+
return registerInput({ type: 'symbol', defval, title, options: opts.options, ...common(opts) }, 'input_symbol');
|
|
264
|
+
},
|
|
255
265
|
/** Date and time as a UNIX time in milliseconds, e.g. `input.time(timestamp('2024-01-01'), 'Start')`. */
|
|
256
266
|
time(defval, title, opts = {}) {
|
|
257
267
|
const v = registerInput({ type: 'time', defval, title, ...common(opts) }, 'input_time');
|
|
@@ -315,10 +325,12 @@ export const ta = {
|
|
|
315
325
|
lowestbars: (sourceOrLength, length) => typeof sourceOrLength === 'number'
|
|
316
326
|
? taSeries.lowestbars(low, sourceOrLength)
|
|
317
327
|
: taSeries.lowestbars(sourceOrLength, length),
|
|
318
|
-
/** `ta.vwap(source, anchor?, stdev_mult?)` with the chart volume; the default anchor is a new trading
|
|
319
|
-
*
|
|
328
|
+
/** `ta.vwap(source, anchor?, stdev_mult?)` with the chart volume; the default anchor is a new trading day
|
|
329
|
+
* and the first bar: as in PineScript, the first loaded bars (a day in progress) have a value. On D / W / M
|
|
330
|
+
* charts every bar is a new day (no time zone or session needed); on intraday charts it needs the chart context
|
|
331
|
+
* (timeframe, time zone, session). */
|
|
320
332
|
vwap: ((source, anchor, stdev_mult) => {
|
|
321
|
-
const a = anchor ??
|
|
333
|
+
const a = anchor ?? vwapAnchor();
|
|
322
334
|
return stdev_mult === undefined ? taSeries.vwap(source, volume, a) : taSeries.vwapBands(source, volume, a, stdev_mult);
|
|
323
335
|
}),
|
|
324
336
|
/** `ta.mfi(source, length)` with the chart volume (PineScript takes 2 arguments). */
|
|
@@ -814,6 +826,7 @@ export function eachBar(fn) {
|
|
|
814
826
|
if (pendingError && pendingError.bar === idx)
|
|
815
827
|
throw new Error(historyError(idx, pendingError.barsBack));
|
|
816
828
|
currentBar = idx;
|
|
829
|
+
setDrawingBar(idx, idx === b.length - 1, b.length - 1);
|
|
817
830
|
const r = fn(ctx);
|
|
818
831
|
out[idx] = typeof r === 'number' ? r : typeof r === 'boolean' ? (r ? 1 : 0) : NaN;
|
|
819
832
|
// grow the history buffers from the largest reference of this bar
|
|
@@ -832,6 +845,7 @@ export function eachBar(fn) {
|
|
|
832
845
|
}
|
|
833
846
|
finally {
|
|
834
847
|
currentBar = -1;
|
|
848
|
+
setDrawingBar(null);
|
|
835
849
|
}
|
|
836
850
|
return Series.fromArray(ctxBars, out);
|
|
837
851
|
}
|
|
@@ -1012,6 +1026,21 @@ for (const name of [...STRATEGY_NUMBER_VARIABLES, ...STRATEGY_TEXT_VARIABLES]) {
|
|
|
1012
1026
|
* ```
|
|
1013
1027
|
*/
|
|
1014
1028
|
export const strategy = Object.defineProperties(Object.assign((title, options) => declareStrategy(title, options), strategyMembers), variableGetters);
|
|
1029
|
+
/**
|
|
1030
|
+
* Thrown by `executeScript` when `request.security` asked for bars of another symbol that `ChartContext.bars` did not
|
|
1031
|
+
* give (#179). `requests` lists them all, once each: load them, then run the script again.
|
|
1032
|
+
*/
|
|
1033
|
+
export class MissingSymbolDataError extends Error {
|
|
1034
|
+
requests;
|
|
1035
|
+
constructor(requests) {
|
|
1036
|
+
super(`request.security needs the bars of ${requests.map((r) => `${r.symbol} (${r.timeframe})`).join(', ')}: ` +
|
|
1037
|
+
'pass them with ChartContext.bars.');
|
|
1038
|
+
this.requests = requests;
|
|
1039
|
+
this.name = 'MissingSymbolDataError';
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
/** Requests of other symbols without bars in the current run. */
|
|
1043
|
+
let missingSymbols = [];
|
|
1015
1044
|
let chartCtx = {};
|
|
1016
1045
|
function chartTimeframe() {
|
|
1017
1046
|
collector();
|
|
@@ -1087,6 +1116,15 @@ function symbolProperty(key) {
|
|
|
1087
1116
|
}
|
|
1088
1117
|
return value;
|
|
1089
1118
|
}
|
|
1119
|
+
/** A text value of the chart or the symbol that the host passes in the chart context. */
|
|
1120
|
+
function chartText(key, name) {
|
|
1121
|
+
collector();
|
|
1122
|
+
const value = chartCtx[key];
|
|
1123
|
+
if (value === undefined) {
|
|
1124
|
+
throw new Error(`${name} is not known: pass it as executeScript(body, bars, inputs, { ${key} }).`);
|
|
1125
|
+
}
|
|
1126
|
+
return value;
|
|
1127
|
+
}
|
|
1090
1128
|
/** `[minmove, pricescale]` of the chart symbol: from the chart context, else from its mintick. */
|
|
1091
1129
|
function symbolScale() {
|
|
1092
1130
|
collector();
|
|
@@ -1098,7 +1136,8 @@ function symbolScale() {
|
|
|
1098
1136
|
}
|
|
1099
1137
|
/**
|
|
1100
1138
|
* PineScript `syminfo.timezone`, `syminfo.tickerid`, `syminfo.session` (session type), `syminfo.mintick`,
|
|
1101
|
-
* `syminfo.minmove`, `syminfo.pricescale`, `syminfo.pointvalue
|
|
1139
|
+
* `syminfo.minmove`, `syminfo.pricescale`, `syminfo.pointvalue`, `syminfo.mincontract`, `syminfo.type`,
|
|
1140
|
+
* `syminfo.currency`, `syminfo.basecurrency` and `syminfo.root`, from the chart context.
|
|
1102
1141
|
*/
|
|
1103
1142
|
export const syminfo = {
|
|
1104
1143
|
get mintick() {
|
|
@@ -1130,6 +1169,18 @@ export const syminfo = {
|
|
|
1130
1169
|
collector();
|
|
1131
1170
|
return chartCtx.sessionType ?? 'regular';
|
|
1132
1171
|
},
|
|
1172
|
+
get type() {
|
|
1173
|
+
return chartText('symbolType', 'syminfo.type');
|
|
1174
|
+
},
|
|
1175
|
+
get currency() {
|
|
1176
|
+
return chartText('currency', 'syminfo.currency');
|
|
1177
|
+
},
|
|
1178
|
+
get basecurrency() {
|
|
1179
|
+
return chartText('basecurrency', 'syminfo.basecurrency');
|
|
1180
|
+
},
|
|
1181
|
+
get root() {
|
|
1182
|
+
return chartText('root', 'syminfo.root');
|
|
1183
|
+
},
|
|
1133
1184
|
};
|
|
1134
1185
|
function calendar(fn) {
|
|
1135
1186
|
return ((time, timezone) => {
|
|
@@ -1211,7 +1262,7 @@ function sessionBars() {
|
|
|
1211
1262
|
* session. As in PineScript, an explicit time zone reads the symbol session hours in that zone without
|
|
1212
1263
|
* corrections and holidays, and an explicit session follows PineScript's session string rules.
|
|
1213
1264
|
*/
|
|
1214
|
-
function calendarFor(session, timezone) {
|
|
1265
|
+
function calendarFor(session, timezone, dayPlacement = false) {
|
|
1215
1266
|
const { bars } = sessionBars();
|
|
1216
1267
|
if (!session && !timezone)
|
|
1217
1268
|
return { bars, test: false };
|
|
@@ -1224,7 +1275,14 @@ function calendarFor(session, timezone) {
|
|
|
1224
1275
|
return { bars: new SessionBars(new TradingCalendar(tz, { session: toSpec(symbol).session }, 'symbol'), bars.chart.period), test: true };
|
|
1225
1276
|
}
|
|
1226
1277
|
const spec = session ? { session } : { session: toSpec(chartCtx.session).session };
|
|
1227
|
-
return {
|
|
1278
|
+
return {
|
|
1279
|
+
bars: new SessionBars(new TradingCalendar(tz, spec, session ? 'pine' : 'symbol', dayPlacement), bars.chart.period),
|
|
1280
|
+
test: true,
|
|
1281
|
+
// a PineScript session string: the rules of time.inSession (runs ending at 0000, days around a clock change)
|
|
1282
|
+
inside: session
|
|
1283
|
+
? (time) => timeCore.inSession(time, session, tz, timeframeCore.in_seconds(bars.chart.period) * 1000)
|
|
1284
|
+
: undefined,
|
|
1285
|
+
};
|
|
1228
1286
|
}
|
|
1229
1287
|
function perBar(fn) {
|
|
1230
1288
|
const times = ctxBars.bars.map(barTime);
|
|
@@ -1236,22 +1294,57 @@ function callableSeries(series, fn) {
|
|
|
1236
1294
|
Object.assign(fn, series);
|
|
1237
1295
|
return fn;
|
|
1238
1296
|
}
|
|
1297
|
+
/**
|
|
1298
|
+
* `time(tf, session)` / `time_close(tf, session)` of a bar, as PineScript computes them (#173): an intraday timeframe
|
|
1299
|
+
* tests the bar with the session rules of time.inSession and aligns its periods on the start of the run that
|
|
1300
|
+
* contains the bar; "D" tests the bar against the day's session period (the calendar); a timeframe of more than one
|
|
1301
|
+
* day ("2D", "W", "M") has no test and keeps the period's time.
|
|
1302
|
+
*/
|
|
1303
|
+
function sessionPeriods(timeframeString, session, timezone) {
|
|
1304
|
+
const tf = timeframeCore.info(timeframeString);
|
|
1305
|
+
const { bars, test, inside } = calendarFor(session, timezone, !tf.isintraday);
|
|
1306
|
+
const open = (t) => bars.timeOf(t, timeframeString);
|
|
1307
|
+
const close = (t) => bars.closeOf(t, timeframeString);
|
|
1308
|
+
if (!test)
|
|
1309
|
+
return { open, close };
|
|
1310
|
+
if (tf.isintraday) {
|
|
1311
|
+
if (session !== undefined && parsePineSession(session) !== null && !bars.chart.isdwm) {
|
|
1312
|
+
const tz = timezone ?? exchangeTimezone();
|
|
1313
|
+
const barMs = timeframeCore.in_seconds(bars.chart.period) * 1000;
|
|
1314
|
+
const ms = timeframeCore.in_seconds(tf.period) * 1000;
|
|
1315
|
+
const run = (t) => sessionRun(t, session, tz, barMs);
|
|
1316
|
+
const start = (t, r) => r[0] + Math.floor((t - r[0]) / ms) * ms;
|
|
1317
|
+
return {
|
|
1318
|
+
open: (t) => { const r = run(t); return r ? start(t, r) : NaN; },
|
|
1319
|
+
close: (t) => { const r = run(t); return r ? Math.min(start(t, r) + ms, r[1]) : NaN; },
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
const isIn = (t) => (inside ? inside(t) : bars.inside(t));
|
|
1323
|
+
return { open: (t) => (isIn(t) ? open(t) : NaN), close: (t) => (isIn(t) ? close(t) : NaN) };
|
|
1324
|
+
}
|
|
1325
|
+
if (tf.isdaily && tf.multiplier === 1) {
|
|
1326
|
+
return { open: (t) => (bars.inside(t) ? open(t) : NaN), close: (t) => (bars.inside(t) ? close(t) : NaN) };
|
|
1327
|
+
}
|
|
1328
|
+
return { open, close };
|
|
1329
|
+
}
|
|
1239
1330
|
function timeFunction(tf, session, timezone) {
|
|
1240
1331
|
collector();
|
|
1241
|
-
const {
|
|
1242
|
-
|
|
1243
|
-
return perBar((t) => (test && !bars.inside(t) ? NaN : bars.timeOf(t, timeframeString)));
|
|
1332
|
+
const { open } = sessionPeriods(tf || chartTimeframe().period, session, timezone);
|
|
1333
|
+
return perBar((t) => open(t));
|
|
1244
1334
|
}
|
|
1245
1335
|
function timeCloseFunction(tf, session, timezone) {
|
|
1246
1336
|
collector();
|
|
1247
|
-
const {
|
|
1248
|
-
|
|
1249
|
-
return perBar((t) => (test && !bars.inside(t) ? NaN : bars.closeOf(t, timeframeString)));
|
|
1337
|
+
const { close } = sessionPeriods(tf || chartTimeframe().period, session, timezone);
|
|
1338
|
+
return perBar((t) => close(t));
|
|
1250
1339
|
}
|
|
1251
1340
|
/**
|
|
1252
1341
|
* PineScript `time`: the bar open time in ms (a Series), and `time(timeframe?, session?, timezone?)`:
|
|
1253
1342
|
* the open time of the `timeframe` period that contains the bar, `na` outside `session`.
|
|
1254
1343
|
*/
|
|
1344
|
+
/** Current time of the run in ms, see `ChartContext.now`. */
|
|
1345
|
+
let runNow = NaN;
|
|
1346
|
+
/** PineScript `timenow`: the current time in ms, the same on every bar of a run (`ChartContext.now`). */
|
|
1347
|
+
export const timenow = new Series(ctxBars, () => runNow);
|
|
1255
1348
|
export const time = callableSeries(new Series(ctxBars, (b) => barTime(b)), timeFunction);
|
|
1256
1349
|
/**
|
|
1257
1350
|
* PineScript `time_close`: the bar close time in ms (a Series), and `time_close(timeframe?, session?, timezone?)`.
|
|
@@ -1260,9 +1353,32 @@ export const time = callableSeries(new Series(ctxBars, (b) => barTime(b)), timeF
|
|
|
1260
1353
|
export const time_close = callableSeries(new Series(ctxBars, (_b, i) => sessionBars().bars.closeOf(barTime(ctxBars.bars[i]), chartTimeframe().period)), timeCloseFunction);
|
|
1261
1354
|
/** PineScript `time_tradingday`: 00:00 UTC of the trading day of each bar, in ms. */
|
|
1262
1355
|
export const time_tradingday = new Series(ctxBars, (b) => sessionBars().bars.tradingDayTime(barTime(b)));
|
|
1263
|
-
function changeOf(tf) {
|
|
1356
|
+
function changeOf(tf, firstBar = false) {
|
|
1357
|
+
const { bars } = sessionBars();
|
|
1358
|
+
// a bar without a period (after an early close) is no change, and neither is the bar after it
|
|
1359
|
+
const changed = (a, b) => !Number.isNaN(a) && !Number.isNaN(b) && a !== b;
|
|
1360
|
+
return perBar((t, i, times) => ((i > 0 ? changed(bars.timeOf(t, tf), bars.timeOf(times[i - 1], tf)) : firstBar) ? 1 : 0));
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Default anchor of `ta.vwap` (#173, #178): the first bar and each bar whose trading day differs from the last bar
|
|
1364
|
+
* that has one. Bars after an early close have no day: the first of them continues the day before it, each further
|
|
1365
|
+
* one starts a new sum, and the next day starts on its first bar (where `timeframe.change("D")` is false). On
|
|
1366
|
+
* D / W / M charts every bar.
|
|
1367
|
+
*/
|
|
1368
|
+
function vwapAnchor() {
|
|
1369
|
+
if (chartTimeframe().isdwm)
|
|
1370
|
+
return barFlag(() => true);
|
|
1264
1371
|
const { bars } = sessionBars();
|
|
1265
|
-
|
|
1372
|
+
let last = NaN;
|
|
1373
|
+
let previous = NaN;
|
|
1374
|
+
return perBar((t, i) => {
|
|
1375
|
+
const day = bars.timeOf(t, '1D');
|
|
1376
|
+
const isNew = i === 0 || (Number.isNaN(day) ? Number.isNaN(previous) : day !== last);
|
|
1377
|
+
if (!Number.isNaN(day))
|
|
1378
|
+
last = day;
|
|
1379
|
+
previous = day;
|
|
1380
|
+
return isNew ? 1 : 0;
|
|
1381
|
+
});
|
|
1266
1382
|
}
|
|
1267
1383
|
function flag(key) {
|
|
1268
1384
|
const { bars, regular } = sessionBars();
|
|
@@ -1339,26 +1455,31 @@ function valuesOf(value, length) {
|
|
|
1339
1455
|
return value.toArray().slice();
|
|
1340
1456
|
}
|
|
1341
1457
|
/**
|
|
1342
|
-
* PineScript `request.security`
|
|
1343
|
-
* `ticker.standard(...)`)
|
|
1344
|
-
*
|
|
1345
|
-
*
|
|
1458
|
+
* PineScript `request.security` on a timeframe equal to or higher than the chart's. For the chart symbol
|
|
1459
|
+
* (`syminfo.tickerid`, `""`, `ticker.heikinashi(...)`, `ticker.standard(...)`) the higher-timeframe bars are built by
|
|
1460
|
+
* grouping the chart bars; another symbol's bars come from `ChartContext.bars` (#179) and are merged on the chart bars
|
|
1461
|
+
* by close time (lookahead off) or by open time / trading day (lookahead on), see `securityOther`. `expression` runs
|
|
1462
|
+
* on those bars (its `ta.*` calls, tuples, `timeframe.*` and `syminfo.*` see them). A lower timeframe throws.
|
|
1463
|
+
*
|
|
1464
|
+
* A symbol that does not exist (bars []) stops the script with "Invalid symbol: X", or gives na with
|
|
1465
|
+
* `ignore_invalid_symbol`.
|
|
1346
1466
|
*
|
|
1347
1467
|
* PineScript rules: lookahead off shows a period's value from the
|
|
1348
1468
|
* bar that completes it; lookahead on from its first bar; gaps on keeps only the bar where the value appears.
|
|
1349
1469
|
* Built bars can differ from the exchange's bars (official open / close, volume).
|
|
1350
1470
|
*/
|
|
1351
|
-
function security(symbol, timeframe, expression, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off) {
|
|
1471
|
+
function security(symbol, timeframe, expression, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off, ignore_invalid_symbol = false) {
|
|
1352
1472
|
collector();
|
|
1353
1473
|
const heikin = symbol.startsWith(HEIKIN_ASHI);
|
|
1354
1474
|
const base = heikin ? symbol.slice(HEIKIN_ASHI.length) : symbol;
|
|
1355
|
-
if (base !== '' && base !== chartCtx.tickerid) {
|
|
1356
|
-
throw new Error(`request.security: "${symbol}" is not the chart symbol; other symbols need external data.`);
|
|
1357
|
-
}
|
|
1358
1475
|
const chart = chartTimeframe();
|
|
1359
1476
|
const tf = timeframeCore.info(timeframe || chart.period).period;
|
|
1477
|
+
if (base !== '' && base !== chartCtx.tickerid) {
|
|
1478
|
+
return externalSecurity(base, heikin, tf, expression, gaps, lookahead, ignore_invalid_symbol, false);
|
|
1479
|
+
}
|
|
1360
1480
|
if (timeframeCore.in_seconds(tf) < timeframeCore.in_seconds(chart.period)) {
|
|
1361
|
-
|
|
1481
|
+
// a lower timeframe of the chart symbol: its bars come from ChartContext.bars (#181)
|
|
1482
|
+
return externalSecurity(chartCtx.tickerid ?? '', heikin, tf, expression, gaps, lookahead, ignore_invalid_symbol, true);
|
|
1362
1483
|
}
|
|
1363
1484
|
const chartBars = ctxBars.bars;
|
|
1364
1485
|
const times = chartBars.map(barTime);
|
|
@@ -1380,6 +1501,8 @@ function security(symbol, timeframe, expression, gaps = barmerge.gaps_off, looka
|
|
|
1380
1501
|
const periods = at ? calendar.calendar.periods(at.date) : [];
|
|
1381
1502
|
return periods.length ? calendar.timeOf(periods[0][0], tf) : starts[i];
|
|
1382
1503
|
});
|
|
1504
|
+
const tfInfo = timeframeCore.info(tf);
|
|
1505
|
+
strayPeriods(starts, dayPeriods, times, loaded, tfInfo.isdwm ? null : timeframeCore.in_seconds(tf) * 1000);
|
|
1383
1506
|
const reaches = times.map((t) => loaded.closeOf(t, chart.period) >= calendar.periodEnd(t, tf));
|
|
1384
1507
|
({ group, latest } = periodsOf(starts, dayPeriods, reaches));
|
|
1385
1508
|
const unit = chartCtx.timeUnit === 'ms' ? 1 : 1000;
|
|
@@ -1407,8 +1530,108 @@ function security(symbol, timeframe, expression, gaps = barmerge.gaps_off, looka
|
|
|
1407
1530
|
const mapped = values.map((v) => Series.fromArray(ctxBars, mapToChart(v, group, latest, lookahead === barmerge.lookahead_on, gaps === barmerge.gaps_on)));
|
|
1408
1531
|
return (Array.isArray(result) ? mapped : mapped[0]);
|
|
1409
1532
|
}
|
|
1410
|
-
/**
|
|
1411
|
-
|
|
1533
|
+
/**
|
|
1534
|
+
* Runs `expression` on the bars of `symbol` / `tf` given by `ChartContext.bars` (#179, #181). Bars not given: the
|
|
1535
|
+
* request is recorded for the {@link MissingSymbolDataError} and the expression runs on no bar. `own`: the chart
|
|
1536
|
+
* symbol, whose calendar defaults to the chart's.
|
|
1537
|
+
*/
|
|
1538
|
+
function runExternal(symbol, heikin, tf, expression, ignoreInvalid, own) {
|
|
1539
|
+
let bars = chartCtx.bars?.(symbol, tf);
|
|
1540
|
+
if (bars === undefined && !missingSymbols.some((r) => r.symbol === symbol && r.timeframe === tf)) {
|
|
1541
|
+
missingSymbols.push({ symbol, timeframe: tf });
|
|
1542
|
+
}
|
|
1543
|
+
if (bars !== undefined && bars.length === 0 && !ignoreInvalid)
|
|
1544
|
+
throw new Error(`Invalid symbol: ${symbol}`);
|
|
1545
|
+
bars ??= [];
|
|
1546
|
+
if (heikin)
|
|
1547
|
+
bars = heikinAshi(bars);
|
|
1548
|
+
const given = chartCtx.symbolInfo?.(symbol);
|
|
1549
|
+
const info = own ? { ...chartCtx, ...given } : (given ?? {});
|
|
1550
|
+
const chartBars = ctxBars.bars;
|
|
1551
|
+
const savedCtx = chartCtx;
|
|
1552
|
+
let result;
|
|
1553
|
+
let values;
|
|
1554
|
+
ctxBars.setAll(bars);
|
|
1555
|
+
// the expression sees that symbol: its ticker, timeframe, calendar and properties
|
|
1556
|
+
chartCtx = {
|
|
1557
|
+
...chartCtx,
|
|
1558
|
+
tickerid: symbol,
|
|
1559
|
+
timeframe: tf,
|
|
1560
|
+
timezone: info.timezone,
|
|
1561
|
+
session: info.session,
|
|
1562
|
+
regularSession: info.regularSession,
|
|
1563
|
+
mintick: info.mintick,
|
|
1564
|
+
minmove: info.minmove,
|
|
1565
|
+
pricescale: info.pricescale,
|
|
1566
|
+
pointvalue: info.pointvalue,
|
|
1567
|
+
mincontract: info.mincontract,
|
|
1568
|
+
symbolType: info.symbolType,
|
|
1569
|
+
currency: info.currency,
|
|
1570
|
+
basecurrency: info.basecurrency,
|
|
1571
|
+
root: info.root,
|
|
1572
|
+
};
|
|
1573
|
+
try {
|
|
1574
|
+
result = expression();
|
|
1575
|
+
const parts = Array.isArray(result) ? result : [result];
|
|
1576
|
+
values = parts.map((p) => valuesOf(p, bars.length));
|
|
1577
|
+
}
|
|
1578
|
+
finally {
|
|
1579
|
+
ctxBars.setAll(chartBars);
|
|
1580
|
+
chartCtx = savedCtx;
|
|
1581
|
+
}
|
|
1582
|
+
return { bars, info, result, values };
|
|
1583
|
+
}
|
|
1584
|
+
/** The chart for the merge functions of `security/other`. */
|
|
1585
|
+
function mergeChart() {
|
|
1586
|
+
return {
|
|
1587
|
+
timeframe: chartTimeframe().period,
|
|
1588
|
+
timezone: chartCtx.timezone,
|
|
1589
|
+
session: chartCtx.session,
|
|
1590
|
+
timeUnit: chartCtx.timeUnit,
|
|
1591
|
+
// the realtime rule applies to a last bar that receives live updates
|
|
1592
|
+
lastBarConfirmed: chartCtx.realtime !== true,
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* `request.security` of another symbol (#179), or of the chart symbol on a lower timeframe (#181): its bars from
|
|
1597
|
+
* `ChartContext.bars`, merged by `securityOther`.
|
|
1598
|
+
*/
|
|
1599
|
+
function externalSecurity(symbol, heikin, tf, expression, gaps, lookahead, ignoreInvalid, own) {
|
|
1600
|
+
const { bars, info, result, values } = runExternal(symbol, heikin, tf, expression, ignoreInvalid, own);
|
|
1601
|
+
const index = securityOther(ctxBars.bars, bars, tf, mergeChart(), {
|
|
1602
|
+
gaps: gaps === barmerge.gaps_on,
|
|
1603
|
+
lookahead: lookahead === barmerge.lookahead_on,
|
|
1604
|
+
other: info,
|
|
1605
|
+
});
|
|
1606
|
+
const mapped = values.map((v) => Series.fromArray(ctxBars, index.map((j) => (j < 0 ? NaN : (v[j] ?? NaN)))));
|
|
1607
|
+
return (Array.isArray(result) ? mapped : mapped[0]);
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* PineScript `request.security_lower_tf(symbol, timeframe, expression, ignore_invalid_symbol)` (#181): for each
|
|
1611
|
+
* chart bar, the array of the values of `expression` on the bars of the lower timeframe that the chart bar contains
|
|
1612
|
+
* (those whose close is after the open of the chart bar and at or before its close; empty when there are none).
|
|
1613
|
+
* The bars come from `ChartContext.bars`, for the chart symbol (`""`, `syminfo.tickerid`) as for another symbol.
|
|
1614
|
+
* Returns one array per chart bar (oldest first), or one such list per tuple part.
|
|
1615
|
+
* @throws Error when `timeframe` is higher than the chart timeframe
|
|
1616
|
+
*/
|
|
1617
|
+
function security_lower_tf(symbol, timeframe, expression, ignore_invalid_symbol = false) {
|
|
1618
|
+
collector();
|
|
1619
|
+
const heikin = symbol.startsWith(HEIKIN_ASHI);
|
|
1620
|
+
const base = heikin ? symbol.slice(HEIKIN_ASHI.length) : symbol;
|
|
1621
|
+
const chart = chartTimeframe();
|
|
1622
|
+
const tf = timeframeCore.info(timeframe || chart.period).period;
|
|
1623
|
+
if (timeframeCore.in_seconds(tf) > timeframeCore.in_seconds(chart.period)) {
|
|
1624
|
+
throw new Error(`request.security_lower_tf: timeframe "${tf}" is higher than the chart timeframe "${chart.period}".`);
|
|
1625
|
+
}
|
|
1626
|
+
const own = base === '' || base === chartCtx.tickerid;
|
|
1627
|
+
const ticker = own ? (chartCtx.tickerid ?? '') : base;
|
|
1628
|
+
const { bars, info, result, values } = runExternal(ticker, heikin, tf, expression, ignore_invalid_symbol, own);
|
|
1629
|
+
const ranges = securityLowerTf(ctxBars.bars, bars, tf, mergeChart(), { other: info });
|
|
1630
|
+
const mapped = values.map((v) => ranges.map(([from, to]) => v.slice(from, to)));
|
|
1631
|
+
return (Array.isArray(result) ? mapped : mapped[0]);
|
|
1632
|
+
}
|
|
1633
|
+
/** PineScript `request.*`: `security` (issue #101, other symbols #179, lower timeframes #181) and `security_lower_tf`. */
|
|
1634
|
+
export const request = { security, security_lower_tf };
|
|
1412
1635
|
// ── Drawing objects ──────────────────────────────────────────────────────────
|
|
1413
1636
|
// The drawing namespaces come from this bundle so that they share the registry that
|
|
1414
1637
|
// executeScript() resets. `line.all` etc. are read like PineScript variables.
|
|
@@ -1465,6 +1688,14 @@ export const text = { ...textCore };
|
|
|
1465
1688
|
* (index, time, and `price`, default the close), so it runs inside eachBar().
|
|
1466
1689
|
*/
|
|
1467
1690
|
export const chart = {
|
|
1691
|
+
/** Chart foreground colour (PineScript `chart.fg_color`), from the chart context. */
|
|
1692
|
+
get fg_color() {
|
|
1693
|
+
return chartText('foregroundColor', 'chart.fg_color');
|
|
1694
|
+
},
|
|
1695
|
+
/** Chart background colour (PineScript `chart.bg_color`), from the chart context. */
|
|
1696
|
+
get bg_color() {
|
|
1697
|
+
return chartText('backgroundColor', 'chart.bg_color');
|
|
1698
|
+
},
|
|
1468
1699
|
point: {
|
|
1469
1700
|
...chartPointCore,
|
|
1470
1701
|
now(price) {
|