@adaptic/utils 0.1.45 → 0.1.46

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.
Files changed (70) hide show
  1. package/README.md +153 -61
  2. package/dist/index.cjs +1929 -1443
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.mjs +1925 -1445
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/test.js +288 -170
  7. package/dist/test.js.map +1 -1
  8. package/dist/types/__tests__/financial-regression.test.d.ts +2 -0
  9. package/dist/types/__tests__/financial-regression.test.d.ts.map +1 -0
  10. package/dist/types/__tests__/price-utils.test.d.ts +2 -0
  11. package/dist/types/__tests__/price-utils.test.d.ts.map +1 -0
  12. package/dist/types/adaptic.d.ts +2 -2
  13. package/dist/types/adaptic.d.ts.map +1 -1
  14. package/dist/types/alpaca-functions.d.ts +21 -21
  15. package/dist/types/alpaca-functions.d.ts.map +1 -1
  16. package/dist/types/alpaca-market-data-api.d.ts +16 -3
  17. package/dist/types/alpaca-market-data-api.d.ts.map +1 -1
  18. package/dist/types/alpaca-trading-api.d.ts +11 -11
  19. package/dist/types/alpaca-trading-api.d.ts.map +1 -1
  20. package/dist/types/alphavantage.d.ts +1 -1
  21. package/dist/types/alphavantage.d.ts.map +1 -1
  22. package/dist/types/asset-allocation-algorithm.d.ts +1 -1
  23. package/dist/types/asset-allocation-algorithm.d.ts.map +1 -1
  24. package/dist/types/cache/stampede-protected-cache.d.ts.map +1 -1
  25. package/dist/types/crypto.d.ts +2 -2
  26. package/dist/types/crypto.d.ts.map +1 -1
  27. package/dist/types/display-manager.d.ts +1 -1
  28. package/dist/types/display-manager.d.ts.map +1 -1
  29. package/dist/types/examples/asset-allocation-example.d.ts.map +1 -1
  30. package/dist/types/format-tools.d.ts.map +1 -1
  31. package/dist/types/index.d.ts +23 -21
  32. package/dist/types/index.d.ts.map +1 -1
  33. package/dist/types/logging.d.ts +1 -1
  34. package/dist/types/logging.d.ts.map +1 -1
  35. package/dist/types/market-hours.d.ts.map +1 -1
  36. package/dist/types/market-time.d.ts +2 -2
  37. package/dist/types/market-time.d.ts.map +1 -1
  38. package/dist/types/metrics-calcs.d.ts.map +1 -1
  39. package/dist/types/misc-utils.d.ts +1 -1
  40. package/dist/types/misc-utils.d.ts.map +1 -1
  41. package/dist/types/performance-metrics.d.ts +2 -2
  42. package/dist/types/performance-metrics.d.ts.map +1 -1
  43. package/dist/types/polygon-indices.d.ts +1 -1
  44. package/dist/types/polygon-indices.d.ts.map +1 -1
  45. package/dist/types/polygon.d.ts +1 -1
  46. package/dist/types/polygon.d.ts.map +1 -1
  47. package/dist/types/price-utils.d.ts.map +1 -1
  48. package/dist/types/technical-analysis.d.ts +9 -9
  49. package/dist/types/technical-analysis.d.ts.map +1 -1
  50. package/dist/types/time-utils.d.ts.map +1 -1
  51. package/dist/types/trading-policy/defaults.d.ts +46 -0
  52. package/dist/types/trading-policy/defaults.d.ts.map +1 -0
  53. package/dist/types/trading-policy/index.d.ts +8 -0
  54. package/dist/types/trading-policy/index.d.ts.map +1 -0
  55. package/dist/types/types/alpaca-types.d.ts +177 -88
  56. package/dist/types/types/alpaca-types.d.ts.map +1 -1
  57. package/dist/types/types/alphavantage-types.d.ts +2 -2
  58. package/dist/types/types/asset-allocation-types.d.ts +11 -11
  59. package/dist/types/types/index.d.ts +8 -8
  60. package/dist/types/types/index.d.ts.map +1 -1
  61. package/dist/types/types/logging-types.d.ts +1 -1
  62. package/dist/types/types/logging-types.d.ts.map +1 -1
  63. package/dist/types/types/market-time-types.d.ts +4 -4
  64. package/dist/types/types/market-time-types.d.ts.map +1 -1
  65. package/dist/types/types/metrics-types.d.ts +4 -4
  66. package/dist/types/types/metrics-types.d.ts.map +1 -1
  67. package/dist/types/types/polygon-indices-types.d.ts +6 -6
  68. package/dist/types/types/polygon-types.d.ts +3 -3
  69. package/dist/types/types/ta-types.d.ts +3 -3
  70. package/package.json +6 -3
package/dist/test.js CHANGED
@@ -493,7 +493,7 @@ createChalk({level: stderrColor ? stderrColor.level : 0});
493
493
 
