@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/index.cjs
CHANGED
|
@@ -162,6 +162,11 @@ const MARKET_CONFIG = {
|
|
|
162
162
|
},
|
|
163
163
|
};
|
|
164
164
|
// Helper: Get NY offset for a given UTC date (DST rules for US)
|
|
165
|
+
/**
|
|
166
|
+
* Returns the NY timezone offset (in hours) for a given UTC date, accounting for US DST rules.
|
|
167
|
+
* @param date - UTC date
|
|
168
|
+
* @returns offset in hours (-5 for EST, -4 for EDT)
|
|
169
|
+
*/
|
|
165
170
|
function getNYOffset(date) {
|
|
166
171
|
// US DST starts 2nd Sunday in March, ends 1st Sunday in November
|
|
167
172
|
const year = date.getUTCFullYear();
|
|
@@ -174,6 +179,14 @@ function getNYOffset(date) {
|
|
|
174
179
|
return MARKET_CONFIG.UTC_OFFSET_STANDARD;
|
|
175
180
|
}
|
|
176
181
|
// Helper: Get nth weekday of month in UTC
|
|
182
|
+
/**
|
|
183
|
+
* Returns the nth weekday of a given month in UTC.
|
|
184
|
+
* @param year - Year
|
|
185
|
+
* @param month - 1-based month (e.g. March = 3)
|
|
186
|
+
* @param weekday - 0=Sunday, 1=Monday, ...
|
|
187
|
+
* @param n - nth occurrence
|
|
188
|
+
* @returns Date object for the nth weekday
|
|
189
|
+
*/
|
|
177
190
|
function getNthWeekdayOfMonth(year, month, weekday, n) {
|
|
178
191
|
let count = 0;
|
|
179
192
|
for (let d = 1; d <= 31; d++) {
|
|
@@ -190,6 +203,11 @@ function getNthWeekdayOfMonth(year, month, weekday, n) {
|
|
|
190
203
|
return new Date(Date.UTC(year, month - 1, 28));
|
|
191
204
|
}
|
|
192
205
|
// Helper: Convert UTC date to NY time (returns new Date object)
|
|
206
|
+
/**
|
|
207
|
+
* Converts a UTC date to NY time (returns a new Date object).
|
|
208
|
+
* @param date - UTC date
|
|
209
|
+
* @returns Date object in NY time
|
|
210
|
+
*/
|
|
193
211
|
function toNYTime(date) {
|
|
194
212
|
const offset = getNYOffset(date);
|
|
195
213
|
// NY offset in hours
|
|
@@ -198,6 +216,11 @@ function toNYTime(date) {
|
|
|
198
216
|
return new Date(nyMillis);
|
|
199
217
|
}
|
|
200
218
|
// Helper: Convert NY time to UTC (returns new Date object)
|
|
219
|
+
/**
|
|
220
|
+
* Converts a NY time date to UTC (returns a new Date object).
|
|
221
|
+
* @param date - NY time date
|
|
222
|
+
* @returns Date object in UTC
|
|
223
|
+
*/
|
|
201
224
|
function fromNYTime(date) {
|
|
202
225
|
const offset = getNYOffset(date);
|
|
203
226
|
const nyMillis = date.getTime();
|
|
@@ -205,6 +228,12 @@ function fromNYTime(date) {
|
|
|
205
228
|
return new Date(utcMillis);
|
|
206
229
|
}
|
|
207
230
|
// Helper: Format date in ISO, unix, etc.
|
|
231
|
+
/**
|
|
232
|
+
* Formats a date in ISO, unix-seconds, or unix-ms format.
|
|
233
|
+
* @param date - Date object
|
|
234
|
+
* @param outputFormat - Output format ('iso', 'unix-seconds', 'unix-ms')
|
|
235
|
+
* @returns Formatted date string or number
|
|
236
|
+
*/
|
|
208
237
|
function formatDate(date, outputFormat = 'iso') {
|
|
209
238
|
switch (outputFormat) {
|
|
210
239
|
case 'unix-seconds':
|
|
@@ -217,15 +246,33 @@ function formatDate(date, outputFormat = 'iso') {
|
|
|
217
246
|
}
|
|
218
247
|
}
|
|
219
248
|
// Helper: Format date in NY locale string
|
|
249
|
+
/**
|
|
250
|
+
* Formats a date in NY locale string.
|
|
251
|
+
* @param date - Date object
|
|
252
|
+
* @returns NY locale string
|
|
253
|
+
*/
|
|
220
254
|
function formatNYLocale(date) {
|
|
221
255
|
return date.toLocaleString('en-US', { timeZone: 'America/New_York' });
|
|
222
256
|
}
|
|
223
257
|
// Market calendar logic
|
|
258
|
+
/**
|
|
259
|
+
* Market calendar logic for holidays, weekends, and market days.
|
|
260
|
+
*/
|
|
224
261
|
class MarketCalendar {
|
|
262
|
+
/**
|
|
263
|
+
* Checks if a date is a weekend in NY time.
|
|
264
|
+
* @param date - Date object
|
|
265
|
+
* @returns true if weekend, false otherwise
|
|
266
|
+
*/
|
|
225
267
|
isWeekend(date) {
|
|
226
268
|
const day = toNYTime(date).getUTCDay();
|
|
227
269
|
return day === 0 || day === 6;
|
|
228
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Checks if a date is a market holiday in NY time.
|
|
273
|
+
* @param date - Date object
|
|
274
|
+
* @returns true if holiday, false otherwise
|
|
275
|
+
*/
|
|
229
276
|
isHoliday(date) {
|
|
230
277
|
const nyDate = toNYTime(date);
|
|
231
278
|
const year = nyDate.getUTCFullYear();
|
|
@@ -235,8 +282,13 @@ class MarketCalendar {
|
|
|
235
282
|
const yearHolidays = marketHolidays[year];
|
|
236
283
|
if (!yearHolidays)
|
|
237
284
|
return false;
|
|
238
|
-
return Object.values(yearHolidays).some(holiday => holiday.date === formattedDate);
|
|
285
|
+
return Object.values(yearHolidays).some((holiday) => holiday.date === formattedDate);
|
|
239
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* Checks if a date is an early close day in NY time.
|
|
289
|
+
* @param date - Date object
|
|
290
|
+
* @returns true if early close, false otherwise
|
|
291
|
+
*/
|
|
240
292
|
isEarlyCloseDay(date) {
|
|
241
293
|
const nyDate = toNYTime(date);
|
|
242
294
|
const year = nyDate.getUTCFullYear();
|
|
@@ -246,6 +298,11 @@ class MarketCalendar {
|
|
|
246
298
|
const yearEarlyCloses = marketEarlyCloses[year];
|
|
247
299
|
return yearEarlyCloses && yearEarlyCloses[formattedDate] !== undefined;
|
|
248
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Gets the early close time (in minutes from midnight) for a given date.
|
|
303
|
+
* @param date - Date object
|
|
304
|
+
* @returns minutes from midnight or null
|
|
305
|
+
*/
|
|
249
306
|
getEarlyCloseTime(date) {
|
|
250
307
|
const nyDate = toNYTime(date);
|
|
251
308
|
const year = nyDate.getUTCFullYear();
|
|
@@ -259,9 +316,19 @@ class MarketCalendar {
|
|
|
259
316
|
}
|
|
260
317
|
return null;
|
|
261
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Checks if a date is a market day (not weekend or holiday).
|
|
321
|
+
* @param date - Date object
|
|
322
|
+
* @returns true if market day, false otherwise
|
|
323
|
+
*/
|
|
262
324
|
isMarketDay(date) {
|
|
263
325
|
return !this.isWeekend(date) && !this.isHoliday(date);
|
|
264
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* Gets the next market day after the given date.
|
|
329
|
+
* @param date - Date object
|
|
330
|
+
* @returns Date object for next market day
|
|
331
|
+
*/
|
|
265
332
|
getNextMarketDay(date) {
|
|
266
333
|
let nextDay = new Date(date.getTime() + 24 * 60 * 60 * 1000);
|
|
267
334
|
while (!this.isMarketDay(nextDay)) {
|
|
@@ -269,6 +336,11 @@ class MarketCalendar {
|
|
|
269
336
|
}
|
|
270
337
|
return nextDay;
|
|
271
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Gets the previous market day before the given date.
|
|
341
|
+
* @param date - Date object
|
|
342
|
+
* @returns Date object for previous market day
|
|
343
|
+
*/
|
|
272
344
|
getPreviousMarketDay(date) {
|
|
273
345
|
let prevDay = new Date(date.getTime() - 24 * 60 * 60 * 1000);
|
|
274
346
|
while (!this.isMarketDay(prevDay)) {
|
|
@@ -278,6 +350,11 @@ class MarketCalendar {
|
|
|
278
350
|
}
|
|
279
351
|
}
|
|
280
352
|
// Market open/close times
|
|
353
|
+
/**
|
|
354
|
+
* Returns market open/close times for a given date, including extended and early closes.
|
|
355
|
+
* @param date - Date object
|
|
356
|
+
* @returns MarketOpenCloseResult
|
|
357
|
+
*/
|
|
281
358
|
function getMarketTimes(date) {
|
|
282
359
|
const calendar = new MarketCalendar();
|
|
283
360
|
const nyDate = toNYTime(date);
|
|
@@ -315,7 +392,13 @@ function getMarketTimes(date) {
|
|
|
315
392
|
};
|
|
316
393
|
}
|
|
317
394
|
// Is within market hours
|
|
318
|
-
|
|
395
|
+
/**
|
|
396
|
+
* Checks if a date/time is within market hours, extended hours, or continuous.
|
|
397
|
+
* @param date - Date object
|
|
398
|
+
* @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
|
|
399
|
+
* @returns true if within hours, false otherwise
|
|
400
|
+
*/
|
|
401
|
+
function isWithinMarketHours(date, intradayReporting = 'market_hours') {
|
|
319
402
|
const calendar = new MarketCalendar();
|
|
320
403
|
if (!calendar.isMarketDay(date))
|
|
321
404
|
return false;
|
|
@@ -343,6 +426,11 @@ function isWithinMarketHoursImpl(date, intradayReporting = 'market_hours') {
|
|
|
343
426
|
}
|
|
344
427
|
}
|
|
345
428
|
// Get last full trading date
|
|
429
|
+
/**
|
|
430
|
+
* Returns the last full trading date (market close) for a given date.
|
|
431
|
+
* @param currentDate - Date object (default: now)
|
|
432
|
+
* @returns Date object for last full trading date
|
|
433
|
+
*/
|
|
346
434
|
function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
347
435
|
const calendar = new MarketCalendar();
|
|
348
436
|
const nyDate = toNYTime(currentDate);
|
|
@@ -353,7 +441,9 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
353
441
|
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
354
442
|
}
|
|
355
443
|
// If not a market day, or before open, or during market hours, return previous market day's close
|
|
356
|
-
if (!calendar.isMarketDay(currentDate) ||
|
|
444
|
+
if (!calendar.isMarketDay(currentDate) ||
|
|
445
|
+
minutes < marketOpenMinutes ||
|
|
446
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
357
447
|
const prevMarketDay = calendar.getPreviousMarketDay(currentDate);
|
|
358
448
|
let prevCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
359
449
|
if (calendar.isEarlyCloseDay(prevMarketDay)) {
|
|
@@ -375,6 +465,12 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
375
465
|
return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
|
|
376
466
|
}
|
|
377
467
|
// Get day boundaries
|
|
468
|
+
/**
|
|
469
|
+
* Returns the start and end boundaries for a market day, extended hours, or continuous.
|
|
470
|
+
* @param date - Date object
|
|
471
|
+
* @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
|
|
472
|
+
* @returns Object with start and end Date
|
|
473
|
+
*/
|
|
378
474
|
function getDayBoundaries(date, intradayReporting = 'market_hours') {
|
|
379
475
|
const calendar = new MarketCalendar();
|
|
380
476
|
const nyDate = toNYTime(date);
|
|
@@ -410,6 +506,12 @@ function getDayBoundaries(date, intradayReporting = 'market_hours') {
|
|
|
410
506
|
return { start, end };
|
|
411
507
|
}
|
|
412
508
|
// Period calculator
|
|
509
|
+
/**
|
|
510
|
+
* Calculates the start date for a given period ending at endDate.
|
|
511
|
+
* @param endDate - Date object
|
|
512
|
+
* @param period - Period string
|
|
513
|
+
* @returns Date object for period start
|
|
514
|
+
*/
|
|
413
515
|
function calculatePeriodStartDate(endDate, period) {
|
|
414
516
|
const calendar = new MarketCalendar();
|
|
415
517
|
let startDate;
|
|
@@ -451,8 +553,13 @@ function calculatePeriodStartDate(endDate, period) {
|
|
|
451
553
|
return startDate;
|
|
452
554
|
}
|
|
453
555
|
// Get market time period
|
|
556
|
+
/**
|
|
557
|
+
* Returns the start and end dates for a market time period.
|
|
558
|
+
* @param params - MarketTimeParams
|
|
559
|
+
* @returns PeriodDates object
|
|
560
|
+
*/
|
|
454
561
|
function getMarketTimePeriod(params) {
|
|
455
|
-
const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso'
|
|
562
|
+
const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso' } = params;
|
|
456
563
|
if (!period)
|
|
457
564
|
throw new Error('Period is required');
|
|
458
565
|
const calendar = new MarketCalendar();
|
|
@@ -497,6 +604,11 @@ function getMarketTimePeriod(params) {
|
|
|
497
604
|
};
|
|
498
605
|
}
|
|
499
606
|
// Market status
|
|
607
|
+
/**
|
|
608
|
+
* Returns the current market status for a given date.
|
|
609
|
+
* @param date - Date object (default: now)
|
|
610
|
+
* @returns MarketStatus object
|
|
611
|
+
*/
|
|
500
612
|
function getMarketStatusImpl(date = new Date()) {
|
|
501
613
|
const calendar = new MarketCalendar();
|
|
502
614
|
const nyDate = toNYTime(date);
|
|
@@ -510,7 +622,8 @@ function getMarketStatusImpl(date = new Date()) {
|
|
|
510
622
|
let extendedEndMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
|
|
511
623
|
if (isEarlyCloseDay) {
|
|
512
624
|
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
513
|
-
extendedEndMinutes =
|
|
625
|
+
extendedEndMinutes =
|
|
626
|
+
MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
|
|
514
627
|
}
|
|
515
628
|
let status;
|
|
516
629
|
let nextStatus;
|
|
@@ -567,10 +680,20 @@ function getMarketStatusImpl(date = new Date()) {
|
|
|
567
680
|
};
|
|
568
681
|
}
|
|
569
682
|
// API exports
|
|
683
|
+
/**
|
|
684
|
+
* Returns market open/close times for a given date.
|
|
685
|
+
* @param options - { date?: Date }
|
|
686
|
+
* @returns MarketOpenCloseResult
|
|
687
|
+
*/
|
|
570
688
|
function getMarketOpenClose(options = {}) {
|
|
571
689
|
const { date = new Date() } = options;
|
|
572
690
|
return getMarketTimes(date);
|
|
573
691
|
}
|
|
692
|
+
/**
|
|
693
|
+
* Returns the start and end dates for a market time period as Date objects.
|
|
694
|
+
* @param params - MarketTimeParams
|
|
695
|
+
* @returns Object with start and end Date
|
|
696
|
+
*/
|
|
574
697
|
function getStartAndEndDates(params = {}) {
|
|
575
698
|
const { start, end } = getMarketTimePeriod(params);
|
|
576
699
|
return {
|
|
@@ -581,46 +704,103 @@ function getStartAndEndDates(params = {}) {
|
|
|
581
704
|
/**
|
|
582
705
|
* Returns the last full trading date as a Date object.
|
|
583
706
|
*/
|
|
707
|
+
/**
|
|
708
|
+
* Returns the last full trading date as a Date object.
|
|
709
|
+
* @param currentDate - Date object (default: now)
|
|
710
|
+
* @returns Date object for last full trading date
|
|
711
|
+
*/
|
|
584
712
|
function getLastFullTradingDate(currentDate = new Date()) {
|
|
585
713
|
return getLastFullTradingDateImpl(currentDate);
|
|
586
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Returns the next market day after the reference date.
|
|
717
|
+
* @param referenceDate - Date object (default: now)
|
|
718
|
+
* @returns Object with date, yyyymmdd string, and ISO string
|
|
719
|
+
*/
|
|
587
720
|
function getNextMarketDay({ referenceDate } = {}) {
|
|
588
721
|
const calendar = new MarketCalendar();
|
|
589
|
-
const startDate = referenceDate
|
|
722
|
+
const startDate = referenceDate ?? new Date();
|
|
723
|
+
// Find the next trading day (UTC Date object)
|
|
590
724
|
const nextDate = calendar.getNextMarketDay(startDate);
|
|
591
|
-
|
|
725
|
+
// Convert to NY time before extracting Y-M-D parts
|
|
726
|
+
const nyNext = toNYTime(nextDate);
|
|
727
|
+
const yyyymmdd = `${nyNext.getUTCFullYear()}-${String(nyNext.getUTCMonth() + 1).padStart(2, '0')}-${String(nyNext.getUTCDate()).padStart(2, '0')}`;
|
|
592
728
|
return {
|
|
593
|
-
date: nextDate,
|
|
594
|
-
yyyymmdd,
|
|
729
|
+
date: nextDate, // raw Date, unchanged
|
|
730
|
+
yyyymmdd, // correct trading date string
|
|
595
731
|
dateISOString: nextDate.toISOString(),
|
|
596
732
|
};
|
|
597
733
|
}
|
|
734
|
+
/**
|
|
735
|
+
* Returns the previous market day before the reference date.
|
|
736
|
+
* @param referenceDate - Date object (default: now)
|
|
737
|
+
* @returns Object with date, yyyymmdd string, and ISO string
|
|
738
|
+
*/
|
|
739
|
+
function getPreviousMarketDay({ referenceDate } = {}) {
|
|
740
|
+
const calendar = new MarketCalendar();
|
|
741
|
+
const startDate = referenceDate || new Date();
|
|
742
|
+
const prevDate = calendar.getPreviousMarketDay(startDate);
|
|
743
|
+
// convert to NY time first
|
|
744
|
+
const nyPrev = toNYTime(prevDate); // ← already in this file
|
|
745
|
+
const yyyymmdd = `${nyPrev.getUTCFullYear()}-${String(nyPrev.getUTCMonth() + 1).padStart(2, '0')}-${String(nyPrev.getUTCDate()).padStart(2, '0')}`;
|
|
746
|
+
return {
|
|
747
|
+
date: prevDate,
|
|
748
|
+
yyyymmdd,
|
|
749
|
+
dateISOString: prevDate.toISOString(),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
598
752
|
/**
|
|
599
753
|
* Returns the trading date for a given time. Note: Just trims the date string; does not validate if the date is a market day.
|
|
600
754
|
* @param time - a string, number (unix timestamp), or Date object representing the time
|
|
601
755
|
* @returns the trading date as a string in YYYY-MM-DD format
|
|
602
756
|
*/
|
|
757
|
+
/**
|
|
758
|
+
* Returns the trading date for a given time in YYYY-MM-DD format (NY time).
|
|
759
|
+
* @param time - string, number, or Date
|
|
760
|
+
* @returns trading date string
|
|
761
|
+
*/
|
|
603
762
|
function getTradingDate(time) {
|
|
604
763
|
const date = typeof time === 'number' ? new Date(time) : typeof time === 'string' ? new Date(time) : time;
|
|
605
764
|
const nyDate = toNYTime(date);
|
|
606
765
|
return `${nyDate.getUTCFullYear()}-${String(nyDate.getUTCMonth() + 1).padStart(2, '0')}-${String(nyDate.getUTCDate()).padStart(2, '0')}`;
|
|
607
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* Returns the NY timezone offset string for a given date.
|
|
769
|
+
* @param date - Date object (default: now)
|
|
770
|
+
* @returns '-04:00' for EDT, '-05:00' for EST
|
|
771
|
+
*/
|
|
608
772
|
function getNYTimeZone(date) {
|
|
609
773
|
const offset = getNYOffset(date || new Date());
|
|
610
774
|
return offset === -4 ? '-04:00' : '-05:00';
|
|
611
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* Returns the current market status for a given date.
|
|
778
|
+
* @param options - { date?: Date }
|
|
779
|
+
* @returns MarketStatus object
|
|
780
|
+
*/
|
|
612
781
|
function getMarketStatus(options = {}) {
|
|
613
782
|
const { date = new Date() } = options;
|
|
614
783
|
return getMarketStatusImpl(date);
|
|
615
784
|
}
|
|
616
|
-
|
|
617
|
-
|
|
785
|
+
/**
|
|
786
|
+
* Checks if a date is a market day.
|
|
787
|
+
* @param date - Date object
|
|
788
|
+
* @returns true if market day, false otherwise
|
|
789
|
+
*/
|
|
790
|
+
function isMarketDay(date) {
|
|
791
|
+
const calendar = new MarketCalendar();
|
|
792
|
+
return calendar.isMarketDay(date);
|
|
618
793
|
}
|
|
619
794
|
/**
|
|
620
795
|
* Returns full trading days from market open to market close.
|
|
621
796
|
* endDate is always the most recent market close (previous day's close if before open, today's close if after open).
|
|
622
797
|
* days: 1 or not specified = that day's open; 2 = previous market day's open, etc.
|
|
623
798
|
*/
|
|
799
|
+
/**
|
|
800
|
+
* Returns full trading days from market open to market close.
|
|
801
|
+
* @param options - { endDate?: Date, days?: number }
|
|
802
|
+
* @returns Object with startDate and endDate
|
|
803
|
+
*/
|
|
624
804
|
function getTradingStartAndEndDates(options = {}) {
|
|
625
805
|
const { endDate = new Date(), days = 1 } = options;
|
|
626
806
|
const calendar = new MarketCalendar();
|
|
@@ -630,7 +810,9 @@ function getTradingStartAndEndDates(options = {}) {
|
|
|
630
810
|
const marketOpenMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
631
811
|
const marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
632
812
|
const minutes = nyEnd.getUTCHours() * 60 + nyEnd.getUTCMinutes();
|
|
633
|
-
if (!calendar.isMarketDay(endDate) ||
|
|
813
|
+
if (!calendar.isMarketDay(endDate) ||
|
|
814
|
+
minutes < marketOpenMinutes ||
|
|
815
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
634
816
|
// Before market open, not a market day, or during market hours: use previous market day
|
|
635
817
|
endMarketDay = calendar.getPreviousMarketDay(endDate);
|
|
636
818
|
}
|
|
@@ -656,32 +838,102 @@ function getTradingStartAndEndDates(options = {}) {
|
|
|
656
838
|
const startOpen = getMarketOpenClose({ date: startMarketDay }).open;
|
|
657
839
|
return { startDate: startOpen, endDate: endClose };
|
|
658
840
|
}
|
|
841
|
+
/**
|
|
842
|
+
* 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.
|
|
843
|
+
*
|
|
844
|
+
* This function calculates the actual trading time between two dates by:
|
|
845
|
+
* 1. Iterating through each calendar day between startDate and endDate (inclusive)
|
|
846
|
+
* 2. For each day that is a market day (not weekend/holiday), getting market open/close times
|
|
847
|
+
* 3. Calculating the overlap between the time range and market hours for that day
|
|
848
|
+
* 4. Summing up all the trading minutes across all days
|
|
849
|
+
*
|
|
850
|
+
* The function automatically handles:
|
|
851
|
+
* - Weekends (Saturday/Sunday) - skipped entirely
|
|
852
|
+
* - Market holidays - skipped entirely
|
|
853
|
+
* - Early close days (e.g. day before holidays) - uses early close time
|
|
854
|
+
* - Times outside market hours - only counts time within 9:30am-4pm ET (or early close)
|
|
855
|
+
*
|
|
856
|
+
* Examples:
|
|
857
|
+
* - 12pm to 3:30pm same day = 3.5 hours = 210 minutes = 0.54 days
|
|
858
|
+
* - 9:30am to 4pm same day = 6.5 hours = 390 minutes = 1 day
|
|
859
|
+
* - Friday 2pm to Monday 2pm = 6.5 hours (Friday 2pm-4pm + Monday 9:30am-2pm)
|
|
860
|
+
*
|
|
861
|
+
* @param startDate - Start date/time
|
|
862
|
+
* @param endDate - End date/time (default: now)
|
|
863
|
+
* @returns Object containing:
|
|
864
|
+
* - days: Trading time as fraction of full trading days (6.5 hours = 1 day)
|
|
865
|
+
* - hours: Trading time in hours
|
|
866
|
+
* - minutes: Trading time in minutes
|
|
867
|
+
*/
|
|
868
|
+
function countTradingDays(startDate, endDate = new Date()) {
|
|
869
|
+
const calendar = new MarketCalendar();
|
|
870
|
+
// Ensure start is before end
|
|
871
|
+
if (startDate.getTime() > endDate.getTime()) {
|
|
872
|
+
throw new Error('Start date must be before end date');
|
|
873
|
+
}
|
|
874
|
+
let totalMinutes = 0;
|
|
875
|
+
// Get the NY dates for iteration
|
|
876
|
+
const startNY = toNYTime(startDate);
|
|
877
|
+
const endNY = toNYTime(endDate);
|
|
878
|
+
// Create date at start of first day (in NY time)
|
|
879
|
+
const currentNY = new Date(Date.UTC(startNY.getUTCFullYear(), startNY.getUTCMonth(), startNY.getUTCDate(), 0, 0, 0, 0));
|
|
880
|
+
// Iterate through each calendar day
|
|
881
|
+
while (currentNY.getTime() <= endNY.getTime()) {
|
|
882
|
+
const currentUTC = fromNYTime(currentNY);
|
|
883
|
+
// Check if this is a market day
|
|
884
|
+
if (calendar.isMarketDay(currentUTC)) {
|
|
885
|
+
// Get market hours for this day
|
|
886
|
+
const marketTimes = getMarketTimes(currentUTC);
|
|
887
|
+
if (marketTimes.marketOpen && marketTimes.open && marketTimes.close) {
|
|
888
|
+
// Calculate the overlap between our time range and market hours
|
|
889
|
+
const dayStart = Math.max(startDate.getTime(), marketTimes.open.getTime());
|
|
890
|
+
const dayEnd = Math.min(endDate.getTime(), marketTimes.close.getTime());
|
|
891
|
+
// Only count if there's actual overlap
|
|
892
|
+
if (dayStart < dayEnd) {
|
|
893
|
+
totalMinutes += (dayEnd - dayStart) / (1000 * 60);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
// Move to next day
|
|
898
|
+
currentNY.setUTCDate(currentNY.getUTCDate() + 1);
|
|
899
|
+
}
|
|
900
|
+
// Convert to days, hours, minutes
|
|
901
|
+
const MINUTES_PER_TRADING_DAY = 390; // 6.5 hours
|
|
902
|
+
const days = totalMinutes / MINUTES_PER_TRADING_DAY;
|
|
903
|
+
const hours = totalMinutes / 60;
|
|
904
|
+
const minutes = totalMinutes;
|
|
905
|
+
return {
|
|
906
|
+
days: Math.round(days * 1000) / 1000, // Round to 3 decimal places
|
|
907
|
+
hours: Math.round(hours * 100) / 100, // Round to 2 decimal places
|
|
908
|
+
minutes: Math.round(minutes),
|
|
909
|
+
};
|
|
910
|
+
}
|
|
659
911
|
// Export MARKET_TIMES for compatibility
|
|
660
912
|
const MARKET_TIMES = {
|
|
661
913
|
TIMEZONE: MARKET_CONFIG.TIMEZONE,
|
|
662
914
|
PRE: {
|
|
663
915
|
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
664
|
-
END: { HOUR: 9, MINUTE: 30, MINUTES: 570 }
|
|
916
|
+
END: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
665
917
|
},
|
|
666
918
|
EARLY_MORNING: {
|
|
667
919
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
668
|
-
END: { HOUR: 10, MINUTE: 0, MINUTES: 600 }
|
|
920
|
+
END: { HOUR: 10, MINUTE: 0, MINUTES: 600 },
|
|
669
921
|
},
|
|
670
922
|
EARLY_CLOSE_BEFORE_HOLIDAY: {
|
|
671
923
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
672
|
-
END: { HOUR: 13, MINUTE: 0, MINUTES: 780 }
|
|
924
|
+
END: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
673
925
|
},
|
|
674
926
|
EARLY_EXTENDED_BEFORE_HOLIDAY: {
|
|
675
927
|
START: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
676
|
-
END: { HOUR: 17, MINUTE: 0, MINUTES: 1020 }
|
|
928
|
+
END: { HOUR: 17, MINUTE: 0, MINUTES: 1020 },
|
|
677
929
|
},
|
|
678
930
|
REGULAR: {
|
|
679
931
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
680
|
-
END: { HOUR: 16, MINUTE: 0, MINUTES: 960 }
|
|
932
|
+
END: { HOUR: 16, MINUTE: 0, MINUTES: 960 },
|
|
681
933
|
},
|
|
682
934
|
EXTENDED: {
|
|
683
935
|
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
684
|
-
END: { HOUR: 20, MINUTE: 0, MINUTES: 1200 }
|
|
936
|
+
END: { HOUR: 20, MINUTE: 0, MINUTES: 1200 },
|
|
685
937
|
},
|
|
686
938
|
};
|
|
687
939
|
|
|
@@ -1083,6 +1335,12 @@ class Queue {
|
|
|
1083
1335
|
current = current.next;
|
|
1084
1336
|
}
|
|
1085
1337
|
}
|
|
1338
|
+
|
|
1339
|
+
* drain() {
|
|
1340
|
+
while (this.#head) {
|
|
1341
|
+
yield this.dequeue();
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1086
1344
|
}
|
|
1087
1345
|
|
|
1088
1346
|
function pLimit(concurrency) {
|
|
@@ -2112,7 +2370,7 @@ const safeJSON = (text) => {
|
|
|
2112
2370
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2113
2371
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2114
2372
|
|
|
2115
|
-
const VERSION = '5.10.
|
|
2373
|
+
const VERSION = '5.10.2'; // x-release-please-version
|
|
2116
2374
|
|
|
2117
2375
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2118
2376
|
const isRunningInBrowser = () => {
|
|
@@ -15232,7 +15490,7 @@ var config = {};
|
|
|
15232
15490
|
|
|
15233
15491
|
var main = {exports: {}};
|
|
15234
15492
|
|
|
15235
|
-
var version = "17.2.
|
|
15493
|
+
var version = "17.2.1";
|
|
15236
15494
|
var require$$4 = {
|
|
15237
15495
|
version: version};
|
|
15238
15496
|
|
|
@@ -15251,9 +15509,12 @@ function requireMain () {
|
|
|
15251
15509
|
|
|
15252
15510
|
// Array of tips to display randomly
|
|
15253
15511
|
const TIPS = [
|
|
15254
|
-
'🔐 encrypt with
|
|
15512
|
+
'🔐 encrypt with Dotenvx: https://dotenvx.com',
|
|
15255
15513
|
'🔐 prevent committing .env to code: https://dotenvx.com/precommit',
|
|
15256
15514
|
'🔐 prevent building .env in docker: https://dotenvx.com/prebuild',
|
|
15515
|
+
'📡 observe env with Radar: https://dotenvx.com/radar',
|
|
15516
|
+
'📡 auto-backup env with Radar: https://dotenvx.com/radar',
|
|
15517
|
+
'📡 version env with Radar: https://dotenvx.com/radar',
|
|
15257
15518
|
'🛠️ run anywhere with `dotenvx run -- yourcommand`',
|
|
15258
15519
|
'⚙️ specify custom .env file path with { path: \'/custom/path/.env\' }',
|
|
15259
15520
|
'⚙️ enable debug logging with { debug: true }',
|
|
@@ -15554,7 +15815,7 @@ function requireMain () {
|
|
|
15554
15815
|
}
|
|
15555
15816
|
}
|
|
15556
15817
|
|
|
15557
|
-
_log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(
|
|
15818
|
+
_log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(`-- tip: ${_getRandomTip()}`)}`);
|
|
15558
15819
|
}
|
|
15559
15820
|
|
|
15560
15821
|
if (lastError) {
|
|
@@ -18035,10 +18296,15 @@ const disco = {
|
|
|
18035
18296
|
getMarketOpenClose: getMarketOpenClose,
|
|
18036
18297
|
getLastFullTradingDate: getLastFullTradingDate,
|
|
18037
18298
|
getNextMarketDay: getNextMarketDay,
|
|
18299
|
+
getPreviousMarketDay: getPreviousMarketDay,
|
|
18300
|
+
getMarketTimePeriod: getMarketTimePeriod,
|
|
18038
18301
|
getMarketStatus: getMarketStatus,
|
|
18039
18302
|
getNYTimeZone: getNYTimeZone,
|
|
18040
18303
|
getTradingDate: getTradingDate,
|
|
18041
18304
|
getTradingStartAndEndDates: getTradingStartAndEndDates,
|
|
18305
|
+
isMarketDay: isMarketDay,
|
|
18306
|
+
isWithinMarketHours: isWithinMarketHours,
|
|
18307
|
+
countTradingDays: countTradingDays,
|
|
18042
18308
|
MARKET_TIMES: MARKET_TIMES,
|
|
18043
18309
|
},
|
|
18044
18310
|
utils: {
|