@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.mjs
CHANGED
|
@@ -160,6 +160,11 @@ const MARKET_CONFIG = {
|
|
|
160
160
|
},
|
|
161
161
|
};
|
|
162
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
|
+
*/
|
|
163
168
|
function getNYOffset(date) {
|
|
164
169
|
// US DST starts 2nd Sunday in March, ends 1st Sunday in November
|
|
165
170
|
const year = date.getUTCFullYear();
|
|
@@ -172,6 +177,14 @@ function getNYOffset(date) {
|
|
|
172
177
|
return MARKET_CONFIG.UTC_OFFSET_STANDARD;
|
|
173
178
|
}
|
|
174
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
|
+
*/
|
|
175
188
|
function getNthWeekdayOfMonth(year, month, weekday, n) {
|
|
176
189
|
let count = 0;
|
|
177
190
|
for (let d = 1; d <= 31; d++) {
|
|
@@ -188,6 +201,11 @@ function getNthWeekdayOfMonth(year, month, weekday, n) {
|
|
|
188
201
|
return new Date(Date.UTC(year, month - 1, 28));
|
|
189
202
|
}
|
|
190
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
|
+
*/
|
|
191
209
|
function toNYTime(date) {
|
|
192
210
|
const offset = getNYOffset(date);
|
|
193
211
|
// NY offset in hours
|
|
@@ -196,6 +214,11 @@ function toNYTime(date) {
|
|
|
196
214
|
return new Date(nyMillis);
|
|
197
215
|
}
|
|
198
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
|
+
*/
|
|
199
222
|
function fromNYTime(date) {
|
|
200
223
|
const offset = getNYOffset(date);
|
|
201
224
|
const nyMillis = date.getTime();
|
|
@@ -203,6 +226,12 @@ function fromNYTime(date) {
|
|
|
203
226
|
return new Date(utcMillis);
|
|
204
227
|
}
|
|
205
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
|
+
*/
|
|
206
235
|
function formatDate(date, outputFormat = 'iso') {
|
|
207
236
|
switch (outputFormat) {
|
|
208
237
|
case 'unix-seconds':
|
|
@@ -215,15 +244,33 @@ function formatDate(date, outputFormat = 'iso') {
|
|
|
215
244
|
}
|
|
216
245
|
}
|
|
217
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
|
+
*/
|
|
218
252
|
function formatNYLocale(date) {
|
|
219
253
|
return date.toLocaleString('en-US', { timeZone: 'America/New_York' });
|
|
220
254
|
}
|
|
221
255
|
// Market calendar logic
|
|
256
|
+
/**
|
|
257
|
+
* Market calendar logic for holidays, weekends, and market days.
|
|
258
|
+
*/
|
|
222
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
|
+
*/
|
|
223
265
|
isWeekend(date) {
|
|
224
266
|
const day = toNYTime(date).getUTCDay();
|
|
225
267
|
return day === 0 || day === 6;
|
|
226
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
|
+
*/
|
|
227
274
|
isHoliday(date) {
|
|
228
275
|
const nyDate = toNYTime(date);
|
|
229
276
|
const year = nyDate.getUTCFullYear();
|
|
@@ -233,8 +280,13 @@ class MarketCalendar {
|
|
|
233
280
|
const yearHolidays = marketHolidays[year];
|
|
234
281
|
if (!yearHolidays)
|
|
235
282
|
return false;
|
|
236
|
-
return Object.values(yearHolidays).some(holiday => holiday.date === formattedDate);
|
|
283
|
+
return Object.values(yearHolidays).some((holiday) => holiday.date === formattedDate);
|
|
237
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
|
+
*/
|
|
238
290
|
isEarlyCloseDay(date) {
|
|
239
291
|
const nyDate = toNYTime(date);
|
|
240
292
|
const year = nyDate.getUTCFullYear();
|
|
@@ -244,6 +296,11 @@ class MarketCalendar {
|
|
|
244
296
|
const yearEarlyCloses = marketEarlyCloses[year];
|
|
245
297
|
return yearEarlyCloses && yearEarlyCloses[formattedDate] !== undefined;
|
|
246
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
|
+
*/
|
|
247
304
|
getEarlyCloseTime(date) {
|
|
248
305
|
const nyDate = toNYTime(date);
|
|
249
306
|
const year = nyDate.getUTCFullYear();
|
|
@@ -257,9 +314,19 @@ class MarketCalendar {
|
|
|
257
314
|
}
|
|
258
315
|
return null;
|
|
259
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
|
+
*/
|
|
260
322
|
isMarketDay(date) {
|
|
261
323
|
return !this.isWeekend(date) && !this.isHoliday(date);
|
|
262
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
|
+
*/
|
|
263
330
|
getNextMarketDay(date) {
|
|
264
331
|
let nextDay = new Date(date.getTime() + 24 * 60 * 60 * 1000);
|
|
265
332
|
while (!this.isMarketDay(nextDay)) {
|
|
@@ -267,6 +334,11 @@ class MarketCalendar {
|
|
|
267
334
|
}
|
|
268
335
|
return nextDay;
|
|
269
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
|
+
*/
|
|
270
342
|
getPreviousMarketDay(date) {
|
|
271
343
|
let prevDay = new Date(date.getTime() - 24 * 60 * 60 * 1000);
|
|
272
344
|
while (!this.isMarketDay(prevDay)) {
|
|
@@ -276,6 +348,11 @@ class MarketCalendar {
|
|
|
276
348
|
}
|
|
277
349
|
}
|
|
278
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
|
+
*/
|
|
279
356
|
function getMarketTimes(date) {
|
|
280
357
|
const calendar = new MarketCalendar();
|
|
281
358
|
const nyDate = toNYTime(date);
|
|
@@ -313,7 +390,13 @@ function getMarketTimes(date) {
|
|
|
313
390
|
};
|
|
314
391
|
}
|
|
315
392
|
// Is within market hours
|
|
316
|
-
|
|
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') {
|
|
317
400
|
const calendar = new MarketCalendar();
|
|
318
401
|
if (!calendar.isMarketDay(date))
|
|
319
402
|
return false;
|
|
@@ -341,6 +424,11 @@ function isWithinMarketHoursImpl(date, intradayReporting = 'market_hours') {
|
|
|
341
424
|
}
|
|
342
425
|
}
|
|
343
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
|
+
*/
|
|
344
432
|
function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
345
433
|
const calendar = new MarketCalendar();
|
|
346
434
|
const nyDate = toNYTime(currentDate);
|
|
@@ -351,7 +439,9 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
351
439
|
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
352
440
|
}
|
|
353
441
|
// If not a market day, or before open, or during market hours, return previous market day's close
|
|
354
|
-
if (!calendar.isMarketDay(currentDate) ||
|
|
442
|
+
if (!calendar.isMarketDay(currentDate) ||
|
|
443
|
+
minutes < marketOpenMinutes ||
|
|
444
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
355
445
|
const prevMarketDay = calendar.getPreviousMarketDay(currentDate);
|
|
356
446
|
let prevCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
357
447
|
if (calendar.isEarlyCloseDay(prevMarketDay)) {
|
|
@@ -373,6 +463,12 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
373
463
|
return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
|
|
374
464
|
}
|
|
375
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
|
+
*/
|
|
376
472
|
function getDayBoundaries(date, intradayReporting = 'market_hours') {
|
|
377
473
|
const calendar = new MarketCalendar();
|
|
378
474
|
const nyDate = toNYTime(date);
|
|
@@ -408,6 +504,12 @@ function getDayBoundaries(date, intradayReporting = 'market_hours') {
|
|
|
408
504
|
return { start, end };
|
|
409
505
|
}
|
|
410
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
|
+
*/
|
|
411
513
|
function calculatePeriodStartDate(endDate, period) {
|
|
412
514
|
const calendar = new MarketCalendar();
|
|
413
515
|
let startDate;
|
|
@@ -449,8 +551,13 @@ function calculatePeriodStartDate(endDate, period) {
|
|
|
449
551
|
return startDate;
|
|
450
552
|
}
|
|
451
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
|
+
*/
|
|
452
559
|
function getMarketTimePeriod(params) {
|
|
453
|
-
const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso'
|
|
560
|
+
const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso' } = params;
|
|
454
561
|
if (!period)
|
|
455
562
|
throw new Error('Period is required');
|
|
456
563
|
const calendar = new MarketCalendar();
|
|
@@ -495,6 +602,11 @@ function getMarketTimePeriod(params) {
|
|
|
495
602
|
};
|
|
496
603
|
}
|
|
497
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
|
+
*/
|
|
498
610
|
function getMarketStatusImpl(date = new Date()) {
|
|
499
611
|
const calendar = new MarketCalendar();
|
|
500
612
|
const nyDate = toNYTime(date);
|
|
@@ -508,7 +620,8 @@ function getMarketStatusImpl(date = new Date()) {
|
|
|
508
620
|
let extendedEndMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
|
|
509
621
|
if (isEarlyCloseDay) {
|
|
510
622
|
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
511
|
-
extendedEndMinutes =
|
|
623
|
+
extendedEndMinutes =
|
|
624
|
+
MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
|
|
512
625
|
}
|
|
513
626
|
let status;
|
|
514
627
|
let nextStatus;
|
|
@@ -565,10 +678,20 @@ function getMarketStatusImpl(date = new Date()) {
|
|
|
565
678
|
};
|
|
566
679
|
}
|
|
567
680
|
// API exports
|
|
681
|
+
/**
|
|
682
|
+
* Returns market open/close times for a given date.
|
|
683
|
+
* @param options - { date?: Date }
|
|
684
|
+
* @returns MarketOpenCloseResult
|
|
685
|
+
*/
|
|
568
686
|
function getMarketOpenClose(options = {}) {
|
|
569
687
|
const { date = new Date() } = options;
|
|
570
688
|
return getMarketTimes(date);
|
|
571
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
|
+
*/
|
|
572
695
|
function getStartAndEndDates(params = {}) {
|
|
573
696
|
const { start, end } = getMarketTimePeriod(params);
|
|
574
697
|
return {
|
|
@@ -579,46 +702,103 @@ function getStartAndEndDates(params = {}) {
|
|
|
579
702
|
/**
|
|
580
703
|
* Returns the last full trading date as a Date object.
|
|
581
704
|
*/
|
|
705
|
+
/**
|
|
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
|
|
709
|
+
*/
|
|
582
710
|
function getLastFullTradingDate(currentDate = new Date()) {
|
|
583
711
|
return getLastFullTradingDateImpl(currentDate);
|
|
584
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
|
+
*/
|
|
585
718
|
function getNextMarketDay({ referenceDate } = {}) {
|
|
586
719
|
const calendar = new MarketCalendar();
|
|
587
|
-
const startDate = referenceDate
|
|
720
|
+
const startDate = referenceDate ?? new Date();
|
|
721
|
+
// Find the next trading day (UTC Date object)
|
|
588
722
|
const nextDate = calendar.getNextMarketDay(startDate);
|
|
589
|
-
|
|
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')}`;
|
|
590
726
|
return {
|
|
591
|
-
date: nextDate,
|
|
592
|
-
yyyymmdd,
|
|
727
|
+
date: nextDate, // raw Date, unchanged
|
|
728
|
+
yyyymmdd, // correct trading date string
|
|
593
729
|
dateISOString: nextDate.toISOString(),
|
|
594
730
|
};
|
|
595
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
|
+
}
|
|
596
750
|
/**
|
|
597
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.
|
|
598
752
|
* @param time - a string, number (unix timestamp), or Date object representing the time
|
|
599
753
|
* @returns the trading date as a string in YYYY-MM-DD format
|
|
600
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
|
+
*/
|
|
601
760
|
function getTradingDate(time) {
|
|
602
761
|
const date = typeof time === 'number' ? new Date(time) : typeof time === 'string' ? new Date(time) : time;
|
|
603
762
|
const nyDate = toNYTime(date);
|
|
604
763
|
return `${nyDate.getUTCFullYear()}-${String(nyDate.getUTCMonth() + 1).padStart(2, '0')}-${String(nyDate.getUTCDate()).padStart(2, '0')}`;
|
|
605
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
|
+
*/
|
|
606
770
|
function getNYTimeZone(date) {
|
|
607
771
|
const offset = getNYOffset(date || new Date());
|
|
608
772
|
return offset === -4 ? '-04:00' : '-05:00';
|
|
609
773
|
}
|
|
774
|
+
/**
|
|
775
|
+
* Returns the current market status for a given date.
|
|
776
|
+
* @param options - { date?: Date }
|
|
777
|
+
* @returns MarketStatus object
|
|
778
|
+
*/
|
|
610
779
|
function getMarketStatus(options = {}) {
|
|
611
780
|
const { date = new Date() } = options;
|
|
612
781
|
return getMarketStatusImpl(date);
|
|
613
782
|
}
|
|
614
|
-
|
|
615
|
-
|
|
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);
|
|
616
791
|
}
|
|
617
792
|
/**
|
|
618
793
|
* Returns full trading days from market open to market close.
|
|
619
794
|
* endDate is always the most recent market close (previous day's close if before open, today's close if after open).
|
|
620
795
|
* days: 1 or not specified = that day's open; 2 = previous market day's open, etc.
|
|
621
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
|
+
*/
|
|
622
802
|
function getTradingStartAndEndDates(options = {}) {
|
|
623
803
|
const { endDate = new Date(), days = 1 } = options;
|
|
624
804
|
const calendar = new MarketCalendar();
|
|
@@ -628,7 +808,9 @@ function getTradingStartAndEndDates(options = {}) {
|
|
|
628
808
|
const marketOpenMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
629
809
|
const marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
630
810
|
const minutes = nyEnd.getUTCHours() * 60 + nyEnd.getUTCMinutes();
|
|
631
|
-
if (!calendar.isMarketDay(endDate) ||
|
|
811
|
+
if (!calendar.isMarketDay(endDate) ||
|
|
812
|
+
minutes < marketOpenMinutes ||
|
|
813
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
632
814
|
// Before market open, not a market day, or during market hours: use previous market day
|
|
633
815
|
endMarketDay = calendar.getPreviousMarketDay(endDate);
|
|
634
816
|
}
|
|
@@ -654,32 +836,102 @@ function getTradingStartAndEndDates(options = {}) {
|
|
|
654
836
|
const startOpen = getMarketOpenClose({ date: startMarketDay }).open;
|
|
655
837
|
return { startDate: startOpen, endDate: endClose };
|
|
656
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
|
+
}
|
|
657
909
|
// Export MARKET_TIMES for compatibility
|
|
658
910
|
const MARKET_TIMES = {
|
|
659
911
|
TIMEZONE: MARKET_CONFIG.TIMEZONE,
|
|
660
912
|
PRE: {
|
|
661
913
|
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
662
|
-
END: { HOUR: 9, MINUTE: 30, MINUTES: 570 }
|
|
914
|
+
END: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
663
915
|
},
|
|
664
916
|
EARLY_MORNING: {
|
|
665
917
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
666
|
-
END: { HOUR: 10, MINUTE: 0, MINUTES: 600 }
|
|
918
|
+
END: { HOUR: 10, MINUTE: 0, MINUTES: 600 },
|
|
667
919
|
},
|
|
668
920
|
EARLY_CLOSE_BEFORE_HOLIDAY: {
|
|
669
921
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
670
|
-
END: { HOUR: 13, MINUTE: 0, MINUTES: 780 }
|
|
922
|
+
END: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
671
923
|
},
|
|
672
924
|
EARLY_EXTENDED_BEFORE_HOLIDAY: {
|
|
673
925
|
START: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
674
|
-
END: { HOUR: 17, MINUTE: 0, MINUTES: 1020 }
|
|
926
|
+
END: { HOUR: 17, MINUTE: 0, MINUTES: 1020 },
|
|
675
927
|
},
|
|
676
928
|
REGULAR: {
|
|
677
929
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
678
|
-
END: { HOUR: 16, MINUTE: 0, MINUTES: 960 }
|
|
930
|
+
END: { HOUR: 16, MINUTE: 0, MINUTES: 960 },
|
|
679
931
|
},
|
|
680
932
|
EXTENDED: {
|
|
681
933
|
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
682
|
-
END: { HOUR: 20, MINUTE: 0, MINUTES: 1200 }
|
|
934
|
+
END: { HOUR: 20, MINUTE: 0, MINUTES: 1200 },
|
|
683
935
|
},
|
|
684
936
|
};
|
|
685
937
|
|
|
@@ -1081,6 +1333,12 @@ class Queue {
|
|
|
1081
1333
|
current = current.next;
|
|
1082
1334
|
}
|
|
1083
1335
|
}
|
|
1336
|
+
|
|
1337
|
+
* drain() {
|
|
1338
|
+
while (this.#head) {
|
|
1339
|
+
yield this.dequeue();
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1084
1342
|
}
|
|
1085
1343
|
|
|
1086
1344
|
function pLimit(concurrency) {
|
|
@@ -2110,7 +2368,7 @@ const safeJSON = (text) => {
|
|
|
2110
2368
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2111
2369
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2112
2370
|
|
|
2113
|
-
const VERSION = '5.10.
|
|
2371
|
+
const VERSION = '5.10.2'; // x-release-please-version
|
|
2114
2372
|
|
|
2115
2373
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2116
2374
|
const isRunningInBrowser = () => {
|
|
@@ -15230,7 +15488,7 @@ var config = {};
|
|
|
15230
15488
|
|
|
15231
15489
|
var main = {exports: {}};
|
|
15232
15490
|
|
|
15233
|
-
var version = "17.2.
|
|
15491
|
+
var version = "17.2.1";
|
|
15234
15492
|
var require$$4 = {
|
|
15235
15493
|
version: version};
|
|
15236
15494
|
|
|
@@ -15249,9 +15507,12 @@ function requireMain () {
|
|
|
15249
15507
|
|
|
15250
15508
|
// Array of tips to display randomly
|
|
15251
15509
|
const TIPS = [
|
|
15252
|
-
'🔐 encrypt with
|
|
15510
|
+
'🔐 encrypt with Dotenvx: https://dotenvx.com',
|
|
15253
15511
|
'🔐 prevent committing .env to code: https://dotenvx.com/precommit',
|
|
15254
15512
|
'🔐 prevent building .env in docker: https://dotenvx.com/prebuild',
|
|
15513
|
+
'📡 observe env with Radar: https://dotenvx.com/radar',
|
|
15514
|
+
'📡 auto-backup env with Radar: https://dotenvx.com/radar',
|
|
15515
|
+
'📡 version env with Radar: https://dotenvx.com/radar',
|
|
15255
15516
|
'🛠️ run anywhere with `dotenvx run -- yourcommand`',
|
|
15256
15517
|
'⚙️ specify custom .env file path with { path: \'/custom/path/.env\' }',
|
|
15257
15518
|
'⚙️ enable debug logging with { debug: true }',
|
|
@@ -15552,7 +15813,7 @@ function requireMain () {
|
|
|
15552
15813
|
}
|
|
15553
15814
|
}
|
|
15554
15815
|
|
|
15555
|
-
_log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(
|
|
15816
|
+
_log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(`-- tip: ${_getRandomTip()}`)}`);
|
|
15556
15817
|
}
|
|
15557
15818
|
|
|
15558
15819
|
if (lastError) {
|
|
@@ -18033,10 +18294,15 @@ const disco = {
|
|
|
18033
18294
|
getMarketOpenClose: getMarketOpenClose,
|
|
18034
18295
|
getLastFullTradingDate: getLastFullTradingDate,
|
|
18035
18296
|
getNextMarketDay: getNextMarketDay,
|
|
18297
|
+
getPreviousMarketDay: getPreviousMarketDay,
|
|
18298
|
+
getMarketTimePeriod: getMarketTimePeriod,
|
|
18036
18299
|
getMarketStatus: getMarketStatus,
|
|
18037
18300
|
getNYTimeZone: getNYTimeZone,
|
|
18038
18301
|
getTradingDate: getTradingDate,
|
|
18039
18302
|
getTradingStartAndEndDates: getTradingStartAndEndDates,
|
|
18303
|
+
isMarketDay: isMarketDay,
|
|
18304
|
+
isWithinMarketHours: isWithinMarketHours,
|
|
18305
|
+
countTradingDays: countTradingDays,
|
|
18040
18306
|
MARKET_TIMES: MARKET_TIMES,
|
|
18041
18307
|
},
|
|
18042
18308
|
utils: {
|