@discomedia/utils 1.0.19 → 1.0.21
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 +5 -0
- package/dist/index-frontend.cjs +82 -6
- package/dist/index-frontend.cjs.map +1 -1
- package/dist/index-frontend.mjs +82 -6
- package/dist/index-frontend.mjs.map +1 -1
- package/dist/index.cjs +288 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +288 -22
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +5 -9
- package/dist/test.js +689 -14
- package/dist/test.js.map +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/market-time.d.ts +106 -27
- package/dist/types/market-time.d.ts.map +1 -1
- package/dist/types-frontend/index.d.ts +5 -0
- package/dist/types-frontend/index.d.ts.map +1 -1
- package/dist/types-frontend/market-time.d.ts +106 -27
- package/dist/types-frontend/market-time.d.ts.map +1 -1
- package/package.json +5 -9
package/dist/test.js
CHANGED
|
@@ -146,14 +146,25 @@ const marketEarlyCloses = {
|
|
|
146
146
|
|
|
147
147
|
// Constants for NY market times (Eastern Time)
|
|
148
148
|
const MARKET_CONFIG = {
|
|
149
|
+
TIMEZONE: 'America/New_York',
|
|
149
150
|
UTC_OFFSET_STANDARD: -5, // EST
|
|
150
151
|
UTC_OFFSET_DST: -4, // EDT
|
|
151
152
|
TIMES: {
|
|
153
|
+
EXTENDED_START: { hour: 4, minute: 0 },
|
|
152
154
|
MARKET_OPEN: { hour: 9, minute: 30 },
|
|
155
|
+
EARLY_MARKET_END: { hour: 10, minute: 0 },
|
|
153
156
|
MARKET_CLOSE: { hour: 16, minute: 0 },
|
|
154
|
-
EARLY_CLOSE: { hour: 13, minute: 0 }
|
|
157
|
+
EARLY_CLOSE: { hour: 13, minute: 0 },
|
|
158
|
+
EXTENDED_END: { hour: 20, minute: 0 },
|
|
159
|
+
EARLY_EXTENDED_END: { hour: 17, minute: 0 },
|
|
160
|
+
},
|
|
155
161
|
};
|
|
156
162
|
// Helper: Get NY offset for a given UTC date (DST rules for US)
|
|
163
|
+
/**
|
|
164
|
+
* Returns the NY timezone offset (in hours) for a given UTC date, accounting for US DST rules.
|
|
165
|
+
* @param date - UTC date
|
|
166
|
+
* @returns offset in hours (-5 for EST, -4 for EDT)
|
|
167
|
+
*/
|
|
157
168
|
function getNYOffset(date) {
|
|
158
169
|
// US DST starts 2nd Sunday in March, ends 1st Sunday in November
|
|
159
170
|
const year = date.getUTCFullYear();
|
|
@@ -166,6 +177,14 @@ function getNYOffset(date) {
|
|
|
166
177
|
return MARKET_CONFIG.UTC_OFFSET_STANDARD;
|
|
167
178
|
}
|
|
168
179
|
// Helper: Get nth weekday of month in UTC
|
|
180
|
+
/**
|
|
181
|
+
* Returns the nth weekday of a given month in UTC.
|
|
182
|
+
* @param year - Year
|
|
183
|
+
* @param month - 1-based month (e.g. March = 3)
|
|
184
|
+
* @param weekday - 0=Sunday, 1=Monday, ...
|
|
185
|
+
* @param n - nth occurrence
|
|
186
|
+
* @returns Date object for the nth weekday
|
|
187
|
+
*/
|
|
169
188
|
function getNthWeekdayOfMonth(year, month, weekday, n) {
|
|
170
189
|
let count = 0;
|
|
171
190
|
for (let d = 1; d <= 31; d++) {
|
|
@@ -182,6 +201,11 @@ function getNthWeekdayOfMonth(year, month, weekday, n) {
|
|
|
182
201
|
return new Date(Date.UTC(year, month - 1, 28));
|
|
183
202
|
}
|
|
184
203
|
// Helper: Convert UTC date to NY time (returns new Date object)
|
|
204
|
+
/**
|
|
205
|
+
* Converts a UTC date to NY time (returns a new Date object).
|
|
206
|
+
* @param date - UTC date
|
|
207
|
+
* @returns Date object in NY time
|
|
208
|
+
*/
|
|
185
209
|
function toNYTime(date) {
|
|
186
210
|
const offset = getNYOffset(date);
|
|
187
211
|
// NY offset in hours
|
|
@@ -190,18 +214,63 @@ function toNYTime(date) {
|
|
|
190
214
|
return new Date(nyMillis);
|
|
191
215
|
}
|
|
192
216
|
// Helper: Convert NY time to UTC (returns new Date object)
|
|
217
|
+
/**
|
|
218
|
+
* Converts a NY time date to UTC (returns a new Date object).
|
|
219
|
+
* @param date - NY time date
|
|
220
|
+
* @returns Date object in UTC
|
|
221
|
+
*/
|
|
193
222
|
function fromNYTime(date) {
|
|
194
223
|
const offset = getNYOffset(date);
|
|
195
224
|
const nyMillis = date.getTime();
|
|
196
225
|
const utcMillis = nyMillis - offset * 60 * 60 * 1000;
|
|
197
226
|
return new Date(utcMillis);
|
|
198
227
|
}
|
|
228
|
+
// Helper: Format date in ISO, unix, etc.
|
|
229
|
+
/**
|
|
230
|
+
* Formats a date in ISO, unix-seconds, or unix-ms format.
|
|
231
|
+
* @param date - Date object
|
|
232
|
+
* @param outputFormat - Output format ('iso', 'unix-seconds', 'unix-ms')
|
|
233
|
+
* @returns Formatted date string or number
|
|
234
|
+
*/
|
|
235
|
+
function formatDate(date, outputFormat = 'iso') {
|
|
236
|
+
switch (outputFormat) {
|
|
237
|
+
case 'unix-seconds':
|
|
238
|
+
return Math.floor(date.getTime() / 1000);
|
|
239
|
+
case 'unix-ms':
|
|
240
|
+
return date.getTime();
|
|
241
|
+
case 'iso':
|
|
242
|
+
default:
|
|
243
|
+
return date.toISOString();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Helper: Format date in NY locale string
|
|
247
|
+
/**
|
|
248
|
+
* Formats a date in NY locale string.
|
|
249
|
+
* @param date - Date object
|
|
250
|
+
* @returns NY locale string
|
|
251
|
+
*/
|
|
252
|
+
function formatNYLocale(date) {
|
|
253
|
+
return date.toLocaleString('en-US', { timeZone: 'America/New_York' });
|
|
254
|
+
}
|
|
199
255
|
// Market calendar logic
|
|
256
|
+
/**
|
|
257
|
+
* Market calendar logic for holidays, weekends, and market days.
|
|
258
|
+
*/
|
|
200
259
|
class MarketCalendar {
|
|
260
|
+
/**
|
|
261
|
+
* Checks if a date is a weekend in NY time.
|
|
262
|
+
* @param date - Date object
|
|
263
|
+
* @returns true if weekend, false otherwise
|
|
264
|
+
*/
|
|
201
265
|
isWeekend(date) {
|
|
202
266
|
const day = toNYTime(date).getUTCDay();
|
|
203
267
|
return day === 0 || day === 6;
|
|
204
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Checks if a date is a market holiday in NY time.
|
|
271
|
+
* @param date - Date object
|
|
272
|
+
* @returns true if holiday, false otherwise
|
|
273
|
+
*/
|
|
205
274
|
isHoliday(date) {
|
|
206
275
|
const nyDate = toNYTime(date);
|
|
207
276
|
const year = nyDate.getUTCFullYear();
|
|
@@ -211,8 +280,13 @@ class MarketCalendar {
|
|
|
211
280
|
const yearHolidays = marketHolidays[year];
|
|
212
281
|
if (!yearHolidays)
|
|
213
282
|
return false;
|
|
214
|
-
return Object.values(yearHolidays).some(holiday => holiday.date === formattedDate);
|
|
283
|
+
return Object.values(yearHolidays).some((holiday) => holiday.date === formattedDate);
|
|
215
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* Checks if a date is an early close day in NY time.
|
|
287
|
+
* @param date - Date object
|
|
288
|
+
* @returns true if early close, false otherwise
|
|
289
|
+
*/
|
|
216
290
|
isEarlyCloseDay(date) {
|
|
217
291
|
const nyDate = toNYTime(date);
|
|
218
292
|
const year = nyDate.getUTCFullYear();
|
|
@@ -222,6 +296,11 @@ class MarketCalendar {
|
|
|
222
296
|
const yearEarlyCloses = marketEarlyCloses[year];
|
|
223
297
|
return yearEarlyCloses && yearEarlyCloses[formattedDate] !== undefined;
|
|
224
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Gets the early close time (in minutes from midnight) for a given date.
|
|
301
|
+
* @param date - Date object
|
|
302
|
+
* @returns minutes from midnight or null
|
|
303
|
+
*/
|
|
225
304
|
getEarlyCloseTime(date) {
|
|
226
305
|
const nyDate = toNYTime(date);
|
|
227
306
|
const year = nyDate.getUTCFullYear();
|
|
@@ -235,9 +314,19 @@ class MarketCalendar {
|
|
|
235
314
|
}
|
|
236
315
|
return null;
|
|
237
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Checks if a date is a market day (not weekend or holiday).
|
|
319
|
+
* @param date - Date object
|
|
320
|
+
* @returns true if market day, false otherwise
|
|
321
|
+
*/
|
|
238
322
|
isMarketDay(date) {
|
|
239
323
|
return !this.isWeekend(date) && !this.isHoliday(date);
|
|
240
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Gets the next market day after the given date.
|
|
327
|
+
* @param date - Date object
|
|
328
|
+
* @returns Date object for next market day
|
|
329
|
+
*/
|
|
241
330
|
getNextMarketDay(date) {
|
|
242
331
|
let nextDay = new Date(date.getTime() + 24 * 60 * 60 * 1000);
|
|
243
332
|
while (!this.isMarketDay(nextDay)) {
|
|
@@ -245,6 +334,11 @@ class MarketCalendar {
|
|
|
245
334
|
}
|
|
246
335
|
return nextDay;
|
|
247
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Gets the previous market day before the given date.
|
|
339
|
+
* @param date - Date object
|
|
340
|
+
* @returns Date object for previous market day
|
|
341
|
+
*/
|
|
248
342
|
getPreviousMarketDay(date) {
|
|
249
343
|
let prevDay = new Date(date.getTime() - 24 * 60 * 60 * 1000);
|
|
250
344
|
while (!this.isMarketDay(prevDay)) {
|
|
@@ -253,7 +347,88 @@ class MarketCalendar {
|
|
|
253
347
|
return prevDay;
|
|
254
348
|
}
|
|
255
349
|
}
|
|
350
|
+
// Market open/close times
|
|
351
|
+
/**
|
|
352
|
+
* Returns market open/close times for a given date, including extended and early closes.
|
|
353
|
+
* @param date - Date object
|
|
354
|
+
* @returns MarketOpenCloseResult
|
|
355
|
+
*/
|
|
356
|
+
function getMarketTimes(date) {
|
|
357
|
+
const calendar = new MarketCalendar();
|
|
358
|
+
const nyDate = toNYTime(date);
|
|
359
|
+
if (!calendar.isMarketDay(date)) {
|
|
360
|
+
return {
|
|
361
|
+
marketOpen: false,
|
|
362
|
+
open: null,
|
|
363
|
+
close: null,
|
|
364
|
+
openExt: null,
|
|
365
|
+
closeExt: null,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const year = nyDate.getUTCFullYear();
|
|
369
|
+
const month = nyDate.getUTCMonth();
|
|
370
|
+
const day = nyDate.getUTCDate();
|
|
371
|
+
// Helper to build NY time for a given hour/minute
|
|
372
|
+
function buildNYTime(hour, minute) {
|
|
373
|
+
const d = new Date(Date.UTC(year, month, day, hour, minute, 0, 0));
|
|
374
|
+
return fromNYTime(d);
|
|
375
|
+
}
|
|
376
|
+
let open = buildNYTime(MARKET_CONFIG.TIMES.MARKET_OPEN.hour, MARKET_CONFIG.TIMES.MARKET_OPEN.minute);
|
|
377
|
+
let close = buildNYTime(MARKET_CONFIG.TIMES.MARKET_CLOSE.hour, MARKET_CONFIG.TIMES.MARKET_CLOSE.minute);
|
|
378
|
+
let openExt = buildNYTime(MARKET_CONFIG.TIMES.EXTENDED_START.hour, MARKET_CONFIG.TIMES.EXTENDED_START.minute);
|
|
379
|
+
let closeExt = buildNYTime(MARKET_CONFIG.TIMES.EXTENDED_END.hour, MARKET_CONFIG.TIMES.EXTENDED_END.minute);
|
|
380
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
381
|
+
close = buildNYTime(MARKET_CONFIG.TIMES.EARLY_CLOSE.hour, MARKET_CONFIG.TIMES.EARLY_CLOSE.minute);
|
|
382
|
+
closeExt = buildNYTime(MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour, MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute);
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
marketOpen: true,
|
|
386
|
+
open,
|
|
387
|
+
close,
|
|
388
|
+
openExt,
|
|
389
|
+
closeExt,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
// Is within market hours
|
|
393
|
+
/**
|
|
394
|
+
* Checks if a date/time is within market hours, extended hours, or continuous.
|
|
395
|
+
* @param date - Date object
|
|
396
|
+
* @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
|
|
397
|
+
* @returns true if within hours, false otherwise
|
|
398
|
+
*/
|
|
399
|
+
function isWithinMarketHours(date, intradayReporting = 'market_hours') {
|
|
400
|
+
const calendar = new MarketCalendar();
|
|
401
|
+
if (!calendar.isMarketDay(date))
|
|
402
|
+
return false;
|
|
403
|
+
const nyDate = toNYTime(date);
|
|
404
|
+
const minutes = nyDate.getUTCHours() * 60 + nyDate.getUTCMinutes();
|
|
405
|
+
switch (intradayReporting) {
|
|
406
|
+
case 'extended_hours': {
|
|
407
|
+
let endMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
|
|
408
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
409
|
+
endMinutes = MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
|
|
410
|
+
}
|
|
411
|
+
const startMinutes = MARKET_CONFIG.TIMES.EXTENDED_START.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_START.minute;
|
|
412
|
+
return minutes >= startMinutes && minutes <= endMinutes;
|
|
413
|
+
}
|
|
414
|
+
case 'continuous':
|
|
415
|
+
return true;
|
|
416
|
+
default: {
|
|
417
|
+
let endMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
418
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
419
|
+
endMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
420
|
+
}
|
|
421
|
+
const startMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
422
|
+
return minutes >= startMinutes && minutes <= endMinutes;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
256
426
|
// Get last full trading date
|
|
427
|
+
/**
|
|
428
|
+
* Returns the last full trading date (market close) for a given date.
|
|
429
|
+
* @param currentDate - Date object (default: now)
|
|
430
|
+
* @returns Date object for last full trading date
|
|
431
|
+
*/
|
|
257
432
|
function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
258
433
|
const calendar = new MarketCalendar();
|
|
259
434
|
const nyDate = toNYTime(currentDate);
|
|
@@ -264,7 +439,9 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
264
439
|
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
265
440
|
}
|
|
266
441
|
// If not a market day, or before open, or during market hours, return previous market day's close
|
|
267
|
-
if (!calendar.isMarketDay(currentDate) ||
|
|
442
|
+
if (!calendar.isMarketDay(currentDate) ||
|
|
443
|
+
minutes < marketOpenMinutes ||
|
|
444
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
268
445
|
const prevMarketDay = calendar.getPreviousMarketDay(currentDate);
|
|
269
446
|
let prevCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
270
447
|
if (calendar.isEarlyCloseDay(prevMarketDay)) {
|
|
@@ -285,23 +462,478 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
285
462
|
const closeMinute = marketCloseMinutes % 60;
|
|
286
463
|
return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
|
|
287
464
|
}
|
|
465
|
+
// Get day boundaries
|
|
466
|
+
/**
|
|
467
|
+
* Returns the start and end boundaries for a market day, extended hours, or continuous.
|
|
468
|
+
* @param date - Date object
|
|
469
|
+
* @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
|
|
470
|
+
* @returns Object with start and end Date
|
|
471
|
+
*/
|
|
472
|
+
function getDayBoundaries(date, intradayReporting = 'market_hours') {
|
|
473
|
+
const calendar = new MarketCalendar();
|
|
474
|
+
const nyDate = toNYTime(date);
|
|
475
|
+
const year = nyDate.getUTCFullYear();
|
|
476
|
+
const month = nyDate.getUTCMonth();
|
|
477
|
+
const day = nyDate.getUTCDate();
|
|
478
|
+
function buildNYTime(hour, minute, sec = 0, ms = 0) {
|
|
479
|
+
const d = new Date(Date.UTC(year, month, day, hour, minute, sec, ms));
|
|
480
|
+
return fromNYTime(d);
|
|
481
|
+
}
|
|
482
|
+
let start;
|
|
483
|
+
let end;
|
|
484
|
+
switch (intradayReporting) {
|
|
485
|
+
case 'extended_hours':
|
|
486
|
+
start = buildNYTime(MARKET_CONFIG.TIMES.EXTENDED_START.hour, MARKET_CONFIG.TIMES.EXTENDED_START.minute, 0, 0);
|
|
487
|
+
end = buildNYTime(MARKET_CONFIG.TIMES.EXTENDED_END.hour, MARKET_CONFIG.TIMES.EXTENDED_END.minute, 59, 999);
|
|
488
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
489
|
+
end = buildNYTime(MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour, MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute, 59, 999);
|
|
490
|
+
}
|
|
491
|
+
break;
|
|
492
|
+
case 'continuous':
|
|
493
|
+
start = new Date(Date.UTC(year, month, day, 0, 0, 0, 0));
|
|
494
|
+
end = new Date(Date.UTC(year, month, day, 23, 59, 59, 999));
|
|
495
|
+
break;
|
|
496
|
+
default:
|
|
497
|
+
start = buildNYTime(MARKET_CONFIG.TIMES.MARKET_OPEN.hour, MARKET_CONFIG.TIMES.MARKET_OPEN.minute, 0, 0);
|
|
498
|
+
end = buildNYTime(MARKET_CONFIG.TIMES.MARKET_CLOSE.hour, MARKET_CONFIG.TIMES.MARKET_CLOSE.minute, 59, 999);
|
|
499
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
500
|
+
end = buildNYTime(MARKET_CONFIG.TIMES.EARLY_CLOSE.hour, MARKET_CONFIG.TIMES.EARLY_CLOSE.minute, 59, 999);
|
|
501
|
+
}
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
return { start, end };
|
|
505
|
+
}
|
|
506
|
+
// Period calculator
|
|
507
|
+
/**
|
|
508
|
+
* Calculates the start date for a given period ending at endDate.
|
|
509
|
+
* @param endDate - Date object
|
|
510
|
+
* @param period - Period string
|
|
511
|
+
* @returns Date object for period start
|
|
512
|
+
*/
|
|
513
|
+
function calculatePeriodStartDate(endDate, period) {
|
|
514
|
+
const calendar = new MarketCalendar();
|
|
515
|
+
let startDate;
|
|
516
|
+
switch (period) {
|
|
517
|
+
case 'YTD':
|
|
518
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear(), 0, 1));
|
|
519
|
+
break;
|
|
520
|
+
case '1D':
|
|
521
|
+
startDate = calendar.getPreviousMarketDay(endDate);
|
|
522
|
+
break;
|
|
523
|
+
case '3D':
|
|
524
|
+
startDate = new Date(endDate.getTime() - 3 * 24 * 60 * 60 * 1000);
|
|
525
|
+
break;
|
|
526
|
+
case '1W':
|
|
527
|
+
startDate = new Date(endDate.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
528
|
+
break;
|
|
529
|
+
case '2W':
|
|
530
|
+
startDate = new Date(endDate.getTime() - 14 * 24 * 60 * 60 * 1000);
|
|
531
|
+
break;
|
|
532
|
+
case '1M':
|
|
533
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth() - 1, endDate.getUTCDate()));
|
|
534
|
+
break;
|
|
535
|
+
case '3M':
|
|
536
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth() - 3, endDate.getUTCDate()));
|
|
537
|
+
break;
|
|
538
|
+
case '6M':
|
|
539
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth() - 6, endDate.getUTCDate()));
|
|
540
|
+
break;
|
|
541
|
+
case '1Y':
|
|
542
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear() - 1, endDate.getUTCMonth(), endDate.getUTCDate()));
|
|
543
|
+
break;
|
|
544
|
+
default:
|
|
545
|
+
throw new Error(`Invalid period: ${period}`);
|
|
546
|
+
}
|
|
547
|
+
// Ensure start date is a market day
|
|
548
|
+
while (!calendar.isMarketDay(startDate)) {
|
|
549
|
+
startDate = calendar.getNextMarketDay(startDate);
|
|
550
|
+
}
|
|
551
|
+
return startDate;
|
|
552
|
+
}
|
|
553
|
+
// Get market time period
|
|
554
|
+
/**
|
|
555
|
+
* Returns the start and end dates for a market time period.
|
|
556
|
+
* @param params - MarketTimeParams
|
|
557
|
+
* @returns PeriodDates object
|
|
558
|
+
*/
|
|
559
|
+
function getMarketTimePeriod(params) {
|
|
560
|
+
const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso' } = params;
|
|
561
|
+
if (!period)
|
|
562
|
+
throw new Error('Period is required');
|
|
563
|
+
const calendar = new MarketCalendar();
|
|
564
|
+
const nyEndDate = toNYTime(end);
|
|
565
|
+
let endDate;
|
|
566
|
+
const isCurrentMarketDay = calendar.isMarketDay(end);
|
|
567
|
+
const isWithinHours = isWithinMarketHours(end, intraday_reporting);
|
|
568
|
+
const minutes = nyEndDate.getUTCHours() * 60 + nyEndDate.getUTCMinutes();
|
|
569
|
+
const marketStartMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
570
|
+
if (isCurrentMarketDay) {
|
|
571
|
+
if (minutes < marketStartMinutes) {
|
|
572
|
+
// Before market open - use previous day's close
|
|
573
|
+
const lastMarketDay = calendar.getPreviousMarketDay(end);
|
|
574
|
+
const { end: dayEnd } = getDayBoundaries(lastMarketDay, intraday_reporting);
|
|
575
|
+
endDate = dayEnd;
|
|
576
|
+
}
|
|
577
|
+
else if (isWithinHours) {
|
|
578
|
+
// During market hours - use current time
|
|
579
|
+
endDate = end;
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
// After market close - use today's close
|
|
583
|
+
const { end: dayEnd } = getDayBoundaries(end, intraday_reporting);
|
|
584
|
+
endDate = dayEnd;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
// Not a market day - use previous market day's close
|
|
589
|
+
const lastMarketDay = calendar.getPreviousMarketDay(end);
|
|
590
|
+
const { end: dayEnd } = getDayBoundaries(lastMarketDay, intraday_reporting);
|
|
591
|
+
endDate = dayEnd;
|
|
592
|
+
}
|
|
593
|
+
// Calculate start date
|
|
594
|
+
const periodStartDate = calculatePeriodStartDate(endDate, period);
|
|
595
|
+
const { start: dayStart } = getDayBoundaries(periodStartDate, intraday_reporting);
|
|
596
|
+
if (endDate.getTime() < dayStart.getTime()) {
|
|
597
|
+
throw new Error('Start date cannot be after end date');
|
|
598
|
+
}
|
|
599
|
+
return {
|
|
600
|
+
start: formatDate(dayStart, outputFormat),
|
|
601
|
+
end: formatDate(endDate, outputFormat),
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
// Market status
|
|
605
|
+
/**
|
|
606
|
+
* Returns the current market status for a given date.
|
|
607
|
+
* @param date - Date object (default: now)
|
|
608
|
+
* @returns MarketStatus object
|
|
609
|
+
*/
|
|
610
|
+
function getMarketStatusImpl(date = new Date()) {
|
|
611
|
+
const calendar = new MarketCalendar();
|
|
612
|
+
const nyDate = toNYTime(date);
|
|
613
|
+
const minutes = nyDate.getUTCHours() * 60 + nyDate.getUTCMinutes();
|
|
614
|
+
const isMarketDay = calendar.isMarketDay(date);
|
|
615
|
+
const isEarlyCloseDay = calendar.isEarlyCloseDay(date);
|
|
616
|
+
const extendedStartMinutes = MARKET_CONFIG.TIMES.EXTENDED_START.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_START.minute;
|
|
617
|
+
const marketStartMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
618
|
+
const earlyMarketEndMinutes = MARKET_CONFIG.TIMES.EARLY_MARKET_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_MARKET_END.minute;
|
|
619
|
+
let marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
620
|
+
let extendedEndMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
|
|
621
|
+
if (isEarlyCloseDay) {
|
|
622
|
+
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
623
|
+
extendedEndMinutes =
|
|
624
|
+
MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
|
|
625
|
+
}
|
|
626
|
+
let status;
|
|
627
|
+
let nextStatus;
|
|
628
|
+
let nextStatusTime;
|
|
629
|
+
let marketPeriod;
|
|
630
|
+
if (!isMarketDay) {
|
|
631
|
+
status = 'closed';
|
|
632
|
+
nextStatus = 'extended hours';
|
|
633
|
+
marketPeriod = 'closed';
|
|
634
|
+
const nextMarketDay = calendar.getNextMarketDay(date);
|
|
635
|
+
nextStatusTime = getDayBoundaries(nextMarketDay, 'extended_hours').start;
|
|
636
|
+
}
|
|
637
|
+
else if (minutes < extendedStartMinutes) {
|
|
638
|
+
status = 'closed';
|
|
639
|
+
nextStatus = 'extended hours';
|
|
640
|
+
marketPeriod = 'closed';
|
|
641
|
+
nextStatusTime = getDayBoundaries(date, 'extended_hours').start;
|
|
642
|
+
}
|
|
643
|
+
else if (minutes < marketStartMinutes) {
|
|
644
|
+
status = 'extended hours';
|
|
645
|
+
nextStatus = 'open';
|
|
646
|
+
marketPeriod = 'preMarket';
|
|
647
|
+
nextStatusTime = getDayBoundaries(date, 'market_hours').start;
|
|
648
|
+
}
|
|
649
|
+
else if (minutes < marketCloseMinutes) {
|
|
650
|
+
status = 'open';
|
|
651
|
+
nextStatus = 'extended hours';
|
|
652
|
+
marketPeriod = minutes < earlyMarketEndMinutes ? 'earlyMarket' : 'regularMarket';
|
|
653
|
+
nextStatusTime = getDayBoundaries(date, 'market_hours').end;
|
|
654
|
+
}
|
|
655
|
+
else if (minutes < extendedEndMinutes) {
|
|
656
|
+
status = 'extended hours';
|
|
657
|
+
nextStatus = 'closed';
|
|
658
|
+
marketPeriod = 'afterMarket';
|
|
659
|
+
nextStatusTime = getDayBoundaries(date, 'extended_hours').end;
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
status = 'closed';
|
|
663
|
+
nextStatus = 'extended hours';
|
|
664
|
+
marketPeriod = 'closed';
|
|
665
|
+
const nextMarketDay = calendar.getNextMarketDay(date);
|
|
666
|
+
nextStatusTime = getDayBoundaries(nextMarketDay, 'extended_hours').start;
|
|
667
|
+
}
|
|
668
|
+
const nextStatusTimeDifference = nextStatusTime.getTime() - nyDate.getTime();
|
|
669
|
+
return {
|
|
670
|
+
time: date,
|
|
671
|
+
timeString: formatNYLocale(nyDate),
|
|
672
|
+
status,
|
|
673
|
+
nextStatus,
|
|
674
|
+
marketPeriod,
|
|
675
|
+
nextStatusTime,
|
|
676
|
+
nextStatusTimeDifference,
|
|
677
|
+
nextStatusTimeString: formatNYLocale(nextStatusTime),
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
// API exports
|
|
681
|
+
/**
|
|
682
|
+
* Returns market open/close times for a given date.
|
|
683
|
+
* @param options - { date?: Date }
|
|
684
|
+
* @returns MarketOpenCloseResult
|
|
685
|
+
*/
|
|
686
|
+
function getMarketOpenClose(options = {}) {
|
|
687
|
+
const { date = new Date() } = options;
|
|
688
|
+
return getMarketTimes(date);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Returns the start and end dates for a market time period as Date objects.
|
|
692
|
+
* @param params - MarketTimeParams
|
|
693
|
+
* @returns Object with start and end Date
|
|
694
|
+
*/
|
|
695
|
+
function getStartAndEndDates(params = {}) {
|
|
696
|
+
const { start, end } = getMarketTimePeriod(params);
|
|
697
|
+
return {
|
|
698
|
+
start: typeof start === 'string' || typeof start === 'number' ? new Date(start) : start,
|
|
699
|
+
end: typeof end === 'string' || typeof end === 'number' ? new Date(end) : end,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Returns the last full trading date as a Date object.
|
|
704
|
+
*/
|
|
288
705
|
/**
|
|
289
706
|
* Returns the last full trading date as a Date object.
|
|
707
|
+
* @param currentDate - Date object (default: now)
|
|
708
|
+
* @returns Date object for last full trading date
|
|
290
709
|
*/
|
|
291
710
|
function getLastFullTradingDate(currentDate = new Date()) {
|
|
292
711
|
return getLastFullTradingDateImpl(currentDate);
|
|
293
712
|
}
|
|
713
|
+
/**
|
|
714
|
+
* Returns the next market day after the reference date.
|
|
715
|
+
* @param referenceDate - Date object (default: now)
|
|
716
|
+
* @returns Object with date, yyyymmdd string, and ISO string
|
|
717
|
+
*/
|
|
294
718
|
function getNextMarketDay({ referenceDate } = {}) {
|
|
295
719
|
const calendar = new MarketCalendar();
|
|
296
|
-
const startDate = referenceDate
|
|
720
|
+
const startDate = referenceDate ?? new Date();
|
|
721
|
+
// Find the next trading day (UTC Date object)
|
|
297
722
|
const nextDate = calendar.getNextMarketDay(startDate);
|
|
298
|
-
|
|
723
|
+
// Convert to NY time before extracting Y-M-D parts
|
|
724
|
+
const nyNext = toNYTime(nextDate);
|
|
725
|
+
const yyyymmdd = `${nyNext.getUTCFullYear()}-${String(nyNext.getUTCMonth() + 1).padStart(2, '0')}-${String(nyNext.getUTCDate()).padStart(2, '0')}`;
|
|
299
726
|
return {
|
|
300
|
-
date: nextDate,
|
|
301
|
-
yyyymmdd,
|
|
727
|
+
date: nextDate, // raw Date, unchanged
|
|
728
|
+
yyyymmdd, // correct trading date string
|
|
302
729
|
dateISOString: nextDate.toISOString(),
|
|
303
730
|
};
|
|
304
731
|
}
|
|
732
|
+
/**
|
|
733
|
+
* Returns the previous market day before the reference date.
|
|
734
|
+
* @param referenceDate - Date object (default: now)
|
|
735
|
+
* @returns Object with date, yyyymmdd string, and ISO string
|
|
736
|
+
*/
|
|
737
|
+
function getPreviousMarketDay({ referenceDate } = {}) {
|
|
738
|
+
const calendar = new MarketCalendar();
|
|
739
|
+
const startDate = referenceDate || new Date();
|
|
740
|
+
const prevDate = calendar.getPreviousMarketDay(startDate);
|
|
741
|
+
// convert to NY time first
|
|
742
|
+
const nyPrev = toNYTime(prevDate); // ← already in this file
|
|
743
|
+
const yyyymmdd = `${nyPrev.getUTCFullYear()}-${String(nyPrev.getUTCMonth() + 1).padStart(2, '0')}-${String(nyPrev.getUTCDate()).padStart(2, '0')}`;
|
|
744
|
+
return {
|
|
745
|
+
date: prevDate,
|
|
746
|
+
yyyymmdd,
|
|
747
|
+
dateISOString: prevDate.toISOString(),
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Returns the trading date for a given time. Note: Just trims the date string; does not validate if the date is a market day.
|
|
752
|
+
* @param time - a string, number (unix timestamp), or Date object representing the time
|
|
753
|
+
* @returns the trading date as a string in YYYY-MM-DD format
|
|
754
|
+
*/
|
|
755
|
+
/**
|
|
756
|
+
* Returns the trading date for a given time in YYYY-MM-DD format (NY time).
|
|
757
|
+
* @param time - string, number, or Date
|
|
758
|
+
* @returns trading date string
|
|
759
|
+
*/
|
|
760
|
+
function getTradingDate(time) {
|
|
761
|
+
const date = typeof time === 'number' ? new Date(time) : typeof time === 'string' ? new Date(time) : time;
|
|
762
|
+
const nyDate = toNYTime(date);
|
|
763
|
+
return `${nyDate.getUTCFullYear()}-${String(nyDate.getUTCMonth() + 1).padStart(2, '0')}-${String(nyDate.getUTCDate()).padStart(2, '0')}`;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Returns the NY timezone offset string for a given date.
|
|
767
|
+
* @param date - Date object (default: now)
|
|
768
|
+
* @returns '-04:00' for EDT, '-05:00' for EST
|
|
769
|
+
*/
|
|
770
|
+
function getNYTimeZone(date) {
|
|
771
|
+
const offset = getNYOffset(date || new Date());
|
|
772
|
+
return offset === -4 ? '-04:00' : '-05:00';
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Returns the current market status for a given date.
|
|
776
|
+
* @param options - { date?: Date }
|
|
777
|
+
* @returns MarketStatus object
|
|
778
|
+
*/
|
|
779
|
+
function getMarketStatus(options = {}) {
|
|
780
|
+
const { date = new Date() } = options;
|
|
781
|
+
return getMarketStatusImpl(date);
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Checks if a date is a market day.
|
|
785
|
+
* @param date - Date object
|
|
786
|
+
* @returns true if market day, false otherwise
|
|
787
|
+
*/
|
|
788
|
+
function isMarketDay(date) {
|
|
789
|
+
const calendar = new MarketCalendar();
|
|
790
|
+
return calendar.isMarketDay(date);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Returns full trading days from market open to market close.
|
|
794
|
+
* endDate is always the most recent market close (previous day's close if before open, today's close if after open).
|
|
795
|
+
* days: 1 or not specified = that day's open; 2 = previous market day's open, etc.
|
|
796
|
+
*/
|
|
797
|
+
/**
|
|
798
|
+
* Returns full trading days from market open to market close.
|
|
799
|
+
* @param options - { endDate?: Date, days?: number }
|
|
800
|
+
* @returns Object with startDate and endDate
|
|
801
|
+
*/
|
|
802
|
+
function getTradingStartAndEndDates(options = {}) {
|
|
803
|
+
const { endDate = new Date(), days = 1 } = options;
|
|
804
|
+
const calendar = new MarketCalendar();
|
|
805
|
+
// Find the most recent market close
|
|
806
|
+
let endMarketDay = endDate;
|
|
807
|
+
const nyEnd = toNYTime(endDate);
|
|
808
|
+
const marketOpenMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
809
|
+
const marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
810
|
+
const minutes = nyEnd.getUTCHours() * 60 + nyEnd.getUTCMinutes();
|
|
811
|
+
if (!calendar.isMarketDay(endDate) ||
|
|
812
|
+
minutes < marketOpenMinutes ||
|
|
813
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
814
|
+
// Before market open, not a market day, or during market hours: use previous market day
|
|
815
|
+
endMarketDay = calendar.getPreviousMarketDay(endDate);
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
// After market close: use today
|
|
819
|
+
endMarketDay = endDate;
|
|
820
|
+
}
|
|
821
|
+
// Get market close for endMarketDay
|
|
822
|
+
const endClose = getMarketOpenClose({ date: endMarketDay }).close;
|
|
823
|
+
// Find start market day by iterating back over market days
|
|
824
|
+
let startMarketDay = endMarketDay;
|
|
825
|
+
let count = Math.max(1, days);
|
|
826
|
+
for (let i = 1; i < count; i++) {
|
|
827
|
+
startMarketDay = calendar.getPreviousMarketDay(startMarketDay);
|
|
828
|
+
}
|
|
829
|
+
// If days > 1, we need to go back (days-1) market days from endMarketDay
|
|
830
|
+
if (days > 1) {
|
|
831
|
+
startMarketDay = endMarketDay;
|
|
832
|
+
for (let i = 1; i < days; i++) {
|
|
833
|
+
startMarketDay = calendar.getPreviousMarketDay(startMarketDay);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
const startOpen = getMarketOpenClose({ date: startMarketDay }).open;
|
|
837
|
+
return { startDate: startOpen, endDate: endClose };
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Counts trading time between two dates (passed as standard Date objects), excluding weekends and holidays, and closed market hours, using other functions in this library.
|
|
841
|
+
*
|
|
842
|
+
* This function calculates the actual trading time between two dates by:
|
|
843
|
+
* 1. Iterating through each calendar day between startDate and endDate (inclusive)
|
|
844
|
+
* 2. For each day that is a market day (not weekend/holiday), getting market open/close times
|
|
845
|
+
* 3. Calculating the overlap between the time range and market hours for that day
|
|
846
|
+
* 4. Summing up all the trading minutes across all days
|
|
847
|
+
*
|
|
848
|
+
* The function automatically handles:
|
|
849
|
+
* - Weekends (Saturday/Sunday) - skipped entirely
|
|
850
|
+
* - Market holidays - skipped entirely
|
|
851
|
+
* - Early close days (e.g. day before holidays) - uses early close time
|
|
852
|
+
* - Times outside market hours - only counts time within 9:30am-4pm ET (or early close)
|
|
853
|
+
*
|
|
854
|
+
* Examples:
|
|
855
|
+
* - 12pm to 3:30pm same day = 3.5 hours = 210 minutes = 0.54 days
|
|
856
|
+
* - 9:30am to 4pm same day = 6.5 hours = 390 minutes = 1 day
|
|
857
|
+
* - Friday 2pm to Monday 2pm = 6.5 hours (Friday 2pm-4pm + Monday 9:30am-2pm)
|
|
858
|
+
*
|
|
859
|
+
* @param startDate - Start date/time
|
|
860
|
+
* @param endDate - End date/time (default: now)
|
|
861
|
+
* @returns Object containing:
|
|
862
|
+
* - days: Trading time as fraction of full trading days (6.5 hours = 1 day)
|
|
863
|
+
* - hours: Trading time in hours
|
|
864
|
+
* - minutes: Trading time in minutes
|
|
865
|
+
*/
|
|
866
|
+
function countTradingDays(startDate, endDate = new Date()) {
|
|
867
|
+
const calendar = new MarketCalendar();
|
|
868
|
+
// Ensure start is before end
|
|
869
|
+
if (startDate.getTime() > endDate.getTime()) {
|
|
870
|
+
throw new Error('Start date must be before end date');
|
|
871
|
+
}
|
|
872
|
+
let totalMinutes = 0;
|
|
873
|
+
// Get the NY dates for iteration
|
|
874
|
+
const startNY = toNYTime(startDate);
|
|
875
|
+
const endNY = toNYTime(endDate);
|
|
876
|
+
// Create date at start of first day (in NY time)
|
|
877
|
+
const currentNY = new Date(Date.UTC(startNY.getUTCFullYear(), startNY.getUTCMonth(), startNY.getUTCDate(), 0, 0, 0, 0));
|
|
878
|
+
// Iterate through each calendar day
|
|
879
|
+
while (currentNY.getTime() <= endNY.getTime()) {
|
|
880
|
+
const currentUTC = fromNYTime(currentNY);
|
|
881
|
+
// Check if this is a market day
|
|
882
|
+
if (calendar.isMarketDay(currentUTC)) {
|
|
883
|
+
// Get market hours for this day
|
|
884
|
+
const marketTimes = getMarketTimes(currentUTC);
|
|
885
|
+
if (marketTimes.marketOpen && marketTimes.open && marketTimes.close) {
|
|
886
|
+
// Calculate the overlap between our time range and market hours
|
|
887
|
+
const dayStart = Math.max(startDate.getTime(), marketTimes.open.getTime());
|
|
888
|
+
const dayEnd = Math.min(endDate.getTime(), marketTimes.close.getTime());
|
|
889
|
+
// Only count if there's actual overlap
|
|
890
|
+
if (dayStart < dayEnd) {
|
|
891
|
+
totalMinutes += (dayEnd - dayStart) / (1000 * 60);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
// Move to next day
|
|
896
|
+
currentNY.setUTCDate(currentNY.getUTCDate() + 1);
|
|
897
|
+
}
|
|
898
|
+
// Convert to days, hours, minutes
|
|
899
|
+
const MINUTES_PER_TRADING_DAY = 390; // 6.5 hours
|
|
900
|
+
const days = totalMinutes / MINUTES_PER_TRADING_DAY;
|
|
901
|
+
const hours = totalMinutes / 60;
|
|
902
|
+
const minutes = totalMinutes;
|
|
903
|
+
return {
|
|
904
|
+
days: Math.round(days * 1000) / 1000, // Round to 3 decimal places
|
|
905
|
+
hours: Math.round(hours * 100) / 100, // Round to 2 decimal places
|
|
906
|
+
minutes: Math.round(minutes),
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
// Export MARKET_TIMES for compatibility
|
|
910
|
+
const MARKET_TIMES = {
|
|
911
|
+
TIMEZONE: MARKET_CONFIG.TIMEZONE,
|
|
912
|
+
PRE: {
|
|
913
|
+
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
914
|
+
END: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
915
|
+
},
|
|
916
|
+
EARLY_MORNING: {
|
|
917
|
+
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
918
|
+
END: { HOUR: 10, MINUTE: 0, MINUTES: 600 },
|
|
919
|
+
},
|
|
920
|
+
EARLY_CLOSE_BEFORE_HOLIDAY: {
|
|
921
|
+
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
922
|
+
END: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
923
|
+
},
|
|
924
|
+
EARLY_EXTENDED_BEFORE_HOLIDAY: {
|
|
925
|
+
START: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
926
|
+
END: { HOUR: 17, MINUTE: 0, MINUTES: 1020 },
|
|
927
|
+
},
|
|
928
|
+
REGULAR: {
|
|
929
|
+
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
930
|
+
END: { HOUR: 16, MINUTE: 0, MINUTES: 960 },
|
|
931
|
+
},
|
|
932
|
+
EXTENDED: {
|
|
933
|
+
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
934
|
+
END: { HOUR: 20, MINUTE: 0, MINUTES: 1200 },
|
|
935
|
+
},
|
|
936
|
+
};
|
|
305
937
|
|
|
306
938
|
/*
|
|
307
939
|
How it works:
|
|
@@ -380,6 +1012,12 @@ class Queue {
|
|
|
380
1012
|
current = current.next;
|
|
381
1013
|
}
|
|
382
1014
|
}
|
|
1015
|
+
|
|
1016
|
+
* drain() {
|
|
1017
|
+
while (this.#head) {
|
|
1018
|
+
yield this.dequeue();
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
383
1021
|
}
|
|
384
1022
|
|
|
385
1023
|
function pLimit(concurrency) {
|
|
@@ -729,7 +1367,7 @@ const safeJSON = (text) => {
|
|
|
729
1367
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
730
1368
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
731
1369
|
|
|
732
|
-
const VERSION = '5.10.
|
|
1370
|
+
const VERSION = '5.10.2'; // x-release-please-version
|
|
733
1371
|
|
|
734
1372
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
735
1373
|
const isRunningInBrowser = () => {
|
|
@@ -12163,7 +12801,7 @@ var config = {};
|
|
|
12163
12801
|
|
|
12164
12802
|
var main = {exports: {}};
|
|
12165
12803
|
|
|
12166
|
-
var version = "17.2.
|
|
12804
|
+
var version = "17.2.1";
|
|
12167
12805
|
var require$$4 = {
|
|
12168
12806
|
version: version};
|
|
12169
12807
|
|
|
@@ -12182,9 +12820,12 @@ function requireMain () {
|
|
|
12182
12820
|
|
|
12183
12821
|
// Array of tips to display randomly
|
|
12184
12822
|
const TIPS = [
|
|
12185
|
-
'🔐 encrypt with
|
|
12823
|
+
'🔐 encrypt with Dotenvx: https://dotenvx.com',
|
|
12186
12824
|
'🔐 prevent committing .env to code: https://dotenvx.com/precommit',
|
|
12187
12825
|
'🔐 prevent building .env in docker: https://dotenvx.com/prebuild',
|
|
12826
|
+
'📡 observe env with Radar: https://dotenvx.com/radar',
|
|
12827
|
+
'📡 auto-backup env with Radar: https://dotenvx.com/radar',
|
|
12828
|
+
'📡 version env with Radar: https://dotenvx.com/radar',
|
|
12188
12829
|
'🛠️ run anywhere with `dotenvx run -- yourcommand`',
|
|
12189
12830
|
'⚙️ specify custom .env file path with { path: \'/custom/path/.env\' }',
|
|
12190
12831
|
'⚙️ enable debug logging with { debug: true }',
|
|
@@ -12485,7 +13126,7 @@ function requireMain () {
|
|
|
12485
13126
|
}
|
|
12486
13127
|
}
|
|
12487
13128
|
|
|
12488
|
-
_log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(
|
|
13129
|
+
_log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(`-- tip: ${_getRandomTip()}`)}`);
|
|
12489
13130
|
}
|
|
12490
13131
|
|
|
12491
13132
|
if (lastError) {
|
|
@@ -13588,12 +14229,25 @@ AlpacaMarketDataAPI.getInstance();
|
|
|
13588
14229
|
|
|
13589
14230
|
const disco = {
|
|
13590
14231
|
time: {
|
|
13591
|
-
|
|
14232
|
+
getStartAndEndDates: getStartAndEndDates,
|
|
14233
|
+
getMarketOpenClose: getMarketOpenClose,
|
|
14234
|
+
getLastFullTradingDate: getLastFullTradingDate,
|
|
14235
|
+
getNextMarketDay: getNextMarketDay,
|
|
14236
|
+
getPreviousMarketDay: getPreviousMarketDay,
|
|
14237
|
+
getMarketTimePeriod: getMarketTimePeriod,
|
|
14238
|
+
getMarketStatus: getMarketStatus,
|
|
14239
|
+
getNYTimeZone: getNYTimeZone,
|
|
14240
|
+
getTradingDate: getTradingDate,
|
|
14241
|
+
getTradingStartAndEndDates: getTradingStartAndEndDates,
|
|
14242
|
+
isMarketDay: isMarketDay,
|
|
14243
|
+
isWithinMarketHours: isWithinMarketHours,
|
|
14244
|
+
countTradingDays: countTradingDays,
|
|
14245
|
+
MARKET_TIMES: MARKET_TIMES,
|
|
14246
|
+
}};
|
|
13592
14247
|
|
|
13593
14248
|
// Test file for context functionality
|
|
13594
14249
|
// Test getNextMarketDay for various scenarios
|
|
13595
14250
|
function testGetNextMarketDay() {
|
|
13596
|
-
const { getNextMarketDay } = disco.time;
|
|
13597
14251
|
const testCases = [
|
|
13598
14252
|
{
|
|
13599
14253
|
label: 'Weekday (Wednesday)',
|
|
@@ -13614,9 +14268,27 @@ function testGetNextMarketDay() {
|
|
|
13614
14268
|
date: '2025-07-03T15:00:00-04:00',
|
|
13615
14269
|
expected: '2025-07-07', // Should skip to next market day
|
|
13616
14270
|
},
|
|
14271
|
+
// Late on 3 July, should skip to next market day
|
|
14272
|
+
{
|
|
14273
|
+
label: 'Late on 3 July (after early close)',
|
|
14274
|
+
date: '2025-07-03T18:00:00-04:00',
|
|
14275
|
+
expected: '2025-07-07', // Should skip to next market day
|
|
14276
|
+
},
|
|
14277
|
+
// during 3 july market hours, should return next monday
|
|
14278
|
+
{
|
|
14279
|
+
label: 'During 3 July market hours',
|
|
14280
|
+
date: '2025-07-03T10:00:00-04:00',
|
|
14281
|
+
expected: '2025-07-07', // Should skip to next market day
|
|
14282
|
+
},
|
|
14283
|
+
// saturday morning, should return next monday
|
|
14284
|
+
{
|
|
14285
|
+
label: 'Saturday morning',
|
|
14286
|
+
date: '2025-07-12T09:00:00-04:00',
|
|
14287
|
+
expected: '2025-07-14', // Should skip to next market day
|
|
14288
|
+
},
|
|
13617
14289
|
];
|
|
13618
14290
|
for (const { label, date, expected } of testCases) {
|
|
13619
|
-
const result = getNextMarketDay({ referenceDate: new Date(date) });
|
|
14291
|
+
const result = disco.time.getNextMarketDay({ referenceDate: new Date(date) });
|
|
13620
14292
|
const yyyymmdd = result.yyyymmdd;
|
|
13621
14293
|
const pass = yyyymmdd === expected;
|
|
13622
14294
|
console.log(`\nTest: ${label}`);
|
|
@@ -13631,6 +14303,9 @@ function testGetNextMarketDay() {
|
|
|
13631
14303
|
// testGetTradingStartAndEndDates();
|
|
13632
14304
|
// testGetLastFullTradingDate();
|
|
13633
14305
|
//testGetMarketOpenClose();
|
|
14306
|
+
// Test countTradingDays function
|
|
13634
14307
|
//testGetNYTimeZone();
|
|
13635
14308
|
testGetNextMarketDay();
|
|
14309
|
+
//testCountTradingDays();
|
|
14310
|
+
// testGetPreviousMarketDay();
|
|
13636
14311
|
//# sourceMappingURL=test.js.map
|