oakscriptjs 0.10.1 → 0.11.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/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 +75 -0
- package/dist/cjs/drawing/registry.d.ts.map +1 -1
- package/dist/cjs/drawing/registry.js +139 -6
- package/dist/cjs/drawing/registry.js.map +2 -2
- package/dist/cjs/index.d.ts +6 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +28 -1
- package/dist/cjs/index.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.map +1 -1
- package/dist/cjs/line/index.js +7 -1
- package/dist/cjs/line/index.js.map +2 -2
- 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/script/index.d.ts +30 -13
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +48 -9
- package/dist/cjs/script/index.js.map +3 -3
- package/dist/cjs/session/bars.d.ts +15 -1
- package/dist/cjs/session/bars.d.ts.map +1 -1
- package/dist/cjs/session/bars.js +41 -6
- package/dist/cjs/session/bars.js.map +3 -3
- package/dist/cjs/session/calendar.d.ts +9 -1
- package/dist/cjs/session/calendar.d.ts.map +1 -1
- package/dist/cjs/session/calendar.js +28 -2
- package/dist/cjs/session/calendar.js.map +2 -2
- package/dist/cjs/session/context.d.ts +75 -0
- package/dist/cjs/session/context.d.ts.map +1 -0
- package/dist/cjs/session/context.js +113 -0
- package/dist/cjs/session/context.js.map +7 -0
- package/dist/cjs/str/index.d.ts +3 -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 +4 -2
- package/dist/cjs/str/numberformat.d.ts.map +1 -1
- package/dist/cjs/str/numberformat.js +1 -1
- package/dist/cjs/str/numberformat.js.map +2 -2
- package/dist/cjs/ta/index.d.ts +33 -17
- package/dist/cjs/ta/index.d.ts.map +1 -1
- package/dist/cjs/ta/index.js +70 -29
- package/dist/cjs/ta/index.js.map +2 -2
- package/dist/cjs/ta/limits.d.ts +24 -0
- package/dist/cjs/ta/limits.d.ts.map +1 -0
- package/dist/cjs/ta/limits.js +50 -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 +7 -7
- package/dist/cjs/ta-series.d.ts.map +1 -1
- package/dist/cjs/ta-series.js +9 -7
- package/dist/cjs/ta-series.js.map +2 -2
- 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 +17 -1
- package/dist/cjs/time/session.d.ts.map +1 -1
- package/dist/cjs/time/session.js +61 -7
- package/dist/cjs/time/session.js.map +2 -2
- package/dist/cjs/types/metadata.d.ts +27 -3
- package/dist/cjs/types/metadata.d.ts.map +1 -1
- package/dist/cjs/types/metadata.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 +75 -0
- package/dist/esm/drawing/registry.d.ts.map +1 -1
- package/dist/esm/drawing/registry.js +237 -6
- package/dist/esm/drawing/registry.js.map +1 -1
- package/dist/esm/index.d.ts +6 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +10 -1
- package/dist/esm/index.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.map +1 -1
- package/dist/esm/line/index.js +8 -2
- package/dist/esm/line/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/script/index.d.ts +30 -13
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +54 -11
- package/dist/esm/script/index.js.map +1 -1
- package/dist/esm/session/bars.d.ts +15 -1
- package/dist/esm/session/bars.d.ts.map +1 -1
- package/dist/esm/session/bars.js +50 -6
- package/dist/esm/session/bars.js.map +1 -1
- package/dist/esm/session/calendar.d.ts +9 -1
- package/dist/esm/session/calendar.d.ts.map +1 -1
- package/dist/esm/session/calendar.js +35 -3
- package/dist/esm/session/calendar.js.map +1 -1
- package/dist/esm/session/context.d.ts +75 -0
- package/dist/esm/session/context.d.ts.map +1 -0
- package/dist/esm/session/context.js +122 -0
- package/dist/esm/session/context.js.map +1 -0
- package/dist/esm/str/index.d.ts +3 -1
- package/dist/esm/str/index.d.ts.map +1 -1
- package/dist/esm/str/index.js +8 -2
- package/dist/esm/str/index.js.map +1 -1
- package/dist/esm/str/numberformat.d.ts +4 -2
- package/dist/esm/str/numberformat.d.ts.map +1 -1
- package/dist/esm/str/numberformat.js +4 -2
- package/dist/esm/str/numberformat.js.map +1 -1
- package/dist/esm/ta/index.d.ts +33 -17
- package/dist/esm/ta/index.d.ts.map +1 -1
- package/dist/esm/ta/index.js +130 -56
- package/dist/esm/ta/index.js.map +1 -1
- package/dist/esm/ta/limits.d.ts +24 -0
- package/dist/esm/ta/limits.d.ts.map +1 -0
- package/dist/esm/ta/limits.js +42 -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 +7 -7
- package/dist/esm/ta-series.d.ts.map +1 -1
- package/dist/esm/ta-series.js +9 -7
- package/dist/esm/ta-series.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 +17 -1
- package/dist/esm/time/session.d.ts.map +1 -1
- package/dist/esm/time/session.js +97 -10
- package/dist/esm/time/session.js.map +1 -1
- package/dist/esm/types/metadata.d.ts +27 -3
- package/dist/esm/types/metadata.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/box/index.ts +8 -2
- package/src/callsite/index.ts +150 -100
- package/src/drawing/registry.ts +255 -5
- package/src/index.ts +34 -1
- package/src/label/index.ts +4 -1
- package/src/line/index.ts +8 -2
- package/src/math/index.ts +2 -0
- package/src/polyline/index.ts +3 -1
- package/src/script/index.ts +77 -18
- package/src/session/bars.ts +47 -6
- package/src/session/calendar.ts +37 -3
- package/src/session/context.ts +160 -0
- package/src/str/index.ts +7 -2
- package/src/str/numberformat.ts +5 -3
- package/src/ta/index.ts +148 -66
- package/src/ta/limits.ts +47 -0
- package/src/ta/running-sum.ts +2 -0
- package/src/ta-series.ts +16 -14
- package/src/time/index.ts +6 -3
- package/src/time/session.ts +89 -9
- package/src/types/metadata.ts +27 -3
|
@@ -36,7 +36,10 @@ export declare class SessionBars {
|
|
|
36
36
|
private periodRange;
|
|
37
37
|
private dayStart;
|
|
38
38
|
private dayEnd;
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. An intraday bar that
|
|
41
|
+
* opens after an early close has no day: `time("D")` is NaN on it (measured; `time("W")` keeps its value).
|
|
42
|
+
*/
|
|
40
43
|
timeOf(time: number, timeframe: string): number;
|
|
41
44
|
/** PineScript `time_close(tf)` for a bar that opens at `time`. */
|
|
42
45
|
closeOf(time: number, timeframe: string): number;
|
|
@@ -50,6 +53,14 @@ export declare class SessionBars {
|
|
|
50
53
|
periodEnd(time: number, timeframe: string): number;
|
|
51
54
|
/** PineScript `time_tradingday`: 00:00 UTC of the trading day. */
|
|
52
55
|
tradingDayTime(time: number): number;
|
|
56
|
+
/**
|
|
57
|
+
* Open time of the bar after the bar that opens at `time`, on the trading calendar: the next bar of the session
|
|
58
|
+
* period, else the start of the next period or trading day; on D / W / M charts the session start of the first
|
|
59
|
+
* trading day of the next chart period. NaN when none is found within 400 days.
|
|
60
|
+
*/
|
|
61
|
+
nextBarTime(time: number): number;
|
|
62
|
+
/** An intraday bar that opens after an early close of its day (see TradingCalendar.afterEarlyClose). */
|
|
63
|
+
stray(time: number): boolean;
|
|
53
64
|
/** True when the bar at `time` is inside the session. */
|
|
54
65
|
inside(time: number): boolean;
|
|
55
66
|
/** Session start and end of the trading day of `time`, or null. */
|
|
@@ -75,6 +86,9 @@ export interface SessionFlags {
|
|
|
75
86
|
* time; first and last bars are per session period (a trading day can have two periods, e.g. after a
|
|
76
87
|
* futures holiday); pre / post market compare with the regular hours of the same trading day; on
|
|
77
88
|
* D / W / M charts every bar is its own session (first, last, market, first and last regular).
|
|
89
|
+
* The first loaded bar is a first bar only when no earlier bar of its period can exist (it opens less than one chart
|
|
90
|
+
* period after the period start). A bar that opens after an early close is in the market hours (the usual hours of
|
|
91
|
+
* its day) but is no last bar.
|
|
78
92
|
*/
|
|
79
93
|
export declare function sessionFlags(times: number[], bars: SessionBars, regular: SessionBars): SessionFlags;
|
|
80
94
|
//# sourceMappingURL=bars.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bars.d.ts","sourceRoot":"","sources":["../../../src/session/bars.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EACL,eAAe,EAMf,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AAiBvB,+DAA+D;AAC/D,qBAAa,WAAW;IAMpB,QAAQ,CAAC,QAAQ,EAAE,eAAe;IALpC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAE9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;gBAG9C,QAAQ,EAAE,eAAe,EAClC,cAAc,EAAE,MAAM;IAKxB,iDAAiD;IACjD,OAAO,CAAC,aAAa;IAYrB;;;OAGG;IACH,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,MAAM;IAKd
|
|
1
|
+
{"version":3,"file":"bars.d.ts","sourceRoot":"","sources":["../../../src/session/bars.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EACL,eAAe,EAMf,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AAiBvB,+DAA+D;AAC/D,qBAAa,WAAW;IAMpB,QAAQ,CAAC,QAAQ,EAAE,eAAe;IALpC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAE9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;gBAG9C,QAAQ,EAAE,eAAe,EAClC,cAAc,EAAE,MAAM;IAKxB,iDAAiD;IACjD,OAAO,CAAC,aAAa;IAYrB;;;OAGG;IACH,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,MAAM;IAKd;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAiB/C,kEAAkE;IAClE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAuBhD,0FAA0F;IAC1F,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAWxC;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAclD,kEAAkE;IAClE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAKpC;;;;OAIG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAmBjC,wGAAwG;IACxG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI5B,yDAAyD;IACzD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI7B,mEAAmE;IACnE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CAI9E;AAED,gDAAgD;AAChD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,SAAS,EAAE,OAAO,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,OAAO,EAAE,CAAC;IACvB,YAAY,EAAE,OAAO,EAAE,CAAC;IACxB,kBAAkB,EAAE,OAAO,EAAE,CAAC;IAC9B,iBAAiB,EAAE,OAAO,EAAE,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,YAAY,CA6CnG"}
|
package/dist/cjs/session/bars.js
CHANGED
|
@@ -89,7 +89,10 @@ class SessionBars {
|
|
|
89
89
|
const p = this.calendar.periods(date);
|
|
90
90
|
return p[p.length - 1][1];
|
|
91
91
|
}
|
|
92
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. An intraday bar that
|
|
94
|
+
* opens after an early close has no day: `time("D")` is NaN on it (measured; `time("W")` keeps its value).
|
|
95
|
+
*/
|
|
93
96
|
timeOf(time, timeframe) {
|
|
94
97
|
const tf = (0, import_timeframe.info)(timeframe);
|
|
95
98
|
const at = this.calendar.locate(time);
|
|
@@ -101,6 +104,7 @@ class SessionBars {
|
|
|
101
104
|
return start + Math.floor((time - start) / ms) * ms;
|
|
102
105
|
}
|
|
103
106
|
const { unit, n } = unitOf(tf);
|
|
107
|
+
if (unit === "D" && n === 1 && this.stray(time)) return NaN;
|
|
104
108
|
const [from, to] = this.periodRange(at.date, unit, n);
|
|
105
109
|
const first = this.calendar.firstTradingDay(from, to);
|
|
106
110
|
return first === null ? NaN : this.dayStart(first);
|
|
@@ -116,6 +120,7 @@ class SessionBars {
|
|
|
116
120
|
return Math.min(this.timeOf(time, timeframe) + (0, import_timeframe.in_seconds)(tf.period) * 1e3, end);
|
|
117
121
|
}
|
|
118
122
|
const { unit, n } = unitOf(tf);
|
|
123
|
+
if (unit === "D" && n === 1 && this.stray(time)) return NaN;
|
|
119
124
|
const [from, to] = this.periodRange(at.date, unit, n);
|
|
120
125
|
if (this.chart.isintraday && (unit !== "D" || n > 1)) {
|
|
121
126
|
const [nextFrom, nextTo] = this.periodRange((0, import_calendar.addDays)(to, 1), unit, n);
|
|
@@ -128,7 +133,7 @@ class SessionBars {
|
|
|
128
133
|
/** Trading day of a bar: on D / W / M charts the last trading day of the chart period. */
|
|
129
134
|
tradingDay(time) {
|
|
130
135
|
const at = this.calendar.locate(time);
|
|
131
|
-
if (!at) return null;
|
|
136
|
+
if (!at || this.stray(time)) return null;
|
|
132
137
|
if (this.chart.isdwm) {
|
|
133
138
|
const { unit, n } = unitOf(this.chart);
|
|
134
139
|
const [from, to] = this.periodRange(at.date, unit, n);
|
|
@@ -159,6 +164,33 @@ class SessionBars {
|
|
|
159
164
|
const day = this.tradingDay(time);
|
|
160
165
|
return day === null ? NaN : (0, import_calendar.dateUtcMs)(day);
|
|
161
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Open time of the bar after the bar that opens at `time`, on the trading calendar: the next bar of the session
|
|
169
|
+
* period, else the start of the next period or trading day; on D / W / M charts the session start of the first
|
|
170
|
+
* trading day of the next chart period. NaN when none is found within 400 days.
|
|
171
|
+
*/
|
|
172
|
+
nextBarTime(time) {
|
|
173
|
+
const at = this.calendar.locate(time);
|
|
174
|
+
if (!at) return NaN;
|
|
175
|
+
if (!this.chart.isdwm) {
|
|
176
|
+
const periods = this.calendar.periods(at.date);
|
|
177
|
+
const [start, end] = periods[at.index];
|
|
178
|
+
const ms = (0, import_timeframe.in_seconds)(this.chart.period) * 1e3;
|
|
179
|
+
const next = start + (Math.floor((time - start) / ms) + 1) * ms;
|
|
180
|
+
if (at.inside && next < end) return next;
|
|
181
|
+
if (at.index + 1 < periods.length) return periods[at.index + 1][0];
|
|
182
|
+
const day2 = this.calendar.firstTradingDay((0, import_calendar.addDays)(at.date, 1), (0, import_calendar.addDays)(at.date, 400));
|
|
183
|
+
return day2 === null ? NaN : this.dayStart(day2);
|
|
184
|
+
}
|
|
185
|
+
const { unit, n } = unitOf(this.chart);
|
|
186
|
+
const [, to] = this.periodRange(at.date, unit, n);
|
|
187
|
+
const day = this.calendar.firstTradingDay((0, import_calendar.addDays)(to, 1), (0, import_calendar.addDays)(to, 400));
|
|
188
|
+
return day === null ? NaN : this.dayStart(day);
|
|
189
|
+
}
|
|
190
|
+
/** An intraday bar that opens after an early close of its day (see TradingCalendar.afterEarlyClose). */
|
|
191
|
+
stray(time) {
|
|
192
|
+
return !this.chart.isdwm && this.calendar.afterEarlyClose(time);
|
|
193
|
+
}
|
|
162
194
|
/** True when the bar at `time` is inside the session. */
|
|
163
195
|
inside(time) {
|
|
164
196
|
return this.calendar.locate(time)?.inside ?? false;
|
|
@@ -198,18 +230,21 @@ function sessionFlags(times, bars, regular) {
|
|
|
198
230
|
const close = bars.closeOf(t, chart.period);
|
|
199
231
|
const [, end] = bars.calendar.periods(at.date)[at.index];
|
|
200
232
|
const period = `${at.date}/${at.index}`;
|
|
201
|
-
|
|
202
|
-
|
|
233
|
+
const [start] = bars.calendar.periods(at.date)[at.index];
|
|
234
|
+
const barMs = (0, import_timeframe.in_seconds)(chart.period) * 1e3;
|
|
235
|
+
out.isfirstbar[i] = i === 0 ? t - start < barMs : period !== previousPeriod;
|
|
236
|
+
const stray = bars.stray(t);
|
|
237
|
+
out.islastbar[i] = !stray && close >= end;
|
|
203
238
|
const regularPeriods = regular.calendar.periods(at.date);
|
|
204
239
|
const r = regularPeriods.findIndex(([s, e]) => s <= t && t < e);
|
|
205
240
|
const market = r !== -1;
|
|
206
|
-
out.ismarket[i] = market;
|
|
241
|
+
out.ismarket[i] = market || regular.stray(t);
|
|
207
242
|
if (!market && regularPeriods.length) {
|
|
208
243
|
out.ispremarket[i] = t < regularPeriods[0][0];
|
|
209
244
|
out.ispostmarket[i] = t >= regularPeriods[regularPeriods.length - 1][1];
|
|
210
245
|
}
|
|
211
246
|
const regularPeriod = market ? `${at.date}/${r}` : "";
|
|
212
|
-
out.isfirstbar_regular[i] = market && regularPeriod !== previousRegular;
|
|
247
|
+
out.isfirstbar_regular[i] = market && (i === 0 ? t - regularPeriods[r][0] < barMs : regularPeriod !== previousRegular);
|
|
213
248
|
out.islastbar_regular[i] = market && close >= regularPeriods[r][1];
|
|
214
249
|
previousPeriod = period;
|
|
215
250
|
previousRegular = regularPeriod;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/session/bars.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * PineScript values that depend on the chart timeframe and the symbol session (issue #100):\n * `time(tf)`, `time_close(tf)`, `timeframe.change(tf)`, `time_tradingday`, `session.*`.\n * PineScript rules:\n *\n * - intraday periods are aligned on the start of the session period of the trading day: 240 minutes from\n * 09:30 gives 09:30, 13:30; `time(tf)` is the start of the period that contains the bar open\n * - \"D\" is the session start of the trading day; \"W\" / \"M\" the session start of the first trading day\n * of the week (Monday to Sunday) / month\n * - intraday closes are cut at the end of the session period; the \"D\" close is the session end\n * - on intraday charts the \"W\" / \"M\" (and \"nD\") close is the session start of the next period's first\n * trading day; on D / W / M charts it is the session end of the last trading day of the week / month\n * - on D / W / M charts, intraday timeframes give the chart bar's time, and the end of the session\n * period that contains the bar open as close\n * - `time_tradingday` is 00:00 UTC of the trading day (on W / M charts, of the last trading day)\n *\n * Multipliers above one day (issue #100 follow-up): periods restart each\n * calendar year. \"nD\" groups n trading days from the first trading day of the year; \"nW\" groups n weeks from\n * the first week whose Monday is in the year; \"nM\" groups n months from January. The last group of a year\n * can be shorter.\n */\n\nimport { info, in_seconds, type TimeframeInfo } from '../timeframe/index.js';\nimport {\n TradingCalendar,\n addDays,\n dateUtcMs,\n monthEnd,\n monthStart,\n weekStart,\n type DateNum,\n} from './calendar.js';\n\ntype Unit = 'D' | 'W' | 'M';\n\nfunction unitOf(tf: TimeframeInfo): { unit: Unit; n: number } {\n return { unit: tf.isdaily ? 'D' : tf.isweekly ? 'W' : 'M', n: tf.multiplier };\n}\n\nconst yearOf = (date: DateNum): number => Math.floor(date / 10000);\n\n/** Monday of the first week of `year` (the first Monday on or after 1 January). */\nconst firstMonday = (year: number): DateNum => {\n const jan1 = year * 10000 + 101;\n const monday = weekStart(jan1);\n return monday === jan1 ? jan1 : addDays(monday, 7);\n};\n\n/** Values of the bars of one chart, for a trading calendar. */\nexport class SessionBars {\n readonly chart: TimeframeInfo;\n\n private readonly yearDays = new Map<number, DateNum[]>();\n\n constructor(\n readonly calendar: TradingCalendar,\n chartTimeframe: string\n ) {\n this.chart = info(chartTimeframe);\n }\n\n /** Trading days of a calendar year, in order. */\n private tradingDaysOf(year: number): DateNum[] {\n let days = this.yearDays.get(year);\n if (!days) {\n days = [];\n for (let date = year * 10000 + 101; yearOf(date) === year; date = addDays(date, 1)) {\n if (this.calendar.isTradingDay(date)) days.push(date);\n }\n this.yearDays.set(year, days);\n }\n return days;\n }\n\n /**\n * Dates [from, to] of the `n`-`unit` period that contains `date` (for \"D\": its first and last trading days).\n * Periods restart each calendar year.\n */\n private periodRange(date: DateNum, unit: Unit, n: number): [DateNum, DateNum] {\n if (unit === 'D') {\n if (n === 1) return [date, date];\n const days = this.tradingDaysOf(yearOf(date));\n let k = days.findIndex((d) => d >= date);\n if (k === -1) return this.periodRange(yearOf(date) * 10000 + 10000 + 101, unit, n);\n k -= k % n;\n return [days[k]!, days[Math.min(k + n, days.length) - 1]!];\n }\n if (unit === 'W') {\n const monday = weekStart(date);\n const year = yearOf(monday);\n const first = firstMonday(year);\n const next = firstMonday(year + 1);\n const k = Math.round((dateUtcMs(monday) - dateUtcMs(first)) / (7 * 86_400_000));\n const from = addDays(first, (k - (k % n)) * 7);\n const to = addDays(from, n * 7 - 1);\n return [from, to < next ? to : addDays(next, -1)];\n }\n const year = yearOf(date);\n const month = Math.floor(date / 100) % 100;\n const firstMonth = month - ((month - 1) % n);\n const lastMonth = Math.min(firstMonth + n - 1, 12);\n return [monthStart(year * 10000 + firstMonth * 100 + 1), monthEnd(year * 10000 + lastMonth * 100 + 1)];\n }\n\n private dayStart(date: DateNum): number {\n return this.calendar.periods(date)[0]![0];\n }\n\n private dayEnd(date: DateNum): number {\n const p = this.calendar.periods(date);\n return p[p.length - 1]![1];\n }\n\n /** PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. */\n timeOf(time: number, timeframe: string): number {\n const tf = info(timeframe);\n const at = this.calendar.locate(time);\n if (!at) return NaN;\n if (tf.isintraday) {\n if (this.chart.isdwm) return time;\n const [start] = this.calendar.periods(at.date)[at.index]!;\n const ms = in_seconds(tf.period) * 1000;\n return start + Math.floor((time - start) / ms) * ms;\n }\n const { unit, n } = unitOf(tf);\n const [from, to] = this.periodRange(at.date, unit, n);\n const first = this.calendar.firstTradingDay(from, to);\n return first === null ? NaN : this.dayStart(first);\n }\n\n /** PineScript `time_close(tf)` for a bar that opens at `time`. */\n closeOf(time: number, timeframe: string): number {\n const tf = info(timeframe);\n const at = this.calendar.locate(time);\n if (!at) return NaN;\n if (tf.isintraday) {\n const [, end] = this.calendar.periods(at.date)[at.index]!;\n // on D / W / M charts: the end of the session period that contains the bar open\n if (this.chart.isdwm) return end;\n return Math.min(this.timeOf(time, timeframe) + in_seconds(tf.period) * 1000, end);\n }\n const { unit, n } = unitOf(tf);\n const [from, to] = this.periodRange(at.date, unit, n);\n // intraday charts: the start of the next period, except \"1D\" (the session end)\n if (this.chart.isintraday && (unit !== 'D' || n > 1)) {\n const [nextFrom, nextTo] = this.periodRange(addDays(to, 1), unit, n);\n const first = this.calendar.firstTradingDay(nextFrom, addDays(nextTo, 31));\n return first === null ? NaN : this.dayStart(first);\n }\n const last = this.calendar.lastTradingDay(from, to);\n return last === null ? NaN : this.dayEnd(last);\n }\n\n /** Trading day of a bar: on D / W / M charts the last trading day of the chart period. */\n tradingDay(time: number): DateNum | null {\n const at = this.calendar.locate(time);\n if (!at) return null;\n if (this.chart.isdwm) {\n const { unit, n } = unitOf(this.chart);\n const [from, to] = this.periodRange(at.date, unit, n);\n return this.calendar.lastTradingDay(from, to);\n }\n return at.date;\n }\n\n /**\n * End of the `timeframe` period that contains `time`: the intraday close, the session end of the day,\n * or the session end of the last trading day of the period. Used to know whether the last loaded\n * bar completes a higher-timeframe period (request.security).\n */\n periodEnd(time: number, timeframe: string): number {\n const tf = info(timeframe);\n const at = this.calendar.locate(time);\n if (!at) return NaN;\n if (tf.isintraday) {\n const [, end] = this.calendar.periods(at.date)[at.index]!;\n return Math.min(this.timeOf(time, timeframe) + in_seconds(tf.period) * 1000, end);\n }\n const { unit, n } = unitOf(tf);\n const [from, to] = this.periodRange(at.date, unit, n);\n const last = this.calendar.lastTradingDay(from, to);\n return last === null ? NaN : this.dayEnd(last);\n }\n\n /** PineScript `time_tradingday`: 00:00 UTC of the trading day. */\n tradingDayTime(time: number): number {\n const day = this.tradingDay(time);\n return day === null ? NaN : dateUtcMs(day);\n }\n\n /** True when the bar at `time` is inside the session. */\n inside(time: number): boolean {\n return this.calendar.locate(time)?.inside ?? false;\n }\n\n /** Session start and end of the trading day of `time`, or null. */\n dayBounds(time: number): { date: DateNum; start: number; end: number } | null {\n const at = this.calendar.locate(time);\n return at ? { date: at.date, start: this.dayStart(at.date), end: this.dayEnd(at.date) } : null;\n }\n}\n\n/** PineScript `session.*` flags of each bar. */\nexport interface SessionFlags {\n isfirstbar: boolean[];\n islastbar: boolean[];\n ismarket: boolean[];\n ispremarket: boolean[];\n ispostmarket: boolean[];\n isfirstbar_regular: boolean[];\n islastbar_regular: boolean[];\n}\n\n/**\n * `session.*` flags for bars that open at `times` (in order). `regular` gives the regular trading\n * hours when the bars include extended hours. PineScript rules: flags use the bar open\n * time; first and last bars are per session period (a trading day can have two periods, e.g. after a\n * futures holiday); pre / post market compare with the regular hours of the same trading day; on\n * D / W / M charts every bar is its own session (first, last, market, first and last regular).\n */\nexport function sessionFlags(times: number[], bars: SessionBars, regular: SessionBars): SessionFlags {\n const n = times.length;\n const flag = () => new Array<boolean>(n).fill(false);\n const out: SessionFlags = {\n isfirstbar: flag(), islastbar: flag(), ismarket: flag(), ispremarket: flag(), ispostmarket: flag(),\n isfirstbar_regular: flag(), islastbar_regular: flag(),\n };\n const chart = bars.chart;\n let previousPeriod = '';\n let previousRegular = '';\n for (let i = 0; i < n; i++) {\n const t = times[i]!;\n if (chart.isdwm) {\n for (const k of ['isfirstbar', 'islastbar', 'ismarket', 'isfirstbar_regular', 'islastbar_regular'] as const) out[k][i] = true;\n continue;\n }\n const at = bars.calendar.locate(t);\n if (!at) {\n previousPeriod = previousRegular = '';\n continue;\n }\n const close = bars.closeOf(t, chart.period);\n const [, end] = bars.calendar.periods(at.date)[at.index]!;\n const period = `${at.date}/${at.index}`;\n out.isfirstbar[i] = period !== previousPeriod;\n out.islastbar[i] = close >= end;\n const regularPeriods = regular.calendar.periods(at.date);\n const r = regularPeriods.findIndex(([s, e]) => s <= t && t < e);\n const market = r !== -1;\n out.ismarket[i] = market;\n if (!market && regularPeriods.length) {\n out.ispremarket[i] = t < regularPeriods[0]![0];\n out.ispostmarket[i] = t >= regularPeriods[regularPeriods.length - 1]![1];\n }\n const regularPeriod = market ? `${at.date}/${r}` : '';\n out.isfirstbar_regular[i] = market && regularPeriod !== previousRegular;\n out.islastbar_regular[i] = market && close >= regularPeriods[r]![1];\n previousPeriod = period;\n previousRegular = regularPeriod;\n }\n return out;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBA,uBAAqD;AACrD,sBAQO;AAIP,SAAS,OAAO,IAA8C;AAC5D,SAAO,EAAE,MAAM,GAAG,UAAU,MAAM,GAAG,WAAW,MAAM,KAAK,GAAG,GAAG,WAAW;AAC9E;AAEA,MAAM,SAAS,CAAC,SAA0B,KAAK,MAAM,OAAO,GAAK;AAGjE,MAAM,cAAc,CAAC,SAA0B;AAC7C,QAAM,OAAO,OAAO,MAAQ;AAC5B,QAAM,aAAS,2BAAU,IAAI;AAC7B,SAAO,WAAW,OAAO,WAAO,yBAAQ,QAAQ,CAAC;AACnD;AAGO,MAAM,YAAY;AAAA,EAKvB,YACW,UACT,gBACA;AAFS;AAGT,SAAK,YAAQ,uBAAK,cAAc;AAAA,EAClC;AAAA,EAJW;AAAA,EALF;AAAA,EAEQ,WAAW,oBAAI,IAAuB;AAAA;AAAA,EAU/C,cAAc,MAAyB;AAC7C,QAAI,OAAO,KAAK,SAAS,IAAI,IAAI;AACjC,QAAI,CAAC,MAAM;AACT,aAAO,CAAC;AACR,eAAS,OAAO,OAAO,MAAQ,KAAK,OAAO,IAAI,MAAM,MAAM,WAAO,yBAAQ,MAAM,CAAC,GAAG;AAClF,YAAI,KAAK,SAAS,aAAa,IAAI,EAAG,MAAK,KAAK,IAAI;AAAA,MACtD;AACA,WAAK,SAAS,IAAI,MAAM,IAAI;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAY,MAAe,MAAY,GAA+B;AAC5E,QAAI,SAAS,KAAK;AAChB,UAAI,MAAM,EAAG,QAAO,CAAC,MAAM,IAAI;AAC/B,YAAM,OAAO,KAAK,cAAc,OAAO,IAAI,CAAC;AAC5C,UAAI,IAAI,KAAK,UAAU,CAAC,MAAM,KAAK,IAAI;AACvC,UAAI,MAAM,GAAI,QAAO,KAAK,YAAY,OAAO,IAAI,IAAI,MAAQ,MAAQ,KAAK,MAAM,CAAC;AACjF,WAAK,IAAI;AACT,aAAO,CAAC,KAAK,CAAC,GAAI,KAAK,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,CAAE;AAAA,IAC3D;AACA,QAAI,SAAS,KAAK;AAChB,YAAM,aAAS,2BAAU,IAAI;AAC7B,YAAMA,QAAO,OAAO,MAAM;AAC1B,YAAM,QAAQ,YAAYA,KAAI;AAC9B,YAAM,OAAO,YAAYA,QAAO,CAAC;AACjC,YAAM,IAAI,KAAK,WAAO,2BAAU,MAAM,QAAI,2BAAU,KAAK,MAAM,IAAI,MAAW;AAC9E,YAAM,WAAO,yBAAQ,QAAQ,IAAK,IAAI,KAAM,CAAC;AAC7C,YAAM,SAAK,yBAAQ,MAAM,IAAI,IAAI,CAAC;AAClC,aAAO,CAAC,MAAM,KAAK,OAAO,SAAK,yBAAQ,MAAM,EAAE,CAAC;AAAA,IAClD;AACA,UAAM,OAAO,OAAO,IAAI;AACxB,UAAM,QAAQ,KAAK,MAAM,OAAO,GAAG,IAAI;AACvC,UAAM,aAAa,SAAU,QAAQ,KAAK;AAC1C,UAAM,YAAY,KAAK,IAAI,aAAa,IAAI,GAAG,EAAE;AACjD,WAAO,KAAC,4BAAW,OAAO,MAAQ,aAAa,MAAM,CAAC,OAAG,0BAAS,OAAO,MAAQ,YAAY,MAAM,CAAC,CAAC;AAAA,EACvG;AAAA,EAEQ,SAAS,MAAuB;AACtC,WAAO,KAAK,SAAS,QAAQ,IAAI,EAAE,CAAC,EAAG,CAAC;AAAA,EAC1C;AAAA,EAEQ,OAAO,MAAuB;AACpC,UAAM,IAAI,KAAK,SAAS,QAAQ,IAAI;AACpC,WAAO,EAAE,EAAE,SAAS,CAAC,EAAG,CAAC;AAAA,EAC3B;AAAA;AAAA,
|
|
6
|
-
"names": ["year"]
|
|
4
|
+
"sourcesContent": ["/**\n * PineScript values that depend on the chart timeframe and the symbol session (issue #100):\n * `time(tf)`, `time_close(tf)`, `timeframe.change(tf)`, `time_tradingday`, `session.*`.\n * PineScript rules:\n *\n * - intraday periods are aligned on the start of the session period of the trading day: 240 minutes from\n * 09:30 gives 09:30, 13:30; `time(tf)` is the start of the period that contains the bar open\n * - \"D\" is the session start of the trading day; \"W\" / \"M\" the session start of the first trading day\n * of the week (Monday to Sunday) / month\n * - intraday closes are cut at the end of the session period; the \"D\" close is the session end\n * - on intraday charts the \"W\" / \"M\" (and \"nD\") close is the session start of the next period's first\n * trading day; on D / W / M charts it is the session end of the last trading day of the week / month\n * - on D / W / M charts, intraday timeframes give the chart bar's time, and the end of the session\n * period that contains the bar open as close\n * - `time_tradingday` is 00:00 UTC of the trading day (on W / M charts, of the last trading day)\n *\n * Multipliers above one day (issue #100 follow-up): periods restart each\n * calendar year. \"nD\" groups n trading days from the first trading day of the year; \"nW\" groups n weeks from\n * the first week whose Monday is in the year; \"nM\" groups n months from January. The last group of a year\n * can be shorter.\n */\n\nimport { info, in_seconds, type TimeframeInfo } from '../timeframe/index.js';\nimport {\n TradingCalendar,\n addDays,\n dateUtcMs,\n monthEnd,\n monthStart,\n weekStart,\n type DateNum,\n} from './calendar.js';\n\ntype Unit = 'D' | 'W' | 'M';\n\nfunction unitOf(tf: TimeframeInfo): { unit: Unit; n: number } {\n return { unit: tf.isdaily ? 'D' : tf.isweekly ? 'W' : 'M', n: tf.multiplier };\n}\n\nconst yearOf = (date: DateNum): number => Math.floor(date / 10000);\n\n/** Monday of the first week of `year` (the first Monday on or after 1 January). */\nconst firstMonday = (year: number): DateNum => {\n const jan1 = year * 10000 + 101;\n const monday = weekStart(jan1);\n return monday === jan1 ? jan1 : addDays(monday, 7);\n};\n\n/** Values of the bars of one chart, for a trading calendar. */\nexport class SessionBars {\n readonly chart: TimeframeInfo;\n\n private readonly yearDays = new Map<number, DateNum[]>();\n\n constructor(\n readonly calendar: TradingCalendar,\n chartTimeframe: string\n ) {\n this.chart = info(chartTimeframe);\n }\n\n /** Trading days of a calendar year, in order. */\n private tradingDaysOf(year: number): DateNum[] {\n let days = this.yearDays.get(year);\n if (!days) {\n days = [];\n for (let date = year * 10000 + 101; yearOf(date) === year; date = addDays(date, 1)) {\n if (this.calendar.isTradingDay(date)) days.push(date);\n }\n this.yearDays.set(year, days);\n }\n return days;\n }\n\n /**\n * Dates [from, to] of the `n`-`unit` period that contains `date` (for \"D\": its first and last trading days).\n * Periods restart each calendar year.\n */\n private periodRange(date: DateNum, unit: Unit, n: number): [DateNum, DateNum] {\n if (unit === 'D') {\n if (n === 1) return [date, date];\n const days = this.tradingDaysOf(yearOf(date));\n let k = days.findIndex((d) => d >= date);\n if (k === -1) return this.periodRange(yearOf(date) * 10000 + 10000 + 101, unit, n);\n k -= k % n;\n return [days[k]!, days[Math.min(k + n, days.length) - 1]!];\n }\n if (unit === 'W') {\n const monday = weekStart(date);\n const year = yearOf(monday);\n const first = firstMonday(year);\n const next = firstMonday(year + 1);\n const k = Math.round((dateUtcMs(monday) - dateUtcMs(first)) / (7 * 86_400_000));\n const from = addDays(first, (k - (k % n)) * 7);\n const to = addDays(from, n * 7 - 1);\n return [from, to < next ? to : addDays(next, -1)];\n }\n const year = yearOf(date);\n const month = Math.floor(date / 100) % 100;\n const firstMonth = month - ((month - 1) % n);\n const lastMonth = Math.min(firstMonth + n - 1, 12);\n return [monthStart(year * 10000 + firstMonth * 100 + 1), monthEnd(year * 10000 + lastMonth * 100 + 1)];\n }\n\n private dayStart(date: DateNum): number {\n return this.calendar.periods(date)[0]![0];\n }\n\n private dayEnd(date: DateNum): number {\n const p = this.calendar.periods(date);\n return p[p.length - 1]![1];\n }\n\n /**\n * PineScript `time(tf)` for a bar that opens at `time`; NaN when no trading day is found. An intraday bar that\n * opens after an early close has no day: `time(\"D\")` is NaN on it (measured; `time(\"W\")` keeps its value).\n */\n timeOf(time: number, timeframe: string): number {\n const tf = info(timeframe);\n const at = this.calendar.locate(time);\n if (!at) return NaN;\n if (tf.isintraday) {\n if (this.chart.isdwm) return time;\n const [start] = this.calendar.periods(at.date)[at.index]!;\n const ms = in_seconds(tf.period) * 1000;\n return start + Math.floor((time - start) / ms) * ms;\n }\n const { unit, n } = unitOf(tf);\n if (unit === 'D' && n === 1 && this.stray(time)) return NaN;\n const [from, to] = this.periodRange(at.date, unit, n);\n const first = this.calendar.firstTradingDay(from, to);\n return first === null ? NaN : this.dayStart(first);\n }\n\n /** PineScript `time_close(tf)` for a bar that opens at `time`. */\n closeOf(time: number, timeframe: string): number {\n const tf = info(timeframe);\n const at = this.calendar.locate(time);\n if (!at) return NaN;\n if (tf.isintraday) {\n const [, end] = this.calendar.periods(at.date)[at.index]!;\n // on D / W / M charts: the end of the session period that contains the bar open\n if (this.chart.isdwm) return end;\n return Math.min(this.timeOf(time, timeframe) + in_seconds(tf.period) * 1000, end);\n }\n const { unit, n } = unitOf(tf);\n if (unit === 'D' && n === 1 && this.stray(time)) return NaN;\n const [from, to] = this.periodRange(at.date, unit, n);\n // intraday charts: the start of the next period, except \"1D\" (the session end)\n if (this.chart.isintraday && (unit !== 'D' || n > 1)) {\n const [nextFrom, nextTo] = this.periodRange(addDays(to, 1), unit, n);\n const first = this.calendar.firstTradingDay(nextFrom, addDays(nextTo, 31));\n return first === null ? NaN : this.dayStart(first);\n }\n const last = this.calendar.lastTradingDay(from, to);\n return last === null ? NaN : this.dayEnd(last);\n }\n\n /** Trading day of a bar: on D / W / M charts the last trading day of the chart period. */\n tradingDay(time: number): DateNum | null {\n const at = this.calendar.locate(time);\n if (!at || this.stray(time)) return null;\n if (this.chart.isdwm) {\n const { unit, n } = unitOf(this.chart);\n const [from, to] = this.periodRange(at.date, unit, n);\n return this.calendar.lastTradingDay(from, to);\n }\n return at.date;\n }\n\n /**\n * End of the `timeframe` period that contains `time`: the intraday close, the session end of the day,\n * or the session end of the last trading day of the period. Used to know whether the last loaded\n * bar completes a higher-timeframe period (request.security).\n */\n periodEnd(time: number, timeframe: string): number {\n const tf = info(timeframe);\n const at = this.calendar.locate(time);\n if (!at) return NaN;\n if (tf.isintraday) {\n const [, end] = this.calendar.periods(at.date)[at.index]!;\n return Math.min(this.timeOf(time, timeframe) + in_seconds(tf.period) * 1000, end);\n }\n const { unit, n } = unitOf(tf);\n const [from, to] = this.periodRange(at.date, unit, n);\n const last = this.calendar.lastTradingDay(from, to);\n return last === null ? NaN : this.dayEnd(last);\n }\n\n /** PineScript `time_tradingday`: 00:00 UTC of the trading day. */\n tradingDayTime(time: number): number {\n const day = this.tradingDay(time);\n return day === null ? NaN : dateUtcMs(day);\n }\n\n /**\n * Open time of the bar after the bar that opens at `time`, on the trading calendar: the next bar of the session\n * period, else the start of the next period or trading day; on D / W / M charts the session start of the first\n * trading day of the next chart period. NaN when none is found within 400 days.\n */\n nextBarTime(time: number): number {\n const at = this.calendar.locate(time);\n if (!at) return NaN;\n if (!this.chart.isdwm) {\n const periods = this.calendar.periods(at.date);\n const [start, end] = periods[at.index]!;\n const ms = in_seconds(this.chart.period) * 1000;\n const next = start + (Math.floor((time - start) / ms) + 1) * ms;\n if (at.inside && next < end) return next;\n if (at.index + 1 < periods.length) return periods[at.index + 1]![0];\n const day = this.calendar.firstTradingDay(addDays(at.date, 1), addDays(at.date, 400));\n return day === null ? NaN : this.dayStart(day);\n }\n const { unit, n } = unitOf(this.chart);\n const [, to] = this.periodRange(at.date, unit, n);\n const day = this.calendar.firstTradingDay(addDays(to, 1), addDays(to, 400));\n return day === null ? NaN : this.dayStart(day);\n }\n\n /** An intraday bar that opens after an early close of its day (see TradingCalendar.afterEarlyClose). */\n stray(time: number): boolean {\n return !this.chart.isdwm && this.calendar.afterEarlyClose(time);\n }\n\n /** True when the bar at `time` is inside the session. */\n inside(time: number): boolean {\n return this.calendar.locate(time)?.inside ?? false;\n }\n\n /** Session start and end of the trading day of `time`, or null. */\n dayBounds(time: number): { date: DateNum; start: number; end: number } | null {\n const at = this.calendar.locate(time);\n return at ? { date: at.date, start: this.dayStart(at.date), end: this.dayEnd(at.date) } : null;\n }\n}\n\n/** PineScript `session.*` flags of each bar. */\nexport interface SessionFlags {\n isfirstbar: boolean[];\n islastbar: boolean[];\n ismarket: boolean[];\n ispremarket: boolean[];\n ispostmarket: boolean[];\n isfirstbar_regular: boolean[];\n islastbar_regular: boolean[];\n}\n\n/**\n * `session.*` flags for bars that open at `times` (in order). `regular` gives the regular trading\n * hours when the bars include extended hours. PineScript rules: flags use the bar open\n * time; first and last bars are per session period (a trading day can have two periods, e.g. after a\n * futures holiday); pre / post market compare with the regular hours of the same trading day; on\n * D / W / M charts every bar is its own session (first, last, market, first and last regular).\n * The first loaded bar is a first bar only when no earlier bar of its period can exist (it opens less than one chart\n * period after the period start). A bar that opens after an early close is in the market hours (the usual hours of\n * its day) but is no last bar.\n */\nexport function sessionFlags(times: number[], bars: SessionBars, regular: SessionBars): SessionFlags {\n const n = times.length;\n const flag = () => new Array<boolean>(n).fill(false);\n const out: SessionFlags = {\n isfirstbar: flag(), islastbar: flag(), ismarket: flag(), ispremarket: flag(), ispostmarket: flag(),\n isfirstbar_regular: flag(), islastbar_regular: flag(),\n };\n const chart = bars.chart;\n let previousPeriod = '';\n let previousRegular = '';\n for (let i = 0; i < n; i++) {\n const t = times[i]!;\n if (chart.isdwm) {\n for (const k of ['isfirstbar', 'islastbar', 'ismarket', 'isfirstbar_regular', 'islastbar_regular'] as const) out[k][i] = true;\n continue;\n }\n const at = bars.calendar.locate(t);\n if (!at) {\n previousPeriod = previousRegular = '';\n continue;\n }\n const close = bars.closeOf(t, chart.period);\n const [, end] = bars.calendar.periods(at.date)[at.index]!;\n const period = `${at.date}/${at.index}`;\n const [start] = bars.calendar.periods(at.date)[at.index]!;\n const barMs = in_seconds(chart.period) * 1000;\n // the first loaded bar: a first bar only when no earlier bar of the period can exist\n out.isfirstbar[i] = i === 0 ? t - start < barMs : period !== previousPeriod;\n const stray = bars.stray(t);\n out.islastbar[i] = !stray && close >= end;\n const regularPeriods = regular.calendar.periods(at.date);\n const r = regularPeriods.findIndex(([s, e]) => s <= t && t < e);\n const market = r !== -1;\n out.ismarket[i] = market || regular.stray(t);\n if (!market && regularPeriods.length) {\n out.ispremarket[i] = t < regularPeriods[0]![0];\n out.ispostmarket[i] = t >= regularPeriods[regularPeriods.length - 1]![1];\n }\n const regularPeriod = market ? `${at.date}/${r}` : '';\n out.isfirstbar_regular[i] = market && (i === 0 ? t - regularPeriods[r]![0] < barMs : regularPeriod !== previousRegular);\n out.islastbar_regular[i] = market && close >= regularPeriods[r]![1];\n previousPeriod = period;\n previousRegular = regularPeriod;\n }\n return out;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["year", "day"]
|
|
7
7
|
}
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* - periods "HHMM-HHMM", several separated by commas; "24x7" is the whole day, every day
|
|
7
7
|
* - a period whose end is not after its start ("1700-1600", "1700-1700") starts the day before the
|
|
8
8
|
* trading day: the session that opens Sunday 17:00 is Monday's
|
|
9
|
-
* - "HHMMF<n>" moves a time <n> days before the trading day ("1700F2-1200F1")
|
|
9
|
+
* - "HHMMF<n>" moves a time <n> days before the trading day ("1700F2-1200F1"); "F" alone is one day ("1700F-1900":
|
|
10
|
+
* 17:00 of the day before to 19:00)
|
|
11
|
+
* - several sections joined by "|", each with its own days ("1900-1900:3456|1700F-1900:2": Monday opens on Sunday
|
|
12
|
+
* 17:00, the other days at 19:00 of the day before)
|
|
10
13
|
* - optional days after ":" (1 = Sunday ... 7 = Saturday); without days a symbol session trades Monday to
|
|
11
14
|
* Friday ("24x7": every day), and a PineScript session string follows PineScript's rules (one period:
|
|
12
15
|
* every day; several periods: Monday to Friday)
|
|
@@ -47,6 +50,11 @@ export declare class TradingCalendar {
|
|
|
47
50
|
/** Session periods [start, end) of the trading day `date`; empty when the day has no session. */
|
|
48
51
|
periods(date: DateNum): Array<[number, number]>;
|
|
49
52
|
isTradingDay(date: DateNum): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* True for a bar that opens after the session of its day was cut by a correction (an early close) but inside the
|
|
55
|
+
* usual hours of that day: PineScript has no period for such a bar (`time("D")` is na on it).
|
|
56
|
+
*/
|
|
57
|
+
afterEarlyClose(time: number): boolean;
|
|
50
58
|
/**
|
|
51
59
|
* Trading day and period of a time: the period that contains it, else the last period that started
|
|
52
60
|
* before it. Null when no trading day is found near the time.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/session/calendar.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/session/calendar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,2GAA2G;IAC3G,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uGAAuG;IACvG,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC9B;AAED,wBAAwB;AACxB,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAoB7B,eAAO,MAAM,SAAS,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,OAAkC,CAAC;AAM/F,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,GAAG,MAAM,KAAG,OAAgD,CAAC;AACpG,kCAAkC;AAClC,eAAO,MAAM,SAAS,GAAI,MAAM,OAAO,KAAG,MAA+C,CAAC;AAC1F,mEAAmE;AACnE,eAAO,MAAM,SAAS,SATD,OAAO,KAAG,MASD,CAAC;AAiE/B,4EAA4E;AAC5E,qBAAa,eAAe;IAYxB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAX3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuC;IACnE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+C;IAErE;;;;OAIG;gBAEQ,QAAQ,EAAE,MAAM,EACzB,IAAI,EAAE,WAAW,EACjB,IAAI,GAAE,QAAQ,GAAG,MAAiB;IAmBpC,iGAAiG;IACjG,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAuB/C,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAIpC;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAetC;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAoB9E,wDAAwD;IACxD,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI;IAK3D,uDAAuD;IACvD,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI;CAI3D;AAED,iEAAiE;AACjE,eAAO,MAAM,SAAS,GAAI,MAAM,OAAO,KAAG,OAAsD,CAAC;AACjG,wCAAwC;AACxC,eAAO,MAAM,UAAU,GAAI,MAAM,OAAO,KAAG,OAA2C,CAAC;AACvF,uCAAuC;AACvC,eAAO,MAAM,QAAQ,GAAI,MAAM,OAAO,KAAG,OAAiE,CAAC"}
|
|
@@ -40,9 +40,9 @@ const addDays = (date, n) => fromUtc(utcOf(date) + n * 864e5);
|
|
|
40
40
|
const weekdayOf = (date) => new Date(utcOf(date)).getUTCDay() + 1;
|
|
41
41
|
const dateUtcMs = utcOf;
|
|
42
42
|
function parseTime(token, spec) {
|
|
43
|
-
const m = /^(\d{2})(\d{2})(?:F(\d
|
|
43
|
+
const m = /^(\d{2})(\d{2})(?:F(\d*))?$/.exec(token.trim());
|
|
44
44
|
if (!m) throw new SyntaxError(`Invalid session "${spec}"`);
|
|
45
|
-
return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === void 0 ? void 0 : Number(m[3]) };
|
|
45
|
+
return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === void 0 ? void 0 : Number(m[3] || 1) };
|
|
46
46
|
}
|
|
47
47
|
function parsePeriods(text, spec) {
|
|
48
48
|
if (text.trim() === "24x7") return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];
|
|
@@ -73,6 +73,16 @@ function pineVersion(text, from) {
|
|
|
73
73
|
}
|
|
74
74
|
function parseVersion(text, from, spec, rule) {
|
|
75
75
|
if (rule === "pine") return pineVersion(text, from);
|
|
76
|
+
if (text.includes("|")) {
|
|
77
|
+
const byDay = Array.from({ length: 8 }, () => []);
|
|
78
|
+
for (const section of text.split("|").filter((part) => part.trim())) {
|
|
79
|
+
const one = parseVersion(section, from, spec, rule);
|
|
80
|
+
one.days.forEach((on, d) => {
|
|
81
|
+
if (on) byDay[d] = one.periods;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };
|
|
85
|
+
}
|
|
76
86
|
const [periodText = "", dayText] = text.trim().split(":");
|
|
77
87
|
const periods = parsePeriods(periodText, spec);
|
|
78
88
|
const allDays = periodText.trim() === "24x7";
|
|
@@ -137,6 +147,22 @@ class TradingCalendar {
|
|
|
137
147
|
isTradingDay(date) {
|
|
138
148
|
return this.periods(date).length > 0;
|
|
139
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* True for a bar that opens after the session of its day was cut by a correction (an early close) but inside the
|
|
152
|
+
* usual hours of that day: PineScript has no period for such a bar (`time("D")` is na on it).
|
|
153
|
+
*/
|
|
154
|
+
afterEarlyClose(time) {
|
|
155
|
+
const at = this.locate(time);
|
|
156
|
+
if (!at || at.inside || !this.corrections.get(at.date)) return false;
|
|
157
|
+
const version = [...this.versions].reverse().find((v) => v.from <= at.date);
|
|
158
|
+
const usual = version.byDay ? version.byDay[weekdayOf(at.date)] : version.days[weekdayOf(at.date)] ? version.periods : [];
|
|
159
|
+
const y = Math.floor(at.date / 1e4);
|
|
160
|
+
const m = Math.floor(at.date / 100) % 100;
|
|
161
|
+
const d = at.date % 100;
|
|
162
|
+
return usual.some(
|
|
163
|
+
(p) => (0, import_timezone.zonedToUnix)(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60) <= time && time < (0, import_timezone.zonedToUnix)(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
140
166
|
/**
|
|
141
167
|
* Trading day and period of a time: the period that contains it, else the last period that started
|
|
142
168
|
* before it. Null when no trading day is found near the time.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/session/calendar.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Trading calendar: the session periods of each trading day of a symbol, from the symbol session\n * information (issue #100).\n *\n * Session format (symbol session information, also PineScript session strings):\n * - periods \"HHMM-HHMM\", several separated by commas; \"24x7\" is the whole day, every day\n * - a period whose end is not after its start (\"1700-1600\", \"1700-1700\") starts the day before the\n * trading day: the session that opens Sunday 17:00 is Monday's\n * - \"HHMMF<n>\" moves a time <n> days before the trading day (\"1700F2-1200F1\")\n * - optional days after \":\" (1 = Sunday ... 7 = Saturday); without days a symbol session trades Monday to\n * Friday (\"24x7\": every day), and a PineScript session string follows PineScript's rules (one period:\n * every day; several periods: Monday to Friday)\n * - \"#YYYYMMDD/<session>\" changes the session from that date (\"0900-1746#20251201/0800-2200\")\n * - corrections (\"session-correction\"): \"<session>:<dates>;dayoff:<dates>\" replace the session of these\n * dates (early closes, days off); holidays (\"session_holidays\") are dates without session\n */\n\nimport { zonedFields, zonedToUnix } from '../time/timezone.js';\nimport { parsePineSession } from '../time/session.js';\n\n/** Session information of a symbol (hours, corrections, holidays). */\nexport interface SessionSpec {\n /** Session hours: \"0930-1600\", \"1700-1600\", \"24x7\", \"0400-2000\" ... */\n session: string;\n /** Exceptions by date (symbol info \"session-correction\"): \"0930-1300:20251128,20251224;dayoff:20250109\" */\n corrections?: string;\n /** Exchange holidays as \"YYYYMMDD\" (symbol info \"session_holidays\"), comma-separated or as an array */\n holidays?: string | string[];\n}\n\n/** Date as YYYYMMDD. */\nexport type DateNum = number;\n\ninterface Period {\n /** Minutes after midnight */\n start: number;\n end: number;\n /** Days before the trading day */\n startOffset: number;\n endOffset: number;\n}\n\ninterface Version {\n from: DateNum;\n periods: Period[];\n /** Index 1 (Sunday) to 7 (Saturday) */\n days: boolean[];\n /** PineScript session strings: the periods of each weekday (index 1 to 7), from the \"|\" sections */\n byDay?: Period[][];\n}\n\nexport const toDateNum = (y: number, m: number, d: number): DateNum => y * 10000 + m * 100 + d;\nconst utcOf = (date: DateNum): number => Date.UTC(Math.floor(date / 10000), Math.floor(date / 100) % 100 - 1, date % 100);\nconst fromUtc = (ms: number): DateNum => {\n const d = new Date(ms);\n return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());\n};\nexport const addDays = (date: DateNum, n: number): DateNum => fromUtc(utcOf(date) + n * 86_400_000);\n/** 1 = Sunday ... 7 = Saturday */\nexport const weekdayOf = (date: DateNum): number => new Date(utcOf(date)).getUTCDay() + 1;\n/** 00:00 UTC of the date, in ms (PineScript `time_tradingday`). */\nexport const dateUtcMs = utcOf;\n\nfunction parseTime(token: string, spec: string): { minutes: number; offset?: number } {\n const m = /^(\\d{2})(\\d{2})(?:F(\\d+))?$/.exec(token.trim());\n if (!m) throw new SyntaxError(`Invalid session \"${spec}\"`);\n return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === undefined ? undefined : Number(m[3]) };\n}\n\nfunction parsePeriods(text: string, spec: string): Period[] {\n if (text.trim() === '24x7') return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];\n return text.split(',').map((p) => {\n const [a = '', b = ''] = p.split('-');\n const s = parseTime(a, spec);\n const e = parseTime(b, spec);\n if (s.minutes === 0 && e.minutes === 0 && s.offset === undefined && e.offset === undefined) {\n return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };\n }\n return {\n start: s.minutes,\n end: e.minutes,\n startOffset: s.offset ?? (e.minutes <= s.minutes ? 1 : 0),\n endOffset: e.offset ?? 0,\n };\n });\n}\n\n/** A PineScript session string as periods per weekday (rules in time/session.ts). */\nfunction pineVersion(text: string, from: DateNum): Version {\n const ranges = parsePineSession(text);\n if (ranges === null) throw new SyntaxError(`Session \"${text}\" is the symbol session`);\n const byDay: Period[][] = Array.from({ length: 8 }, () => []);\n for (const { start, end, days } of ranges) {\n const period: Period =\n start === 0 && end === 0\n ? { start: 0, end: 1440, startOffset: 0, endOffset: 0 }\n : { start, end, startOffset: end <= start ? 1 : 0, endOffset: 0 };\n for (const d of days) byDay[d]!.push(period);\n }\n return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };\n}\n\nfunction parseVersion(text: string, from: DateNum, spec: string, rule: 'symbol' | 'pine'): Version {\n if (rule === 'pine') return pineVersion(text, from);\n const [periodText = '', dayText] = text.trim().split(':');\n const periods = parsePeriods(periodText, spec);\n const allDays = periodText.trim() === '24x7';\n const digits = dayText ?? (allDays ? '1234567' : '23456');\n if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in \"${spec}\"`);\n const days = new Array<boolean>(8).fill(false);\n for (const d of digits) days[Number(d)] = true;\n return { from, periods, days };\n}\n\n/** Session periods of every trading day of a symbol, as UNIX times (ms). */\nexport class TradingCalendar {\n private readonly versions: Version[];\n private readonly corrections = new Map<DateNum, Period[] | null>();\n private readonly holidays: Set<DateNum>;\n private readonly cache = new Map<DateNum, Array<[number, number]>>();\n\n /**\n * @param timezone - Time zone of the session hours (the exchange time zone)\n * @param spec - Session information (hours, corrections, holidays)\n * @param rule - Days without \":days\": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)\n */\n constructor(\n readonly timezone: string,\n spec: SessionSpec,\n rule: 'symbol' | 'pine' = 'symbol'\n ) {\n const [first = '', ...changes] = spec.session.split('#');\n this.versions = [parseVersion(first, 0, spec.session, rule)];\n for (const change of changes) {\n const m = /^(\\d{8})\\/(.+)$/.exec(change.trim());\n if (!m) throw new SyntaxError(`Invalid session change in \"${spec.session}\"`);\n this.versions.push(parseVersion(m[2]!, Number(m[1]), spec.session, rule));\n }\n for (const part of (spec.corrections ?? '').split(';').filter((p) => p.trim())) {\n const i = part.lastIndexOf(':');\n const text = part.slice(0, i).trim();\n const periods = text === 'dayoff' ? null : parsePeriods(text, part);\n for (const date of part.slice(i + 1).split(',')) this.corrections.set(Number(date), periods);\n }\n const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? '').split(',');\n this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));\n }\n\n /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */\n periods(date: DateNum): Array<[number, number]> {\n let out = this.cache.get(date);\n if (out) return out;\n let periods: Period[] = [];\n const correction = this.corrections.get(date);\n if (correction !== undefined) {\n periods = correction ?? [];\n } else if (!this.holidays.has(date)) {\n const version = [...this.versions].reverse().find((v) => v.from <= date)!;\n if (version.byDay) periods = version.byDay[weekdayOf(date)]!;\n else if (version.days[weekdayOf(date)]) periods = version.periods;\n }\n const y = Math.floor(date / 10000);\n const m = Math.floor(date / 100) % 100;\n const d = date % 100;\n out = periods.map((p) => [\n zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),\n zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60),\n ]);\n this.cache.set(date, out);\n return out;\n }\n\n isTradingDay(date: DateNum): boolean {\n return this.periods(date).length > 0;\n }\n\n /**\n * Trading day and period of a time: the period that contains it, else the last period that started\n * before it. Null when no trading day is found near the time.\n */\n locate(time: number): { date: DateNum; index: number; inside: boolean } | null {\n const f = zonedFields(time, this.timezone);\n const local = toDateNum(f.year, f.month, f.day);\n for (let k = 0; k <= 5; k++) {\n const date = addDays(local, k);\n const index = this.periods(date).findIndex(([s, e]) => s <= time && time < e);\n if (index !== -1) return { date, index, inside: true };\n }\n for (let k = 5; k >= -7; k--) {\n const date = addDays(local, k);\n const periods = this.periods(date);\n let index = -1;\n periods.forEach(([s], i) => {\n if (s <= time) index = i;\n });\n if (index !== -1) return { date, index, inside: false };\n }\n return null;\n }\n\n /** First trading day in [from, to] (dates), or null. */\n firstTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n\n /** Last trading day in [from, to] (dates), or null. */\n lastTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n}\n\n/** Monday of the week of `date` (weeks run Monday to Sunday). */\nexport const weekStart = (date: DateNum): DateNum => addDays(date, -((weekdayOf(date) + 5) % 7));\n/** First day of the month of `date`. */\nexport const monthStart = (date: DateNum): DateNum => Math.floor(date / 100) * 100 + 1;\n/** Last day of the month of `date`. */\nexport const monthEnd = (date: DateNum): DateNum => addDays(monthStart(addDays(monthStart(date), 32)), -1);\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["/**\n * Trading calendar: the session periods of each trading day of a symbol, from the symbol session\n * information (issue #100).\n *\n * Session format (symbol session information, also PineScript session strings):\n * - periods \"HHMM-HHMM\", several separated by commas; \"24x7\" is the whole day, every day\n * - a period whose end is not after its start (\"1700-1600\", \"1700-1700\") starts the day before the\n * trading day: the session that opens Sunday 17:00 is Monday's\n * - \"HHMMF<n>\" moves a time <n> days before the trading day (\"1700F2-1200F1\"); \"F\" alone is one day (\"1700F-1900\":\n * 17:00 of the day before to 19:00)\n * - several sections joined by \"|\", each with its own days (\"1900-1900:3456|1700F-1900:2\": Monday opens on Sunday\n * 17:00, the other days at 19:00 of the day before)\n * - optional days after \":\" (1 = Sunday ... 7 = Saturday); without days a symbol session trades Monday to\n * Friday (\"24x7\": every day), and a PineScript session string follows PineScript's rules (one period:\n * every day; several periods: Monday to Friday)\n * - \"#YYYYMMDD/<session>\" changes the session from that date (\"0900-1746#20251201/0800-2200\")\n * - corrections (\"session-correction\"): \"<session>:<dates>;dayoff:<dates>\" replace the session of these\n * dates (early closes, days off); holidays (\"session_holidays\") are dates without session\n */\n\nimport { zonedFields, zonedToUnix } from '../time/timezone.js';\nimport { parsePineSession } from '../time/session.js';\n\n/** Session information of a symbol (hours, corrections, holidays). */\nexport interface SessionSpec {\n /** Session hours: \"0930-1600\", \"1700-1600\", \"24x7\", \"0400-2000\" ... */\n session: string;\n /** Exceptions by date (symbol info \"session-correction\"): \"0930-1300:20251128,20251224;dayoff:20250109\" */\n corrections?: string;\n /** Exchange holidays as \"YYYYMMDD\" (symbol info \"session_holidays\"), comma-separated or as an array */\n holidays?: string | string[];\n}\n\n/** Date as YYYYMMDD. */\nexport type DateNum = number;\n\ninterface Period {\n /** Minutes after midnight */\n start: number;\n end: number;\n /** Days before the trading day */\n startOffset: number;\n endOffset: number;\n}\n\ninterface Version {\n from: DateNum;\n periods: Period[];\n /** Index 1 (Sunday) to 7 (Saturday) */\n days: boolean[];\n /** PineScript session strings: the periods of each weekday (index 1 to 7), from the \"|\" sections */\n byDay?: Period[][];\n}\n\nexport const toDateNum = (y: number, m: number, d: number): DateNum => y * 10000 + m * 100 + d;\nconst utcOf = (date: DateNum): number => Date.UTC(Math.floor(date / 10000), Math.floor(date / 100) % 100 - 1, date % 100);\nconst fromUtc = (ms: number): DateNum => {\n const d = new Date(ms);\n return toDateNum(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());\n};\nexport const addDays = (date: DateNum, n: number): DateNum => fromUtc(utcOf(date) + n * 86_400_000);\n/** 1 = Sunday ... 7 = Saturday */\nexport const weekdayOf = (date: DateNum): number => new Date(utcOf(date)).getUTCDay() + 1;\n/** 00:00 UTC of the date, in ms (PineScript `time_tradingday`). */\nexport const dateUtcMs = utcOf;\n\nfunction parseTime(token: string, spec: string): { minutes: number; offset?: number } {\n const m = /^(\\d{2})(\\d{2})(?:F(\\d*))?$/.exec(token.trim());\n if (!m) throw new SyntaxError(`Invalid session \"${spec}\"`);\n // \"F\" without a number is one day before the trading day\n return { minutes: Number(m[1]) * 60 + Number(m[2]), offset: m[3] === undefined ? undefined : Number(m[3] || 1) };\n}\n\nfunction parsePeriods(text: string, spec: string): Period[] {\n if (text.trim() === '24x7') return [{ start: 0, end: 1440, startOffset: 0, endOffset: 0 }];\n return text.split(',').map((p) => {\n const [a = '', b = ''] = p.split('-');\n const s = parseTime(a, spec);\n const e = parseTime(b, spec);\n if (s.minutes === 0 && e.minutes === 0 && s.offset === undefined && e.offset === undefined) {\n return { start: 0, end: 1440, startOffset: 0, endOffset: 0 };\n }\n return {\n start: s.minutes,\n end: e.minutes,\n startOffset: s.offset ?? (e.minutes <= s.minutes ? 1 : 0),\n endOffset: e.offset ?? 0,\n };\n });\n}\n\n/** A PineScript session string as periods per weekday (rules in time/session.ts). */\nfunction pineVersion(text: string, from: DateNum): Version {\n const ranges = parsePineSession(text);\n if (ranges === null) throw new SyntaxError(`Session \"${text}\" is the symbol session`);\n const byDay: Period[][] = Array.from({ length: 8 }, () => []);\n for (const { start, end, days } of ranges) {\n const period: Period =\n start === 0 && end === 0\n ? { start: 0, end: 1440, startOffset: 0, endOffset: 0 }\n : { start, end, startOffset: end <= start ? 1 : 0, endOffset: 0 };\n for (const d of days) byDay[d]!.push(period);\n }\n return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };\n}\n\nfunction parseVersion(text: string, from: DateNum, spec: string, rule: 'symbol' | 'pine'): Version {\n if (rule === 'pine') return pineVersion(text, from);\n if (text.includes('|')) {\n // sections with their own days\n const byDay: Period[][] = Array.from({ length: 8 }, () => []);\n for (const section of text.split('|').filter((part) => part.trim())) {\n const one = parseVersion(section, from, spec, rule);\n one.days.forEach((on, d) => {\n if (on) byDay[d] = one.periods;\n });\n }\n return { from, periods: [], days: byDay.map((p) => p.length > 0), byDay };\n }\n const [periodText = '', dayText] = text.trim().split(':');\n const periods = parsePeriods(periodText, spec);\n const allDays = periodText.trim() === '24x7';\n const digits = dayText ?? (allDays ? '1234567' : '23456');\n if (!/^[1-7]+$/.test(digits)) throw new SyntaxError(`Invalid session days in \"${spec}\"`);\n const days = new Array<boolean>(8).fill(false);\n for (const d of digits) days[Number(d)] = true;\n return { from, periods, days };\n}\n\n/** Session periods of every trading day of a symbol, as UNIX times (ms). */\nexport class TradingCalendar {\n private readonly versions: Version[];\n private readonly corrections = new Map<DateNum, Period[] | null>();\n private readonly holidays: Set<DateNum>;\n private readonly cache = new Map<DateNum, Array<[number, number]>>();\n\n /**\n * @param timezone - Time zone of the session hours (the exchange time zone)\n * @param spec - Session information (hours, corrections, holidays)\n * @param rule - Days without \":days\": 'symbol' (Monday to Friday) or 'pine' (PineScript session strings)\n */\n constructor(\n readonly timezone: string,\n spec: SessionSpec,\n rule: 'symbol' | 'pine' = 'symbol'\n ) {\n const [first = '', ...changes] = spec.session.split('#');\n this.versions = [parseVersion(first, 0, spec.session, rule)];\n for (const change of changes) {\n const m = /^(\\d{8})\\/(.+)$/.exec(change.trim());\n if (!m) throw new SyntaxError(`Invalid session change in \"${spec.session}\"`);\n this.versions.push(parseVersion(m[2]!, Number(m[1]), spec.session, rule));\n }\n for (const part of (spec.corrections ?? '').split(';').filter((p) => p.trim())) {\n const i = part.lastIndexOf(':');\n const text = part.slice(0, i).trim();\n const periods = text === 'dayoff' ? null : parsePeriods(text, part);\n for (const date of part.slice(i + 1).split(',')) this.corrections.set(Number(date), periods);\n }\n const holidays = Array.isArray(spec.holidays) ? spec.holidays : (spec.holidays ?? '').split(',');\n this.holidays = new Set(holidays.filter((h) => h.trim()).map(Number));\n }\n\n /** Session periods [start, end) of the trading day `date`; empty when the day has no session. */\n periods(date: DateNum): Array<[number, number]> {\n let out = this.cache.get(date);\n if (out) return out;\n let periods: Period[] = [];\n const correction = this.corrections.get(date);\n if (correction !== undefined) {\n periods = correction ?? [];\n } else if (!this.holidays.has(date)) {\n const version = [...this.versions].reverse().find((v) => v.from <= date)!;\n if (version.byDay) periods = version.byDay[weekdayOf(date)]!;\n else if (version.days[weekdayOf(date)]) periods = version.periods;\n }\n const y = Math.floor(date / 10000);\n const m = Math.floor(date / 100) % 100;\n const d = date % 100;\n out = periods.map((p) => [\n zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60),\n zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60),\n ]);\n this.cache.set(date, out);\n return out;\n }\n\n isTradingDay(date: DateNum): boolean {\n return this.periods(date).length > 0;\n }\n\n /**\n * True for a bar that opens after the session of its day was cut by a correction (an early close) but inside the\n * usual hours of that day: PineScript has no period for such a bar (`time(\"D\")` is na on it).\n */\n afterEarlyClose(time: number): boolean {\n const at = this.locate(time);\n if (!at || at.inside || !this.corrections.get(at.date)) return false;\n const version = [...this.versions].reverse().find((v) => v.from <= at.date)!;\n const usual = version.byDay ? version.byDay[weekdayOf(at.date)]! : version.days[weekdayOf(at.date)] ? version.periods : [];\n const y = Math.floor(at.date / 10000);\n const m = Math.floor(at.date / 100) % 100;\n const d = at.date % 100;\n return usual.some(\n (p) =>\n zonedToUnix(this.timezone, y, m, d - p.startOffset, Math.floor(p.start / 60), p.start % 60) <= time &&\n time < zonedToUnix(this.timezone, y, m, d - p.endOffset, Math.floor(p.end / 60), p.end % 60)\n );\n }\n\n /**\n * Trading day and period of a time: the period that contains it, else the last period that started\n * before it. Null when no trading day is found near the time.\n */\n locate(time: number): { date: DateNum; index: number; inside: boolean } | null {\n const f = zonedFields(time, this.timezone);\n const local = toDateNum(f.year, f.month, f.day);\n for (let k = 0; k <= 5; k++) {\n const date = addDays(local, k);\n const index = this.periods(date).findIndex(([s, e]) => s <= time && time < e);\n if (index !== -1) return { date, index, inside: true };\n }\n for (let k = 5; k >= -7; k--) {\n const date = addDays(local, k);\n const periods = this.periods(date);\n let index = -1;\n periods.forEach(([s], i) => {\n if (s <= time) index = i;\n });\n if (index !== -1) return { date, index, inside: false };\n }\n return null;\n }\n\n /** First trading day in [from, to] (dates), or null. */\n firstTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = from; date <= to; date = addDays(date, 1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n\n /** Last trading day in [from, to] (dates), or null. */\n lastTradingDay(from: DateNum, to: DateNum): DateNum | null {\n for (let date = to; date >= from; date = addDays(date, -1)) if (this.isTradingDay(date)) return date;\n return null;\n }\n}\n\n/** Monday of the week of `date` (weeks run Monday to Sunday). */\nexport const weekStart = (date: DateNum): DateNum => addDays(date, -((weekdayOf(date) + 5) % 7));\n/** First day of the month of `date`. */\nexport const monthStart = (date: DateNum): DateNum => Math.floor(date / 100) * 100 + 1;\n/** Last day of the month of `date`. */\nexport const monthEnd = (date: DateNum): DateNum => addDays(monthStart(addDays(monthStart(date), 32)), -1);\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session of a chart context, for code that does not run a whole script (a port with its own bar loop).
|
|
3
|
+
*
|
|
4
|
+
* The same values as the script API gives (`time(tf)`, `time_close(tf)`, `time_tradingday`, `session.*`, the periods
|
|
5
|
+
* of `request.security`), from the session, corrections and holidays of the context. Bar times are in the unit of
|
|
6
|
+
* the context (`timeUnit`, seconds by default, as `Bar.time`); PineScript time values are returned in ms.
|
|
7
|
+
*
|
|
8
|
+
* @module session/context
|
|
9
|
+
*/
|
|
10
|
+
import type { Bar } from '../types/index.js';
|
|
11
|
+
import { SessionBars, type SessionFlags } from './bars.js';
|
|
12
|
+
import { type SessionSpec } from './calendar.js';
|
|
13
|
+
/** The part of a chart context that the session functions read (a `ChartContext` of the script API fits). */
|
|
14
|
+
export interface SessionContext {
|
|
15
|
+
/** Chart timeframe: "5", "60", "1D"... */
|
|
16
|
+
timeframe?: string;
|
|
17
|
+
/** Exchange time zone: "America/New_York", "Etc/UTC"... */
|
|
18
|
+
timezone?: string;
|
|
19
|
+
/** Session of the loaded bars: hours, corrections, holidays */
|
|
20
|
+
session?: SessionSpec | string;
|
|
21
|
+
/** Regular trading hours when the bars include extended hours (default: `session`) */
|
|
22
|
+
regularSession?: SessionSpec | string;
|
|
23
|
+
/** Unit of the bar times: 's' (default) or 'ms' */
|
|
24
|
+
timeUnit?: 's' | 'ms';
|
|
25
|
+
}
|
|
26
|
+
/** The session calendars of a context: the loaded bars and the regular hours. */
|
|
27
|
+
export interface ChartSessions {
|
|
28
|
+
bars: SessionBars;
|
|
29
|
+
regular: SessionBars;
|
|
30
|
+
/** ms per unit of the bar times */
|
|
31
|
+
unit: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Session calendars of a chart context (cached per context object).
|
|
35
|
+
* @throws Error when the context has no timeframe, time zone or session
|
|
36
|
+
*/
|
|
37
|
+
export declare function chartSessions(chart: SessionContext): ChartSessions;
|
|
38
|
+
/** PineScript `time(timeframe)` (ms) of a bar that opens at `time`: the start of its period; NaN without one. */
|
|
39
|
+
export declare function periodTime(time: number, timeframe: string, chart: SessionContext): number;
|
|
40
|
+
/** PineScript `time_close(timeframe)` (ms) of a bar that opens at `time`. */
|
|
41
|
+
export declare function periodClose(time: number, timeframe: string, chart: SessionContext): number;
|
|
42
|
+
/** PineScript `time_tradingday` (ms, 00:00 UTC of the trading day) of a bar that opens at `time`. */
|
|
43
|
+
export declare function tradingDayTime(time: number, chart: SessionContext): number;
|
|
44
|
+
/** PineScript `session.*` flags of bars that open at `times` (in order). */
|
|
45
|
+
export declare function chartSessionFlags(times: ArrayLike<number>, chart: SessionContext): SessionFlags;
|
|
46
|
+
/** Higher-timeframe bars of the chart bars and what `mapToChart` needs to bring values back to the chart. */
|
|
47
|
+
export interface SessionResample {
|
|
48
|
+
/** Bars of `timeframe`, built from the chart bars */
|
|
49
|
+
bars: Bar[];
|
|
50
|
+
/** Period index of each chart bar */
|
|
51
|
+
group: number[];
|
|
52
|
+
/** Latest completed period at each chart bar */
|
|
53
|
+
latest: number[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Same-symbol `request.security` on a timeframe equal to or higher than the chart's, with the session of the context:
|
|
57
|
+
* D / W / M periods follow the regular hours, corrections and holidays; intraday periods the loaded session.
|
|
58
|
+
* Compute on `bars`, then bring each value series back with `mapToChart(values, group, latest, lookahead, gaps)`.
|
|
59
|
+
* @throws Error when `timeframe` is lower than the chart timeframe
|
|
60
|
+
*/
|
|
61
|
+
export declare function resampleSession(bars: Bar[], timeframe: string, chart: SessionContext): SessionResample;
|
|
62
|
+
/**
|
|
63
|
+
* Open time of the `count`-th bar after the bar that opens at `time`, on the trading calendar of the context: the
|
|
64
|
+
* closed hours, weekends, holidays and days off are skipped. `count` 0 is the bar itself. For a drawing that ends
|
|
65
|
+
* `count` bars after the last bar (PineScript `bar_index + count`).
|
|
66
|
+
*/
|
|
67
|
+
export declare function barTimeAfter(time: number, count: number, chart: SessionContext): number;
|
|
68
|
+
/**
|
|
69
|
+
* Number of bars from the bar that opens at `from` to the bar that contains `to`, on the trading calendar of the
|
|
70
|
+
* context (0 when `to` is in the bar of `from` or before it). For a drawing placed at a time after the last bar
|
|
71
|
+
* (PineScript `time + n days` with `xloc.bar_time`): the chart shows it on the bar of that time, weekends and
|
|
72
|
+
* holidays skipped. NaN when no bar is found within 100,000 bars.
|
|
73
|
+
*/
|
|
74
|
+
export declare function barsUntil(from: number, to: number, chart: SessionContext): number;
|
|
75
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/session/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAgB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAElE,6GAA6G;AAC7G,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC/B,sFAAsF;IACtF,cAAc,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IACtC,mDAAmD;IACnD,QAAQ,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;CACvB;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;IACrB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAKD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,aAAa,CAgBlE;AAED,iHAAiH;AACjH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAGzF;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAG1F;AAED,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAG1E;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,cAAc,GAAG,YAAY,CAG/F;AAED,6GAA6G;AAC7G,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,qCAAqC;IACrC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gDAAgD;IAChD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,eAAe,CAyBtG;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAKvF;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAWjF"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var context_exports = {};
|
|
20
|
+
__export(context_exports, {
|
|
21
|
+
barTimeAfter: () => barTimeAfter,
|
|
22
|
+
barsUntil: () => barsUntil,
|
|
23
|
+
chartSessionFlags: () => chartSessionFlags,
|
|
24
|
+
chartSessions: () => chartSessions,
|
|
25
|
+
periodClose: () => periodClose,
|
|
26
|
+
periodTime: () => periodTime,
|
|
27
|
+
resampleSession: () => resampleSession,
|
|
28
|
+
tradingDayTime: () => tradingDayTime
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(context_exports);
|
|
31
|
+
var import_timeframe = require("../timeframe/index.js");
|
|
32
|
+
var import_resample = require("../security/resample.js");
|
|
33
|
+
var import_bars = require("./bars.js");
|
|
34
|
+
var import_calendar = require("./calendar.js");
|
|
35
|
+
const toSpec = (s) => typeof s === "string" ? { session: s } : s;
|
|
36
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
37
|
+
function chartSessions(chart) {
|
|
38
|
+
const cached = cache.get(chart);
|
|
39
|
+
if (cached) return cached;
|
|
40
|
+
const { timeframe, timezone, session } = chart;
|
|
41
|
+
if (!timeframe || !timezone || !session) {
|
|
42
|
+
throw new Error("The chart context needs timeframe, timezone and session for the session functions.");
|
|
43
|
+
}
|
|
44
|
+
const loaded = toSpec(session);
|
|
45
|
+
const regular = chart.regularSession ? toSpec(chart.regularSession) : loaded;
|
|
46
|
+
const out = {
|
|
47
|
+
bars: new import_bars.SessionBars(new import_calendar.TradingCalendar(timezone, loaded), timeframe),
|
|
48
|
+
regular: new import_bars.SessionBars(new import_calendar.TradingCalendar(timezone, { holidays: loaded.holidays, ...regular }), timeframe),
|
|
49
|
+
unit: chart.timeUnit === "ms" ? 1 : 1e3
|
|
50
|
+
};
|
|
51
|
+
cache.set(chart, out);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
function periodTime(time, timeframe, chart) {
|
|
55
|
+
const s = chartSessions(chart);
|
|
56
|
+
return s.bars.timeOf(time * s.unit, timeframe);
|
|
57
|
+
}
|
|
58
|
+
function periodClose(time, timeframe, chart) {
|
|
59
|
+
const s = chartSessions(chart);
|
|
60
|
+
return s.bars.closeOf(time * s.unit, timeframe);
|
|
61
|
+
}
|
|
62
|
+
function tradingDayTime(time, chart) {
|
|
63
|
+
const s = chartSessions(chart);
|
|
64
|
+
return s.bars.tradingDayTime(time * s.unit);
|
|
65
|
+
}
|
|
66
|
+
function chartSessionFlags(times, chart) {
|
|
67
|
+
const s = chartSessions(chart);
|
|
68
|
+
return (0, import_bars.sessionFlags)(Array.from(times, (t) => t * s.unit), s.bars, s.regular);
|
|
69
|
+
}
|
|
70
|
+
function resampleSession(bars, timeframe, chart) {
|
|
71
|
+
const s = chartSessions(chart);
|
|
72
|
+
const chartTf = s.bars.chart;
|
|
73
|
+
const tf = (0, import_timeframe.info)(timeframe || chartTf.period);
|
|
74
|
+
if (tf.period === chartTf.period) {
|
|
75
|
+
const group2 = bars.map((_, i) => i);
|
|
76
|
+
return { bars, group: group2, latest: group2 };
|
|
77
|
+
}
|
|
78
|
+
if ((0, import_timeframe.in_seconds)(tf.period) < (0, import_timeframe.in_seconds)(chartTf.period)) {
|
|
79
|
+
throw new Error(`resampleSession: timeframe "${tf.period}" is lower than the chart timeframe "${chartTf.period}".`);
|
|
80
|
+
}
|
|
81
|
+
const times = bars.map((b) => b.time * s.unit);
|
|
82
|
+
const calendar = tf.isdwm ? s.regular : s.bars;
|
|
83
|
+
const starts = times.map((t) => calendar.timeOf(t, tf.period));
|
|
84
|
+
const dayPeriods = times.map((t, i) => {
|
|
85
|
+
const at = s.bars.calendar.locate(t);
|
|
86
|
+
const periods = at ? calendar.calendar.periods(at.date) : [];
|
|
87
|
+
return periods.length ? calendar.timeOf(periods[0][0], tf.period) : starts[i];
|
|
88
|
+
});
|
|
89
|
+
const reaches = times.map((t) => s.bars.closeOf(t, chartTf.period) >= calendar.periodEnd(t, tf.period));
|
|
90
|
+
const { group, latest } = (0, import_resample.periodsOf)(starts, dayPeriods, reaches);
|
|
91
|
+
const after = (0, import_resample.afterCompletion)(group, latest);
|
|
92
|
+
const kept = (_, i) => !after[i];
|
|
93
|
+
return { bars: (0, import_resample.resample)(bars.filter(kept), starts.map((x) => x / s.unit).filter(kept)).bars, group, latest };
|
|
94
|
+
}
|
|
95
|
+
function barTimeAfter(time, count, chart) {
|
|
96
|
+
const s = chartSessions(chart);
|
|
97
|
+
let t = time * s.unit;
|
|
98
|
+
for (let k = 0; k < count && !Number.isNaN(t); k++) t = s.bars.nextBarTime(t);
|
|
99
|
+
return t / s.unit;
|
|
100
|
+
}
|
|
101
|
+
function barsUntil(from, to, chart) {
|
|
102
|
+
const s = chartSessions(chart);
|
|
103
|
+
const target = to * s.unit;
|
|
104
|
+
let t = from * s.unit;
|
|
105
|
+
for (let k = 0; k < 1e5; k++) {
|
|
106
|
+
const next = s.bars.nextBarTime(t);
|
|
107
|
+
if (Number.isNaN(next)) return NaN;
|
|
108
|
+
if (next > target) return k;
|
|
109
|
+
t = next;
|
|
110
|
+
}
|
|
111
|
+
return NaN;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=context.js.map
|