@discomedia/utils 1.0.55 → 1.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index-frontend.cjs +8 -33
- package/dist/index-frontend.cjs.map +1 -1
- package/dist/index-frontend.mjs +8 -33
- package/dist/index-frontend.mjs.map +1 -1
- package/dist/index.cjs +53 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +53 -50
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +4 -4
- package/dist/test.js +840 -104
- package/dist/test.js.map +1 -1
- package/dist/types/alpaca-trading-api.d.ts +3 -1
- package/dist/types/alpaca-trading-api.d.ts.map +1 -1
- package/dist/types-frontend/alpaca-trading-api.d.ts +3 -1
- package/dist/types-frontend/alpaca-trading-api.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/test.js
CHANGED
|
@@ -11,7 +11,7 @@ import require$$0$1 from 'buffer';
|
|
|
11
11
|
import require$$0$4 from 'fs';
|
|
12
12
|
import require$$1$1 from 'path';
|
|
13
13
|
import require$$2$1 from 'os';
|
|
14
|
-
import
|
|
14
|
+
import 'console';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Logs a message to the console.
|
|
@@ -147,12 +147,18 @@ const marketEarlyCloses = {
|
|
|
147
147
|
|
|
148
148
|
// Constants for NY market times (Eastern Time)
|
|
149
149
|
const MARKET_CONFIG = {
|
|
150
|
+
TIMEZONE: 'America/New_York',
|
|
150
151
|
UTC_OFFSET_STANDARD: -5, // EST
|
|
151
152
|
UTC_OFFSET_DST: -4, // EDT
|
|
152
153
|
TIMES: {
|
|
154
|
+
EXTENDED_START: { hour: 4, minute: 0 },
|
|
153
155
|
MARKET_OPEN: { hour: 9, minute: 30 },
|
|
156
|
+
EARLY_MARKET_END: { hour: 10, minute: 0 },
|
|
154
157
|
MARKET_CLOSE: { hour: 16, minute: 0 },
|
|
155
|
-
EARLY_CLOSE: { hour: 13, minute: 0 }
|
|
158
|
+
EARLY_CLOSE: { hour: 13, minute: 0 },
|
|
159
|
+
EXTENDED_END: { hour: 20, minute: 0 },
|
|
160
|
+
EARLY_EXTENDED_END: { hour: 17, minute: 0 },
|
|
161
|
+
},
|
|
156
162
|
};
|
|
157
163
|
// Helper: Get NY offset for a given UTC date (DST rules for US)
|
|
158
164
|
/**
|
|
@@ -220,6 +226,33 @@ function fromNYTime(date) {
|
|
|
220
226
|
const utcMillis = nyMillis - offset * 60 * 60 * 1000;
|
|
221
227
|
return new Date(utcMillis);
|
|
222
228
|
}
|
|
229
|
+
// Helper: Format date in ISO, unix, etc.
|
|
230
|
+
/**
|
|
231
|
+
* Formats a date in ISO, unix-seconds, or unix-ms format.
|
|
232
|
+
* @param date - Date object
|
|
233
|
+
* @param outputFormat - Output format ('iso', 'unix-seconds', 'unix-ms')
|
|
234
|
+
* @returns Formatted date string or number
|
|
235
|
+
*/
|
|
236
|
+
function formatDate(date, outputFormat = 'iso') {
|
|
237
|
+
switch (outputFormat) {
|
|
238
|
+
case 'unix-seconds':
|
|
239
|
+
return Math.floor(date.getTime() / 1000);
|
|
240
|
+
case 'unix-ms':
|
|
241
|
+
return date.getTime();
|
|
242
|
+
case 'iso':
|
|
243
|
+
default:
|
|
244
|
+
return date.toISOString();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Helper: Format date in NY locale string
|
|
248
|
+
/**
|
|
249
|
+
* Formats a date in NY locale string.
|
|
250
|
+
* @param date - Date object
|
|
251
|
+
* @returns NY locale string
|
|
252
|
+
*/
|
|
253
|
+
function formatNYLocale(date) {
|
|
254
|
+
return date.toLocaleString('en-US', { timeZone: 'America/New_York' });
|
|
255
|
+
}
|
|
223
256
|
// Market calendar logic
|
|
224
257
|
/**
|
|
225
258
|
* Market calendar logic for holidays, weekends, and market days.
|
|
@@ -315,6 +348,82 @@ class MarketCalendar {
|
|
|
315
348
|
return prevDay;
|
|
316
349
|
}
|
|
317
350
|
}
|
|
351
|
+
// Market open/close times
|
|
352
|
+
/**
|
|
353
|
+
* Returns market open/close times for a given date, including extended and early closes.
|
|
354
|
+
* @param date - Date object
|
|
355
|
+
* @returns MarketOpenCloseResult
|
|
356
|
+
*/
|
|
357
|
+
function getMarketTimes(date) {
|
|
358
|
+
const calendar = new MarketCalendar();
|
|
359
|
+
const nyDate = toNYTime(date);
|
|
360
|
+
if (!calendar.isMarketDay(date)) {
|
|
361
|
+
return {
|
|
362
|
+
marketOpen: false,
|
|
363
|
+
open: null,
|
|
364
|
+
close: null,
|
|
365
|
+
openExt: null,
|
|
366
|
+
closeExt: null,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
const year = nyDate.getUTCFullYear();
|
|
370
|
+
const month = nyDate.getUTCMonth();
|
|
371
|
+
const day = nyDate.getUTCDate();
|
|
372
|
+
// Helper to build NY time for a given hour/minute
|
|
373
|
+
function buildNYTime(hour, minute) {
|
|
374
|
+
const d = new Date(Date.UTC(year, month, day, hour, minute, 0, 0));
|
|
375
|
+
return fromNYTime(d);
|
|
376
|
+
}
|
|
377
|
+
let open = buildNYTime(MARKET_CONFIG.TIMES.MARKET_OPEN.hour, MARKET_CONFIG.TIMES.MARKET_OPEN.minute);
|
|
378
|
+
let close = buildNYTime(MARKET_CONFIG.TIMES.MARKET_CLOSE.hour, MARKET_CONFIG.TIMES.MARKET_CLOSE.minute);
|
|
379
|
+
let openExt = buildNYTime(MARKET_CONFIG.TIMES.EXTENDED_START.hour, MARKET_CONFIG.TIMES.EXTENDED_START.minute);
|
|
380
|
+
let closeExt = buildNYTime(MARKET_CONFIG.TIMES.EXTENDED_END.hour, MARKET_CONFIG.TIMES.EXTENDED_END.minute);
|
|
381
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
382
|
+
close = buildNYTime(MARKET_CONFIG.TIMES.EARLY_CLOSE.hour, MARKET_CONFIG.TIMES.EARLY_CLOSE.minute);
|
|
383
|
+
closeExt = buildNYTime(MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour, MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute);
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
marketOpen: true,
|
|
387
|
+
open,
|
|
388
|
+
close,
|
|
389
|
+
openExt,
|
|
390
|
+
closeExt,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
// Is within market hours
|
|
394
|
+
/**
|
|
395
|
+
* Checks if a date/time is within market hours, extended hours, or continuous.
|
|
396
|
+
* @param date - Date object
|
|
397
|
+
* @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
|
|
398
|
+
* @returns true if within hours, false otherwise
|
|
399
|
+
*/
|
|
400
|
+
function isWithinMarketHours(date, intradayReporting = 'market_hours') {
|
|
401
|
+
const calendar = new MarketCalendar();
|
|
402
|
+
if (!calendar.isMarketDay(date))
|
|
403
|
+
return false;
|
|
404
|
+
const nyDate = toNYTime(date);
|
|
405
|
+
const minutes = nyDate.getUTCHours() * 60 + nyDate.getUTCMinutes();
|
|
406
|
+
switch (intradayReporting) {
|
|
407
|
+
case 'extended_hours': {
|
|
408
|
+
let endMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
|
|
409
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
410
|
+
endMinutes = MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
|
|
411
|
+
}
|
|
412
|
+
const startMinutes = MARKET_CONFIG.TIMES.EXTENDED_START.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_START.minute;
|
|
413
|
+
return minutes >= startMinutes && minutes <= endMinutes;
|
|
414
|
+
}
|
|
415
|
+
case 'continuous':
|
|
416
|
+
return true;
|
|
417
|
+
default: {
|
|
418
|
+
let endMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
419
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
420
|
+
endMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
421
|
+
}
|
|
422
|
+
const startMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
423
|
+
return minutes >= startMinutes && minutes <= endMinutes;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
318
427
|
// Get last full trading date
|
|
319
428
|
/**
|
|
320
429
|
* Returns the last full trading date (market close) for a given date.
|
|
@@ -355,6 +464,244 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
355
464
|
const closeMinute = marketCloseMinutes % 60;
|
|
356
465
|
return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
|
|
357
466
|
}
|
|
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
|
+
*/
|
|
474
|
+
function getDayBoundaries(date, intradayReporting = 'market_hours') {
|
|
475
|
+
const calendar = new MarketCalendar();
|
|
476
|
+
const nyDate = toNYTime(date);
|
|
477
|
+
const year = nyDate.getUTCFullYear();
|
|
478
|
+
const month = nyDate.getUTCMonth();
|
|
479
|
+
const day = nyDate.getUTCDate();
|
|
480
|
+
function buildNYTime(hour, minute, sec = 0, ms = 0) {
|
|
481
|
+
const d = new Date(Date.UTC(year, month, day, hour, minute, sec, ms));
|
|
482
|
+
return fromNYTime(d);
|
|
483
|
+
}
|
|
484
|
+
let start;
|
|
485
|
+
let end;
|
|
486
|
+
switch (intradayReporting) {
|
|
487
|
+
case 'extended_hours':
|
|
488
|
+
start = buildNYTime(MARKET_CONFIG.TIMES.EXTENDED_START.hour, MARKET_CONFIG.TIMES.EXTENDED_START.minute, 0, 0);
|
|
489
|
+
end = buildNYTime(MARKET_CONFIG.TIMES.EXTENDED_END.hour, MARKET_CONFIG.TIMES.EXTENDED_END.minute, 59, 999);
|
|
490
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
491
|
+
end = buildNYTime(MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour, MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute, 59, 999);
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
case 'continuous':
|
|
495
|
+
start = new Date(Date.UTC(year, month, day, 0, 0, 0, 0));
|
|
496
|
+
end = new Date(Date.UTC(year, month, day, 23, 59, 59, 999));
|
|
497
|
+
break;
|
|
498
|
+
default:
|
|
499
|
+
start = buildNYTime(MARKET_CONFIG.TIMES.MARKET_OPEN.hour, MARKET_CONFIG.TIMES.MARKET_OPEN.minute, 0, 0);
|
|
500
|
+
end = buildNYTime(MARKET_CONFIG.TIMES.MARKET_CLOSE.hour, MARKET_CONFIG.TIMES.MARKET_CLOSE.minute, 59, 999);
|
|
501
|
+
if (calendar.isEarlyCloseDay(date)) {
|
|
502
|
+
end = buildNYTime(MARKET_CONFIG.TIMES.EARLY_CLOSE.hour, MARKET_CONFIG.TIMES.EARLY_CLOSE.minute, 59, 999);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
return { start, end };
|
|
507
|
+
}
|
|
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
|
+
*/
|
|
515
|
+
function calculatePeriodStartDate(endDate, period) {
|
|
516
|
+
const calendar = new MarketCalendar();
|
|
517
|
+
let startDate;
|
|
518
|
+
switch (period) {
|
|
519
|
+
case 'YTD':
|
|
520
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear(), 0, 1));
|
|
521
|
+
break;
|
|
522
|
+
case '1D':
|
|
523
|
+
startDate = calendar.getPreviousMarketDay(endDate);
|
|
524
|
+
break;
|
|
525
|
+
case '3D':
|
|
526
|
+
startDate = new Date(endDate.getTime() - 3 * 24 * 60 * 60 * 1000);
|
|
527
|
+
break;
|
|
528
|
+
case '1W':
|
|
529
|
+
startDate = new Date(endDate.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
530
|
+
break;
|
|
531
|
+
case '2W':
|
|
532
|
+
startDate = new Date(endDate.getTime() - 14 * 24 * 60 * 60 * 1000);
|
|
533
|
+
break;
|
|
534
|
+
case '1M':
|
|
535
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth() - 1, endDate.getUTCDate()));
|
|
536
|
+
break;
|
|
537
|
+
case '3M':
|
|
538
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth() - 3, endDate.getUTCDate()));
|
|
539
|
+
break;
|
|
540
|
+
case '6M':
|
|
541
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear(), endDate.getUTCMonth() - 6, endDate.getUTCDate()));
|
|
542
|
+
break;
|
|
543
|
+
case '1Y':
|
|
544
|
+
startDate = new Date(Date.UTC(endDate.getUTCFullYear() - 1, endDate.getUTCMonth(), endDate.getUTCDate()));
|
|
545
|
+
break;
|
|
546
|
+
default:
|
|
547
|
+
throw new Error(`Invalid period: ${period}`);
|
|
548
|
+
}
|
|
549
|
+
// Ensure start date is a market day
|
|
550
|
+
while (!calendar.isMarketDay(startDate)) {
|
|
551
|
+
startDate = calendar.getNextMarketDay(startDate);
|
|
552
|
+
}
|
|
553
|
+
return startDate;
|
|
554
|
+
}
|
|
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
|
+
*/
|
|
561
|
+
function getMarketTimePeriod(params) {
|
|
562
|
+
const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso' } = params;
|
|
563
|
+
if (!period)
|
|
564
|
+
throw new Error('Period is required');
|
|
565
|
+
const calendar = new MarketCalendar();
|
|
566
|
+
const nyEndDate = toNYTime(end);
|
|
567
|
+
let endDate;
|
|
568
|
+
const isCurrentMarketDay = calendar.isMarketDay(end);
|
|
569
|
+
const isWithinHours = isWithinMarketHours(end, intraday_reporting);
|
|
570
|
+
const minutes = nyEndDate.getUTCHours() * 60 + nyEndDate.getUTCMinutes();
|
|
571
|
+
const marketStartMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
572
|
+
if (isCurrentMarketDay) {
|
|
573
|
+
if (minutes < marketStartMinutes) {
|
|
574
|
+
// Before market open - use previous day's close
|
|
575
|
+
const lastMarketDay = calendar.getPreviousMarketDay(end);
|
|
576
|
+
const { end: dayEnd } = getDayBoundaries(lastMarketDay, intraday_reporting);
|
|
577
|
+
endDate = dayEnd;
|
|
578
|
+
}
|
|
579
|
+
else if (isWithinHours) {
|
|
580
|
+
// During market hours - use current time
|
|
581
|
+
endDate = end;
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
// After market close - use today's close
|
|
585
|
+
const { end: dayEnd } = getDayBoundaries(end, intraday_reporting);
|
|
586
|
+
endDate = dayEnd;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
// Not a market day - use previous market day's close
|
|
591
|
+
const lastMarketDay = calendar.getPreviousMarketDay(end);
|
|
592
|
+
const { end: dayEnd } = getDayBoundaries(lastMarketDay, intraday_reporting);
|
|
593
|
+
endDate = dayEnd;
|
|
594
|
+
}
|
|
595
|
+
// Calculate start date
|
|
596
|
+
const periodStartDate = calculatePeriodStartDate(endDate, period);
|
|
597
|
+
const { start: dayStart } = getDayBoundaries(periodStartDate, intraday_reporting);
|
|
598
|
+
if (endDate.getTime() < dayStart.getTime()) {
|
|
599
|
+
throw new Error('Start date cannot be after end date');
|
|
600
|
+
}
|
|
601
|
+
return {
|
|
602
|
+
start: formatDate(dayStart, outputFormat),
|
|
603
|
+
end: formatDate(endDate, outputFormat),
|
|
604
|
+
};
|
|
605
|
+
}
|
|
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
|
+
*/
|
|
612
|
+
function getMarketStatusImpl(date = new Date()) {
|
|
613
|
+
const calendar = new MarketCalendar();
|
|
614
|
+
const nyDate = toNYTime(date);
|
|
615
|
+
const minutes = nyDate.getUTCHours() * 60 + nyDate.getUTCMinutes();
|
|
616
|
+
const isMarketDay = calendar.isMarketDay(date);
|
|
617
|
+
const isEarlyCloseDay = calendar.isEarlyCloseDay(date);
|
|
618
|
+
const extendedStartMinutes = MARKET_CONFIG.TIMES.EXTENDED_START.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_START.minute;
|
|
619
|
+
const marketStartMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
620
|
+
const earlyMarketEndMinutes = MARKET_CONFIG.TIMES.EARLY_MARKET_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_MARKET_END.minute;
|
|
621
|
+
let marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
622
|
+
let extendedEndMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
|
|
623
|
+
if (isEarlyCloseDay) {
|
|
624
|
+
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
625
|
+
extendedEndMinutes =
|
|
626
|
+
MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
|
|
627
|
+
}
|
|
628
|
+
let status;
|
|
629
|
+
let nextStatus;
|
|
630
|
+
let nextStatusTime;
|
|
631
|
+
let marketPeriod;
|
|
632
|
+
if (!isMarketDay) {
|
|
633
|
+
status = 'closed';
|
|
634
|
+
nextStatus = 'extended hours';
|
|
635
|
+
marketPeriod = 'closed';
|
|
636
|
+
const nextMarketDay = calendar.getNextMarketDay(date);
|
|
637
|
+
nextStatusTime = getDayBoundaries(nextMarketDay, 'extended_hours').start;
|
|
638
|
+
}
|
|
639
|
+
else if (minutes < extendedStartMinutes) {
|
|
640
|
+
status = 'closed';
|
|
641
|
+
nextStatus = 'extended hours';
|
|
642
|
+
marketPeriod = 'closed';
|
|
643
|
+
nextStatusTime = getDayBoundaries(date, 'extended_hours').start;
|
|
644
|
+
}
|
|
645
|
+
else if (minutes < marketStartMinutes) {
|
|
646
|
+
status = 'extended hours';
|
|
647
|
+
nextStatus = 'open';
|
|
648
|
+
marketPeriod = 'preMarket';
|
|
649
|
+
nextStatusTime = getDayBoundaries(date, 'market_hours').start;
|
|
650
|
+
}
|
|
651
|
+
else if (minutes < marketCloseMinutes) {
|
|
652
|
+
status = 'open';
|
|
653
|
+
nextStatus = 'extended hours';
|
|
654
|
+
marketPeriod = minutes < earlyMarketEndMinutes ? 'earlyMarket' : 'regularMarket';
|
|
655
|
+
nextStatusTime = getDayBoundaries(date, 'market_hours').end;
|
|
656
|
+
}
|
|
657
|
+
else if (minutes < extendedEndMinutes) {
|
|
658
|
+
status = 'extended hours';
|
|
659
|
+
nextStatus = 'closed';
|
|
660
|
+
marketPeriod = 'afterMarket';
|
|
661
|
+
nextStatusTime = getDayBoundaries(date, 'extended_hours').end;
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
status = 'closed';
|
|
665
|
+
nextStatus = 'extended hours';
|
|
666
|
+
marketPeriod = 'closed';
|
|
667
|
+
const nextMarketDay = calendar.getNextMarketDay(date);
|
|
668
|
+
nextStatusTime = getDayBoundaries(nextMarketDay, 'extended_hours').start;
|
|
669
|
+
}
|
|
670
|
+
// I think using nyDate here may be wrong - should use current time? i.e. date.getTime()
|
|
671
|
+
const nextStatusTimeDifference = nextStatusTime.getTime() - date.getTime();
|
|
672
|
+
return {
|
|
673
|
+
time: date,
|
|
674
|
+
timeString: formatNYLocale(nyDate),
|
|
675
|
+
status,
|
|
676
|
+
nextStatus,
|
|
677
|
+
marketPeriod,
|
|
678
|
+
nextStatusTime,
|
|
679
|
+
nextStatusTimeDifference,
|
|
680
|
+
nextStatusTimeString: formatNYLocale(nextStatusTime),
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
// API exports
|
|
684
|
+
/**
|
|
685
|
+
* Returns market open/close times for a given date.
|
|
686
|
+
* @param options - { date?: Date }
|
|
687
|
+
* @returns MarketOpenCloseResult
|
|
688
|
+
*/
|
|
689
|
+
function getMarketOpenClose(options = {}) {
|
|
690
|
+
const { date = new Date() } = options;
|
|
691
|
+
return getMarketTimes(date);
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Returns the start and end dates for a market time period as Date objects.
|
|
695
|
+
* @param params - MarketTimeParams
|
|
696
|
+
* @returns Object with start and end Date
|
|
697
|
+
*/
|
|
698
|
+
function getStartAndEndDates(params = {}) {
|
|
699
|
+
const { start, end } = getMarketTimePeriod(params);
|
|
700
|
+
return {
|
|
701
|
+
start: typeof start === 'string' || typeof start === 'number' ? new Date(start) : start,
|
|
702
|
+
end: typeof end === 'string' || typeof end === 'number' ? new Date(end) : end,
|
|
703
|
+
};
|
|
704
|
+
}
|
|
358
705
|
/**
|
|
359
706
|
* Returns the last full trading date as a Date object.
|
|
360
707
|
*/
|
|
@@ -366,6 +713,348 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
366
713
|
function getLastFullTradingDate(currentDate = new Date()) {
|
|
367
714
|
return getLastFullTradingDateImpl(currentDate);
|
|
368
715
|
}
|
|
716
|
+
/**
|
|
717
|
+
* Returns the next market day after the reference date.
|
|
718
|
+
* @param referenceDate - Date object (default: now)
|
|
719
|
+
* @returns Object with date, yyyymmdd string, and ISO string
|
|
720
|
+
*/
|
|
721
|
+
function getNextMarketDay({ referenceDate } = {}) {
|
|
722
|
+
const calendar = new MarketCalendar();
|
|
723
|
+
const startDate = referenceDate ?? new Date();
|
|
724
|
+
// Find the next trading day (UTC Date object)
|
|
725
|
+
const nextDate = calendar.getNextMarketDay(startDate);
|
|
726
|
+
// Convert to NY time before extracting Y-M-D parts
|
|
727
|
+
const nyNext = toNYTime(nextDate);
|
|
728
|
+
const yyyymmdd = `${nyNext.getUTCFullYear()}-${String(nyNext.getUTCMonth() + 1).padStart(2, '0')}-${String(nyNext.getUTCDate()).padStart(2, '0')}`;
|
|
729
|
+
return {
|
|
730
|
+
date: nextDate, // raw Date, unchanged
|
|
731
|
+
yyyymmdd, // correct trading date string
|
|
732
|
+
dateISOString: nextDate.toISOString(),
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Returns the previous market day before the reference date.
|
|
737
|
+
* @param referenceDate - Date object (default: now)
|
|
738
|
+
* @returns Object with date, yyyymmdd string, and ISO string
|
|
739
|
+
*/
|
|
740
|
+
function getPreviousMarketDay({ referenceDate } = {}) {
|
|
741
|
+
const calendar = new MarketCalendar();
|
|
742
|
+
const startDate = referenceDate || new Date();
|
|
743
|
+
const prevDate = calendar.getPreviousMarketDay(startDate);
|
|
744
|
+
// convert to NY time first
|
|
745
|
+
const nyPrev = toNYTime(prevDate); // ← already in this file
|
|
746
|
+
const yyyymmdd = `${nyPrev.getUTCFullYear()}-${String(nyPrev.getUTCMonth() + 1).padStart(2, '0')}-${String(nyPrev.getUTCDate()).padStart(2, '0')}`;
|
|
747
|
+
return {
|
|
748
|
+
date: prevDate,
|
|
749
|
+
yyyymmdd,
|
|
750
|
+
dateISOString: prevDate.toISOString(),
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Returns the trading date for a given time. Note: Just trims the date string; does not validate if the date is a market day.
|
|
755
|
+
* @param time - a string, number (unix timestamp), or Date object representing the time
|
|
756
|
+
* @returns the trading date as a string in YYYY-MM-DD format
|
|
757
|
+
*/
|
|
758
|
+
/**
|
|
759
|
+
* Returns the trading date for a given time in YYYY-MM-DD format (NY time).
|
|
760
|
+
* @param time - string, number, or Date
|
|
761
|
+
* @returns trading date string
|
|
762
|
+
*/
|
|
763
|
+
function getTradingDate(time) {
|
|
764
|
+
const date = typeof time === 'number' ? new Date(time) : typeof time === 'string' ? new Date(time) : time;
|
|
765
|
+
const nyDate = toNYTime(date);
|
|
766
|
+
return `${nyDate.getUTCFullYear()}-${String(nyDate.getUTCMonth() + 1).padStart(2, '0')}-${String(nyDate.getUTCDate()).padStart(2, '0')}`;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Returns the NY timezone offset string for a given date.
|
|
770
|
+
* @param date - Date object (default: now)
|
|
771
|
+
* @returns '-04:00' for EDT, '-05:00' for EST
|
|
772
|
+
*/
|
|
773
|
+
function getNYTimeZone(date) {
|
|
774
|
+
const offset = getNYOffset(date || new Date());
|
|
775
|
+
return offset === -4 ? '-04:00' : '-05:00';
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Returns the regular market open and close Date objects for a given trading day string in the
|
|
779
|
+
* America/New_York timezone (NYSE/NASDAQ calendar).
|
|
780
|
+
*
|
|
781
|
+
* This helper is convenient when you have a calendar date like '2025-10-03' and want the precise
|
|
782
|
+
* open and close Date values for that day. It internally:
|
|
783
|
+
* - Determines the NY offset for the day using `getNYTimeZone()`.
|
|
784
|
+
* - Anchors a noon-time Date on that day in NY time to avoid DST edge cases.
|
|
785
|
+
* - Verifies the day is a market day via `isMarketDay()`.
|
|
786
|
+
* - Fetches the open/close times via `getMarketOpenClose()`.
|
|
787
|
+
*
|
|
788
|
+
* Throws if the provided day is not a market day or if open/close times are unavailable.
|
|
789
|
+
*
|
|
790
|
+
* See also:
|
|
791
|
+
* - `getNYTimeZone(date?: Date)`
|
|
792
|
+
* - `isMarketDay(date: Date)`
|
|
793
|
+
* - `getMarketOpenClose(options?: { date?: Date })`
|
|
794
|
+
*
|
|
795
|
+
* @param dateStr - Trading day string in 'YYYY-MM-DD' format (Eastern Time date)
|
|
796
|
+
* @returns An object containing `{ open: Date; close: Date }`
|
|
797
|
+
* @example
|
|
798
|
+
* ```ts
|
|
799
|
+
* const { open, close } = disco.time.getOpenCloseForTradingDay('2025-10-03');
|
|
800
|
+
* ```
|
|
801
|
+
*/
|
|
802
|
+
function getOpenCloseForTradingDay(dateStr) {
|
|
803
|
+
// Build a UTC midnight anchor for the date, then derive the NY offset for that day.
|
|
804
|
+
const utcAnchor = new Date(`${dateStr}T00:00:00Z`);
|
|
805
|
+
const nyOffset = getNYTimeZone(utcAnchor); // '-04:00' | '-05:00'
|
|
806
|
+
// Create a NY-local noon date to avoid DST midnight transitions.
|
|
807
|
+
const nyNoon = new Date(`${dateStr}T12:00:00${nyOffset}`);
|
|
808
|
+
if (!isMarketDay(nyNoon)) {
|
|
809
|
+
throw new Error(`Not a market day in ET: ${dateStr}`);
|
|
810
|
+
}
|
|
811
|
+
const { open, close } = getMarketOpenClose({ date: nyNoon });
|
|
812
|
+
if (!open || !close) {
|
|
813
|
+
throw new Error(`No market times available for ${dateStr}`);
|
|
814
|
+
}
|
|
815
|
+
return { open, close };
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Converts any date to the market time zone (America/New_York, Eastern Time).
|
|
819
|
+
* Returns a new Date object representing the same moment in time but adjusted to NY/Eastern timezone.
|
|
820
|
+
* Automatically handles daylight saving time transitions (EST/EDT).
|
|
821
|
+
*
|
|
822
|
+
* @param date - Date object to convert to market time zone
|
|
823
|
+
* @returns Date object in NY/Eastern time zone
|
|
824
|
+
* @example
|
|
825
|
+
* ```typescript
|
|
826
|
+
* const utcDate = new Date('2024-01-15T15:30:00Z'); // 3:30 PM UTC
|
|
827
|
+
* const nyDate = convertDateToMarketTimeZone(utcDate); // 10:30 AM EST (winter) or 11:30 AM EDT (summer)
|
|
828
|
+
* ```
|
|
829
|
+
*/
|
|
830
|
+
function convertDateToMarketTimeZone(date) {
|
|
831
|
+
return toNYTime(date);
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Returns the current market status for a given date.
|
|
835
|
+
* @param options - { date?: Date }
|
|
836
|
+
* @returns MarketStatus object
|
|
837
|
+
*/
|
|
838
|
+
function getMarketStatus(options = {}) {
|
|
839
|
+
const { date = new Date() } = options;
|
|
840
|
+
return getMarketStatusImpl(date);
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Checks if a date is a market day.
|
|
844
|
+
* @param date - Date object
|
|
845
|
+
* @returns true if market day, false otherwise
|
|
846
|
+
*/
|
|
847
|
+
function isMarketDay(date) {
|
|
848
|
+
const calendar = new MarketCalendar();
|
|
849
|
+
return calendar.isMarketDay(date);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Returns full trading days from market open to market close.
|
|
853
|
+
* endDate is always the most recent market close (previous day's close if before open, today's close if after open).
|
|
854
|
+
* days: 1 or not specified = that day's open; 2 = previous market day's open, etc.
|
|
855
|
+
*/
|
|
856
|
+
/**
|
|
857
|
+
* Returns full trading days from market open to market close.
|
|
858
|
+
* @param options - { endDate?: Date, days?: number }
|
|
859
|
+
* @returns Object with startDate and endDate
|
|
860
|
+
*/
|
|
861
|
+
function getTradingStartAndEndDates(options = {}) {
|
|
862
|
+
const { endDate = new Date(), days = 1 } = options;
|
|
863
|
+
const calendar = new MarketCalendar();
|
|
864
|
+
// Find the most recent market close
|
|
865
|
+
let endMarketDay = endDate;
|
|
866
|
+
const nyEnd = toNYTime(endDate);
|
|
867
|
+
const marketOpenMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
868
|
+
const marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
869
|
+
const minutes = nyEnd.getUTCHours() * 60 + nyEnd.getUTCMinutes();
|
|
870
|
+
if (!calendar.isMarketDay(endDate) ||
|
|
871
|
+
minutes < marketOpenMinutes ||
|
|
872
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
873
|
+
// Before market open, not a market day, or during market hours: use previous market day
|
|
874
|
+
endMarketDay = calendar.getPreviousMarketDay(endDate);
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
// After market close: use today
|
|
878
|
+
endMarketDay = endDate;
|
|
879
|
+
}
|
|
880
|
+
// Get market close for endMarketDay
|
|
881
|
+
const endClose = getMarketOpenClose({ date: endMarketDay }).close;
|
|
882
|
+
// Find start market day by iterating back over market days
|
|
883
|
+
let startMarketDay = endMarketDay;
|
|
884
|
+
let count = Math.max(1, days);
|
|
885
|
+
for (let i = 1; i < count; i++) {
|
|
886
|
+
startMarketDay = calendar.getPreviousMarketDay(startMarketDay);
|
|
887
|
+
}
|
|
888
|
+
// If days > 1, we need to go back (days-1) market days from endMarketDay
|
|
889
|
+
if (days > 1) {
|
|
890
|
+
startMarketDay = endMarketDay;
|
|
891
|
+
for (let i = 1; i < days; i++) {
|
|
892
|
+
startMarketDay = calendar.getPreviousMarketDay(startMarketDay);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
const startOpen = getMarketOpenClose({ date: startMarketDay }).open;
|
|
896
|
+
return { startDate: startOpen, endDate: endClose };
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* 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.
|
|
900
|
+
*
|
|
901
|
+
* This function calculates the actual trading time between two dates by:
|
|
902
|
+
* 1. Iterating through each calendar day between startDate and endDate (inclusive)
|
|
903
|
+
* 2. For each day that is a market day (not weekend/holiday), getting market open/close times
|
|
904
|
+
* 3. Calculating the overlap between the time range and market hours for that day
|
|
905
|
+
* 4. Summing up all the trading minutes across all days
|
|
906
|
+
*
|
|
907
|
+
* The function automatically handles:
|
|
908
|
+
* - Weekends (Saturday/Sunday) - skipped entirely
|
|
909
|
+
* - Market holidays - skipped entirely
|
|
910
|
+
* - Early close days (e.g. day before holidays) - uses early close time
|
|
911
|
+
* - Times outside market hours - only counts time within 9:30am-4pm ET (or early close)
|
|
912
|
+
*
|
|
913
|
+
* Examples:
|
|
914
|
+
* - 12pm to 3:30pm same day = 3.5 hours = 210 minutes = 0.54 days
|
|
915
|
+
* - 9:30am to 4pm same day = 6.5 hours = 390 minutes = 1 day
|
|
916
|
+
* - Friday 2pm to Monday 2pm = 6.5 hours (Friday 2pm-4pm + Monday 9:30am-2pm)
|
|
917
|
+
*
|
|
918
|
+
* @param startDate - Start date/time
|
|
919
|
+
* @param endDate - End date/time (default: now)
|
|
920
|
+
* @returns Object containing:
|
|
921
|
+
* - days: Trading time as fraction of full trading days (6.5 hours = 1 day)
|
|
922
|
+
* - hours: Trading time in hours
|
|
923
|
+
* - minutes: Trading time in minutes
|
|
924
|
+
*/
|
|
925
|
+
function countTradingDays(startDate, endDate = new Date()) {
|
|
926
|
+
const calendar = new MarketCalendar();
|
|
927
|
+
// Ensure start is before end
|
|
928
|
+
if (startDate.getTime() > endDate.getTime()) {
|
|
929
|
+
throw new Error('Start date must be before end date');
|
|
930
|
+
}
|
|
931
|
+
let totalMinutes = 0;
|
|
932
|
+
// Get the NY dates for iteration
|
|
933
|
+
const startNY = toNYTime(startDate);
|
|
934
|
+
const endNY = toNYTime(endDate);
|
|
935
|
+
// Create date at start of first day (in NY time)
|
|
936
|
+
const currentNY = new Date(Date.UTC(startNY.getUTCFullYear(), startNY.getUTCMonth(), startNY.getUTCDate(), 0, 0, 0, 0));
|
|
937
|
+
// Iterate through each calendar day
|
|
938
|
+
while (currentNY.getTime() <= endNY.getTime()) {
|
|
939
|
+
const currentUTC = fromNYTime(currentNY);
|
|
940
|
+
// Check if this is a market day
|
|
941
|
+
if (calendar.isMarketDay(currentUTC)) {
|
|
942
|
+
// Get market hours for this day
|
|
943
|
+
const marketTimes = getMarketTimes(currentUTC);
|
|
944
|
+
if (marketTimes.marketOpen && marketTimes.open && marketTimes.close) {
|
|
945
|
+
// Calculate the overlap between our time range and market hours
|
|
946
|
+
const dayStart = Math.max(startDate.getTime(), marketTimes.open.getTime());
|
|
947
|
+
const dayEnd = Math.min(endDate.getTime(), marketTimes.close.getTime());
|
|
948
|
+
// Only count if there's actual overlap
|
|
949
|
+
if (dayStart < dayEnd) {
|
|
950
|
+
totalMinutes += (dayEnd - dayStart) / (1000 * 60);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
// Move to next day
|
|
955
|
+
currentNY.setUTCDate(currentNY.getUTCDate() + 1);
|
|
956
|
+
}
|
|
957
|
+
// Convert to days, hours, minutes
|
|
958
|
+
const MINUTES_PER_TRADING_DAY = 390; // 6.5 hours
|
|
959
|
+
const days = totalMinutes / MINUTES_PER_TRADING_DAY;
|
|
960
|
+
const hours = totalMinutes / 60;
|
|
961
|
+
const minutes = totalMinutes;
|
|
962
|
+
return {
|
|
963
|
+
days: Math.round(days * 1000) / 1000, // Round to 3 decimal places
|
|
964
|
+
hours: Math.round(hours * 100) / 100, // Round to 2 decimal places
|
|
965
|
+
minutes: Math.round(minutes),
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Returns the trading day N days back from a reference date, along with its market open time.
|
|
970
|
+
* Trading days are counted as full or half trading days (days that end count as 1 full trading day).
|
|
971
|
+
* By default, the most recent completed trading day counts as day 1.
|
|
972
|
+
* Set includeMostRecentFullDay to false to count strictly before that day.
|
|
973
|
+
*
|
|
974
|
+
* @param options - Object with:
|
|
975
|
+
* - referenceDate: Date to count back from (default: now)
|
|
976
|
+
* - days: Number of trading days to go back (must be an integer >= 1)
|
|
977
|
+
* - includeMostRecentFullDay: Whether to include the most recent completed trading day (default: true)
|
|
978
|
+
* @returns Object containing:
|
|
979
|
+
* - date: Trading date in YYYY-MM-DD format
|
|
980
|
+
* - marketOpenISO: Market open time as ISO string (e.g., "2025-11-15T13:30:00.000Z")
|
|
981
|
+
* - unixTimestamp: Market open time as Unix timestamp in seconds
|
|
982
|
+
* @example
|
|
983
|
+
* ```typescript
|
|
984
|
+
* // Get the trading day 1 day back (most recent full trading day)
|
|
985
|
+
* const result = getTradingDaysBack({ days: 1 });
|
|
986
|
+
* console.log(result.date); // "2025-11-01"
|
|
987
|
+
* console.log(result.marketOpenISO); // "2025-11-01T13:30:00.000Z"
|
|
988
|
+
* console.log(result.unixTimestamp); // 1730466600
|
|
989
|
+
*
|
|
990
|
+
* // Get the trading day 5 days back from a specific date
|
|
991
|
+
* const result2 = getTradingDaysBack({
|
|
992
|
+
* referenceDate: new Date('2025-11-15T12:00:00-05:00'),
|
|
993
|
+
* days: 5
|
|
994
|
+
* });
|
|
995
|
+
* ```
|
|
996
|
+
*/
|
|
997
|
+
function getTradingDaysBack(options) {
|
|
998
|
+
const calendar = new MarketCalendar();
|
|
999
|
+
const { referenceDate, days, includeMostRecentFullDay = true } = options;
|
|
1000
|
+
const refDate = referenceDate || new Date();
|
|
1001
|
+
const daysBack = days;
|
|
1002
|
+
if (!Number.isInteger(daysBack) || daysBack < 1) {
|
|
1003
|
+
throw new Error('days must be an integer >= 1');
|
|
1004
|
+
}
|
|
1005
|
+
// Start from the last full trading date relative to reference
|
|
1006
|
+
let targetDate = getLastFullTradingDateImpl(refDate);
|
|
1007
|
+
if (!includeMostRecentFullDay) {
|
|
1008
|
+
targetDate = calendar.getPreviousMarketDay(targetDate);
|
|
1009
|
+
}
|
|
1010
|
+
// Go back the specified number of days (we're already at day 1, so go back days-1 more)
|
|
1011
|
+
for (let i = 1; i < daysBack; i++) {
|
|
1012
|
+
targetDate = calendar.getPreviousMarketDay(targetDate);
|
|
1013
|
+
}
|
|
1014
|
+
// Get market open time for this date
|
|
1015
|
+
const marketTimes = getMarketTimes(targetDate);
|
|
1016
|
+
if (!marketTimes.open) {
|
|
1017
|
+
throw new Error(`No market open time for target date`);
|
|
1018
|
+
}
|
|
1019
|
+
// Format the date string (YYYY-MM-DD) in NY time
|
|
1020
|
+
const nyDate = toNYTime(marketTimes.open);
|
|
1021
|
+
const dateStr = `${nyDate.getUTCFullYear()}-${String(nyDate.getUTCMonth() + 1).padStart(2, '0')}-${String(nyDate.getUTCDate()).padStart(2, '0')}`;
|
|
1022
|
+
const marketOpenISO = marketTimes.open.toISOString();
|
|
1023
|
+
const unixTimestamp = Math.floor(marketTimes.open.getTime() / 1000);
|
|
1024
|
+
return {
|
|
1025
|
+
date: dateStr,
|
|
1026
|
+
marketOpenISO,
|
|
1027
|
+
unixTimestamp,
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
// Export MARKET_TIMES for compatibility
|
|
1031
|
+
const MARKET_TIMES = {
|
|
1032
|
+
TIMEZONE: MARKET_CONFIG.TIMEZONE,
|
|
1033
|
+
PRE: {
|
|
1034
|
+
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
1035
|
+
END: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
1036
|
+
},
|
|
1037
|
+
EARLY_MORNING: {
|
|
1038
|
+
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
1039
|
+
END: { HOUR: 10, MINUTE: 0, MINUTES: 600 },
|
|
1040
|
+
},
|
|
1041
|
+
EARLY_CLOSE_BEFORE_HOLIDAY: {
|
|
1042
|
+
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
1043
|
+
END: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
1044
|
+
},
|
|
1045
|
+
EARLY_EXTENDED_BEFORE_HOLIDAY: {
|
|
1046
|
+
START: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
1047
|
+
END: { HOUR: 17, MINUTE: 0, MINUTES: 1020 },
|
|
1048
|
+
},
|
|
1049
|
+
REGULAR: {
|
|
1050
|
+
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
1051
|
+
END: { HOUR: 16, MINUTE: 0, MINUTES: 960 },
|
|
1052
|
+
},
|
|
1053
|
+
EXTENDED: {
|
|
1054
|
+
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
1055
|
+
END: { HOUR: 20, MINUTE: 0, MINUTES: 1200 },
|
|
1056
|
+
},
|
|
1057
|
+
};
|
|
369
1058
|
|
|
370
1059
|
/*
|
|
371
1060
|
How it works:
|
|
@@ -453,8 +1142,18 @@ class Queue {
|
|
|
453
1142
|
}
|
|
454
1143
|
|
|
455
1144
|
function pLimit(concurrency) {
|
|
1145
|
+
let rejectOnClear = false;
|
|
1146
|
+
|
|
1147
|
+
if (typeof concurrency === 'object') {
|
|
1148
|
+
({concurrency, rejectOnClear = false} = concurrency);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
456
1151
|
validateConcurrency(concurrency);
|
|
457
1152
|
|
|
1153
|
+
if (typeof rejectOnClear !== 'boolean') {
|
|
1154
|
+
throw new TypeError('Expected `rejectOnClear` to be a boolean');
|
|
1155
|
+
}
|
|
1156
|
+
|
|
458
1157
|
const queue = new Queue();
|
|
459
1158
|
let activeCount = 0;
|
|
460
1159
|
|
|
@@ -462,7 +1161,7 @@ function pLimit(concurrency) {
|
|
|
462
1161
|
// Process the next queued function if we're under the concurrency limit
|
|
463
1162
|
if (activeCount < concurrency && queue.size > 0) {
|
|
464
1163
|
activeCount++;
|
|
465
|
-
queue.dequeue()();
|
|
1164
|
+
queue.dequeue().run();
|
|
466
1165
|
}
|
|
467
1166
|
};
|
|
468
1167
|
|
|
@@ -489,11 +1188,14 @@ function pLimit(concurrency) {
|
|
|
489
1188
|
next();
|
|
490
1189
|
};
|
|
491
1190
|
|
|
492
|
-
const enqueue = (function_, resolve, arguments_) => {
|
|
1191
|
+
const enqueue = (function_, resolve, reject, arguments_) => {
|
|
1192
|
+
const queueItem = {reject};
|
|
1193
|
+
|
|
493
1194
|
// Queue the internal resolve function instead of the run function
|
|
494
1195
|
// to preserve the asynchronous execution context.
|
|
495
1196
|
new Promise(internalResolve => { // eslint-disable-line promise/param-names
|
|
496
|
-
|
|
1197
|
+
queueItem.run = internalResolve;
|
|
1198
|
+
queue.enqueue(queueItem);
|
|
497
1199
|
}).then(run.bind(undefined, function_, resolve, arguments_)); // eslint-disable-line promise/prefer-await-to-then
|
|
498
1200
|
|
|
499
1201
|
// Start processing immediately if we haven't reached the concurrency limit
|
|
@@ -502,8 +1204,8 @@ function pLimit(concurrency) {
|
|
|
502
1204
|
}
|
|
503
1205
|
};
|
|
504
1206
|
|
|
505
|
-
const generator = (function_, ...arguments_) => new Promise(resolve => {
|
|
506
|
-
enqueue(function_, resolve, arguments_);
|
|
1207
|
+
const generator = (function_, ...arguments_) => new Promise((resolve, reject) => {
|
|
1208
|
+
enqueue(function_, resolve, reject, arguments_);
|
|
507
1209
|
});
|
|
508
1210
|
|
|
509
1211
|
Object.defineProperties(generator, {
|
|
@@ -515,7 +1217,16 @@ function pLimit(concurrency) {
|
|
|
515
1217
|
},
|
|
516
1218
|
clearQueue: {
|
|
517
1219
|
value() {
|
|
518
|
-
|
|
1220
|
+
if (!rejectOnClear) {
|
|
1221
|
+
queue.clear();
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
const abortError = AbortSignal.abort().reason;
|
|
1226
|
+
|
|
1227
|
+
while (queue.size > 0) {
|
|
1228
|
+
queue.dequeue().reject(abortError);
|
|
1229
|
+
}
|
|
519
1230
|
},
|
|
520
1231
|
},
|
|
521
1232
|
concurrency: {
|
|
@@ -801,7 +1512,7 @@ const safeJSON = (text) => {
|
|
|
801
1512
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
802
1513
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
803
1514
|
|
|
804
|
-
const VERSION = '6.
|
|
1515
|
+
const VERSION = '6.17.0'; // x-release-please-version
|
|
805
1516
|
|
|
806
1517
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
807
1518
|
const isRunningInBrowser = () => {
|
|
@@ -4094,12 +4805,7 @@ class Assistants extends APIResource {
|
|
|
4094
4805
|
/**
|
|
4095
4806
|
* Create an assistant with a model and instructions.
|
|
4096
4807
|
*
|
|
4097
|
-
* @
|
|
4098
|
-
* ```ts
|
|
4099
|
-
* const assistant = await client.beta.assistants.create({
|
|
4100
|
-
* model: 'gpt-4o',
|
|
4101
|
-
* });
|
|
4102
|
-
* ```
|
|
4808
|
+
* @deprecated
|
|
4103
4809
|
*/
|
|
4104
4810
|
create(body, options) {
|
|
4105
4811
|
return this._client.post('/assistants', {
|
|
@@ -4111,12 +4817,7 @@ class Assistants extends APIResource {
|
|
|
4111
4817
|
/**
|
|
4112
4818
|
* Retrieves an assistant.
|
|
4113
4819
|
*
|
|
4114
|
-
* @
|
|
4115
|
-
* ```ts
|
|
4116
|
-
* const assistant = await client.beta.assistants.retrieve(
|
|
4117
|
-
* 'assistant_id',
|
|
4118
|
-
* );
|
|
4119
|
-
* ```
|
|
4820
|
+
* @deprecated
|
|
4120
4821
|
*/
|
|
4121
4822
|
retrieve(assistantID, options) {
|
|
4122
4823
|
return this._client.get(path `/assistants/${assistantID}`, {
|
|
@@ -4127,12 +4828,7 @@ class Assistants extends APIResource {
|
|
|
4127
4828
|
/**
|
|
4128
4829
|
* Modifies an assistant.
|
|
4129
4830
|
*
|
|
4130
|
-
* @
|
|
4131
|
-
* ```ts
|
|
4132
|
-
* const assistant = await client.beta.assistants.update(
|
|
4133
|
-
* 'assistant_id',
|
|
4134
|
-
* );
|
|
4135
|
-
* ```
|
|
4831
|
+
* @deprecated
|
|
4136
4832
|
*/
|
|
4137
4833
|
update(assistantID, body, options) {
|
|
4138
4834
|
return this._client.post(path `/assistants/${assistantID}`, {
|
|
@@ -4144,13 +4840,7 @@ class Assistants extends APIResource {
|
|
|
4144
4840
|
/**
|
|
4145
4841
|
* Returns a list of assistants.
|
|
4146
4842
|
*
|
|
4147
|
-
* @
|
|
4148
|
-
* ```ts
|
|
4149
|
-
* // Automatically fetches more pages as needed.
|
|
4150
|
-
* for await (const assistant of client.beta.assistants.list()) {
|
|
4151
|
-
* // ...
|
|
4152
|
-
* }
|
|
4153
|
-
* ```
|
|
4843
|
+
* @deprecated
|
|
4154
4844
|
*/
|
|
4155
4845
|
list(query = {}, options) {
|
|
4156
4846
|
return this._client.getAPIList('/assistants', (CursorPage), {
|
|
@@ -4162,11 +4852,7 @@ class Assistants extends APIResource {
|
|
|
4162
4852
|
/**
|
|
4163
4853
|
* Delete an assistant.
|
|
4164
4854
|
*
|
|
4165
|
-
* @
|
|
4166
|
-
* ```ts
|
|
4167
|
-
* const assistantDeleted =
|
|
4168
|
-
* await client.beta.assistants.delete('assistant_id');
|
|
4169
|
-
* ```
|
|
4855
|
+
* @deprecated
|
|
4170
4856
|
*/
|
|
4171
4857
|
delete(assistantID, options) {
|
|
4172
4858
|
return this._client.delete(path `/assistants/${assistantID}`, {
|
|
@@ -5640,8 +6326,8 @@ Evals.Runs = Runs;
|
|
|
5640
6326
|
let Files$1 = class Files extends APIResource {
|
|
5641
6327
|
/**
|
|
5642
6328
|
* Upload a file that can be used across various endpoints. Individual files can be
|
|
5643
|
-
* up to 512 MB, and
|
|
5644
|
-
*
|
|
6329
|
+
* up to 512 MB, and each project can store up to 2.5 TB of files in total. There
|
|
6330
|
+
* is no organization-wide storage limit.
|
|
5645
6331
|
*
|
|
5646
6332
|
* - The Assistants API supports files up to 2 million tokens and of specific file
|
|
5647
6333
|
* types. See the
|
|
@@ -14506,69 +15192,118 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
14506
15192
|
// Export the singleton instance
|
|
14507
15193
|
AlpacaMarketDataAPI.getInstance();
|
|
14508
15194
|
|
|
15195
|
+
const disco = {
|
|
15196
|
+
time: {
|
|
15197
|
+
convertDateToMarketTimeZone: convertDateToMarketTimeZone,
|
|
15198
|
+
getStartAndEndDates: getStartAndEndDates,
|
|
15199
|
+
getMarketOpenClose: getMarketOpenClose,
|
|
15200
|
+
getOpenCloseForTradingDay: getOpenCloseForTradingDay,
|
|
15201
|
+
getLastFullTradingDate: getLastFullTradingDate,
|
|
15202
|
+
getNextMarketDay: getNextMarketDay,
|
|
15203
|
+
getPreviousMarketDay: getPreviousMarketDay,
|
|
15204
|
+
getMarketTimePeriod: getMarketTimePeriod,
|
|
15205
|
+
getMarketStatus: getMarketStatus,
|
|
15206
|
+
getNYTimeZone: getNYTimeZone,
|
|
15207
|
+
getTradingDate: getTradingDate,
|
|
15208
|
+
getTradingStartAndEndDates: getTradingStartAndEndDates,
|
|
15209
|
+
getTradingDaysBack: getTradingDaysBack,
|
|
15210
|
+
isMarketDay: isMarketDay,
|
|
15211
|
+
isWithinMarketHours: isWithinMarketHours,
|
|
15212
|
+
countTradingDays: countTradingDays,
|
|
15213
|
+
MARKET_TIMES: MARKET_TIMES,
|
|
15214
|
+
}};
|
|
15215
|
+
|
|
14509
15216
|
// Test file for context functionality
|
|
14510
|
-
//
|
|
14511
|
-
//
|
|
14512
|
-
|
|
14513
|
-
|
|
14514
|
-
|
|
14515
|
-
|
|
14516
|
-
|
|
14517
|
-
|
|
14518
|
-
|
|
14519
|
-
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
|
|
14523
|
-
|
|
14524
|
-
|
|
14525
|
-
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
|
|
14532
|
-
|
|
14533
|
-
|
|
14534
|
-
|
|
14535
|
-
|
|
14536
|
-
|
|
14537
|
-
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
|
|
14546
|
-
|
|
14547
|
-
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
|
|
14551
|
-
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
|
|
14563
|
-
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
: '
|
|
14568
|
-
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
15217
|
+
//testGetMarketStatus();
|
|
15218
|
+
// Test getTradingDate for grouping data
|
|
15219
|
+
function testGetTradingDate() {
|
|
15220
|
+
const testCases = [
|
|
15221
|
+
{
|
|
15222
|
+
label: 'Market day (regular)',
|
|
15223
|
+
input: '2025-07-10T10:00:00-04:00',
|
|
15224
|
+
expected: '2025-07-10',
|
|
15225
|
+
},
|
|
15226
|
+
{
|
|
15227
|
+
label: 'Weekend (Saturday, should return it anyway )',
|
|
15228
|
+
input: '2025-07-12T12:00:00-04:00',
|
|
15229
|
+
expected: '2025-07-12',
|
|
15230
|
+
},
|
|
15231
|
+
{
|
|
15232
|
+
label: 'Holiday (Independence Day, should return it anyway)',
|
|
15233
|
+
input: '2025-07-04T12:00:00-04:00',
|
|
15234
|
+
expected: '2025-07-04',
|
|
15235
|
+
},
|
|
15236
|
+
{
|
|
15237
|
+
label: 'Input as Date object',
|
|
15238
|
+
input: new Date('2025-07-10T10:00:00-04:00'),
|
|
15239
|
+
expected: '2025-07-10',
|
|
15240
|
+
},
|
|
15241
|
+
{
|
|
15242
|
+
label: 'Input as timestamp (number)',
|
|
15243
|
+
input: new Date('2025-07-10T10:00:00-04:00').getTime(),
|
|
15244
|
+
expected: '2025-07-10',
|
|
15245
|
+
},
|
|
15246
|
+
{
|
|
15247
|
+
label: 'Input as ISO string',
|
|
15248
|
+
input: '2025-07-10T10:00:00-04:00',
|
|
15249
|
+
expected: '2025-07-10',
|
|
15250
|
+
},
|
|
15251
|
+
{
|
|
15252
|
+
label: 'Edge case (Jan 24, 2026, early morning EST)',
|
|
15253
|
+
input: '2026-01-24T00:30:00-05:00',
|
|
15254
|
+
expected: '2026-01-24',
|
|
15255
|
+
},
|
|
15256
|
+
{
|
|
15257
|
+
label: 'Edge case (Jan 24, 2026, late night EST)',
|
|
15258
|
+
input: '2026-01-24T23:59:59-05:00',
|
|
15259
|
+
expected: '2026-01-24',
|
|
15260
|
+
},
|
|
15261
|
+
{
|
|
15262
|
+
label: 'Edge case (Jan 25, 2026, start of day EST)',
|
|
15263
|
+
input: '2026-01-25T00:00:00-05:00',
|
|
15264
|
+
expected: '2026-01-25',
|
|
15265
|
+
},
|
|
15266
|
+
{
|
|
15267
|
+
label: 'Edge case (Jan 25, 2026, late night EST)',
|
|
15268
|
+
input: '2026-01-25T23:59:59-05:00',
|
|
15269
|
+
expected: '2026-01-25',
|
|
15270
|
+
},
|
|
15271
|
+
{
|
|
15272
|
+
label: 'Edge case (Jan 26, 2026, start of day EST)',
|
|
15273
|
+
input: '2026-01-26T00:00:00-05:00',
|
|
15274
|
+
expected: '2026-01-26',
|
|
15275
|
+
},
|
|
15276
|
+
{
|
|
15277
|
+
label: 'Edge case (Jan 26, 2026, 02:00Z = Jan 25, 21:00 EST)',
|
|
15278
|
+
input: '2026-01-26T02:00:00Z',
|
|
15279
|
+
expected: '2026-01-25',
|
|
15280
|
+
},
|
|
15281
|
+
{
|
|
15282
|
+
label: 'Edge case (Jan 26, 2026, 04:59:59Z = Jan 25, 23:59:59 EST)',
|
|
15283
|
+
input: '2026-01-26T04:59:59Z',
|
|
15284
|
+
expected: '2026-01-25',
|
|
15285
|
+
},
|
|
15286
|
+
{
|
|
15287
|
+
label: 'Edge case (Jan 26, 2026, 05:00:00Z = Jan 26, 00:00:00 EST)',
|
|
15288
|
+
input: '2026-01-26T05:00:00Z',
|
|
15289
|
+
expected: '2026-01-26',
|
|
15290
|
+
},
|
|
15291
|
+
{
|
|
15292
|
+
label: 'Edge case (Jan 24, 2026, timestamp input EST)',
|
|
15293
|
+
input: new Date('2026-01-24T12:00:00-05:00').getTime(),
|
|
15294
|
+
expected: '2026-01-24',
|
|
15295
|
+
},
|
|
15296
|
+
];
|
|
15297
|
+
for (const { label, input, expected } of testCases) {
|
|
15298
|
+
const result = disco.time.getTradingDate(input);
|
|
15299
|
+
const pass = result === expected;
|
|
15300
|
+
console.log(`\nTest: ${label}`);
|
|
15301
|
+
console.log(` Input: ${input instanceof Date ? input.toISOString() : input}`);
|
|
15302
|
+
console.log(` Result: ${result} ${pass ? '✓' : `✗ (expected ${expected})`}`);
|
|
15303
|
+
if (!pass) {
|
|
15304
|
+
console.error(` FAILED: Expected ${expected}, got ${result}`);
|
|
15305
|
+
}
|
|
15306
|
+
}
|
|
14572
15307
|
}
|
|
14573
15308
|
// testGetPortfolioDailyHistory();
|
|
14574
15309
|
// testWebSocketConnectAndDisconnect();
|
|
@@ -14581,5 +15316,6 @@ function testMarketDataSubscription(symbol) {
|
|
|
14581
15316
|
// Test market data subscription with a real symbol or FAKEPACA
|
|
14582
15317
|
// Uncomment one of the following to test:
|
|
14583
15318
|
// testMarketDataSubscription('SPY');
|
|
14584
|
-
testMarketDataSubscription('FAKEPACA');
|
|
15319
|
+
// testMarketDataSubscription('FAKEPACA');
|
|
15320
|
+
testGetTradingDate();
|
|
14585
15321
|
//# sourceMappingURL=test.js.map
|