oakscriptjs 0.9.6 → 0.10.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/README.md +1 -1
- package/dist/cjs/array/index.d.ts +9 -23
- package/dist/cjs/array/index.d.ts.map +1 -1
- package/dist/cjs/array/index.js +4 -0
- package/dist/cjs/array/index.js.map +2 -2
- package/dist/cjs/drawing/bar-time.d.ts +24 -0
- package/dist/cjs/drawing/bar-time.d.ts.map +1 -0
- package/dist/cjs/drawing/bar-time.js +47 -0
- package/dist/cjs/drawing/bar-time.js.map +7 -0
- package/dist/cjs/drawing/output.d.ts +32 -0
- package/dist/cjs/drawing/output.d.ts.map +1 -0
- package/dist/cjs/drawing/output.js +184 -0
- package/dist/cjs/drawing/output.js.map +7 -0
- package/dist/cjs/drawing/registry.d.ts +5 -4
- package/dist/cjs/drawing/registry.d.ts.map +1 -1
- package/dist/cjs/drawing/registry.js +11 -2
- package/dist/cjs/drawing/registry.js.map +2 -2
- package/dist/cjs/index.d.ts +7 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +11 -2
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/polyline/index.d.ts +3 -5
- package/dist/cjs/polyline/index.d.ts.map +1 -1
- package/dist/cjs/polyline/index.js +0 -5
- package/dist/cjs/polyline/index.js.map +2 -2
- package/dist/cjs/runtime/index.d.ts +2 -1
- package/dist/cjs/runtime/index.d.ts.map +1 -1
- package/dist/cjs/runtime/index.js +2 -0
- package/dist/cjs/runtime/index.js.map +2 -2
- package/dist/cjs/runtime/input-active.d.ts +27 -0
- package/dist/cjs/runtime/input-active.d.ts.map +1 -0
- package/dist/cjs/runtime/input-active.js +44 -0
- package/dist/cjs/runtime/input-active.js.map +7 -0
- package/dist/cjs/runtime/types.d.ts +51 -2
- 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 +104 -8
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +163 -28
- package/dist/cjs/script/index.js.map +3 -3
- package/dist/cjs/str/index.d.ts +9 -3
- package/dist/cjs/str/index.d.ts.map +1 -1
- package/dist/cjs/str/index.js +11 -6
- package/dist/cjs/str/index.js.map +2 -2
- package/dist/cjs/str/numberformat.d.ts +6 -0
- package/dist/cjs/str/numberformat.d.ts.map +1 -1
- package/dist/cjs/str/numberformat.js +9 -1
- package/dist/cjs/str/numberformat.js.map +2 -2
- package/dist/cjs/table/index.d.ts +87 -0
- package/dist/cjs/table/index.d.ts.map +1 -0
- package/dist/cjs/table/index.js +150 -0
- package/dist/cjs/table/index.js.map +7 -0
- package/dist/cjs/text/index.d.ts +17 -2
- package/dist/cjs/text/index.d.ts.map +1 -1
- package/dist/cjs/text/index.js +15 -1
- package/dist/cjs/text/index.js.map +2 -2
- package/dist/cjs/types/index.d.ts +30 -1
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js +12 -0
- package/dist/cjs/types/index.js.map +2 -2
- package/dist/cjs/types/metadata.d.ts +258 -26
- 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 +9 -23
- package/dist/esm/array/index.d.ts.map +1 -1
- package/dist/esm/array/index.js +10 -22
- package/dist/esm/array/index.js.map +1 -1
- package/dist/esm/drawing/bar-time.d.ts +24 -0
- package/dist/esm/drawing/bar-time.d.ts.map +1 -0
- package/dist/esm/drawing/bar-time.js +44 -0
- package/dist/esm/drawing/bar-time.js.map +1 -0
- package/dist/esm/drawing/output.d.ts +32 -0
- package/dist/esm/drawing/output.d.ts.map +1 -0
- package/dist/esm/drawing/output.js +190 -0
- package/dist/esm/drawing/output.js.map +1 -0
- package/dist/esm/drawing/registry.d.ts +5 -4
- package/dist/esm/drawing/registry.d.ts.map +1 -1
- package/dist/esm/drawing/registry.js +12 -2
- package/dist/esm/drawing/registry.js.map +1 -1
- package/dist/esm/index.d.ts +7 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +8 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/polyline/index.d.ts +3 -5
- package/dist/esm/polyline/index.d.ts.map +1 -1
- package/dist/esm/polyline/index.js +3 -9
- package/dist/esm/polyline/index.js.map +1 -1
- package/dist/esm/runtime/index.d.ts +2 -1
- package/dist/esm/runtime/index.d.ts.map +1 -1
- package/dist/esm/runtime/index.js +1 -0
- package/dist/esm/runtime/index.js.map +1 -1
- package/dist/esm/runtime/input-active.d.ts +27 -0
- package/dist/esm/runtime/input-active.d.ts.map +1 -0
- package/dist/esm/runtime/input-active.js +54 -0
- package/dist/esm/runtime/input-active.js.map +1 -0
- package/dist/esm/runtime/types.d.ts +51 -2
- package/dist/esm/runtime/types.d.ts.map +1 -1
- package/dist/esm/script/index.d.ts +104 -8
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +194 -24
- package/dist/esm/script/index.js.map +1 -1
- package/dist/esm/str/index.d.ts +9 -3
- package/dist/esm/str/index.d.ts.map +1 -1
- package/dist/esm/str/index.js +25 -10
- package/dist/esm/str/index.js.map +1 -1
- package/dist/esm/str/numberformat.d.ts +6 -0
- package/dist/esm/str/numberformat.d.ts.map +1 -1
- package/dist/esm/str/numberformat.js +12 -0
- package/dist/esm/str/numberformat.js.map +1 -1
- package/dist/esm/table/index.d.ts +87 -0
- package/dist/esm/table/index.d.ts.map +1 -0
- package/dist/esm/table/index.js +169 -0
- package/dist/esm/table/index.js.map +1 -0
- package/dist/esm/text/index.d.ts +17 -2
- package/dist/esm/text/index.d.ts.map +1 -1
- package/dist/esm/text/index.js +17 -2
- package/dist/esm/text/index.js.map +1 -1
- package/dist/esm/types/index.d.ts +30 -1
- package/dist/esm/types/index.d.ts.map +1 -1
- package/dist/esm/types/index.js +14 -0
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm/types/metadata.d.ts +258 -26
- package/dist/esm/types/metadata.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/array/index.ts +12 -23
- package/src/drawing/bar-time.ts +42 -0
- package/src/drawing/output.ts +214 -0
- package/src/drawing/registry.ts +16 -6
- package/src/index.ts +11 -3
- package/src/polyline/index.ts +3 -12
- package/src/runtime/index.ts +3 -0
- package/src/runtime/input-active.ts +50 -0
- package/src/runtime/types.ts +48 -2
- package/src/script/index.ts +301 -30
- package/src/str/index.ts +23 -10
- package/src/str/numberformat.ts +13 -0
- package/src/table/index.ts +221 -0
- package/src/text/index.ts +18 -2
- package/src/types/index.ts +32 -1
- package/src/types/metadata.ts +325 -26
package/src/script/index.ts
CHANGED
|
@@ -37,21 +37,39 @@ import type { Bar } from '../types/index.js';
|
|
|
37
37
|
import type {
|
|
38
38
|
ArrowData,
|
|
39
39
|
BarColorData,
|
|
40
|
+
BgColorData,
|
|
41
|
+
BoxData,
|
|
40
42
|
FillData,
|
|
41
43
|
FillGradient,
|
|
42
44
|
IndicatorResult,
|
|
45
|
+
LabelData,
|
|
46
|
+
LabelStyle,
|
|
47
|
+
LineDrawingData,
|
|
48
|
+
LinefillData,
|
|
43
49
|
MarkerData,
|
|
44
50
|
MarkerLocation,
|
|
51
|
+
MarkerPosition,
|
|
52
|
+
MarkerShape,
|
|
45
53
|
MarkerSize,
|
|
54
|
+
PineSize,
|
|
55
|
+
PlotBarData,
|
|
56
|
+
PlotCandleData,
|
|
57
|
+
PolylineData,
|
|
46
58
|
ShapeStyle,
|
|
59
|
+
TableCellData,
|
|
60
|
+
TableData,
|
|
61
|
+
TableMergeData,
|
|
62
|
+
TablePosition,
|
|
47
63
|
TimeValue,
|
|
48
64
|
} from '../types/metadata.js';
|
|
49
65
|
import type {
|
|
50
66
|
ArrowConfig,
|
|
51
67
|
BarColorConfig,
|
|
68
|
+
CandleConfig,
|
|
52
69
|
FillConfig,
|
|
53
70
|
HLineConfig,
|
|
54
71
|
InputConfig,
|
|
72
|
+
InputCondition,
|
|
55
73
|
PlotConfig,
|
|
56
74
|
ShapeConfig,
|
|
57
75
|
} from '../runtime/types.js';
|
|
@@ -65,17 +83,21 @@ import * as labelCore from '../label/index.js';
|
|
|
65
83
|
import * as boxCore from '../box/index.js';
|
|
66
84
|
import * as linefillCore from '../linefill/index.js';
|
|
67
85
|
import * as polylineCore from '../polyline/index.js';
|
|
86
|
+
import * as tableCore from '../table/index.js';
|
|
68
87
|
import * as chartPointCore from '../chartpoint/index.js';
|
|
69
88
|
import * as textCore from '../text/index.js';
|
|
70
89
|
import * as mathCore from '../math/index.js';
|
|
71
90
|
import { resetDrawings, setDrawingLimits } from '../drawing/registry.js';
|
|
91
|
+
import { drawingOutputs } from '../drawing/output.js';
|
|
72
92
|
import * as timeframeCore from '../timeframe/index.js';
|
|
73
93
|
import * as timeCore from '../time/index.js';
|
|
74
94
|
import * as strCore from '../str/index.js';
|
|
75
95
|
import { formatMessage } from '../str/messageformat.js';
|
|
96
|
+
import { tickScale } from '../str/numberformat.js';
|
|
76
97
|
import { TradingCalendar, type SessionSpec } from '../session/calendar.js';
|
|
77
98
|
import { SessionBars, sessionFlags, type SessionFlags } from '../session/bars.js';
|
|
78
99
|
import { afterCompletion, heikinAshi, mapToChart, periodsOf, resample } from '../security/resample.js';
|
|
100
|
+
import { position } from '../types/index.js';
|
|
79
101
|
import type { ChartPoint } from '../types/index.js';
|
|
80
102
|
import {
|
|
81
103
|
STRATEGY_CONSTANTS,
|
|
@@ -110,11 +132,17 @@ export interface ScriptRunResult {
|
|
|
110
132
|
shapeConfig: ShapeConfig[];
|
|
111
133
|
arrowConfig: ArrowConfig[];
|
|
112
134
|
barColorConfig: BarColorConfig[];
|
|
135
|
+
/** plotcandle() / plotbar() declarations */
|
|
136
|
+
candleConfig: CandleConfig[];
|
|
113
137
|
defaultInputs: Record<string, unknown>;
|
|
114
138
|
alertConfig: AlertConditionConfig[];
|
|
115
139
|
/** Strategy properties (declared values, else the PineScript defaults) when the script declares strategy(). */
|
|
116
140
|
strategyConfig?: StrategyProperties;
|
|
117
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* The renderable output: plots keyed by plotConfig ids, plot-pair fills, markers, arrows, background and bar
|
|
143
|
+
* colours, candles and bars, and the drawings (labels, lines, boxes, linefills, polylines, tables) alive after the
|
|
144
|
+
* last bar.
|
|
145
|
+
*/
|
|
118
146
|
result: IndicatorResult & { alerts?: AlertState[] };
|
|
119
147
|
}
|
|
120
148
|
|
|
@@ -159,8 +187,11 @@ interface Collector {
|
|
|
159
187
|
arrowConfig: ArrowConfig[];
|
|
160
188
|
arrows: ArrowData[];
|
|
161
189
|
barColorConfig: BarColorConfig[];
|
|
162
|
-
|
|
163
|
-
|
|
190
|
+
bgColors: BgColorData[];
|
|
191
|
+
barColors: BarColorData[];
|
|
192
|
+
candleConfig: CandleConfig[];
|
|
193
|
+
plotCandles: Record<string, PlotCandleData[]>;
|
|
194
|
+
plotBars: Record<string, PlotBarData[]>;
|
|
164
195
|
alertConfig: AlertConditionConfig[];
|
|
165
196
|
alerts: AlertState[];
|
|
166
197
|
strategy?: StrategyRun;
|
|
@@ -210,8 +241,11 @@ function freshCollector(inputValues: Record<string, unknown>): Collector {
|
|
|
210
241
|
arrowConfig: [],
|
|
211
242
|
arrows: [],
|
|
212
243
|
barColorConfig: [],
|
|
213
|
-
|
|
214
|
-
|
|
244
|
+
bgColors: [],
|
|
245
|
+
barColors: [],
|
|
246
|
+
candleConfig: [],
|
|
247
|
+
plotCandles: {},
|
|
248
|
+
plotBars: {},
|
|
215
249
|
alertConfig: [],
|
|
216
250
|
alerts: [],
|
|
217
251
|
};
|
|
@@ -284,6 +318,7 @@ export function executeScript(
|
|
|
284
318
|
shapeConfig: c.shapeConfig,
|
|
285
319
|
arrowConfig: c.arrowConfig,
|
|
286
320
|
barColorConfig: c.barColorConfig,
|
|
321
|
+
candleConfig: c.candleConfig,
|
|
287
322
|
defaultInputs: c.defaultInputs,
|
|
288
323
|
alertConfig: c.alertConfig,
|
|
289
324
|
strategyConfig: c.strategy?.properties,
|
|
@@ -293,8 +328,11 @@ export function executeScript(
|
|
|
293
328
|
fills: c.fills.length ? c.fills : undefined,
|
|
294
329
|
markers: c.markers.length ? c.markers : undefined,
|
|
295
330
|
arrows: c.arrows.length ? c.arrows : undefined,
|
|
296
|
-
|
|
297
|
-
|
|
331
|
+
bgColors: c.bgColors.length ? c.bgColors : undefined,
|
|
332
|
+
barColors: c.barColors.length ? c.barColors : undefined,
|
|
333
|
+
plotCandles: Object.keys(c.plotCandles).length ? c.plotCandles : undefined,
|
|
334
|
+
plotBars: Object.keys(c.plotBars).length ? c.plotBars : undefined,
|
|
335
|
+
...drawingOutputs(ctxBars.bars, chartCtx.timeUnit),
|
|
298
336
|
alerts: c.alerts.length ? c.alerts : undefined,
|
|
299
337
|
},
|
|
300
338
|
};
|
|
@@ -344,13 +382,19 @@ function declare(c: Collector, kind: 'indicator' | 'strategy'): void {
|
|
|
344
382
|
c.declaration = kind;
|
|
345
383
|
}
|
|
346
384
|
|
|
347
|
-
/** Options of every `input.*` kind (PineScript `group`, `inline`, `tooltip`, `confirm`, `display`). */
|
|
385
|
+
/** Options of every `input.*` kind (PineScript `group`, `inline`, `tooltip`, `confirm`, `display`, `active`). */
|
|
348
386
|
export interface InputOptions {
|
|
349
387
|
group?: string;
|
|
350
388
|
inline?: string;
|
|
351
389
|
tooltip?: string;
|
|
352
390
|
confirm?: boolean;
|
|
353
391
|
display?: InputConfig['display'];
|
|
392
|
+
/**
|
|
393
|
+
* PineScript `active`: false greys the input out in the settings (it keeps its value). A boolean as in
|
|
394
|
+
* PineScript (`active: showMa`), or a condition on input ids that a host can apply without running the script
|
|
395
|
+
* (`active: 'ma_1'`, `active: { input: 'type', ne: 'None' }`)
|
|
396
|
+
*/
|
|
397
|
+
active?: boolean | InputCondition;
|
|
354
398
|
}
|
|
355
399
|
|
|
356
400
|
export interface NumericInputOptions extends InputOptions {
|
|
@@ -371,6 +415,7 @@ function common(opts: InputOptions): Partial<InputConfig> {
|
|
|
371
415
|
if (opts.tooltip !== undefined) out.tooltip = opts.tooltip;
|
|
372
416
|
if (opts.confirm !== undefined) out.confirm = opts.confirm;
|
|
373
417
|
if (opts.display !== undefined) out.display = opts.display;
|
|
418
|
+
if (opts.active !== undefined) out.active = opts.active;
|
|
374
419
|
return out;
|
|
375
420
|
}
|
|
376
421
|
|
|
@@ -779,8 +824,46 @@ export interface PlotCharOptions extends Omit<PlotShapeOptions, 'style'> {
|
|
|
779
824
|
char?: string;
|
|
780
825
|
}
|
|
781
826
|
|
|
827
|
+
/** PineScript shape.* style -> marker shape. */
|
|
828
|
+
const MARKER_SHAPES: Record<ShapeStyle, MarkerShape> = {
|
|
829
|
+
xcross: 'xcross',
|
|
830
|
+
cross: 'cross',
|
|
831
|
+
triangleup: 'triangleUp',
|
|
832
|
+
triangledown: 'triangleDown',
|
|
833
|
+
flag: 'flag',
|
|
834
|
+
circle: 'circle',
|
|
835
|
+
arrowup: 'arrowUp',
|
|
836
|
+
arrowdown: 'arrowDown',
|
|
837
|
+
labelup: 'labelUp',
|
|
838
|
+
labeldown: 'labelDown',
|
|
839
|
+
square: 'square',
|
|
840
|
+
diamond: 'diamond',
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Marker position of a PineScript location. With location.absolute the shape sits on the price: a label pointing
|
|
845
|
+
* down above it, a label pointing up below it, the other shapes (and the characters) centred on it.
|
|
846
|
+
*/
|
|
847
|
+
function markerPosition(location: MarkerLocation, shape: ShapeStyle | undefined): MarkerPosition {
|
|
848
|
+
switch (location) {
|
|
849
|
+
case 'abovebar':
|
|
850
|
+
return 'aboveBar';
|
|
851
|
+
case 'belowbar':
|
|
852
|
+
return 'belowBar';
|
|
853
|
+
case 'top':
|
|
854
|
+
return 'top';
|
|
855
|
+
case 'bottom':
|
|
856
|
+
return 'bottom';
|
|
857
|
+
case 'absolute':
|
|
858
|
+
return shape === 'labeldown' ? 'atPriceTop' : shape === 'labelup' ? 'atPriceBottom' : 'atPriceMiddle';
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/** Default colour of plotshape / plotchar markers. */
|
|
863
|
+
const MARKER_COLOR = '#2962FF';
|
|
864
|
+
|
|
782
865
|
/** Shared emitter for plotshape()/plotchar(). Emits one marker per bar where
|
|
783
|
-
* the condition is truthy (not na, not 0). */
|
|
866
|
+
* the condition is truthy (not na, not 0). A plotchar marker has the shape 'circle' and its character in `char`. */
|
|
784
867
|
function emitMarkers(
|
|
785
868
|
kind: 'shape' | 'char',
|
|
786
869
|
condition: Series,
|
|
@@ -806,6 +889,19 @@ function emitMarkers(
|
|
|
806
889
|
offset: options.offset,
|
|
807
890
|
forceOverlay: options.force_overlay,
|
|
808
891
|
});
|
|
892
|
+
const shapeStyle = kind === 'shape' ? (style as ShapeStyle) : undefined;
|
|
893
|
+
const template: Omit<MarkerData, 'time'> = {
|
|
894
|
+
position: markerPosition(location, shapeStyle),
|
|
895
|
+
shape: shapeStyle ? MARKER_SHAPES[shapeStyle] : 'circle',
|
|
896
|
+
color: options.color ?? MARKER_COLOR,
|
|
897
|
+
id,
|
|
898
|
+
};
|
|
899
|
+
if (char !== undefined) template.char = char;
|
|
900
|
+
if (options.text !== undefined) template.text = options.text;
|
|
901
|
+
if (options.textcolor !== undefined) template.textColor = options.textcolor;
|
|
902
|
+
if (options.size !== undefined) template.size = options.size;
|
|
903
|
+
if (options.tooltip !== undefined) template.tooltip = options.tooltip;
|
|
904
|
+
if (options.force_overlay) template.forceOverlay = true;
|
|
809
905
|
const values = condition.toArray();
|
|
810
906
|
const b = bars();
|
|
811
907
|
const off = options.offset ?? 0;
|
|
@@ -814,19 +910,8 @@ function emitMarkers(
|
|
|
814
910
|
if (v === undefined || Number.isNaN(v) || v === 0) continue;
|
|
815
911
|
const j = i + off;
|
|
816
912
|
if (j < 0 || j >= b.length) continue;
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
id,
|
|
820
|
-
location,
|
|
821
|
-
style,
|
|
822
|
-
char,
|
|
823
|
-
color: options.color,
|
|
824
|
-
text: options.text,
|
|
825
|
-
textcolor: options.textcolor,
|
|
826
|
-
size: options.size,
|
|
827
|
-
tooltip: options.tooltip,
|
|
828
|
-
price: location === 'absolute' ? v : undefined,
|
|
829
|
-
});
|
|
913
|
+
const time = b[j]!.time;
|
|
914
|
+
c.markers.push(location === 'absolute' ? { time, ...template, price: v } : { time, ...template });
|
|
830
915
|
}
|
|
831
916
|
}
|
|
832
917
|
|
|
@@ -906,13 +991,14 @@ function emitBarColors(
|
|
|
906
991
|
c.barColorConfig.push({ id, kind, title: options.title, offset: options.offset, forceOverlay: options.force_overlay });
|
|
907
992
|
const b = bars();
|
|
908
993
|
const off = options.offset ?? 0;
|
|
909
|
-
const target = kind === 'bgcolor' ? c.
|
|
994
|
+
const target: BgColorData[] = kind === 'bgcolor' ? c.bgColors : c.barColors;
|
|
995
|
+
const forceOverlay = kind === 'bgcolor' && options.force_overlay === true;
|
|
910
996
|
for (let i = 0; i < b.length; i++) {
|
|
911
997
|
const col = typeof colors === 'string' ? colors : colors[i];
|
|
912
998
|
if (!col) continue;
|
|
913
999
|
const j = i + off;
|
|
914
1000
|
if (j < 0 || j >= b.length) continue;
|
|
915
|
-
target.push({ time: b[j]!.time, color: col });
|
|
1001
|
+
target.push(forceOverlay ? { time: b[j]!.time, color: col, forceOverlay } : { time: b[j]!.time, color: col });
|
|
916
1002
|
}
|
|
917
1003
|
}
|
|
918
1004
|
|
|
@@ -935,6 +1021,129 @@ export function barcolor(
|
|
|
935
1021
|
emitBarColors('barcolor', colors, options);
|
|
936
1022
|
}
|
|
937
1023
|
|
|
1024
|
+
/** A colour argument of plotcandle() / plotbar(): a colour, or per-bar colours (null / undefined entries are na). */
|
|
1025
|
+
export type ScriptCandleColor = string | Array<string | null | undefined>;
|
|
1026
|
+
|
|
1027
|
+
export interface PlotCandleOptions {
|
|
1028
|
+
/** Body colour: a colour or a per-bar array (color.when); an na entry gives a transparent body */
|
|
1029
|
+
color?: ScriptCandleColor;
|
|
1030
|
+
/** Wick colour: a colour or a per-bar array */
|
|
1031
|
+
wickcolor?: ScriptCandleColor;
|
|
1032
|
+
/** Border colour: a colour or a per-bar array */
|
|
1033
|
+
bordercolor?: ScriptCandleColor;
|
|
1034
|
+
display?: CandleConfig['display'];
|
|
1035
|
+
/** Draw on the main chart pane even when the script is not an overlay (PineScript `force_overlay`); default false. */
|
|
1036
|
+
force_overlay?: boolean;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
export interface PlotBarOptions {
|
|
1040
|
+
/** Bar colour: a colour or a per-bar array (color.when); an na entry gives a transparent bar */
|
|
1041
|
+
color?: ScriptCandleColor;
|
|
1042
|
+
display?: CandleConfig['display'];
|
|
1043
|
+
/** Draw on the main chart pane even when the script is not an overlay (PineScript `force_overlay`); default false. */
|
|
1044
|
+
force_overlay?: boolean;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/** Colour of bar i: undefined when the argument is not given, 'transparent' for an na entry. */
|
|
1048
|
+
function candleColor(col: ScriptCandleColor | undefined, i: number): string | undefined {
|
|
1049
|
+
if (col === undefined) return undefined;
|
|
1050
|
+
if (typeof col === 'string') return col;
|
|
1051
|
+
return col[i] ?? 'transparent';
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
interface OhlcRow {
|
|
1055
|
+
i: number;
|
|
1056
|
+
open: number;
|
|
1057
|
+
high: number;
|
|
1058
|
+
low: number;
|
|
1059
|
+
close: number;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/** The bars where open, high, low and close are all not na (PineScript draws no candle / bar otherwise). */
|
|
1063
|
+
function ohlcRows(o: Series, h: Series, l: Series, cl: Series): OhlcRow[] {
|
|
1064
|
+
const [ov, hv, lv, cv] = [o.toArray(), h.toArray(), l.toArray(), cl.toArray()];
|
|
1065
|
+
const rows: OhlcRow[] = [];
|
|
1066
|
+
for (let i = 0; i < bars().length; i++) {
|
|
1067
|
+
const [open, high, low, close] = [ov[i]!, hv[i]!, lv[i]!, cv[i]!];
|
|
1068
|
+
if (Number.isFinite(open) && Number.isFinite(high) && Number.isFinite(low) && Number.isFinite(close)) {
|
|
1069
|
+
rows.push({ i, open, high, low, close });
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return rows;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
const staticColor = (col: ScriptCandleColor | undefined): string | undefined => (typeof col === 'string' ? col : undefined);
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* PineScript `plotcandle(open, high, low, close, title?, { color, wickcolor, bordercolor, display, force_overlay })`:
|
|
1079
|
+
* candles in `result.plotCandles[id]`, the declaration in `candleConfig`. A bar with an na value has no candle.
|
|
1080
|
+
*/
|
|
1081
|
+
export function plotcandle(
|
|
1082
|
+
open: Series,
|
|
1083
|
+
high: Series,
|
|
1084
|
+
low: Series,
|
|
1085
|
+
close: Series,
|
|
1086
|
+
title?: string,
|
|
1087
|
+
options: PlotCandleOptions = {}
|
|
1088
|
+
): void {
|
|
1089
|
+
const c = collector();
|
|
1090
|
+
const id = `plotcandle${c.candleConfig.filter((x) => x.kind === 'candle').length}`;
|
|
1091
|
+
c.candleConfig.push({
|
|
1092
|
+
id,
|
|
1093
|
+
kind: 'candle',
|
|
1094
|
+
title,
|
|
1095
|
+
color: staticColor(options.color),
|
|
1096
|
+
wickColor: staticColor(options.wickcolor),
|
|
1097
|
+
borderColor: staticColor(options.bordercolor),
|
|
1098
|
+
display: options.display,
|
|
1099
|
+
forceOverlay: options.force_overlay,
|
|
1100
|
+
});
|
|
1101
|
+
const b = bars();
|
|
1102
|
+
c.plotCandles[id] = ohlcRows(open, high, low, close).map(({ i, ...ohlc }) => {
|
|
1103
|
+
const candle: PlotCandleData = { time: b[i]!.time, ...ohlc };
|
|
1104
|
+
const color = candleColor(options.color, i);
|
|
1105
|
+
const wickColor = candleColor(options.wickcolor, i);
|
|
1106
|
+
const borderColor = candleColor(options.bordercolor, i);
|
|
1107
|
+
if (color !== undefined) candle.color = color;
|
|
1108
|
+
if (wickColor !== undefined) candle.wickColor = wickColor;
|
|
1109
|
+
if (borderColor !== undefined) candle.borderColor = borderColor;
|
|
1110
|
+
if (options.force_overlay) candle.forceOverlay = true;
|
|
1111
|
+
return candle;
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* PineScript `plotbar(open, high, low, close, title?, { color, display, force_overlay })`: OHLC bars in
|
|
1117
|
+
* `result.plotBars[id]`, the declaration in `candleConfig`. A bar with an na value is not drawn.
|
|
1118
|
+
*/
|
|
1119
|
+
export function plotbar(
|
|
1120
|
+
open: Series,
|
|
1121
|
+
high: Series,
|
|
1122
|
+
low: Series,
|
|
1123
|
+
close: Series,
|
|
1124
|
+
title?: string,
|
|
1125
|
+
options: PlotBarOptions = {}
|
|
1126
|
+
): void {
|
|
1127
|
+
const c = collector();
|
|
1128
|
+
const id = `plotbar${c.candleConfig.filter((x) => x.kind === 'bar').length}`;
|
|
1129
|
+
c.candleConfig.push({
|
|
1130
|
+
id,
|
|
1131
|
+
kind: 'bar',
|
|
1132
|
+
title,
|
|
1133
|
+
color: staticColor(options.color),
|
|
1134
|
+
display: options.display,
|
|
1135
|
+
forceOverlay: options.force_overlay,
|
|
1136
|
+
});
|
|
1137
|
+
const b = bars();
|
|
1138
|
+
c.plotBars[id] = ohlcRows(open, high, low, close).map(({ i, ...ohlc }) => {
|
|
1139
|
+
const bar: PlotBarData = { time: b[i]!.time, ...ohlc };
|
|
1140
|
+
const color = candleColor(options.color, i);
|
|
1141
|
+
if (color !== undefined) bar.color = color;
|
|
1142
|
+
if (options.force_overlay) bar.forceOverlay = true;
|
|
1143
|
+
return bar;
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
|
|
938
1147
|
// ── Per-bar execution (eachBar) ──────────────────────────────────────────────
|
|
939
1148
|
|
|
940
1149
|
/**
|
|
@@ -1322,8 +1531,18 @@ export interface ChartContext {
|
|
|
1322
1531
|
tickerid?: string;
|
|
1323
1532
|
/** Unit of `Bar.time`: 's' (default, as lightweight-charts) or 'ms'. PineScript times are in ms. */
|
|
1324
1533
|
timeUnit?: 's' | 'ms';
|
|
1325
|
-
/**
|
|
1534
|
+
/**
|
|
1535
|
+
* Minimum price move of the symbol (PineScript `syminfo.mintick`), e.g. 0.01; used by `math.round_to_mintick` and
|
|
1536
|
+
* `format.mintick`. Default: `minmove / pricescale` when both are given
|
|
1537
|
+
*/
|
|
1326
1538
|
mintick?: number;
|
|
1539
|
+
/**
|
|
1540
|
+
* PineScript `syminfo.minmove` and `syminfo.pricescale` (mintick = minmove / pricescale). Default: from `mintick`,
|
|
1541
|
+
* with pricescale 10^(its decimals) (0.25 is 25 / 100). Pass them for a symbol priced in fractions (1 / 32):
|
|
1542
|
+
* `format.mintick` shows floor(log10(pricescale)) decimals
|
|
1543
|
+
*/
|
|
1544
|
+
minmove?: number;
|
|
1545
|
+
pricescale?: number;
|
|
1327
1546
|
/** Currency value of one point of price move for one contract (PineScript `syminfo.pointvalue`); 1 for stocks */
|
|
1328
1547
|
pointvalue?: number;
|
|
1329
1548
|
/** Smallest tradable quantity (PineScript `syminfo.mincontract`); 1 for whole shares */
|
|
@@ -1411,19 +1630,37 @@ function symbolProperty(key: 'mintick' | 'pointvalue' | 'mincontract'): number {
|
|
|
1411
1630
|
collector();
|
|
1412
1631
|
const value = chartCtx[key];
|
|
1413
1632
|
if (value === undefined) {
|
|
1633
|
+
if (key === 'mintick' && chartCtx.minmove !== undefined && chartCtx.pricescale !== undefined) {
|
|
1634
|
+
return chartCtx.minmove / chartCtx.pricescale;
|
|
1635
|
+
}
|
|
1414
1636
|
throw new Error(`syminfo.${key} is not known: pass it as executeScript(body, bars, inputs, { ${key} }).`);
|
|
1415
1637
|
}
|
|
1416
1638
|
return value;
|
|
1417
1639
|
}
|
|
1418
1640
|
|
|
1641
|
+
/** `[minmove, pricescale]` of the chart symbol: from the chart context, else from its mintick. */
|
|
1642
|
+
function symbolScale(): [number, number] {
|
|
1643
|
+
collector();
|
|
1644
|
+
const { minmove, pricescale } = chartCtx;
|
|
1645
|
+
if (minmove !== undefined && pricescale !== undefined) return [minmove, pricescale];
|
|
1646
|
+
const [move, scale] = tickScale(symbolProperty('mintick'), pricescale);
|
|
1647
|
+
return [minmove ?? move, scale];
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1419
1650
|
/**
|
|
1420
1651
|
* PineScript `syminfo.timezone`, `syminfo.tickerid`, `syminfo.session` (session type), `syminfo.mintick`,
|
|
1421
|
-
* `syminfo.pointvalue` and `syminfo.mincontract`, from the chart context.
|
|
1652
|
+
* `syminfo.minmove`, `syminfo.pricescale`, `syminfo.pointvalue` and `syminfo.mincontract`, from the chart context.
|
|
1422
1653
|
*/
|
|
1423
1654
|
export const syminfo = {
|
|
1424
1655
|
get mintick(): number {
|
|
1425
1656
|
return symbolProperty('mintick');
|
|
1426
1657
|
},
|
|
1658
|
+
get minmove(): number {
|
|
1659
|
+
return symbolScale()[0];
|
|
1660
|
+
},
|
|
1661
|
+
get pricescale(): number {
|
|
1662
|
+
return symbolScale()[1];
|
|
1663
|
+
},
|
|
1427
1664
|
get pointvalue(): number {
|
|
1428
1665
|
return symbolProperty('pointvalue');
|
|
1429
1666
|
},
|
|
@@ -1509,9 +1746,11 @@ export function inSession(time: number, session: string, timezone?: string): boo
|
|
|
1509
1746
|
/** PineScript `str.*`; `format_time` and the dates of `format` use the exchange time zone by default. */
|
|
1510
1747
|
export const str = {
|
|
1511
1748
|
...strCore,
|
|
1512
|
-
// format.mintick
|
|
1513
|
-
tostring: ((value: unknown, format?: string, mintick?: number) =>
|
|
1514
|
-
|
|
1749
|
+
// format.mintick uses the chart symbol's tick and price scale (syminfo.mintick, syminfo.pricescale)
|
|
1750
|
+
tostring: ((value: unknown, format?: string, mintick?: number, pricescale?: number) =>
|
|
1751
|
+
format === 'mintick' && mintick === undefined
|
|
1752
|
+
? strCore.tostring(value as never, format, syminfo.mintick, pricescale ?? syminfo.pricescale)
|
|
1753
|
+
: strCore.tostring(value as never, format, mintick, pricescale)) as typeof strCore.tostring,
|
|
1515
1754
|
format_time(time: number, format: string = "yyyy-MM-dd'T'HH:mm:ssZ", timezone?: string): string {
|
|
1516
1755
|
return strCore.format_time(time, format, timezone ?? exchangeTimezone());
|
|
1517
1756
|
},
|
|
@@ -1831,7 +2070,20 @@ export const polyline = {
|
|
|
1831
2070
|
},
|
|
1832
2071
|
};
|
|
1833
2072
|
|
|
1834
|
-
/**
|
|
2073
|
+
/**
|
|
2074
|
+
* PineScript `table.*`; `table.all` lists the live tables in creation order. The live tables go to `result.tables`.
|
|
2075
|
+
*/
|
|
2076
|
+
export const table = {
|
|
2077
|
+
...tableCore,
|
|
2078
|
+
get all() {
|
|
2079
|
+
return tableCore.all();
|
|
2080
|
+
},
|
|
2081
|
+
};
|
|
2082
|
+
|
|
2083
|
+
/** PineScript `position.*` constants (table positions). */
|
|
2084
|
+
export { position };
|
|
2085
|
+
|
|
2086
|
+
/** PineScript `text.format_*`, `text.align_*` and `text.wrap_*` constants. */
|
|
1835
2087
|
export const text = { ...textCore };
|
|
1836
2088
|
|
|
1837
2089
|
/**
|
|
@@ -1902,21 +2154,40 @@ export const math = {
|
|
|
1902
2154
|
}) as typeof mathCore.round_to_mintick,
|
|
1903
2155
|
};
|
|
1904
2156
|
export * as compare from '../compare/index.js';
|
|
2157
|
+
export { isInputActive } from '../runtime/input-active.js';
|
|
1905
2158
|
export type {
|
|
1906
2159
|
Bar,
|
|
1907
2160
|
IndicatorResult,
|
|
1908
2161
|
TimeValue,
|
|
1909
2162
|
InputConfig,
|
|
2163
|
+
InputCondition,
|
|
1910
2164
|
PlotConfig,
|
|
1911
2165
|
HLineConfig,
|
|
1912
2166
|
FillConfig,
|
|
1913
2167
|
ShapeConfig,
|
|
1914
2168
|
ArrowConfig,
|
|
1915
2169
|
BarColorConfig,
|
|
2170
|
+
CandleConfig,
|
|
1916
2171
|
MarkerData,
|
|
2172
|
+
MarkerPosition,
|
|
2173
|
+
MarkerShape,
|
|
1917
2174
|
ArrowData,
|
|
1918
2175
|
BarColorData,
|
|
2176
|
+
BgColorData,
|
|
2177
|
+
PlotCandleData,
|
|
2178
|
+
PlotBarData,
|
|
2179
|
+
LabelData,
|
|
2180
|
+
LabelStyle,
|
|
2181
|
+
LineDrawingData,
|
|
2182
|
+
BoxData,
|
|
2183
|
+
LinefillData,
|
|
2184
|
+
PolylineData,
|
|
2185
|
+
TableData,
|
|
2186
|
+
TableCellData,
|
|
2187
|
+
TableMergeData,
|
|
2188
|
+
TablePosition,
|
|
1919
2189
|
MarkerLocation,
|
|
1920
2190
|
MarkerSize,
|
|
2191
|
+
PineSize,
|
|
1921
2192
|
ShapeStyle,
|
|
1922
2193
|
};
|
package/src/str/index.ts
CHANGED
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
* @version 6
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { formatNumber } from './numberformat.js';
|
|
12
|
+
import { formatNumber, tickScale } from './numberformat.js';
|
|
13
13
|
import { formatMessage } from './messageformat.js';
|
|
14
14
|
import { formatDate } from './dateformat.js';
|
|
15
|
-
import { round_to_mintick } from '../math/index.js';
|
|
16
15
|
import type { int, bool, float, simple_int, simple_string } from '../types/index.js';
|
|
17
16
|
|
|
18
17
|
/**
|
|
@@ -40,6 +39,8 @@ export function length(str: simple_string): int {
|
|
|
40
39
|
* `format.price` / `format.inherit`
|
|
41
40
|
* @param mintick - Tick size for `format.mintick` (PineScript `syminfo.mintick`, implicit there). Not part of the
|
|
42
41
|
* PineScript signature: the core function cannot see the chart, so pass it, as for `math.round_to_mintick`
|
|
42
|
+
* @param pricescale - Price scale of the symbol for `format.mintick` (PineScript `syminfo.pricescale`), needed for a
|
|
43
|
+
* symbol priced in fractions (32 for a 1/32 tick). Default: 10^(decimals of `mintick`)
|
|
43
44
|
* @returns String representation of the value
|
|
44
45
|
*
|
|
45
46
|
* @remarks
|
|
@@ -53,8 +54,12 @@ export function length(str: simple_string): int {
|
|
|
53
54
|
* - `format.percent` shows 2 decimals and "%" without multiplying; `format.volume` uses K, M, B, T
|
|
54
55
|
* - `format.price` and `format.inherit` give "price1" / "inherit1" in PineScript: they are patterns
|
|
55
56
|
* without digit characters, and oakscriptjs does the same
|
|
56
|
-
* - `format.mintick`
|
|
57
|
-
*
|
|
57
|
+
* - `format.mintick` shows floor(log10(pricescale)) decimals, zeros included ("1.00", "0.40" for tick 0.01), and
|
|
58
|
+
* rounds in two steps, with mintick = minmove / pricescale: the double quotient `|x| / (1 / pricescale)` half away
|
|
59
|
+
* from zero (140.355 / 0.01 = 14035.499999999998 gives "140.35", where `math.round_to_mintick` gives 140.36),
|
|
60
|
+
* then `floor(units / minmove + 0.5)` ticks (tick 0.02: 0.005 gives "0.02", -0.01 gives "0.00"). With a
|
|
61
|
+
* pricescale that is no power of 10 the tick value is then rounded half even to those decimals (1/32: 25.25 gives
|
|
62
|
+
* "25.2", -0.02 gives "-0.0"). It throws without `mintick`
|
|
58
63
|
* - na gives "NaN" ("NaN%" with `format.percent`); an infinity gives "NaN" too ("NaN%" for `format.percent`,
|
|
59
64
|
* "NaNT" for `format.volume`), except `format.mintick`, which gives "Infinity" / "-Infinity"
|
|
60
65
|
*
|
|
@@ -66,7 +71,7 @@ export function length(str: simple_string): int {
|
|
|
66
71
|
* str.tostring(12345.678, format.volume) // "12.346K"
|
|
67
72
|
* ```
|
|
68
73
|
*/
|
|
69
|
-
export function tostring(value: any, format?: simple_string, mintick?: float): string {
|
|
74
|
+
export function tostring(value: any, format?: simple_string, mintick?: float, pricescale?: float): string {
|
|
70
75
|
if (typeof value !== 'number') return String(value);
|
|
71
76
|
if (Number.isNaN(value)) return format === 'percent' ? 'NaN%' : 'NaN';
|
|
72
77
|
if (!Number.isFinite(value)) {
|
|
@@ -94,11 +99,19 @@ export function tostring(value: any, format?: simple_string, mintick?: float): s
|
|
|
94
99
|
if (mintick === undefined) {
|
|
95
100
|
throw new Error('str.tostring(x, format.mintick) needs the tick size: pass it as str.tostring(x, format.mintick, syminfo.mintick)');
|
|
96
101
|
}
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
const [minmove, scale] = tickScale(mintick, pricescale);
|
|
103
|
+
const decimals = Math.floor(Math.log10(scale) + 1e-9);
|
|
104
|
+
const mask = decimals > 0 ? '0.' + '0'.repeat(decimals) : '0';
|
|
105
|
+
if (!(minmove >= 1)) return formatNumber(value, mask, 'halfUpShortest');
|
|
106
|
+
// 1. the double quotient |x| / (1 / pricescale), half away from zero (not math.round_to_mintick's exact decimal)
|
|
107
|
+
const scaled = Math.abs(value) / (1 / scale);
|
|
108
|
+
let units = Math.trunc(scaled);
|
|
109
|
+
if (scaled - units >= 0.5) units++;
|
|
110
|
+
// 2. to the tick, a half going up (minmove 2: -0.01 gives "0.00", 0.01 gives "0.02")
|
|
111
|
+
const ticks = Math.floor((value < 0 ? -units : units) / minmove + 0.5);
|
|
112
|
+
// 3. the tick value at the scale's decimals; a negative one keeps its sign when it prints as zero ("-0.0")
|
|
113
|
+
const text = formatNumber((Math.abs(ticks) * minmove) / scale, mask, 'halfEvenExact');
|
|
114
|
+
return ticks < 0 ? '-' + text : text;
|
|
102
115
|
}
|
|
103
116
|
default:
|
|
104
117
|
return formatNumber(value, format, 'halfUpShortest');
|
package/src/str/numberformat.ts
CHANGED
|
@@ -230,3 +230,16 @@ function affixes(p: Pattern, negative: boolean, body: string): string {
|
|
|
230
230
|
if (p.negPrefix === null) return '-' + p.prefix + body + p.suffix;
|
|
231
231
|
return p.negPrefix + body + p.negSuffix!;
|
|
232
232
|
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* `[minmove, pricescale]` of a tick (PineScript `syminfo.mintick = syminfo.minmove / syminfo.pricescale`).
|
|
236
|
+
* Without `pricescale` it is 10^(decimals of the tick): 0.02 is 2 / 100, 0.25 is 25 / 100. A symbol priced in
|
|
237
|
+
* fractions needs its own (1/32 is 1 / 32).
|
|
238
|
+
*/
|
|
239
|
+
export function tickScale(mintick: number, pricescale?: number): [number, number] {
|
|
240
|
+
if (pricescale === undefined) {
|
|
241
|
+
const m = /^\d+(?:\.(\d+))?(?:e-(\d+))?$/.exec(String(mintick));
|
|
242
|
+
pricescale = Number(`1e${m ? (m[1]?.length ?? 0) + Number(m[2] ?? 0) : 0}`);
|
|
243
|
+
}
|
|
244
|
+
return [Math.round(mintick * pricescale), pricescale];
|
|
245
|
+
}
|