494
494
  class DisplayManager {
495
495
  static instance;
496
- promptText = '';
496
+ promptText = "";
497
497
  constructor() { }
498
498
  static getInstance() {
499
499
  if (!DisplayManager.instance) {
@@ -513,20 +513,22 @@ class DisplayManager {
513
513
  cursorTo(process.stdout, 0);
514
514
  // Format the timestamp
515
515
  const date = new Date();
516
- const timestamp = date.toLocaleString('en-US', { timeZone: 'America/New_York' });
516
+ const timestamp = date.toLocaleString("en-US", {
517
+ timeZone: "America/New_York",
518
+ });
517
519
  const account = options?.account;
518
520
  const symbol = options?.symbol;
519
521
  // Build the log message
520
- let logMessage = `[${timestamp}]${options?.source ? ` [${options.source}] ` : ''}${account ? ` [${account}] ` : ''}${symbol ? ` [${symbol}] ` : ''}${message}`;
522
+ let logMessage = `[${timestamp}]${options?.source ? ` [${options.source}] ` : ""}${account ? ` [${account}] ` : ""}${symbol ? ` [${symbol}] ` : ""}${message}`;
521
523
  // Add color based on type
522
- if (options?.type === 'error') {
524
+ if (options?.type === "error") {
523
525
  logMessage = chalk.red(logMessage);
524
526
  }
525
- else if (options?.type === 'warn') {
527
+ else if (options?.type === "warn") {
526
528
  logMessage = chalk.yellow(logMessage);
527
529
  }
528
530
  // Write the log message
529
- process.stdout.write(logMessage + '\n');
531
+ process.stdout.write(logMessage + "\n");
530
532
  // Log to file
531
533
  if (symbol) {
532
534
  // Log to symbol-specific file if symbol is provided
@@ -545,20 +547,20 @@ class DisplayManager {
545
547
  writeSymbolLog(symbol, date, logMessage, options) {
546
548
  try {
547
549
  // Create logs directory if it doesn't exist
548
- if (!fs.existsSync('logs')) {
549
- fs.mkdirSync('logs', { recursive: true });
550
+ if (!fs.existsSync("logs")) {
551
+ fs.mkdirSync("logs", { recursive: true });
550
552
  }
551
553
  // Format date for filename: YYYY-MM-DD
552
554
  const year = date.getFullYear();
553
- const month = String(date.getMonth() + 1).padStart(2, '0');
554
- const day = String(date.getDate()).padStart(2, '0');
555
+ const month = String(date.getMonth() + 1).padStart(2, "0");
556
+ const day = String(date.getDate()).padStart(2, "0");
555
557
  // Create filename: SYM-YYYY-MM-DD.log
556
558
  const filename = `${symbol}-${year}-${month}-${day}.log`;
557
- const filePath = path.join('logs', filename);
559
+ const filePath = path.join("logs", filename);
558
560
  // Strip ANSI color codes from log message
559
- const plainLogMessage = logMessage.replace(/\x1B\[\d+m/g, '');
561
+ const plainLogMessage = logMessage.replace(/\x1B\[\d+m/g, "");
560
562
  // Write to file (append if exists, create if not)
561
- fs.appendFileSync(filePath, plainLogMessage + '\n');
563
+ fs.appendFileSync(filePath, plainLogMessage + "\n");
562
564
  }
563
565
  catch (error) {
564
566
  // Only log to console - don't try to log to file again to avoid potential infinite loop
@@ -571,21 +573,21 @@ class DisplayManager {
571
573
  writeGenericLog(date, logMessage, options) {
572
574
  try {
573
575
  // Create logs directory if it doesn't exist
574
- if (!fs.existsSync('logs')) {
575
- fs.mkdirSync('logs', { recursive: true });
576
+ if (!fs.existsSync("logs")) {
577
+ fs.mkdirSync("logs", { recursive: true });
576
578
  }
577
579
  // Format date for filename: YYYY-MM-DD
578
580
  const year = date.getFullYear();
579
- const month = String(date.getMonth() + 1).padStart(2, '0');
580
- const day = String(date.getDate()).padStart(2, '0');
581
+ const month = String(date.getMonth() + 1).padStart(2, "0");
582
+ const day = String(date.getDate()).padStart(2, "0");
581
583
  // Create filename: system-YYYY-MM-DD.log
582
- const source = options?.source?.toLowerCase().replace(/\s+/g, '-') || 'system';
584
+ const source = options?.source?.toLowerCase().replace(/\s+/g, "-") || "system";
583
585
  const filename = `${source}-${year}-${month}-${day}.log`;
584
- const filePath = path.join('logs', filename);
586
+ const filePath = path.join("logs", filename);
585
587
  // Strip ANSI color codes from log message
586
- const plainLogMessage = logMessage.replace(/\x1B\[\d+m/g, '');
588
+ const plainLogMessage = logMessage.replace(/\x1B\[\d+m/g, "");
587
589
  // Write to file (append if exists, create if not)
588
- fs.appendFileSync(filePath, plainLogMessage + '\n');
590
+ fs.appendFileSync(filePath, plainLogMessage + "\n");
589
591
  }
590
592
  catch (error) {
591
593
  // Only log to console - don't try to log to file again to avoid potential infinite loop
@@ -613,7 +615,7 @@ class DisplayManager {
613
615
  * @param options.symbol The trading symbol associated with this log.
614
616
  * @param options.logToFile Force logging to a file even when no symbol is provided.
615
617
  */
616
- function log$2(message, options = { source: 'Server', type: 'info' }) {
618
+ function log$2(message, options = { source: "Server", type: "info" }) {
617
619
  const displayManager = DisplayManager.getInstance();
618
620
  displayManager.log(message, options);
619
621
  }
@@ -621,104 +623,104 @@ function log$2(message, options = { source: 'Server', type: 'info' }) {
621
623
  // market-hours.ts
622
624
  const marketHolidays = {
623
625
  2024: {
624
- 'New Year\'s Day': { date: '2024-01-01' },
625
- 'Martin Luther King, Jr. Day': { date: '2024-01-15' },
626
- 'Washington\'s Birthday': { date: '2024-02-19' },
627
- 'Good Friday': { date: '2024-03-29' },
628
- 'Memorial Day': { date: '2024-05-27' },
629
- 'Juneteenth National Independence Day': { date: '2024-06-19' },
630
- 'Independence Day': { date: '2024-07-04' },
631
- 'Labor Day': { date: '2024-09-02' },
632
- 'Thanksgiving Day': { date: '2024-11-28' },
633
- 'Christmas Day': { date: '2024-12-25' }
626
+ "New Year's Day": { date: "2024-01-01" },
627
+ "Martin Luther King, Jr. Day": { date: "2024-01-15" },
628
+ "Washington's Birthday": { date: "2024-02-19" },
629
+ "Good Friday": { date: "2024-03-29" },
630
+ "Memorial Day": { date: "2024-05-27" },
631
+ "Juneteenth National Independence Day": { date: "2024-06-19" },
632
+ "Independence Day": { date: "2024-07-04" },
633
+ "Labor Day": { date: "2024-09-02" },
634
+ "Thanksgiving Day": { date: "2024-11-28" },
635
+ "Christmas Day": { date: "2024-12-25" },
634
636
  },
635
637
  2025: {
636
- 'New Year\'s Day': { date: '2025-01-01' },
637
- 'Jimmy Carter Memorial Day': { date: '2025-01-09' },
638
- 'Martin Luther King, Jr. Day': { date: '2025-01-20' },
639
- 'Washington\'s Birthday': { date: '2025-02-17' },
640
- 'Good Friday': { date: '2025-04-18' },
641
- 'Memorial Day': { date: '2025-05-26' },
642
- 'Juneteenth National Independence Day': { date: '2025-06-19' },
643
- 'Independence Day': { date: '2025-07-04' },
644
- 'Labor Day': { date: '2025-09-01' },
645
- 'Thanksgiving Day': { date: '2025-11-27' },
646
- 'Christmas Day': { date: '2025-12-25' }
638
+ "New Year's Day": { date: "2025-01-01" },
639
+ "Jimmy Carter Memorial Day": { date: "2025-01-09" },
640
+ "Martin Luther King, Jr. Day": { date: "2025-01-20" },
641
+ "Washington's Birthday": { date: "2025-02-17" },
642
+ "Good Friday": { date: "2025-04-18" },
643
+ "Memorial Day": { date: "2025-05-26" },
644
+ "Juneteenth National Independence Day": { date: "2025-06-19" },
645
+ "Independence Day": { date: "2025-07-04" },
646
+ "Labor Day": { date: "2025-09-01" },
647
+ "Thanksgiving Day": { date: "2025-11-27" },
648
+ "Christmas Day": { date: "2025-12-25" },
647
649
  },
648
650
  2026: {
649
- 'New Year\'s Day': { date: '2026-01-01' },
650
- 'Martin Luther King, Jr. Day': { date: '2026-01-19' },
651
- 'Washington\'s Birthday': { date: '2026-02-16' },
652
- 'Good Friday': { date: '2026-04-03' },
653
- 'Memorial Day': { date: '2026-05-25' },
654
- 'Juneteenth National Independence Day': { date: '2026-06-19' },
655
- 'Independence Day': { date: '2026-07-03' },
656
- 'Labor Day': { date: '2026-09-07' },
657
- 'Thanksgiving Day': { date: '2026-11-26' },
658
- 'Christmas Day': { date: '2026-12-25' }
659
- }
651
+ "New Year's Day": { date: "2026-01-01" },
652
+ "Martin Luther King, Jr. Day": { date: "2026-01-19" },
653
+ "Washington's Birthday": { date: "2026-02-16" },
654
+ "Good Friday": { date: "2026-04-03" },
655
+ "Memorial Day": { date: "2026-05-25" },
656
+ "Juneteenth National Independence Day": { date: "2026-06-19" },
657
+ "Independence Day": { date: "2026-07-03" },
658
+ "Labor Day": { date: "2026-09-07" },
659
+ "Thanksgiving Day": { date: "2026-11-26" },
660
+ "Christmas Day": { date: "2026-12-25" },
661
+ },
660
662
  };
661
663
  const marketEarlyCloses = {
662
664
  2024: {
663
- '2024-07-03': {
664
- date: '2024-07-03',
665
- time: '13:00',
666
- optionsTime: '13:15',
667
- notes: 'Market closes early on Wednesday, July 3, 2024 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
665
+ "2024-07-03": {
666
+ date: "2024-07-03",
667
+ time: "13:00",
668
+ optionsTime: "13:15",
669
+ notes: "Market closes early on Wednesday, July 3, 2024 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
668
670
  },
669
- '2024-11-29': {
670
- date: '2024-11-29',
671
- time: '13:00',
672
- optionsTime: '13:15',
673
- notes: 'Market closes early on Friday, November 29, 2024 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
671
+ "2024-11-29": {
672
+ date: "2024-11-29",
673
+ time: "13:00",
674
+ optionsTime: "13:15",
675
+ notes: "Market closes early on Friday, November 29, 2024 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
676
+ },
677
+ "2024-12-24": {
678
+ date: "2024-12-24",
679
+ time: "13:00",
680
+ optionsTime: "13:15",
681
+ notes: "Market closes early on Tuesday, December 24, 2024 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
674
682
  },
675
- '2024-12-24': {
676
- date: '2024-12-24',
677
- time: '13:00',
678
- optionsTime: '13:15',
679
- notes: 'Market closes early on Tuesday, December 24, 2024 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
680
- }
681
683
  },
682
684
  2025: {
683
- '2025-07-03': {
684
- date: '2025-07-03',
685
- time: '13:00',
686
- optionsTime: '13:15',
687
- notes: 'Market closes early on Thursday, July 3, 2025 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
685
+ "2025-07-03": {
686
+ date: "2025-07-03",
687
+ time: "13:00",
688
+ optionsTime: "13:15",
689
+ notes: "Market closes early on Thursday, July 3, 2025 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
688
690
  },
689
- '2025-11-28': {
690
- date: '2025-11-28',
691
- time: '13:00',
692
- optionsTime: '13:15',
693
- notes: 'Market closes early on Friday, November 28, 2025 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
691
+ "2025-11-28": {
692
+ date: "2025-11-28",
693
+ time: "13:00",
694
+ optionsTime: "13:15",
695
+ notes: "Market closes early on Friday, November 28, 2025 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
696
+ },
697
+ "2025-12-24": {
698
+ date: "2025-12-24",
699
+ time: "13:00",
700
+ optionsTime: "13:15",
701
+ notes: "Market closes early on Wednesday, December 24, 2025 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
694
702
  },
695
- '2025-12-24': {
696
- date: '2025-12-24',
697
- time: '13:00',
698
- optionsTime: '13:15',
699
- notes: 'Market closes early on Wednesday, December 24, 2025 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
700
- }
701
703
  },
702
704
  2026: {
703
- '2026-07-02': {
704
- date: '2026-07-02',
705
- time: '13:00',
706
- optionsTime: '13:15',
707
- notes: 'Independence Day observed, market closes early at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
705
+ "2026-07-02": {
706
+ date: "2026-07-02",
707
+ time: "13:00",
708
+ optionsTime: "13:15",
709
+ notes: "Independence Day observed, market closes early at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
708
710
  },
709
- '2026-11-27': {
710
- date: '2026-11-27',
711
- time: '13:00',
712
- optionsTime: '13:15',
713
- notes: 'Market closes early on Friday, November 27, 2026 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
711
+ "2026-11-27": {
712
+ date: "2026-11-27",
713
+ time: "13:00",
714
+ optionsTime: "13:15",
715
+ notes: "Market closes early on Friday, November 27, 2026 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
714
716
  },
715
- '2026-12-24': {
716
- date: '2026-12-24',
717
- time: '13:00',
718
- optionsTime: '13:15',
719
- notes: 'Market closes early on Thursday, December 24, 2026 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.'
720
- }
721
- }
717
+ "2026-12-24": {
718
+ date: "2026-12-24",
719
+ time: "13:00",
720
+ optionsTime: "13:15",
721
+ notes: "Market closes early on Thursday, December 24, 2026 at 1:00 p.m. (1:15 p.m. for eligible options). NYSE American Equities, NYSE Arca Equities, NYSE Chicago, and NYSE National late trading sessions will close at 5:00 p.m. Eastern Time.",
722
+ },
723
+ },
722
724
  };
723
725
 
724
726
  // market-time.ts
@@ -731,9 +733,15 @@ const marketEarlyCloses = {
731
733
  * Early extended market hours are 1:00pm-5:00pm on early close days
732
734
  */
733
735
  const MARKET_TIMES = {
734
- TIMEZONE: 'America/New_York',
735
- REGULAR: { START: { HOUR: 9, MINUTE: 30}, END: { HOUR: 16, MINUTE: 0} },
736
- EXTENDED: { START: { HOUR: 4, MINUTE: 0}, END: { HOUR: 20, MINUTE: 0} },
736
+ TIMEZONE: "America/New_York",
737
+ REGULAR: {
738
+ START: { HOUR: 9, MINUTE: 30},
739
+ END: { HOUR: 16, MINUTE: 0},
740
+ },
741
+ EXTENDED: {
742
+ START: { HOUR: 4, MINUTE: 0},
743
+ END: { HOUR: 20, MINUTE: 0},
744
+ },
737
745
  };
738
746
  /**
739
747
  * Utility class for handling market time-related operations
@@ -746,7 +754,7 @@ class MarketTimeUtil {
746
754
  * @param {string} [timezone='America/New_York'] - The timezone to use for market time calculations
747
755
  * @param {IntradayReporting} [intradayReporting='market_hours'] - The intraday reporting mode
748
756
  */
749
- constructor(timezone = MARKET_TIMES.TIMEZONE, intradayReporting = 'market_hours') {
757
+ constructor(timezone = MARKET_TIMES.TIMEZONE, intradayReporting = "market_hours") {
750
758
  this.validateTimezone(timezone);
751
759
  this.timezone = timezone;
752
760
  this.intradayReporting = intradayReporting;
@@ -765,13 +773,13 @@ class MarketTimeUtil {
765
773
  throw new Error(`Invalid timezone: ${timezone}`);
766
774
  }
767
775
  }
768
- formatDate(date, outputFormat = 'iso') {
776
+ formatDate(date, outputFormat = "iso") {
769
777
  switch (outputFormat) {
770
- case 'unix-seconds':
778
+ case "unix-seconds":
771
779
  return Math.floor(date.getTime() / 1000);
772
- case 'unix-ms':
780
+ case "unix-ms":
773
781
  return date.getTime();
774
- case 'iso':
782
+ case "iso":
775
783
  default:
776
784
  // return with timezone offset
777
785
  return formatInTimeZone(date, this.timezone, "yyyy-MM-dd'T'HH:mm:ssXXX");
@@ -782,7 +790,7 @@ class MarketTimeUtil {
782
790
  return day === 0 || day === 6;
783
791
  }
784
792
  isHoliday(date) {
785
- const formattedDate = format(date, 'yyyy-MM-dd');
793
+ const formattedDate = format(date, "yyyy-MM-dd");
786
794
  const yearHolidays = marketHolidays[date.getFullYear()];
787
795
  for (const holiday in yearHolidays) {
788
796
  if (yearHolidays[holiday].date === formattedDate) {
@@ -792,7 +800,7 @@ class MarketTimeUtil {
792
800
  return false;
793
801
  }
794
802
  isEarlyCloseDay(date) {
795
- const formattedDate = format(date, 'yyyy-MM-dd');
803
+ const formattedDate = format(date, "yyyy-MM-dd");
796
804
  const yearEarlyCloses = marketEarlyCloses[date.getFullYear()];
797
805
  return yearEarlyCloses && yearEarlyCloses[formattedDate] !== undefined;
798
806
  }
@@ -802,10 +810,12 @@ class MarketTimeUtil {
802
810
  * @returns The early close time in minutes from midnight, or null if there is no early close
803
811
  */
804
812
  getEarlyCloseTime(date) {
805
- const formattedDate = format(date, 'yyyy-MM-dd');
813
+ const formattedDate = format(date, "yyyy-MM-dd");
806
814
  const yearEarlyCloses = marketEarlyCloses[date.getFullYear()];
807
815
  if (yearEarlyCloses && yearEarlyCloses[formattedDate]) {
808
- const [hours, minutes] = yearEarlyCloses[formattedDate].time.split(':').map(Number);
816
+ const [hours, minutes] = yearEarlyCloses[formattedDate].time
817
+ .split(":")
818
+ .map(Number);
809
819
  return hours * 60 + minutes;
810
820
  }
811
821
  return null;
@@ -842,23 +852,30 @@ class MarketTimeUtil {
842
852
  // Regular market hours logic
843
853
  let returner;
844
854
  switch (this.intradayReporting) {
845
- case 'extended_hours': {
846
- const extendedStartMinutes = MARKET_TIMES.EXTENDED.START.HOUR * 60 + MARKET_TIMES.EXTENDED.START.MINUTE;
847
- const extendedEndMinutes = MARKET_TIMES.EXTENDED.END.HOUR * 60 + MARKET_TIMES.EXTENDED.END.MINUTE;
855
+ case "extended_hours": {
856
+ const extendedStartMinutes = MARKET_TIMES.EXTENDED.START.HOUR * 60 +
857
+ MARKET_TIMES.EXTENDED.START.MINUTE;
858
+ const extendedEndMinutes = MARKET_TIMES.EXTENDED.END.HOUR * 60 +
859
+ MARKET_TIMES.EXTENDED.END.MINUTE;
848
860
  // Comprehensive handling of times crossing midnight
849
861
  const adjustedDate = timeInMinutes < extendedStartMinutes ? sub(date, { days: 1 }) : date;
850
862
  const adjustedTimeInMinutes = adjustedDate.getHours() * 60 + adjustedDate.getMinutes();
851
- returner = adjustedTimeInMinutes >= extendedStartMinutes && adjustedTimeInMinutes <= extendedEndMinutes;
863
+ returner =
864
+ adjustedTimeInMinutes >= extendedStartMinutes &&
865
+ adjustedTimeInMinutes <= extendedEndMinutes;
852
866
  break;
853
867
  }
854
- case 'continuous':
868
+ case "continuous":
855
869
  returner = true;
856
870
  break;
857
871
  default: {
858
872
  // market_hours
859
- const regularStartMinutes = MARKET_TIMES.REGULAR.START.HOUR * 60 + MARKET_TIMES.REGULAR.START.MINUTE;
873
+ const regularStartMinutes = MARKET_TIMES.REGULAR.START.HOUR * 60 +
874
+ MARKET_TIMES.REGULAR.START.MINUTE;
860
875
  const regularEndMinutes = MARKET_TIMES.REGULAR.END.HOUR * 60 + MARKET_TIMES.REGULAR.END.MINUTE;
861
- returner = timeInMinutes >= regularStartMinutes && timeInMinutes <= regularEndMinutes;
876
+ returner =
877
+ timeInMinutes >= regularStartMinutes &&
878
+ timeInMinutes <= regularEndMinutes;
862
879
  break;
863
880
  }
864
881
  }
@@ -871,9 +888,11 @@ class MarketTimeUtil {
871
888
  */
872
889
  isBeforeMarketHours(date) {
873
890
  const timeInMinutes = date.getHours() * 60 + date.getMinutes();
874
- const startMinutes = this.intradayReporting === 'extended_hours'
875
- ? MARKET_TIMES.EXTENDED.START.HOUR * 60 + MARKET_TIMES.EXTENDED.START.MINUTE
876
- : MARKET_TIMES.REGULAR.START.HOUR * 60 + MARKET_TIMES.REGULAR.START.MINUTE;
891
+ const startMinutes = this.intradayReporting === "extended_hours"
892
+ ? MARKET_TIMES.EXTENDED.START.HOUR * 60 +
893
+ MARKET_TIMES.EXTENDED.START.MINUTE
894
+ : MARKET_TIMES.REGULAR.START.HOUR * 60 +
895
+ MARKET_TIMES.REGULAR.START.MINUTE;
877
896
  return timeInMinutes < startMinutes;
878
897
  }
879
898
  /**
@@ -945,7 +964,7 @@ class MarketTimeUtil {
945
964
  let start;
946
965
  let end;
947
966
  switch (this.intradayReporting) {
948
- case 'extended_hours': {
967
+ case "extended_hours": {
949
968
  start = set(date, {
950
969
  hours: MARKET_TIMES.EXTENDED.START.HOUR,
951
970
  minutes: MARKET_TIMES.EXTENDED.START.MINUTE,
@@ -960,7 +979,7 @@ class MarketTimeUtil {
960
979
  });
961
980
  break;
962
981
  }
963
- case 'continuous': {
982
+ case "continuous": {
964
983
  start = startOfDay(date);
965
984
  end = endOfDay(date);
966
985
  break;
@@ -1002,31 +1021,31 @@ class MarketTimeUtil {
1002
1021
  calculatePeriodStartDate(endDate, period) {
1003
1022
  let startDate;
1004
1023
  switch (period) {
1005
- case 'YTD':
1024
+ case "YTD":
1006
1025
  startDate = set(endDate, { month: 0, date: 1 });
1007
1026
  break;
1008
- case '1D':
1027
+ case "1D":
1009
1028
  startDate = this.getLastMarketDay(endDate);
1010
1029
  break;
1011
- case '3D':
1030
+ case "3D":
1012
1031
  startDate = sub(endDate, { days: 3 });
1013
1032
  break;
1014
- case '1W':
1033
+ case "1W":
1015
1034
  startDate = sub(endDate, { weeks: 1 });
1016
1035
  break;
1017
- case '2W':
1036
+ case "2W":
1018
1037
  startDate = sub(endDate, { weeks: 2 });
1019
1038
  break;
1020
- case '1M':
1039
+ case "1M":
1021
1040
  startDate = sub(endDate, { months: 1 });
1022
1041
  break;
1023
- case '3M':
1042
+ case "3M":
1024
1043
  startDate = sub(endDate, { months: 3 });
1025
1044
  break;
1026
- case '6M':
1045
+ case "6M":
1027
1046
  startDate = sub(endDate, { months: 6 });
1028
1047
  break;
1029
- case '1Y':
1048
+ case "1Y":
1030
1049
  startDate = sub(endDate, { years: 1 });
1031
1050
  break;
1032
1051
  default:
@@ -1037,9 +1056,9 @@ class MarketTimeUtil {
1037
1056
  }
1038
1057
  return startDate;
1039
1058
  }
1040
- getMarketTimePeriod({ period, end = new Date(), intraday_reporting, outputFormat = 'iso', }) {
1059
+ getMarketTimePeriod({ period, end = new Date(), intraday_reporting, outputFormat = "iso", }) {
1041
1060
  if (!period) {
1042
- throw new Error('Period is required');
1061
+ throw new Error("Period is required");
1043
1062
  }
1044
1063
  if (intraday_reporting) {
1045
1064
  this.intradayReporting = intraday_reporting;
@@ -1084,7 +1103,7 @@ class MarketTimeUtil {
1084
1103
  const utcEnd = fromZonedTime(endDate, this.timezone);
1085
1104
  // Ensure start is not after end
1086
1105
  if (isBefore(utcEnd, utcStart)) {
1087
- throw new Error('Start date cannot be after end date');
1106
+ throw new Error("Start date cannot be after end date");
1088
1107
  }
1089
1108
  return {
1090
1109
  start: this.formatDate(utcStart, outputFormat),
@@ -1128,10 +1147,22 @@ class MarketTimeUtil {
1128
1147
  };
1129
1148
  }
1130
1149
  }
1131
- const open = fromZonedTime(set(dayStart, { hours: regularOpenTime.HOUR, minutes: regularOpenTime.MINUTE }), this.timezone);
1132
- const close = fromZonedTime(set(dayStart, { hours: regularCloseTime.HOUR, minutes: regularCloseTime.MINUTE }), this.timezone);
1133
- const openExt = fromZonedTime(set(dayStart, { hours: extendedOpenTime.HOUR, minutes: extendedOpenTime.MINUTE }), this.timezone);
1134
- const closeExt = fromZonedTime(set(dayStart, { hours: extendedCloseTime.HOUR, minutes: extendedCloseTime.MINUTE }), this.timezone);
1150
+ const open = fromZonedTime(set(dayStart, {
1151
+ hours: regularOpenTime.HOUR,
1152
+ minutes: regularOpenTime.MINUTE,
1153
+ }), this.timezone);
1154
+ const close = fromZonedTime(set(dayStart, {
1155
+ hours: regularCloseTime.HOUR,
1156
+ minutes: regularCloseTime.MINUTE,
1157
+ }), this.timezone);
1158
+ const openExt = fromZonedTime(set(dayStart, {
1159
+ hours: extendedOpenTime.HOUR,
1160
+ minutes: extendedOpenTime.MINUTE,
1161
+ }), this.timezone);
1162
+ const closeExt = fromZonedTime(set(dayStart, {
1163
+ hours: extendedCloseTime.HOUR,
1164
+ minutes: extendedCloseTime.MINUTE,
1165
+ }), this.timezone);
1135
1166
  return {
1136
1167
  marketOpen: true,
1137
1168
  open,
@@ -1154,7 +1185,7 @@ function getLastFullTradingDate(currentDate = new Date()) {
1154
1185
  // Format the date in NY timezone to ensure consistency
1155
1186
  return {
1156
1187
  date,
1157
- YYYYMMDD: formatInTimeZone(date, MARKET_TIMES.TIMEZONE, 'yyyy-MM-dd'),
1188
+ YYYYMMDD: formatInTimeZone(date, MARKET_TIMES.TIMEZONE, "yyyy-MM-dd"),
1158
1189
  };
1159
1190
  }
1160
1191
 
@@ -6147,22 +6178,28 @@ class AlpacaMarketDataAPI extends EventEmitter {
6147
6178
  v1beta1url;
6148
6179
  stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip'; // production values
6149
6180
  optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // production values
6181
+ cryptoStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/crypto/us'; // production values
6150
6182
  stockWs = null;
6151
6183
  optionWs = null;
6184
+ cryptoWs = null;
6152
6185
  stockSubscriptions = { trades: [], quotes: [], bars: [] };
6153
6186
  optionSubscriptions = { trades: [], quotes: [], bars: [] };
6187
+ cryptoSubscriptions = { trades: [], quotes: [], bars: [] };
6154
6188
  setMode(mode = 'production') {
6155
6189
  if (mode === 'sandbox') { // sandbox mode
6156
6190
  this.stockStreamUrl = 'wss://stream.data.sandbox.alpaca.markets/v2/sip';
6157
6191
  this.optionStreamUrl = 'wss://stream.data.sandbox.alpaca.markets/v1beta3/options';
6192
+ this.cryptoStreamUrl = 'wss://stream.data.sandbox.alpaca.markets/v1beta3/crypto/us';
6158
6193
  }
6159
6194
  else if (mode === 'test') { // test mode, can only use ticker FAKEPACA
6160
6195
  this.stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/test';
6161
6196
  this.optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // there's no test mode for options
6197
+ this.cryptoStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/crypto/us'; // there's no test mode for crypto
6162
6198
  }
6163
6199
  else { // production
6164
6200
  this.stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip';
6165
6201
  this.optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options';
6202
+ this.cryptoStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/crypto/us';
6166
6203
  }
6167
6204
  }
6168
6205
  getMode() {
@@ -6204,14 +6241,26 @@ class AlpacaMarketDataAPI extends EventEmitter {
6204
6241
  return super.emit(event, ...args);
6205
6242
  }
6206
6243
  connect(streamType) {
6207
- const url = streamType === 'stock' ? this.stockStreamUrl : this.optionStreamUrl;
6244
+ let url;
6245
+ if (streamType === 'stock') {
6246
+ url = this.stockStreamUrl;
6247
+ }
6248
+ else if (streamType === 'option') {
6249
+ url = this.optionStreamUrl;
6250
+ }
6251
+ else {
6252
+ url = this.cryptoStreamUrl;
6253
+ }
6208
6254
  const ws = new WebSocket(url);
6209
6255
  if (streamType === 'stock') {
6210
6256
  this.stockWs = ws;
6211
6257
  }
6212
- else {
6258
+ else if (streamType === 'option') {
6213
6259
  this.optionWs = ws;
6214
6260
  }
6261
+ else {
6262
+ this.cryptoWs = ws;
6263
+ }
6215
6264
  ws.on('open', () => {
6216
6265
  log$1(`${streamType} stream connected`, { type: 'info' });
6217
6266
  const authMessage = {
@@ -6222,7 +6271,6 @@ class AlpacaMarketDataAPI extends EventEmitter {
6222
6271
  ws.send(JSON.stringify(authMessage));
6223
6272
  });
6224
6273
  ws.on('message', (data) => {
6225
- //log(`RAW MESSASGE: ${data.toString()}`);
6226
6274
  const messages = JSON.parse(data.toString());
6227
6275
  for (const message of messages) {
6228
6276
  if (message.T === 'success' && message.msg === 'authenticated') {
@@ -6230,7 +6278,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
6230
6278
  this.sendSubscription(streamType);
6231
6279
  }
6232
6280
  else if (message.T === 'error') {
6233
- log$1(`${streamType} stream error: ${message.msg}`, { type: 'error' });
6281
+ log$1(`${streamType} stream error: ${message.msg} (code: ${message.code})`, { type: 'error' });
6234
6282
  }
6235
6283
  else if (message.S) {
6236
6284
  super.emit(`${streamType}-${message.T}`, message);
@@ -6243,9 +6291,12 @@ class AlpacaMarketDataAPI extends EventEmitter {
6243
6291
  if (streamType === 'stock') {
6244
6292
  this.stockWs = null;
6245
6293
  }
6246
- else {
6294
+ else if (streamType === 'option') {
6247
6295
  this.optionWs = null;
6248
6296
  }
6297
+ else {
6298
+ this.cryptoWs = null;
6299
+ }
6249
6300
  // Optional: implement reconnect logic
6250
6301
  });
6251
6302
  ws.on('error', (error) => {
@@ -6253,8 +6304,20 @@ class AlpacaMarketDataAPI extends EventEmitter {
6253
6304
  });
6254
6305
  }
6255
6306
  sendSubscription(streamType) {
6256
- const ws = streamType === 'stock' ? this.stockWs : this.optionWs;
6257
- const subscriptions = streamType === 'stock' ? this.stockSubscriptions : this.optionSubscriptions;
6307
+ let ws;
6308
+ let subscriptions;
6309
+ if (streamType === 'stock') {
6310
+ ws = this.stockWs;
6311
+ subscriptions = this.stockSubscriptions;
6312
+ }
6313
+ else if (streamType === 'option') {
6314
+ ws = this.optionWs;
6315
+ subscriptions = this.optionSubscriptions;
6316
+ }
6317
+ else {
6318
+ ws = this.cryptoWs;
6319
+ subscriptions = this.cryptoSubscriptions;
6320
+ }
6258
6321
  if (ws && ws.readyState === WebSocket.OPEN) {
6259
6322
  const subMessagePayload = {};
6260
6323
  if (subscriptions.trades.length > 0) {
@@ -6285,6 +6348,11 @@ class AlpacaMarketDataAPI extends EventEmitter {
6285
6348
  this.connect('option');
6286
6349
  }
6287
6350
  }
6351
+ connectCryptoStream() {
6352
+ if (!this.cryptoWs) {
6353
+ this.connect('crypto');
6354
+ }
6355
+ }
6288
6356
  disconnectStockStream() {
6289
6357
  if (this.stockWs) {
6290
6358
  this.stockWs.close();
@@ -6295,8 +6363,38 @@ class AlpacaMarketDataAPI extends EventEmitter {
6295
6363
  this.optionWs.close();
6296
6364
  }
6297
6365
  }
6366
+ disconnectCryptoStream() {
6367
+ if (this.cryptoWs) {
6368
+ this.cryptoWs.close();
6369
+ }
6370
+ }
6371
+ /**
6372
+ * Check if a specific stream is connected
6373
+ * @param streamType - The type of stream to check
6374
+ * @returns True if the stream is connected
6375
+ */
6376
+ isStreamConnected(streamType) {
6377
+ if (streamType === 'stock') {
6378
+ return this.stockWs !== null && this.stockWs.readyState === WebSocket.OPEN;
6379
+ }
6380
+ else if (streamType === 'option') {
6381
+ return this.optionWs !== null && this.optionWs.readyState === WebSocket.OPEN;
6382
+ }
6383
+ else {
6384
+ return this.cryptoWs !== null && this.cryptoWs.readyState === WebSocket.OPEN;
6385
+ }
6386
+ }
6298
6387
  subscribe(streamType, subscriptions) {
6299
- const currentSubscriptions = streamType === 'stock' ? this.stockSubscriptions : this.optionSubscriptions;
6388
+ let currentSubscriptions;
6389
+ if (streamType === 'stock') {
6390
+ currentSubscriptions = this.stockSubscriptions;
6391
+ }
6392
+ else if (streamType === 'option') {
6393
+ currentSubscriptions = this.optionSubscriptions;
6394
+ }
6395
+ else {
6396
+ currentSubscriptions = this.cryptoSubscriptions;
6397
+ }
6300
6398
  Object.entries(subscriptions).forEach(([key, value]) => {
6301
6399
  if (value) {
6302
6400
  currentSubscriptions[key] = [...new Set([...(currentSubscriptions[key] || []), ...value])];
@@ -6305,7 +6403,16 @@ class AlpacaMarketDataAPI extends EventEmitter {
6305
6403
  this.sendSubscription(streamType);
6306
6404
  }
6307
6405
  unsubscribe(streamType, subscriptions) {
6308
- const currentSubscriptions = streamType === 'stock' ? this.stockSubscriptions : this.optionSubscriptions;
6406
+ let currentSubscriptions;
6407
+ if (streamType === 'stock') {
6408
+ currentSubscriptions = this.stockSubscriptions;
6409
+ }
6410
+ else if (streamType === 'option') {
6411
+ currentSubscriptions = this.optionSubscriptions;
6412
+ }
6413
+ else {
6414
+ currentSubscriptions = this.cryptoSubscriptions;
6415
+ }
6309
6416
  Object.entries(subscriptions).forEach(([key, value]) => {
6310
6417
  if (value) {
6311
6418
  currentSubscriptions[key] = (currentSubscriptions[key] || []).filter(s => !value.includes(s));
@@ -6315,7 +6422,16 @@ class AlpacaMarketDataAPI extends EventEmitter {
6315
6422
  action: 'unsubscribe',
6316
6423
  ...subscriptions,
6317
6424
  };
6318
- const ws = streamType === 'stock' ? this.stockWs : this.optionWs;
6425
+ let ws;
6426
+ if (streamType === 'stock') {
6427
+ ws = this.stockWs;
6428
+ }
6429
+ else if (streamType === 'option') {
6430
+ ws = this.optionWs;
6431
+ }
6432
+ else {
6433
+ ws = this.cryptoWs;
6434
+ }
6319
6435
  if (ws && ws.readyState === WebSocket.OPEN) {
6320
6436
  ws.send(JSON.stringify(unsubMessage));
6321
6437
  }
@@ -7048,11 +7164,11 @@ const marketDataAPI = AlpacaMarketDataAPI.getInstance();
7048
7164
  */
7049
7165
  function formatCurrency(value) {
7050
7166
  if (isNaN(value)) {
7051
- return '$0.00';
7167
+ return "$0.00";
7052
7168
  }
7053
- return new Intl.NumberFormat('en-US', {
7054
- style: 'currency',
7055
- currency: 'USD',
7169
+ return new Intl.NumberFormat("en-US", {
7170
+ style: "currency",
7171
+ currency: "USD",
7056
7172
  }).format(value);
7057
7173
  }
7058
7174
  /**
@@ -7065,13 +7181,13 @@ function formatCurrency(value) {
7065
7181
  */
7066
7182
  function formatNumber(value) {
7067
7183
  if (isNaN(value)) {
7068
- return '0';
7184
+ return "0";
7069
7185
  }
7070
- return new Intl.NumberFormat('en-US').format(value);
7186
+ return new Intl.NumberFormat("en-US").format(value);
7071
7187
  }
7072
7188
 
7073
7189
  const log = (message) => {
7074
- console.log(`[${new Date().toLocaleString('en-US', { timeZone: 'America/New_York' })}] ${message}`);
7190
+ console.log(`[${new Date().toLocaleString("en-US", { timeZone: "America/New_York" })}] ${message}`);
7075
7191
  };
7076
7192
  // async function testCreateEquitiesTrade() {
7077
7193
  // try {
@@ -7295,18 +7411,18 @@ const log = (message) => {
7295
7411
  // testing retrieving pre-market data (just 9:00am to 9:30am on 1 july 2025 for SPY) using the market data api
7296
7412
  async function testPreMarketData() {
7297
7413
  try {
7298
- log('Starting pre-market data test for SPY (9:00am-9:30am, July 1, 2025)...');
7414
+ log("Starting pre-market data test for SPY (9:00am-9:30am, July 1, 2025)...");
7299
7415
  // Set up the time range in America/New_York, convert to UTC ISO strings
7300
- const symbol = 'SPY';
7301
- const nyTimeZone = 'America/New_York';
7416
+ const symbol = "SPY";
7417
+ const nyTimeZone = "America/New_York";
7302
7418
  // 9:00am and 9:30am in NY time
7303
- const startNY = new Date('2025-07-01T09:00:00-04:00');
7304
- const endNY = new Date('2025-07-01T09:30:00-04:00');
7419
+ const startNY = new Date("2025-07-01T09:00:00-04:00");
7420
+ const endNY = new Date("2025-07-01T09:30:00-04:00");
7305
7421
  const startUTC = startNY.toISOString();
7306
7422
  const endUTC = endNY.toISOString();
7307
7423
  const barsResponse = await marketDataAPI.getHistoricalBars({
7308
7424
  symbols: [symbol],
7309
- timeframe: '1Min',
7425
+ timeframe: "1Min",
7310
7426
  start: startUTC,
7311
7427
  end: endUTC,
7312
7428
  limit: 1000,
@@ -7314,19 +7430,21 @@ async function testPreMarketData() {
7314
7430
  const bars = barsResponse.bars[symbol] || [];
7315
7431
  log(`Fetched ${bars.length} 1-min bars for SPY from 9:00am to 9:30am (NY) on 2025-07-01.`);
7316
7432
  if (bars.length === 0) {
7317
- log('No pre-market bars returned.');
7433
+ log("No pre-market bars returned.");
7318
7434
  return;
7319
7435
  }
7320
7436
  // Print each bar
7321
7437
  bars.forEach((bar, i) => {
7322
- const barTime = new Date(bar.t).toLocaleString('en-US', { timeZone: nyTimeZone });
7438
+ const barTime = new Date(bar.t).toLocaleString("en-US", {
7439
+ timeZone: nyTimeZone,
7440
+ });
7323
7441
  log(`Bar ${i + 1}: ${barTime} | O: ${formatCurrency(bar.o)} H: ${formatCurrency(bar.h)} L: ${formatCurrency(bar.l)} C: ${formatCurrency(bar.c)} V: ${formatNumber(bar.v)} VWAP: ${formatCurrency(bar.vw)} N: ${bar.n}`);
7324
7442
  });
7325
7443
  // Print summary
7326
7444
  const summary = AlpacaMarketDataAPI.analyzeBars(bars);
7327
7445
  if (summary)
7328
7446
  log(`Summary: ${summary}`);
7329
- log('Pre-market data test complete.');
7447
+ log("Pre-market data test complete.");
7330
7448
  }
7331
7449
  catch (error) {
7332
7450
  log(`❌ Error in testPreMarketData: ${error instanceof Error ? error.message : error}`);