oakscriptjs 0.9.6 → 0.10.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/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 +6 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +9 -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/types.d.ts +22 -0
- 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 +82 -5
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +141 -26
- package/dist/cjs/script/index.js.map +3 -3
- 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 +6 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +7 -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/types.d.ts +22 -0
- package/dist/esm/runtime/types.d.ts.map +1 -1
- package/dist/esm/script/index.d.ts +82 -5
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +167 -21
- package/dist/esm/script/index.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 +9 -3
- package/src/polyline/index.ts +3 -12
- package/src/runtime/types.ts +23 -0
- package/src/script/index.ts +254 -24
- 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,18 +37,35 @@ 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,
|
|
@@ -65,10 +82,12 @@ import * as labelCore from '../label/index.js';
|
|
|
65
82
|
import * as boxCore from '../box/index.js';
|
|
66
83
|
import * as linefillCore from '../linefill/index.js';
|
|
67
84
|
import * as polylineCore from '../polyline/index.js';
|
|
85
|
+
import * as tableCore from '../table/index.js';
|
|
68
86
|
import * as chartPointCore from '../chartpoint/index.js';
|
|
69
87
|
import * as textCore from '../text/index.js';
|
|
70
88
|
import * as mathCore from '../math/index.js';
|
|
71
89
|
import { resetDrawings, setDrawingLimits } from '../drawing/registry.js';
|
|
90
|
+
import { drawingOutputs } from '../drawing/output.js';
|
|
72
91
|
import * as timeframeCore from '../timeframe/index.js';
|
|
73
92
|
import * as timeCore from '../time/index.js';
|
|
74
93
|
import * as strCore from '../str/index.js';
|
|
@@ -76,6 +95,7 @@ import { formatMessage } from '../str/messageformat.js';
|
|
|
76
95
|
import { TradingCalendar, type SessionSpec } from '../session/calendar.js';
|
|
77
96
|
import { SessionBars, sessionFlags, type SessionFlags } from '../session/bars.js';
|
|
78
97
|
import { afterCompletion, heikinAshi, mapToChart, periodsOf, resample } from '../security/resample.js';
|
|
98
|
+
import { position } from '../types/index.js';
|
|
79
99
|
import type { ChartPoint } from '../types/index.js';
|
|
80
100
|
import {
|
|
81
101
|
STRATEGY_CONSTANTS,
|
|
@@ -110,11 +130,17 @@ export interface ScriptRunResult {
|
|
|
110
130
|
shapeConfig: ShapeConfig[];
|
|
111
131
|
arrowConfig: ArrowConfig[];
|
|
112
132
|
barColorConfig: BarColorConfig[];
|
|
133
|
+
/** plotcandle() / plotbar() declarations */
|
|
134
|
+
candleConfig: CandleConfig[];
|
|
113
135
|
defaultInputs: Record<string, unknown>;
|
|
114
136
|
alertConfig: AlertConditionConfig[];
|
|
115
137
|
/** Strategy properties (declared values, else the PineScript defaults) when the script declares strategy(). */
|
|
116
138
|
strategyConfig?: StrategyProperties;
|
|
117
|
-
/**
|
|
139
|
+
/**
|
|
140
|
+
* The renderable output: plots keyed by plotConfig ids, plot-pair fills, markers, arrows, background and bar
|
|
141
|
+
* colours, candles and bars, and the drawings (labels, lines, boxes, linefills, polylines, tables) alive after the
|
|
142
|
+
* last bar.
|
|
143
|
+
*/
|
|
118
144
|
result: IndicatorResult & { alerts?: AlertState[] };
|
|
119
145
|
}
|
|
120
146
|
|
|
@@ -159,8 +185,11 @@ interface Collector {
|
|
|
159
185
|
arrowConfig: ArrowConfig[];
|
|
160
186
|
arrows: ArrowData[];
|
|
161
187
|
barColorConfig: BarColorConfig[];
|
|
162
|
-
|
|
163
|
-
|
|
188
|
+
bgColors: BgColorData[];
|
|
189
|
+
barColors: BarColorData[];
|
|
190
|
+
candleConfig: CandleConfig[];
|
|
191
|
+
plotCandles: Record<string, PlotCandleData[]>;
|
|
192
|
+
plotBars: Record<string, PlotBarData[]>;
|
|
164
193
|
alertConfig: AlertConditionConfig[];
|
|
165
194
|
alerts: AlertState[];
|
|
166
195
|
strategy?: StrategyRun;
|
|
@@ -210,8 +239,11 @@ function freshCollector(inputValues: Record<string, unknown>): Collector {
|
|
|
210
239
|
arrowConfig: [],
|
|
211
240
|
arrows: [],
|
|
212
241
|
barColorConfig: [],
|
|
213
|
-
|
|
214
|
-
|
|
242
|
+
bgColors: [],
|
|
243
|
+
barColors: [],
|
|
244
|
+
candleConfig: [],
|
|
245
|
+
plotCandles: {},
|
|
246
|
+
plotBars: {},
|
|
215
247
|
alertConfig: [],
|
|
216
248
|
alerts: [],
|
|
217
249
|
};
|
|
@@ -284,6 +316,7 @@ export function executeScript(
|
|
|
284
316
|
shapeConfig: c.shapeConfig,
|
|
285
317
|
arrowConfig: c.arrowConfig,
|
|
286
318
|
barColorConfig: c.barColorConfig,
|
|
319
|
+
candleConfig: c.candleConfig,
|
|
287
320
|
defaultInputs: c.defaultInputs,
|
|
288
321
|
alertConfig: c.alertConfig,
|
|
289
322
|
strategyConfig: c.strategy?.properties,
|
|
@@ -293,8 +326,11 @@ export function executeScript(
|
|
|
293
326
|
fills: c.fills.length ? c.fills : undefined,
|
|
294
327
|
markers: c.markers.length ? c.markers : undefined,
|
|
295
328
|
arrows: c.arrows.length ? c.arrows : undefined,
|
|
296
|
-
|
|
297
|
-
|
|
329
|
+
bgColors: c.bgColors.length ? c.bgColors : undefined,
|
|
330
|
+
barColors: c.barColors.length ? c.barColors : undefined,
|
|
331
|
+
plotCandles: Object.keys(c.plotCandles).length ? c.plotCandles : undefined,
|
|
332
|
+
plotBars: Object.keys(c.plotBars).length ? c.plotBars : undefined,
|
|
333
|
+
...drawingOutputs(ctxBars.bars, chartCtx.timeUnit),
|
|
298
334
|
alerts: c.alerts.length ? c.alerts : undefined,
|
|
299
335
|
},
|
|
300
336
|
};
|
|
@@ -779,8 +815,46 @@ export interface PlotCharOptions extends Omit<PlotShapeOptions, 'style'> {
|
|
|
779
815
|
char?: string;
|
|
780
816
|
}
|
|
781
817
|
|
|
818
|
+
/** PineScript shape.* style -> marker shape. */
|
|
819
|
+
const MARKER_SHAPES: Record<ShapeStyle, MarkerShape> = {
|
|
820
|
+
xcross: 'xcross',
|
|
821
|
+
cross: 'cross',
|
|
822
|
+
triangleup: 'triangleUp',
|
|
823
|
+
triangledown: 'triangleDown',
|
|
824
|
+
flag: 'flag',
|
|
825
|
+
circle: 'circle',
|
|
826
|
+
arrowup: 'arrowUp',
|
|
827
|
+
arrowdown: 'arrowDown',
|
|
828
|
+
labelup: 'labelUp',
|
|
829
|
+
labeldown: 'labelDown',
|
|
830
|
+
square: 'square',
|
|
831
|
+
diamond: 'diamond',
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Marker position of a PineScript location. With location.absolute the shape sits on the price: a label pointing
|
|
836
|
+
* down above it, a label pointing up below it, the other shapes (and the characters) centred on it.
|
|
837
|
+
*/
|
|
838
|
+
function markerPosition(location: MarkerLocation, shape: ShapeStyle | undefined): MarkerPosition {
|
|
839
|
+
switch (location) {
|
|
840
|
+
case 'abovebar':
|
|
841
|
+
return 'aboveBar';
|
|
842
|
+
case 'belowbar':
|
|
843
|
+
return 'belowBar';
|
|
844
|
+
case 'top':
|
|
845
|
+
return 'top';
|
|
846
|
+
case 'bottom':
|
|
847
|
+
return 'bottom';
|
|
848
|
+
case 'absolute':
|
|
849
|
+
return shape === 'labeldown' ? 'atPriceTop' : shape === 'labelup' ? 'atPriceBottom' : 'atPriceMiddle';
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/** Default colour of plotshape / plotchar markers. */
|
|
854
|
+
const MARKER_COLOR = '#2962FF';
|
|
855
|
+
|
|
782
856
|
/** Shared emitter for plotshape()/plotchar(). Emits one marker per bar where
|
|
783
|
-
* the condition is truthy (not na, not 0). */
|
|
857
|
+
* the condition is truthy (not na, not 0). A plotchar marker has the shape 'circle' and its character in `char`. */
|
|
784
858
|
function emitMarkers(
|
|
785
859
|
kind: 'shape' | 'char',
|
|
786
860
|
condition: Series,
|
|
@@ -806,6 +880,19 @@ function emitMarkers(
|
|
|
806
880
|
offset: options.offset,
|
|
807
881
|
forceOverlay: options.force_overlay,
|
|
808
882
|
});
|
|
883
|
+
const shapeStyle = kind === 'shape' ? (style as ShapeStyle) : undefined;
|
|
884
|
+
const template: Omit<MarkerData, 'time'> = {
|
|
885
|
+
position: markerPosition(location, shapeStyle),
|
|
886
|
+
shape: shapeStyle ? MARKER_SHAPES[shapeStyle] : 'circle',
|
|
887
|
+
color: options.color ?? MARKER_COLOR,
|
|
888
|
+
id,
|
|
889
|
+
};
|
|
890
|
+
if (char !== undefined) template.char = char;
|
|
891
|
+
if (options.text !== undefined) template.text = options.text;
|
|
892
|
+
if (options.textcolor !== undefined) template.textColor = options.textcolor;
|
|
893
|
+
if (options.size !== undefined) template.size = options.size;
|
|
894
|
+
if (options.tooltip !== undefined) template.tooltip = options.tooltip;
|
|
895
|
+
if (options.force_overlay) template.forceOverlay = true;
|
|
809
896
|
const values = condition.toArray();
|
|
810
897
|
const b = bars();
|
|
811
898
|
const off = options.offset ?? 0;
|
|
@@ -814,19 +901,8 @@ function emitMarkers(
|
|
|
814
901
|
if (v === undefined || Number.isNaN(v) || v === 0) continue;
|
|
815
902
|
const j = i + off;
|
|
816
903
|
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
|
-
});
|
|
904
|
+
const time = b[j]!.time;
|
|
905
|
+
c.markers.push(location === 'absolute' ? { time, ...template, price: v } : { time, ...template });
|
|
830
906
|
}
|
|
831
907
|
}
|
|
832
908
|
|
|
@@ -906,13 +982,14 @@ function emitBarColors(
|
|
|
906
982
|
c.barColorConfig.push({ id, kind, title: options.title, offset: options.offset, forceOverlay: options.force_overlay });
|
|
907
983
|
const b = bars();
|
|
908
984
|
const off = options.offset ?? 0;
|
|
909
|
-
const target = kind === 'bgcolor' ? c.
|
|
985
|
+
const target: BgColorData[] = kind === 'bgcolor' ? c.bgColors : c.barColors;
|
|
986
|
+
const forceOverlay = kind === 'bgcolor' && options.force_overlay === true;
|
|
910
987
|
for (let i = 0; i < b.length; i++) {
|
|
911
988
|
const col = typeof colors === 'string' ? colors : colors[i];
|
|
912
989
|
if (!col) continue;
|
|
913
990
|
const j = i + off;
|
|
914
991
|
if (j < 0 || j >= b.length) continue;
|
|
915
|
-
target.push({ time: b[j]!.time, color: col });
|
|
992
|
+
target.push(forceOverlay ? { time: b[j]!.time, color: col, forceOverlay } : { time: b[j]!.time, color: col });
|
|
916
993
|
}
|
|
917
994
|
}
|
|
918
995
|
|
|
@@ -935,6 +1012,129 @@ export function barcolor(
|
|
|
935
1012
|
emitBarColors('barcolor', colors, options);
|
|
936
1013
|
}
|
|
937
1014
|
|
|
1015
|
+
/** A colour argument of plotcandle() / plotbar(): a colour, or per-bar colours (null / undefined entries are na). */
|
|
1016
|
+
export type ScriptCandleColor = string | Array<string | null | undefined>;
|
|
1017
|
+
|
|
1018
|
+
export interface PlotCandleOptions {
|
|
1019
|
+
/** Body colour: a colour or a per-bar array (color.when); an na entry gives a transparent body */
|
|
1020
|
+
color?: ScriptCandleColor;
|
|
1021
|
+
/** Wick colour: a colour or a per-bar array */
|
|
1022
|
+
wickcolor?: ScriptCandleColor;
|
|
1023
|
+
/** Border colour: a colour or a per-bar array */
|
|
1024
|
+
bordercolor?: ScriptCandleColor;
|
|
1025
|
+
display?: CandleConfig['display'];
|
|
1026
|
+
/** Draw on the main chart pane even when the script is not an overlay (PineScript `force_overlay`); default false. */
|
|
1027
|
+
force_overlay?: boolean;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
export interface PlotBarOptions {
|
|
1031
|
+
/** Bar colour: a colour or a per-bar array (color.when); an na entry gives a transparent bar */
|
|
1032
|
+
color?: ScriptCandleColor;
|
|
1033
|
+
display?: CandleConfig['display'];
|
|
1034
|
+
/** Draw on the main chart pane even when the script is not an overlay (PineScript `force_overlay`); default false. */
|
|
1035
|
+
force_overlay?: boolean;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/** Colour of bar i: undefined when the argument is not given, 'transparent' for an na entry. */
|
|
1039
|
+
function candleColor(col: ScriptCandleColor | undefined, i: number): string | undefined {
|
|
1040
|
+
if (col === undefined) return undefined;
|
|
1041
|
+
if (typeof col === 'string') return col;
|
|
1042
|
+
return col[i] ?? 'transparent';
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
interface OhlcRow {
|
|
1046
|
+
i: number;
|
|
1047
|
+
open: number;
|
|
1048
|
+
high: number;
|
|
1049
|
+
low: number;
|
|
1050
|
+
close: number;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/** The bars where open, high, low and close are all not na (PineScript draws no candle / bar otherwise). */
|
|
1054
|
+
function ohlcRows(o: Series, h: Series, l: Series, cl: Series): OhlcRow[] {
|
|
1055
|
+
const [ov, hv, lv, cv] = [o.toArray(), h.toArray(), l.toArray(), cl.toArray()];
|
|
1056
|
+
const rows: OhlcRow[] = [];
|
|
1057
|
+
for (let i = 0; i < bars().length; i++) {
|
|
1058
|
+
const [open, high, low, close] = [ov[i]!, hv[i]!, lv[i]!, cv[i]!];
|
|
1059
|
+
if (Number.isFinite(open) && Number.isFinite(high) && Number.isFinite(low) && Number.isFinite(close)) {
|
|
1060
|
+
rows.push({ i, open, high, low, close });
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return rows;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
const staticColor = (col: ScriptCandleColor | undefined): string | undefined => (typeof col === 'string' ? col : undefined);
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* PineScript `plotcandle(open, high, low, close, title?, { color, wickcolor, bordercolor, display, force_overlay })`:
|
|
1070
|
+
* candles in `result.plotCandles[id]`, the declaration in `candleConfig`. A bar with an na value has no candle.
|
|
1071
|
+
*/
|
|
1072
|
+
export function plotcandle(
|
|
1073
|
+
open: Series,
|
|
1074
|
+
high: Series,
|
|
1075
|
+
low: Series,
|
|
1076
|
+
close: Series,
|
|
1077
|
+
title?: string,
|
|
1078
|
+
options: PlotCandleOptions = {}
|
|
1079
|
+
): void {
|
|
1080
|
+
const c = collector();
|
|
1081
|
+
const id = `plotcandle${c.candleConfig.filter((x) => x.kind === 'candle').length}`;
|
|
1082
|
+
c.candleConfig.push({
|
|
1083
|
+
id,
|
|
1084
|
+
kind: 'candle',
|
|
1085
|
+
title,
|
|
1086
|
+
color: staticColor(options.color),
|
|
1087
|
+
wickColor: staticColor(options.wickcolor),
|
|
1088
|
+
borderColor: staticColor(options.bordercolor),
|
|
1089
|
+
display: options.display,
|
|
1090
|
+
forceOverlay: options.force_overlay,
|
|
1091
|
+
});
|
|
1092
|
+
const b = bars();
|
|
1093
|
+
c.plotCandles[id] = ohlcRows(open, high, low, close).map(({ i, ...ohlc }) => {
|
|
1094
|
+
const candle: PlotCandleData = { time: b[i]!.time, ...ohlc };
|
|
1095
|
+
const color = candleColor(options.color, i);
|
|
1096
|
+
const wickColor = candleColor(options.wickcolor, i);
|
|
1097
|
+
const borderColor = candleColor(options.bordercolor, i);
|
|
1098
|
+
if (color !== undefined) candle.color = color;
|
|
1099
|
+
if (wickColor !== undefined) candle.wickColor = wickColor;
|
|
1100
|
+
if (borderColor !== undefined) candle.borderColor = borderColor;
|
|
1101
|
+
if (options.force_overlay) candle.forceOverlay = true;
|
|
1102
|
+
return candle;
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* PineScript `plotbar(open, high, low, close, title?, { color, display, force_overlay })`: OHLC bars in
|
|
1108
|
+
* `result.plotBars[id]`, the declaration in `candleConfig`. A bar with an na value is not drawn.
|
|
1109
|
+
*/
|
|
1110
|
+
export function plotbar(
|
|
1111
|
+
open: Series,
|
|
1112
|
+
high: Series,
|
|
1113
|
+
low: Series,
|
|
1114
|
+
close: Series,
|
|
1115
|
+
title?: string,
|
|
1116
|
+
options: PlotBarOptions = {}
|
|
1117
|
+
): void {
|
|
1118
|
+
const c = collector();
|
|
1119
|
+
const id = `plotbar${c.candleConfig.filter((x) => x.kind === 'bar').length}`;
|
|
1120
|
+
c.candleConfig.push({
|
|
1121
|
+
id,
|
|
1122
|
+
kind: 'bar',
|
|
1123
|
+
title,
|
|
1124
|
+
color: staticColor(options.color),
|
|
1125
|
+
display: options.display,
|
|
1126
|
+
forceOverlay: options.force_overlay,
|
|
1127
|
+
});
|
|
1128
|
+
const b = bars();
|
|
1129
|
+
c.plotBars[id] = ohlcRows(open, high, low, close).map(({ i, ...ohlc }) => {
|
|
1130
|
+
const bar: PlotBarData = { time: b[i]!.time, ...ohlc };
|
|
1131
|
+
const color = candleColor(options.color, i);
|
|
1132
|
+
if (color !== undefined) bar.color = color;
|
|
1133
|
+
if (options.force_overlay) bar.forceOverlay = true;
|
|
1134
|
+
return bar;
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
|
|
938
1138
|
// ── Per-bar execution (eachBar) ──────────────────────────────────────────────
|
|
939
1139
|
|
|
940
1140
|
/**
|
|
@@ -1831,7 +2031,20 @@ export const polyline = {
|
|
|
1831
2031
|
},
|
|
1832
2032
|
};
|
|
1833
2033
|
|
|
1834
|
-
/**
|
|
2034
|
+
/**
|
|
2035
|
+
* PineScript `table.*`; `table.all` lists the live tables in creation order. The live tables go to `result.tables`.
|
|
2036
|
+
*/
|
|
2037
|
+
export const table = {
|
|
2038
|
+
...tableCore,
|
|
2039
|
+
get all() {
|
|
2040
|
+
return tableCore.all();
|
|
2041
|
+
},
|
|
2042
|
+
};
|
|
2043
|
+
|
|
2044
|
+
/** PineScript `position.*` constants (table positions). */
|
|
2045
|
+
export { position };
|
|
2046
|
+
|
|
2047
|
+
/** PineScript `text.format_*`, `text.align_*` and `text.wrap_*` constants. */
|
|
1835
2048
|
export const text = { ...textCore };
|
|
1836
2049
|
|
|
1837
2050
|
/**
|
|
@@ -1913,10 +2126,27 @@ export type {
|
|
|
1913
2126
|
ShapeConfig,
|
|
1914
2127
|
ArrowConfig,
|
|
1915
2128
|
BarColorConfig,
|
|
2129
|
+
CandleConfig,
|
|
1916
2130
|
MarkerData,
|
|
2131
|
+
MarkerPosition,
|
|
2132
|
+
MarkerShape,
|
|
1917
2133
|
ArrowData,
|
|
1918
2134
|
BarColorData,
|
|
2135
|
+
BgColorData,
|
|
2136
|
+
PlotCandleData,
|
|
2137
|
+
PlotBarData,
|
|
2138
|
+
LabelData,
|
|
2139
|
+
LabelStyle,
|
|
2140
|
+
LineDrawingData,
|
|
2141
|
+
BoxData,
|
|
2142
|
+
LinefillData,
|
|
2143
|
+
PolylineData,
|
|
2144
|
+
TableData,
|
|
2145
|
+
TableCellData,
|
|
2146
|
+
TableMergeData,
|
|
2147
|
+
TablePosition,
|
|
1919
2148
|
MarkerLocation,
|
|
1920
2149
|
MarkerSize,
|
|
2150
|
+
PineSize,
|
|
1921
2151
|
ShapeStyle,
|
|
1922
2152
|
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table namespace
|
|
3
|
+
* Functions for creating and filling table drawing objects.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* A table is drawn at a fixed position of the pane (`position.*`), not at a bar. Tables have no getters in
|
|
7
|
+
* PineScript; the script API returns the live tables in `result.tables`.
|
|
8
|
+
*
|
|
9
|
+
* PineScript rules:
|
|
10
|
+
* - `table.cell` defines a cell: the arguments it does not give get their default, a previous definition of the
|
|
11
|
+
* cell is replaced
|
|
12
|
+
* - `table.cell_set_*` changes one property of a cell (a cell that was not defined is created)
|
|
13
|
+
* - a column or row outside the table is a runtime error
|
|
14
|
+
* - a deleted table is `na`: its functions do nothing
|
|
15
|
+
*
|
|
16
|
+
* @version 6
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { all as allDrawings, isDeleted, register, remove } from '../drawing/registry.js';
|
|
20
|
+
import type { Table, TableCell, color } from '../types/index.js';
|
|
21
|
+
|
|
22
|
+
type TablePosition = Table['position'];
|
|
23
|
+
type HAlign = 'left' | 'center' | 'right';
|
|
24
|
+
type VAlign = 'top' | 'center' | 'bottom';
|
|
25
|
+
type FontFamily = 'default' | 'monospace';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates a new table (PineScript `table.new`).
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const t = table.new(position.top_right, 2, 3, '#131722', '#787B86', 1);
|
|
33
|
+
* table.cell(t, 0, 0, 'RSI', 0, 0, '#FFFFFF');
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function new_table(
|
|
37
|
+
position: TablePosition,
|
|
38
|
+
columns: number,
|
|
39
|
+
rows: number,
|
|
40
|
+
bgcolor?: color,
|
|
41
|
+
frame_color?: color,
|
|
42
|
+
frame_width?: number,
|
|
43
|
+
border_color?: color,
|
|
44
|
+
border_width?: number,
|
|
45
|
+
force_overlay?: boolean
|
|
46
|
+
): Table {
|
|
47
|
+
return register('table', {
|
|
48
|
+
position,
|
|
49
|
+
columns,
|
|
50
|
+
rows,
|
|
51
|
+
bgcolor,
|
|
52
|
+
frame_color,
|
|
53
|
+
frame_width,
|
|
54
|
+
border_color,
|
|
55
|
+
border_width,
|
|
56
|
+
force_overlay: force_overlay ?? false,
|
|
57
|
+
cells: new Map(),
|
|
58
|
+
merges: [],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function checkCell(caller: string, id: Table, column: number, row: number): void {
|
|
63
|
+
if (!(column >= 0 && column < id.columns && row >= 0 && row < id.rows)) {
|
|
64
|
+
throw new RangeError(
|
|
65
|
+
`${caller}: column ${column}, row ${row} is outside the table (${id.columns} columns, ${id.rows} rows).`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const key = (column: number, row: number): string => `${column},${row}`;
|
|
71
|
+
|
|
72
|
+
/** The cell at column / row, created when it was not defined. */
|
|
73
|
+
function cellOf(caller: string, id: Table, column: number, row: number): TableCell {
|
|
74
|
+
checkCell(caller, id, column, row);
|
|
75
|
+
const cells = (id.cells ??= new Map());
|
|
76
|
+
let cell = cells.get(key(column, row));
|
|
77
|
+
if (!cell) cells.set(key(column, row), (cell = { text: '' }));
|
|
78
|
+
return cell;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Defines a cell (PineScript `table.cell`): its text and properties. Omitted properties get their default.
|
|
83
|
+
*/
|
|
84
|
+
export function cell(
|
|
85
|
+
table_id: Table,
|
|
86
|
+
column: number,
|
|
87
|
+
row: number,
|
|
88
|
+
text?: string,
|
|
89
|
+
width?: number,
|
|
90
|
+
height?: number,
|
|
91
|
+
text_color?: color,
|
|
92
|
+
text_halign?: HAlign,
|
|
93
|
+
text_valign?: VAlign,
|
|
94
|
+
text_size?: string | number,
|
|
95
|
+
bgcolor?: color,
|
|
96
|
+
tooltip?: string,
|
|
97
|
+
text_font_family?: FontFamily,
|
|
98
|
+
text_formatting?: number
|
|
99
|
+
): void {
|
|
100
|
+
if (isDeleted(table_id)) return;
|
|
101
|
+
checkCell('table.cell()', table_id, column, row);
|
|
102
|
+
(table_id.cells ??= new Map()).set(key(column, row), {
|
|
103
|
+
text: text ?? '',
|
|
104
|
+
width,
|
|
105
|
+
height,
|
|
106
|
+
text_color,
|
|
107
|
+
text_halign,
|
|
108
|
+
text_valign,
|
|
109
|
+
text_size,
|
|
110
|
+
bgcolor,
|
|
111
|
+
tooltip,
|
|
112
|
+
text_font_family,
|
|
113
|
+
text_formatting,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function setCell<K extends keyof TableCell>(name: string, field: K) {
|
|
118
|
+
return (table_id: Table, column: number, row: number, value: TableCell[K]): void => {
|
|
119
|
+
if (isDeleted(table_id)) return;
|
|
120
|
+
cellOf(`table.${name}()`, table_id, column, row)[field] = value;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Sets the text of a cell. */
|
|
125
|
+
export const cell_set_text = setCell('cell_set_text', 'text');
|
|
126
|
+
/** Sets the width of a cell (% of the chart width; 0 fits the text). */
|
|
127
|
+
export const cell_set_width = setCell('cell_set_width', 'width');
|
|
128
|
+
/** Sets the height of a cell (% of the chart height; 0 fits the text). */
|
|
129
|
+
export const cell_set_height = setCell('cell_set_height', 'height');
|
|
130
|
+
/** Sets the text colour of a cell. */
|
|
131
|
+
export const cell_set_text_color = setCell('cell_set_text_color', 'text_color');
|
|
132
|
+
/** Sets the horizontal text alignment of a cell. */
|
|
133
|
+
export const cell_set_text_halign = setCell('cell_set_text_halign', 'text_halign');
|
|
134
|
+
/** Sets the vertical text alignment of a cell. */
|
|
135
|
+
export const cell_set_text_valign = setCell('cell_set_text_valign', 'text_valign');
|
|
136
|
+
/** Sets the text size of a cell. */
|
|
137
|
+
export const cell_set_text_size = setCell('cell_set_text_size', 'text_size');
|
|
138
|
+
/** Sets the background colour of a cell. */
|
|
139
|
+
export const cell_set_bgcolor = setCell('cell_set_bgcolor', 'bgcolor');
|
|
140
|
+
/** Sets the tooltip of a cell. */
|
|
141
|
+
export const cell_set_tooltip = setCell('cell_set_tooltip', 'tooltip');
|
|
142
|
+
/** Sets the font family of a cell. */
|
|
143
|
+
export const cell_set_text_font_family = setCell('cell_set_text_font_family', 'text_font_family');
|
|
144
|
+
/** Sets the text formatting of a cell (`text.format_*` flags). */
|
|
145
|
+
export const cell_set_text_formatting = setCell('cell_set_text_formatting', 'text_formatting');
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Merges the cells from start_column / start_row to end_column / end_row (PineScript `table.merge_cells`). The
|
|
149
|
+
* merged cell shows the content of the start cell.
|
|
150
|
+
*/
|
|
151
|
+
export function merge_cells(
|
|
152
|
+
table_id: Table,
|
|
153
|
+
start_column: number,
|
|
154
|
+
start_row: number,
|
|
155
|
+
end_column: number,
|
|
156
|
+
end_row: number
|
|
157
|
+
): void {
|
|
158
|
+
if (isDeleted(table_id)) return;
|
|
159
|
+
checkCell('table.merge_cells()', table_id, start_column, start_row);
|
|
160
|
+
checkCell('table.merge_cells()', table_id, end_column, end_row);
|
|
161
|
+
if (end_column < start_column || end_row < start_row) {
|
|
162
|
+
throw new RangeError('table.merge_cells(): the end cell is before the start cell.');
|
|
163
|
+
}
|
|
164
|
+
(table_id.merges ??= []).push({ start_column, start_row, end_column, end_row });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Removes the cells from start_column / start_row to end_column / end_row (PineScript `table.clear`; default end:
|
|
169
|
+
* the start cell), and the merges that start in that range.
|
|
170
|
+
*/
|
|
171
|
+
export function clear(
|
|
172
|
+
table_id: Table,
|
|
173
|
+
start_column: number,
|
|
174
|
+
start_row: number,
|
|
175
|
+
end_column: number = start_column,
|
|
176
|
+
end_row: number = start_row
|
|
177
|
+
): void {
|
|
178
|
+
if (isDeleted(table_id)) return;
|
|
179
|
+
checkCell('table.clear()', table_id, start_column, start_row);
|
|
180
|
+
checkCell('table.clear()', table_id, end_column, end_row);
|
|
181
|
+
const inside = (column: number, row: number): boolean =>
|
|
182
|
+
column >= start_column && column <= end_column && row >= start_row && row <= end_row;
|
|
183
|
+
for (let column = start_column; column <= end_column; column++) {
|
|
184
|
+
for (let row = start_row; row <= end_row; row++) table_id.cells?.delete(key(column, row));
|
|
185
|
+
}
|
|
186
|
+
if (table_id.merges) table_id.merges = table_id.merges.filter((m) => !inside(m.start_column, m.start_row));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function setTable<K extends keyof Table>(field: K) {
|
|
190
|
+
return (table_id: Table, value: Table[K]): void => {
|
|
191
|
+
if (isDeleted(table_id)) return;
|
|
192
|
+
table_id[field] = value;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Moves the table to another position (`position.*`). */
|
|
197
|
+
export const set_position = setTable('position');
|
|
198
|
+
/** Sets the background colour of the table. */
|
|
199
|
+
export const set_bgcolor = setTable('bgcolor');
|
|
200
|
+
/** Sets the colour of the outer frame. */
|
|
201
|
+
export const set_frame_color = setTable('frame_color');
|
|
202
|
+
/** Sets the width of the outer frame. */
|
|
203
|
+
export const set_frame_width = setTable('frame_width');
|
|
204
|
+
/** Sets the colour of the cell borders. */
|
|
205
|
+
export const set_border_color = setTable('border_color');
|
|
206
|
+
/** Sets the width of the cell borders. */
|
|
207
|
+
export const set_border_width = setTable('border_width');
|
|
208
|
+
|
|
209
|
+
/** Deletes the table (PineScript `table.delete`). */
|
|
210
|
+
export function delete_table(table_id: Table): void {
|
|
211
|
+
remove('table', table_id);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** All live tables, in creation order (PineScript `table.all`). A new array. */
|
|
215
|
+
export function all(): Table[] {
|
|
216
|
+
return allDrawings('table');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Alias to match PineScript API
|
|
220
|
+
export { new_table as new };
|
|
221
|
+
export { delete_table as delete };
|
package/src/text/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Text namespace
|
|
3
|
-
* PineScript `text.format_*` constants for `label.set_text_formatting` and `box.set_text_formatting
|
|
3
|
+
* PineScript `text.format_*` constants for `label.set_text_formatting` and `box.set_text_formatting`, and the
|
|
4
|
+
* `text.align_*` / `text.wrap_*` constants.
|
|
4
5
|
*
|
|
5
|
-
* The values are flags that add up, as in PineScript (`text.format_bold + text.format_italic`).
|
|
6
|
+
* The format values are flags that add up, as in PineScript (`text.format_bold + text.format_italic`).
|
|
6
7
|
* They are the values PineScript uses for drawn labels and boxes.
|
|
7
8
|
*/
|
|
8
9
|
|
|
@@ -12,3 +13,18 @@ export const format_none = 0;
|
|
|
12
13
|
export const format_bold = 1;
|
|
13
14
|
/** Italic text. */
|
|
14
15
|
export const format_italic = 2;
|
|
16
|
+
|
|
17
|
+
/** Text aligned to the left (`table.cell`, `box.new`, `label.new` textalign). */
|
|
18
|
+
export const align_left = 'left' as const;
|
|
19
|
+
/** Text centred horizontally or vertically. */
|
|
20
|
+
export const align_center = 'center' as const;
|
|
21
|
+
/** Text aligned to the right. */
|
|
22
|
+
export const align_right = 'right' as const;
|
|
23
|
+
/** Text aligned to the top (`table.cell`, `box.new` text_valign). */
|
|
24
|
+
export const align_top = 'top' as const;
|
|
25
|
+
/** Text aligned to the bottom. */
|
|
26
|
+
export const align_bottom = 'bottom' as const;
|
|
27
|
+
/** Text wrapped to the box width (`box.new` text_wrap). */
|
|
28
|
+
export const wrap_auto = 'auto' as const;
|
|
29
|
+
/** Text not wrapped. */
|
|
30
|
+
export const wrap_none = 'none' as const;
|