@discomedia/utils 1.0.20 → 1.0.22
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 +45 -32
- package/dist/index-frontend.cjs.map +1 -1
- package/dist/index-frontend.mjs +45 -32
- package/dist/index-frontend.mjs.map +1 -1
- package/dist/index.cjs +93 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +93 -52
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +1 -1
- package/dist/test.js +944 -5020
- package/dist/test.js.map +1 -1
- package/dist/types/alpaca-market-data-api.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/market-time.d.ts +25 -43
- package/dist/types/market-time.d.ts.map +1 -1
- package/dist/types-frontend/alpaca-market-data-api.d.ts.map +1 -1
- package/dist/types-frontend/index.d.ts +4 -0
- package/dist/types-frontend/index.d.ts.map +1 -1
- package/dist/types-frontend/market-time.d.ts +25 -43
- package/dist/types-frontend/market-time.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -280,7 +280,7 @@ class MarketCalendar {
|
|
|
280
280
|
const yearHolidays = marketHolidays[year];
|
|
281
281
|
if (!yearHolidays)
|
|
282
282
|
return false;
|
|
283
|
-
return Object.values(yearHolidays).some(holiday => holiday.date === formattedDate);
|
|
283
|
+
return Object.values(yearHolidays).some((holiday) => holiday.date === formattedDate);
|
|
284
284
|
}
|
|
285
285
|
/**
|
|
286
286
|
* Checks if a date is an early close day in NY time.
|
|
@@ -396,7 +396,7 @@ function getMarketTimes(date) {
|
|
|
396
396
|
* @param intradayReporting - 'market_hours', 'extended_hours', or 'continuous'
|
|
397
397
|
* @returns true if within hours, false otherwise
|
|
398
398
|
*/
|
|
399
|
-
function
|
|
399
|
+
function isWithinMarketHours(date, intradayReporting = 'market_hours') {
|
|
400
400
|
const calendar = new MarketCalendar();
|
|
401
401
|
if (!calendar.isMarketDay(date))
|
|
402
402
|
return false;
|
|
@@ -439,7 +439,9 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
439
439
|
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
440
440
|
}
|
|
441
441
|
// If not a market day, or before open, or during market hours, return previous market day's close
|
|
442
|
-
if (!calendar.isMarketDay(currentDate) ||
|
|
442
|
+
if (!calendar.isMarketDay(currentDate) ||
|
|
443
|
+
minutes < marketOpenMinutes ||
|
|
444
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
443
445
|
const prevMarketDay = calendar.getPreviousMarketDay(currentDate);
|
|
444
446
|
let prevCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
445
447
|
if (calendar.isEarlyCloseDay(prevMarketDay)) {
|
|
@@ -555,7 +557,7 @@ function calculatePeriodStartDate(endDate, period) {
|
|
|
555
557
|
* @returns PeriodDates object
|
|
556
558
|
*/
|
|
557
559
|
function getMarketTimePeriod(params) {
|
|
558
|
-
const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso'
|
|
560
|
+
const { period, end = new Date(), intraday_reporting = 'market_hours', outputFormat = 'iso' } = params;
|
|
559
561
|
if (!period)
|
|
560
562
|
throw new Error('Period is required');
|
|
561
563
|
const calendar = new MarketCalendar();
|
|
@@ -618,7 +620,8 @@ function getMarketStatusImpl(date = new Date()) {
|
|
|
618
620
|
let extendedEndMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
|
|
619
621
|
if (isEarlyCloseDay) {
|
|
620
622
|
marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
|
|
621
|
-
extendedEndMinutes =
|
|
623
|
+
extendedEndMinutes =
|
|
624
|
+
MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
|
|
622
625
|
}
|
|
623
626
|
let status;
|
|
624
627
|
let nextStatus;
|
|
@@ -714,15 +717,36 @@ function getLastFullTradingDate(currentDate = new Date()) {
|
|
|
714
717
|
*/
|
|
715
718
|
function getNextMarketDay({ referenceDate } = {}) {
|
|
716
719
|
const calendar = new MarketCalendar();
|
|
717
|
-
const startDate = referenceDate
|
|
720
|
+
const startDate = referenceDate ?? new Date();
|
|
721
|
+
// Find the next trading day (UTC Date object)
|
|
718
722
|
const nextDate = calendar.getNextMarketDay(startDate);
|
|
719
|
-
|
|
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')}`;
|
|
720
726
|
return {
|
|
721
|
-
date: nextDate,
|
|
722
|
-
yyyymmdd,
|
|
727
|
+
date: nextDate, // raw Date, unchanged
|
|
728
|
+
yyyymmdd, // correct trading date string
|
|
723
729
|
dateISOString: nextDate.toISOString(),
|
|
724
730
|
};
|
|
725
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
|
+
}
|
|
726
750
|
/**
|
|
727
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.
|
|
728
752
|
* @param time - a string, number (unix timestamp), or Date object representing the time
|
|
@@ -757,13 +781,13 @@ function getMarketStatus(options = {}) {
|
|
|
757
781
|
return getMarketStatusImpl(date);
|
|
758
782
|
}
|
|
759
783
|
/**
|
|
760
|
-
* Checks if a date
|
|
784
|
+
* Checks if a date is a market day.
|
|
761
785
|
* @param date - Date object
|
|
762
|
-
* @
|
|
763
|
-
* @returns true if within hours, false otherwise
|
|
786
|
+
* @returns true if market day, false otherwise
|
|
764
787
|
*/
|
|
765
|
-
function
|
|
766
|
-
|
|
788
|
+
function isMarketDay(date) {
|
|
789
|
+
const calendar = new MarketCalendar();
|
|
790
|
+
return calendar.isMarketDay(date);
|
|
767
791
|
}
|
|
768
792
|
/**
|
|
769
793
|
* Returns full trading days from market open to market close.
|
|
@@ -784,7 +808,9 @@ function getTradingStartAndEndDates(options = {}) {
|
|
|
784
808
|
const marketOpenMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
|
|
785
809
|
const marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
|
|
786
810
|
const minutes = nyEnd.getUTCHours() * 60 + nyEnd.getUTCMinutes();
|
|
787
|
-
if (!calendar.isMarketDay(endDate) ||
|
|
811
|
+
if (!calendar.isMarketDay(endDate) ||
|
|
812
|
+
minutes < marketOpenMinutes ||
|
|
813
|
+
(minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
|
|
788
814
|
// Before market open, not a market day, or during market hours: use previous market day
|
|
789
815
|
endMarketDay = calendar.getPreviousMarketDay(endDate);
|
|
790
816
|
}
|
|
@@ -877,7 +903,7 @@ function countTradingDays(startDate, endDate = new Date()) {
|
|
|
877
903
|
return {
|
|
878
904
|
days: Math.round(days * 1000) / 1000, // Round to 3 decimal places
|
|
879
905
|
hours: Math.round(hours * 100) / 100, // Round to 2 decimal places
|
|
880
|
-
minutes: Math.round(minutes)
|
|
906
|
+
minutes: Math.round(minutes),
|
|
881
907
|
};
|
|
882
908
|
}
|
|
883
909
|
// Export MARKET_TIMES for compatibility
|
|
@@ -885,27 +911,27 @@ const MARKET_TIMES = {
|
|
|
885
911
|
TIMEZONE: MARKET_CONFIG.TIMEZONE,
|
|
886
912
|
PRE: {
|
|
887
913
|
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
888
|
-
END: { HOUR: 9, MINUTE: 30, MINUTES: 570 }
|
|
914
|
+
END: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
889
915
|
},
|
|
890
916
|
EARLY_MORNING: {
|
|
891
917
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
892
|
-
END: { HOUR: 10, MINUTE: 0, MINUTES: 600 }
|
|
918
|
+
END: { HOUR: 10, MINUTE: 0, MINUTES: 600 },
|
|
893
919
|
},
|
|
894
920
|
EARLY_CLOSE_BEFORE_HOLIDAY: {
|
|
895
921
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
896
|
-
END: { HOUR: 13, MINUTE: 0, MINUTES: 780 }
|
|
922
|
+
END: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
897
923
|
},
|
|
898
924
|
EARLY_EXTENDED_BEFORE_HOLIDAY: {
|
|
899
925
|
START: { HOUR: 13, MINUTE: 0, MINUTES: 780 },
|
|
900
|
-
END: { HOUR: 17, MINUTE: 0, MINUTES: 1020 }
|
|
926
|
+
END: { HOUR: 17, MINUTE: 0, MINUTES: 1020 },
|
|
901
927
|
},
|
|
902
928
|
REGULAR: {
|
|
903
929
|
START: { HOUR: 9, MINUTE: 30, MINUTES: 570 },
|
|
904
|
-
END: { HOUR: 16, MINUTE: 0, MINUTES: 960 }
|
|
930
|
+
END: { HOUR: 16, MINUTE: 0, MINUTES: 960 },
|
|
905
931
|
},
|
|
906
932
|
EXTENDED: {
|
|
907
933
|
START: { HOUR: 4, MINUTE: 0, MINUTES: 240 },
|
|
908
|
-
END: { HOUR: 20, MINUTE: 0, MINUTES: 1200 }
|
|
934
|
+
END: { HOUR: 20, MINUTE: 0, MINUTES: 1200 },
|
|
909
935
|
},
|
|
910
936
|
};
|
|
911
937
|
|
|
@@ -16011,15 +16037,18 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16011
16037
|
stockSubscriptions = { trades: [], quotes: [], bars: [] };
|
|
16012
16038
|
optionSubscriptions = { trades: [], quotes: [], bars: [] };
|
|
16013
16039
|
setMode(mode = 'production') {
|
|
16014
|
-
if (mode === 'sandbox') {
|
|
16040
|
+
if (mode === 'sandbox') {
|
|
16041
|
+
// sandbox mode
|
|
16015
16042
|
this.stockStreamUrl = 'wss://stream.data.sandbox.alpaca.markets/v2/sip';
|
|
16016
16043
|
this.optionStreamUrl = 'wss://stream.data.sandbox.alpaca.markets/v1beta3/options';
|
|
16017
16044
|
}
|
|
16018
|
-
else if (mode === 'test') {
|
|
16045
|
+
else if (mode === 'test') {
|
|
16046
|
+
// test mode, can only use ticker FAKEPACA
|
|
16019
16047
|
this.stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/test';
|
|
16020
16048
|
this.optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // there's no test mode for options
|
|
16021
16049
|
}
|
|
16022
|
-
else {
|
|
16050
|
+
else {
|
|
16051
|
+
// production
|
|
16023
16052
|
this.stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip';
|
|
16024
16053
|
this.optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options';
|
|
16025
16054
|
}
|
|
@@ -16167,7 +16196,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16167
16196
|
const currentSubscriptions = streamType === 'stock' ? this.stockSubscriptions : this.optionSubscriptions;
|
|
16168
16197
|
Object.entries(subscriptions).forEach(([key, value]) => {
|
|
16169
16198
|
if (value) {
|
|
16170
|
-
currentSubscriptions[key] = (currentSubscriptions[key] || []).filter(s => !value.includes(s));
|
|
16199
|
+
currentSubscriptions[key] = (currentSubscriptions[key] || []).filter((s) => !value.includes(s));
|
|
16171
16200
|
}
|
|
16172
16201
|
});
|
|
16173
16202
|
const unsubMessage = {
|
|
@@ -16230,11 +16259,11 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16230
16259
|
let pageCount = 0;
|
|
16231
16260
|
let currency = '';
|
|
16232
16261
|
// Initialize bar arrays for each symbol
|
|
16233
|
-
symbols.forEach(symbol => {
|
|
16262
|
+
symbols.forEach((symbol) => {
|
|
16234
16263
|
allBars[symbol] = [];
|
|
16235
16264
|
});
|
|
16236
|
-
log(`Starting historical bars fetch for ${symbolsStr} (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, {
|
|
16237
|
-
type: 'info'
|
|
16265
|
+
log(`Starting historical bars fetch for ${symbolsStr.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, {
|
|
16266
|
+
type: 'info',
|
|
16238
16267
|
});
|
|
16239
16268
|
while (hasMorePages) {
|
|
16240
16269
|
pageCount++;
|
|
@@ -16246,7 +16275,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16246
16275
|
};
|
|
16247
16276
|
const response = await this.makeRequest('/stocks/bars', 'GET', requestParams);
|
|
16248
16277
|
if (!response.bars) {
|
|
16249
|
-
log(`No bars data found in response for ${symbolsStr}`, { type: 'warn' });
|
|
16278
|
+
log(`No bars data found in response for ${symbolsStr.length} symbols`, { type: 'warn' });
|
|
16250
16279
|
break;
|
|
16251
16280
|
}
|
|
16252
16281
|
// Track currency from first response
|
|
@@ -16262,7 +16291,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16262
16291
|
allBars[symbol] = [...allBars[symbol], ...bars];
|
|
16263
16292
|
pageBarsCount += bars.length;
|
|
16264
16293
|
// Track date range for this page
|
|
16265
|
-
bars.forEach(bar => {
|
|
16294
|
+
bars.forEach((bar) => {
|
|
16266
16295
|
const barDate = new Date(bar.t);
|
|
16267
16296
|
if (!earliestTimestamp || barDate < earliestTimestamp) {
|
|
16268
16297
|
earliestTimestamp = barDate;
|
|
@@ -16281,7 +16310,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16281
16310
|
? `${earliestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${latestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
|
|
16282
16311
|
: 'unknown range';
|
|
16283
16312
|
log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} bars (total: ${totalBarsCount.toLocaleString()}) for ${symbolsStr}, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, {
|
|
16284
|
-
type: 'info'
|
|
16313
|
+
type: 'info',
|
|
16285
16314
|
});
|
|
16286
16315
|
// Prevent infinite loops
|
|
16287
16316
|
if (pageCount > 1000) {
|
|
@@ -16290,9 +16319,11 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16290
16319
|
}
|
|
16291
16320
|
}
|
|
16292
16321
|
// Final summary
|
|
16293
|
-
const symbolCounts = Object.entries(allBars)
|
|
16322
|
+
const symbolCounts = Object.entries(allBars)
|
|
16323
|
+
.map(([symbol, bars]) => `${symbol}: ${bars.length}`)
|
|
16324
|
+
.join(', ');
|
|
16294
16325
|
log(`Historical bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages (${symbolCounts})`, {
|
|
16295
|
-
type: 'info'
|
|
16326
|
+
type: 'info',
|
|
16296
16327
|
});
|
|
16297
16328
|
return {
|
|
16298
16329
|
bars: allBars,
|
|
@@ -16560,11 +16591,11 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16560
16591
|
let totalBarsCount = 0;
|
|
16561
16592
|
let pageCount = 0;
|
|
16562
16593
|
// Initialize bar arrays for each symbol
|
|
16563
|
-
symbols.forEach(symbol => {
|
|
16594
|
+
symbols.forEach((symbol) => {
|
|
16564
16595
|
allBars[symbol] = [];
|
|
16565
16596
|
});
|
|
16566
|
-
log(`Starting historical options bars fetch for ${symbolsStr} (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, {
|
|
16567
|
-
type: 'info'
|
|
16597
|
+
log(`Starting historical options bars fetch for ${symbolsStr.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, {
|
|
16598
|
+
type: 'info',
|
|
16568
16599
|
});
|
|
16569
16600
|
while (hasMorePages) {
|
|
16570
16601
|
pageCount++;
|
|
@@ -16574,7 +16605,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16574
16605
|
};
|
|
16575
16606
|
const response = await this.makeRequest('/options/bars', 'GET', requestParams, 'v1beta1');
|
|
16576
16607
|
if (!response.bars) {
|
|
16577
|
-
log(`No options bars data found in response for ${symbolsStr}`, { type: 'warn' });
|
|
16608
|
+
log(`No options bars data found in response for ${symbolsStr.length} symbols`, { type: 'warn' });
|
|
16578
16609
|
break;
|
|
16579
16610
|
}
|
|
16580
16611
|
// Combine bars for each symbol
|
|
@@ -16586,7 +16617,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16586
16617
|
allBars[symbol] = [...allBars[symbol], ...bars];
|
|
16587
16618
|
pageBarsCount += bars.length;
|
|
16588
16619
|
// Track date range for this page
|
|
16589
|
-
bars.forEach(bar => {
|
|
16620
|
+
bars.forEach((bar) => {
|
|
16590
16621
|
const barDate = new Date(bar.t);
|
|
16591
16622
|
if (!earliestTimestamp || barDate < earliestTimestamp) {
|
|
16592
16623
|
earliestTimestamp = barDate;
|
|
@@ -16605,7 +16636,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16605
16636
|
? `${earliestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${latestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
|
|
16606
16637
|
: 'unknown range';
|
|
16607
16638
|
log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} option bars (total: ${totalBarsCount.toLocaleString()}) for ${symbolsStr}, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, {
|
|
16608
|
-
type: 'info'
|
|
16639
|
+
type: 'info',
|
|
16609
16640
|
});
|
|
16610
16641
|
// Prevent infinite loops
|
|
16611
16642
|
if (pageCount > 1000) {
|
|
@@ -16614,9 +16645,11 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16614
16645
|
}
|
|
16615
16646
|
}
|
|
16616
16647
|
// Final summary
|
|
16617
|
-
const symbolCounts = Object.entries(allBars)
|
|
16648
|
+
const symbolCounts = Object.entries(allBars)
|
|
16649
|
+
.map(([symbol, bars]) => `${symbol}: ${bars.length}`)
|
|
16650
|
+
.join(', ');
|
|
16618
16651
|
log(`Historical options bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages (${symbolCounts})`, {
|
|
16619
|
-
type: 'info'
|
|
16652
|
+
type: 'info',
|
|
16620
16653
|
});
|
|
16621
16654
|
return {
|
|
16622
16655
|
bars: allBars,
|
|
@@ -16640,11 +16673,11 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16640
16673
|
let totalTradesCount = 0;
|
|
16641
16674
|
let pageCount = 0;
|
|
16642
16675
|
// Initialize trades arrays for each symbol
|
|
16643
|
-
symbols.forEach(symbol => {
|
|
16676
|
+
symbols.forEach((symbol) => {
|
|
16644
16677
|
allTrades[symbol] = [];
|
|
16645
16678
|
});
|
|
16646
|
-
log(`Starting historical options trades fetch for ${symbolsStr} (${params.start || 'no start'} to ${params.end || 'no end'})`, {
|
|
16647
|
-
type: 'info'
|
|
16679
|
+
log(`Starting historical options trades fetch for ${symbolsStr.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, {
|
|
16680
|
+
type: 'info',
|
|
16648
16681
|
});
|
|
16649
16682
|
while (hasMorePages) {
|
|
16650
16683
|
pageCount++;
|
|
@@ -16654,7 +16687,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16654
16687
|
};
|
|
16655
16688
|
const response = await this.makeRequest('/options/trades', 'GET', requestParams, 'v1beta1');
|
|
16656
16689
|
if (!response.trades) {
|
|
16657
|
-
log(`No options trades data found in response for ${symbolsStr}`, { type: 'warn' });
|
|
16690
|
+
log(`No options trades data found in response for ${symbolsStr.length} symbols`, { type: 'warn' });
|
|
16658
16691
|
break;
|
|
16659
16692
|
}
|
|
16660
16693
|
// Combine trades for each symbol
|
|
@@ -16666,7 +16699,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16666
16699
|
allTrades[symbol] = [...allTrades[symbol], ...trades];
|
|
16667
16700
|
pageTradesCount += trades.length;
|
|
16668
16701
|
// Track date range for this page
|
|
16669
|
-
trades.forEach(trade => {
|
|
16702
|
+
trades.forEach((trade) => {
|
|
16670
16703
|
const tradeDate = new Date(trade.t);
|
|
16671
16704
|
if (!earliestTimestamp || tradeDate < earliestTimestamp) {
|
|
16672
16705
|
earliestTimestamp = tradeDate;
|
|
@@ -16684,8 +16717,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16684
16717
|
const dateRangeStr = earliestTimestamp && latestTimestamp
|
|
16685
16718
|
? `${earliestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${latestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
|
|
16686
16719
|
: 'unknown range';
|
|
16687
|
-
log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} option trades (total: ${totalTradesCount.toLocaleString()}) for ${symbolsStr}, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, {
|
|
16688
|
-
type: 'info'
|
|
16720
|
+
log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} option trades (total: ${totalTradesCount.toLocaleString()}) for ${symbolsStr.length} symbols, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, {
|
|
16721
|
+
type: 'info',
|
|
16689
16722
|
});
|
|
16690
16723
|
// Prevent infinite loops
|
|
16691
16724
|
if (pageCount > 1000) {
|
|
@@ -16694,9 +16727,11 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16694
16727
|
}
|
|
16695
16728
|
}
|
|
16696
16729
|
// Final summary
|
|
16697
|
-
const symbolCounts = Object.entries(allTrades)
|
|
16730
|
+
const symbolCounts = Object.entries(allTrades)
|
|
16731
|
+
.map(([symbol, trades]) => `${symbol}: ${trades.length}`)
|
|
16732
|
+
.join(', ');
|
|
16698
16733
|
log(`Historical options trades fetch complete: ${totalTradesCount.toLocaleString()} total trades across ${pageCount} pages (${symbolCounts})`, {
|
|
16699
|
-
type: 'info'
|
|
16734
|
+
type: 'info',
|
|
16700
16735
|
});
|
|
16701
16736
|
return {
|
|
16702
16737
|
trades: allTrades,
|
|
@@ -16841,7 +16876,9 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
16841
16876
|
...(symbol && { symbols: symbol }),
|
|
16842
16877
|
...(mergedParams.limit && { limit: Math.min(50, maxLimit - fetchedCount).toString() }),
|
|
16843
16878
|
...(mergedParams.sort && { sort: mergedParams.sort }),
|
|
16844
|
-
...(mergedParams.include_content !== undefined
|
|
16879
|
+
...(mergedParams.include_content !== undefined
|
|
16880
|
+
? { include_content: mergedParams.include_content.toString() }
|
|
16881
|
+
: {}),
|
|
16845
16882
|
...(pageToken && { page_token: pageToken }),
|
|
16846
16883
|
});
|
|
16847
16884
|
const url = `${this.v1beta1url}/news?${queryParams}`;
|
|
@@ -18268,10 +18305,14 @@ const disco = {
|
|
|
18268
18305
|
getMarketOpenClose: getMarketOpenClose,
|
|
18269
18306
|
getLastFullTradingDate: getLastFullTradingDate,
|
|
18270
18307
|
getNextMarketDay: getNextMarketDay,
|
|
18308
|
+
getPreviousMarketDay: getPreviousMarketDay,
|
|
18309
|
+
getMarketTimePeriod: getMarketTimePeriod,
|
|
18271
18310
|
getMarketStatus: getMarketStatus,
|
|
18272
18311
|
getNYTimeZone: getNYTimeZone,
|
|
18273
18312
|
getTradingDate: getTradingDate,
|
|
18274
18313
|
getTradingStartAndEndDates: getTradingStartAndEndDates,
|
|
18314
|
+
isMarketDay: isMarketDay,
|
|
18315
|
+
isWithinMarketHours: isWithinMarketHours,
|
|
18275
18316
|
countTradingDays: countTradingDays,
|
|
18276
18317
|
MARKET_TIMES: MARKET_TIMES,
|
|
18277
18318
|
},
|