@discomedia/utils 1.0.32 → 1.0.34
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 +279 -4
- package/dist/index-frontend.cjs.map +1 -1
- package/dist/index-frontend.mjs +279 -4
- package/dist/index-frontend.mjs.map +1 -1
- package/dist/index.cjs +264 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +264 -4
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +1 -1
- package/dist/test.js +5605 -1062
- package/dist/test.js.map +1 -1
- package/dist/types/alpaca-market-data-api.d.ts +60 -1
- package/dist/types/alpaca-market-data-api.d.ts.map +1 -1
- package/dist/types/alpaca-trading-api.d.ts.map +1 -1
- package/dist/types/types/alpaca-crypto-pairs.d.ts +11 -0
- package/dist/types/types/alpaca-crypto-pairs.d.ts.map +1 -0
- package/dist/types/types/alpaca-types.d.ts +156 -1
- package/dist/types/types/alpaca-types.d.ts.map +1 -1
- package/dist/types-frontend/alpaca-market-data-api.d.ts +60 -1
- package/dist/types-frontend/alpaca-market-data-api.d.ts.map +1 -1
- package/dist/types-frontend/alpaca-trading-api.d.ts.map +1 -1
- package/dist/types-frontend/types/alpaca-crypto-pairs.d.ts +11 -0
- package/dist/types-frontend/types/alpaca-crypto-pairs.d.ts.map +1 -0
- package/dist/types-frontend/types/alpaca-types.d.ts +156 -1
- package/dist/types-frontend/types/alpaca-types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index-frontend.mjs
CHANGED
|
@@ -13862,6 +13862,21 @@ function getLastFullTradingDateImpl(currentDate = new Date()) {
|
|
|
13862
13862
|
function getLastFullTradingDate(currentDate = new Date()) {
|
|
13863
13863
|
return getLastFullTradingDateImpl(currentDate);
|
|
13864
13864
|
}
|
|
13865
|
+
/**
|
|
13866
|
+
* Returns the trading date for a given time. Note: Just trims the date string; does not validate if the date is a market day.
|
|
13867
|
+
* @param time - a string, number (unix timestamp), or Date object representing the time
|
|
13868
|
+
* @returns the trading date as a string in YYYY-MM-DD format
|
|
13869
|
+
*/
|
|
13870
|
+
/**
|
|
13871
|
+
* Returns the trading date for a given time in YYYY-MM-DD format (NY time).
|
|
13872
|
+
* @param time - string, number, or Date
|
|
13873
|
+
* @returns trading date string
|
|
13874
|
+
*/
|
|
13875
|
+
function getTradingDate(time) {
|
|
13876
|
+
const date = typeof time === 'number' ? new Date(time) : typeof time === 'string' ? new Date(time) : time;
|
|
13877
|
+
const nyDate = toNYTime(date);
|
|
13878
|
+
return `${nyDate.getUTCFullYear()}-${String(nyDate.getUTCMonth() + 1).padStart(2, '0')}-${String(nyDate.getUTCDate()).padStart(2, '0')}`;
|
|
13879
|
+
}
|
|
13865
13880
|
|
|
13866
13881
|
const log = (message, options = { type: 'info' }) => {
|
|
13867
13882
|
log$1(message, { ...options, source: 'AlpacaMarketDataAPI' });
|
|
@@ -13880,6 +13895,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
13880
13895
|
dataURL;
|
|
13881
13896
|
apiURL;
|
|
13882
13897
|
v1beta1url;
|
|
13898
|
+
v1beta3url;
|
|
13883
13899
|
stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip'; // production values
|
|
13884
13900
|
optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // production values
|
|
13885
13901
|
stockWs = null;
|
|
@@ -13922,6 +13938,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
13922
13938
|
? 'https://paper-api.alpaca.markets/v2'
|
|
13923
13939
|
: 'https://api.alpaca.markets/v2'; // used by some, e.g. getAssets
|
|
13924
13940
|
this.v1beta1url = 'https://data.alpaca.markets/v1beta1'; // used for options endpoints
|
|
13941
|
+
this.v1beta3url = 'https://data.alpaca.markets/v1beta3'; // used for crypto endpoints
|
|
13925
13942
|
this.setMode('production'); // sets stockStreamUrl and optionStreamUrl
|
|
13926
13943
|
this.headers = {
|
|
13927
13944
|
'APCA-API-KEY-ID': process.env.ALPACA_API_KEY,
|
|
@@ -14059,7 +14076,9 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
14059
14076
|
}
|
|
14060
14077
|
}
|
|
14061
14078
|
async makeRequest(endpoint, method = 'GET', params, baseUrlName = 'data') {
|
|
14062
|
-
const baseUrl = baseUrlName === 'data' ? this.dataURL :
|
|
14079
|
+
const baseUrl = baseUrlName === 'data' ? this.dataURL :
|
|
14080
|
+
baseUrlName === 'api' ? this.apiURL :
|
|
14081
|
+
baseUrlName === 'v1beta1' ? this.v1beta1url : this.v1beta3url;
|
|
14063
14082
|
const url = new URL(`${baseUrl}${endpoint}`);
|
|
14064
14083
|
try {
|
|
14065
14084
|
if (params) {
|
|
@@ -14767,6 +14786,253 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
14767
14786
|
}
|
|
14768
14787
|
return newsArticles;
|
|
14769
14788
|
}
|
|
14789
|
+
// ===== CRYPTO MARKET DATA METHODS =====
|
|
14790
|
+
/**
|
|
14791
|
+
* Get historical OHLCV bars for crypto symbols
|
|
14792
|
+
* Automatically handles pagination to fetch all available data
|
|
14793
|
+
* @param params Parameters for crypto historical bars request
|
|
14794
|
+
* @returns Historical bars data with all pages combined
|
|
14795
|
+
*/
|
|
14796
|
+
async getCryptoHistoricalBars(params) {
|
|
14797
|
+
const symbols = params.symbols;
|
|
14798
|
+
const symbolsStr = symbols.join(',');
|
|
14799
|
+
let allBars = {};
|
|
14800
|
+
let pageToken = null;
|
|
14801
|
+
let hasMorePages = true;
|
|
14802
|
+
let totalBarsCount = 0;
|
|
14803
|
+
let pageCount = 0;
|
|
14804
|
+
// Initialize bar arrays for each symbol
|
|
14805
|
+
symbols.forEach((symbol) => {
|
|
14806
|
+
allBars[symbol] = [];
|
|
14807
|
+
});
|
|
14808
|
+
log(`Starting crypto historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
|
|
14809
|
+
while (hasMorePages) {
|
|
14810
|
+
pageCount++;
|
|
14811
|
+
const requestParams = {
|
|
14812
|
+
...params,
|
|
14813
|
+
symbols: symbolsStr,
|
|
14814
|
+
...(pageToken && { page_token: pageToken }),
|
|
14815
|
+
};
|
|
14816
|
+
const response = await this.makeRequest('/crypto/us/bars', 'GET', requestParams, 'v1beta3');
|
|
14817
|
+
if (!response.bars) {
|
|
14818
|
+
log(`No crypto bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
|
|
14819
|
+
break;
|
|
14820
|
+
}
|
|
14821
|
+
// Combine bars for each symbol
|
|
14822
|
+
let pageBarsCount = 0;
|
|
14823
|
+
Object.entries(response.bars).forEach(([symbol, bars]) => {
|
|
14824
|
+
if (bars && bars.length > 0) {
|
|
14825
|
+
allBars[symbol] = [...allBars[symbol], ...bars];
|
|
14826
|
+
pageBarsCount += bars.length;
|
|
14827
|
+
}
|
|
14828
|
+
});
|
|
14829
|
+
totalBarsCount += pageBarsCount;
|
|
14830
|
+
pageToken = response.next_page_token || null;
|
|
14831
|
+
hasMorePages = !!pageToken;
|
|
14832
|
+
log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} crypto bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
|
|
14833
|
+
// Prevent infinite loops
|
|
14834
|
+
if (pageCount > 1000) {
|
|
14835
|
+
log(`Stopping crypto bars pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
|
|
14836
|
+
break;
|
|
14837
|
+
}
|
|
14838
|
+
}
|
|
14839
|
+
log(`Crypto historical bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages`, { type: 'info' });
|
|
14840
|
+
return {
|
|
14841
|
+
bars: allBars,
|
|
14842
|
+
next_page_token: null, // Always null since we fetch all pages
|
|
14843
|
+
};
|
|
14844
|
+
}
|
|
14845
|
+
/**
|
|
14846
|
+
* Get the most recent minute bar for requested crypto symbols
|
|
14847
|
+
* @param symbols Array of crypto symbols to query
|
|
14848
|
+
* @returns Latest bar data for each symbol
|
|
14849
|
+
*/
|
|
14850
|
+
async getCryptoLatestBars(symbols) {
|
|
14851
|
+
return this.makeRequest('/crypto/us/latest/bars', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14852
|
+
}
|
|
14853
|
+
/**
|
|
14854
|
+
* Get historical quotes for crypto symbols
|
|
14855
|
+
* Automatically handles pagination to fetch all available data
|
|
14856
|
+
* @param params Parameters for crypto historical quotes request
|
|
14857
|
+
* @returns Historical quotes data with all pages combined
|
|
14858
|
+
*/
|
|
14859
|
+
async getCryptoHistoricalQuotes(params) {
|
|
14860
|
+
const symbols = params.symbols;
|
|
14861
|
+
const symbolsStr = symbols.join(',');
|
|
14862
|
+
let allQuotes = {};
|
|
14863
|
+
let pageToken = null;
|
|
14864
|
+
let hasMorePages = true;
|
|
14865
|
+
let totalQuotesCount = 0;
|
|
14866
|
+
let pageCount = 0;
|
|
14867
|
+
// Initialize quotes arrays for each symbol
|
|
14868
|
+
symbols.forEach((symbol) => {
|
|
14869
|
+
allQuotes[symbol] = [];
|
|
14870
|
+
});
|
|
14871
|
+
log(`Starting crypto historical quotes fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
|
|
14872
|
+
while (hasMorePages) {
|
|
14873
|
+
pageCount++;
|
|
14874
|
+
const requestParams = {
|
|
14875
|
+
...params,
|
|
14876
|
+
symbols: symbolsStr,
|
|
14877
|
+
...(pageToken && { page_token: pageToken }),
|
|
14878
|
+
};
|
|
14879
|
+
const response = await this.makeRequest('/crypto/us/quotes', 'GET', requestParams, 'v1beta3');
|
|
14880
|
+
if (!response.quotes) {
|
|
14881
|
+
log(`No crypto quotes data found in response for ${symbols.length} symbols`, { type: 'warn' });
|
|
14882
|
+
break;
|
|
14883
|
+
}
|
|
14884
|
+
// Combine quotes for each symbol
|
|
14885
|
+
let pageQuotesCount = 0;
|
|
14886
|
+
Object.entries(response.quotes).forEach(([symbol, quotes]) => {
|
|
14887
|
+
if (quotes && quotes.length > 0) {
|
|
14888
|
+
allQuotes[symbol] = [...allQuotes[symbol], ...quotes];
|
|
14889
|
+
pageQuotesCount += quotes.length;
|
|
14890
|
+
}
|
|
14891
|
+
});
|
|
14892
|
+
totalQuotesCount += pageQuotesCount;
|
|
14893
|
+
pageToken = response.next_page_token || null;
|
|
14894
|
+
hasMorePages = !!pageToken;
|
|
14895
|
+
log(`Page ${pageCount}: Fetched ${pageQuotesCount.toLocaleString()} crypto quotes (total: ${totalQuotesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
|
|
14896
|
+
// Prevent infinite loops
|
|
14897
|
+
if (pageCount > 1000) {
|
|
14898
|
+
log(`Stopping crypto quotes pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
|
|
14899
|
+
break;
|
|
14900
|
+
}
|
|
14901
|
+
}
|
|
14902
|
+
log(`Crypto historical quotes fetch complete: ${totalQuotesCount.toLocaleString()} total quotes across ${pageCount} pages`, { type: 'info' });
|
|
14903
|
+
return {
|
|
14904
|
+
quotes: allQuotes,
|
|
14905
|
+
next_page_token: null, // Always null since we fetch all pages
|
|
14906
|
+
};
|
|
14907
|
+
}
|
|
14908
|
+
/**
|
|
14909
|
+
* Get the most recent quotes for requested crypto symbols
|
|
14910
|
+
* @param symbols Array of crypto symbols to query
|
|
14911
|
+
* @returns Latest quote data for each symbol
|
|
14912
|
+
*/
|
|
14913
|
+
async getCryptoLatestQuotes(symbols) {
|
|
14914
|
+
if (!symbols || symbols.length === 0) {
|
|
14915
|
+
log('No symbols provided to getCryptoLatestQuotes, returning empty response', { type: 'warn' });
|
|
14916
|
+
return { quotes: {} };
|
|
14917
|
+
}
|
|
14918
|
+
return this.makeRequest('/crypto/us/latest/quotes', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14919
|
+
}
|
|
14920
|
+
/**
|
|
14921
|
+
* Get historical trades for crypto symbols
|
|
14922
|
+
* Automatically handles pagination to fetch all available data
|
|
14923
|
+
* @param params Parameters for crypto historical trades request
|
|
14924
|
+
* @returns Historical trades data with all pages combined
|
|
14925
|
+
*/
|
|
14926
|
+
async getCryptoHistoricalTrades(params) {
|
|
14927
|
+
const symbols = params.symbols;
|
|
14928
|
+
const symbolsStr = symbols.join(',');
|
|
14929
|
+
let allTrades = {};
|
|
14930
|
+
let pageToken = null;
|
|
14931
|
+
let hasMorePages = true;
|
|
14932
|
+
let totalTradesCount = 0;
|
|
14933
|
+
let pageCount = 0;
|
|
14934
|
+
// Initialize trades arrays for each symbol
|
|
14935
|
+
symbols.forEach((symbol) => {
|
|
14936
|
+
allTrades[symbol] = [];
|
|
14937
|
+
});
|
|
14938
|
+
log(`Starting crypto historical trades fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
|
|
14939
|
+
while (hasMorePages) {
|
|
14940
|
+
pageCount++;
|
|
14941
|
+
const requestParams = {
|
|
14942
|
+
...params,
|
|
14943
|
+
symbols: symbolsStr,
|
|
14944
|
+
...(pageToken && { page_token: pageToken }),
|
|
14945
|
+
};
|
|
14946
|
+
const response = await this.makeRequest('/crypto/us/trades', 'GET', requestParams, 'v1beta3');
|
|
14947
|
+
if (!response.trades) {
|
|
14948
|
+
log(`No crypto trades data found in response for ${symbols.length} symbols`, { type: 'warn' });
|
|
14949
|
+
break;
|
|
14950
|
+
}
|
|
14951
|
+
// Combine trades for each symbol
|
|
14952
|
+
let pageTradesCount = 0;
|
|
14953
|
+
Object.entries(response.trades).forEach(([symbol, trades]) => {
|
|
14954
|
+
if (trades && trades.length > 0) {
|
|
14955
|
+
allTrades[symbol] = [...allTrades[symbol], ...trades];
|
|
14956
|
+
pageTradesCount += trades.length;
|
|
14957
|
+
}
|
|
14958
|
+
});
|
|
14959
|
+
totalTradesCount += pageTradesCount;
|
|
14960
|
+
pageToken = response.next_page_token || null;
|
|
14961
|
+
hasMorePages = !!pageToken;
|
|
14962
|
+
log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} crypto trades (total: ${totalTradesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
|
|
14963
|
+
// Prevent infinite loops
|
|
14964
|
+
if (pageCount > 1000) {
|
|
14965
|
+
log(`Stopping crypto trades pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
|
|
14966
|
+
break;
|
|
14967
|
+
}
|
|
14968
|
+
}
|
|
14969
|
+
log(`Crypto historical trades fetch complete: ${totalTradesCount.toLocaleString()} total trades across ${pageCount} pages`, { type: 'info' });
|
|
14970
|
+
return {
|
|
14971
|
+
trades: allTrades,
|
|
14972
|
+
next_page_token: null, // Always null since we fetch all pages
|
|
14973
|
+
};
|
|
14974
|
+
}
|
|
14975
|
+
/**
|
|
14976
|
+
* Get the most recent trades for requested crypto symbols
|
|
14977
|
+
* @param symbols Array of crypto symbols to query
|
|
14978
|
+
* @returns Latest trade data for each symbol
|
|
14979
|
+
*/
|
|
14980
|
+
async getCryptoLatestTrades(symbols) {
|
|
14981
|
+
if (!symbols || symbols.length === 0) {
|
|
14982
|
+
log('No symbols provided to getCryptoLatestTrades, returning empty response', { type: 'warn' });
|
|
14983
|
+
return { trades: {} };
|
|
14984
|
+
}
|
|
14985
|
+
return this.makeRequest('/crypto/us/latest/trades', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14986
|
+
}
|
|
14987
|
+
/**
|
|
14988
|
+
* Get snapshots for crypto symbols
|
|
14989
|
+
* Returns the latest trade, latest quote, latest minute bar, latest daily bar, and previous daily bar data
|
|
14990
|
+
* @param symbols Array of crypto symbols to query
|
|
14991
|
+
* @returns Snapshot data for each symbol
|
|
14992
|
+
*/
|
|
14993
|
+
async getCryptoSnapshots(symbols) {
|
|
14994
|
+
if (!symbols || symbols.length === 0) {
|
|
14995
|
+
log('No symbols provided to getCryptoSnapshots, returning empty response', { type: 'warn' });
|
|
14996
|
+
return { snapshots: {} };
|
|
14997
|
+
}
|
|
14998
|
+
return this.makeRequest('/crypto/us/snapshots', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14999
|
+
}
|
|
15000
|
+
/**
|
|
15001
|
+
* Get the latest orderbook for requested crypto symbols
|
|
15002
|
+
* @param symbols Array of crypto symbols to query
|
|
15003
|
+
* @returns Latest orderbook data for each symbol
|
|
15004
|
+
*/
|
|
15005
|
+
async getCryptoLatestOrderbooks(symbols) {
|
|
15006
|
+
if (!symbols || symbols.length === 0) {
|
|
15007
|
+
log('No symbols provided to getCryptoLatestOrderbooks, returning empty response', { type: 'warn' });
|
|
15008
|
+
return { orderbooks: {} };
|
|
15009
|
+
}
|
|
15010
|
+
return this.makeRequest('/crypto/us/latest/orderbooks', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
15011
|
+
}
|
|
15012
|
+
/**
|
|
15013
|
+
* Analyzes an array of crypto bars and returns a summary string
|
|
15014
|
+
* @param bars Array of crypto bars to analyze
|
|
15015
|
+
* @returns A string summarizing the crypto price data
|
|
15016
|
+
*/
|
|
15017
|
+
static analyzeCryptoBars(bars) {
|
|
15018
|
+
if (!bars || bars.length === 0) {
|
|
15019
|
+
return 'No crypto price data available';
|
|
15020
|
+
}
|
|
15021
|
+
const firstBar = bars[0];
|
|
15022
|
+
const lastBar = bars[bars.length - 1];
|
|
15023
|
+
const priceChange = lastBar.c - firstBar.o;
|
|
15024
|
+
const percentChange = (priceChange / firstBar.o) * 100;
|
|
15025
|
+
const volumeChange = lastBar.v - firstBar.v;
|
|
15026
|
+
const percentVolumeChange = firstBar.v > 0 ? (volumeChange / firstBar.v) * 100 : 0;
|
|
15027
|
+
const high = Math.max(...bars.map((bar) => bar.h));
|
|
15028
|
+
const low = Math.min(...bars.map((bar) => bar.l));
|
|
15029
|
+
const totalVolume = bars.reduce((sum, bar) => sum + bar.v, 0);
|
|
15030
|
+
const avgVolume = totalVolume / bars.length;
|
|
15031
|
+
return (`Crypto Price: $${firstBar.o.toFixed(6)} -> $${lastBar.c.toFixed(6)} (${percentChange.toFixed(2)}%), ` +
|
|
15032
|
+
`Volume: ${firstBar.v.toLocaleString()} -> ${lastBar.v.toLocaleString()} (${percentVolumeChange.toFixed(2)}%), ` +
|
|
15033
|
+
`High: $${high.toFixed(6)}, Low: $${low.toFixed(6)}, ` +
|
|
15034
|
+
`Avg Volume: ${avgVolume.toLocaleString()}`);
|
|
15035
|
+
}
|
|
14770
15036
|
}
|
|
14771
15037
|
// Export the singleton instance
|
|
14772
15038
|
const marketDataAPI = AlpacaMarketDataAPI.getInstance();
|
|
@@ -15527,9 +15793,18 @@ class AlpacaTradingAPI {
|
|
|
15527
15793
|
// Get the most recent hourly data point
|
|
15528
15794
|
const mostRecentHourly = recentHourlyData[recentHourlyData.length - 1];
|
|
15529
15795
|
const mostRecentIndex = mostRecentHourly.index;
|
|
15530
|
-
// Calculate the timestamp for the new daily entry
|
|
15531
|
-
|
|
15532
|
-
|
|
15796
|
+
// Calculate the timestamp for the new daily entry.
|
|
15797
|
+
// Alpaca's daily history timestamps are at 00:00:00Z for the calendar day
|
|
15798
|
+
// following the NY trading date. Derive the trading date in NY time from the
|
|
15799
|
+
// most recent intraday timestamp, then set the new daily timestamp to
|
|
15800
|
+
// midnight UTC of the next calendar day.
|
|
15801
|
+
const mostRecentMs = mostRecentHourly.timestamp * 1000; // hourly timestamps are seconds
|
|
15802
|
+
const tradingDateStr = getTradingDate(new Date(mostRecentMs)); // e.g., '2025-09-05' (NY trading date)
|
|
15803
|
+
const [yearStr, monthStr, dayStr] = tradingDateStr.split('-');
|
|
15804
|
+
const year = Number(yearStr);
|
|
15805
|
+
const month = Number(monthStr); // 1-based
|
|
15806
|
+
const day = Number(dayStr);
|
|
15807
|
+
const newDailyTimestamp = Math.floor(Date.UTC(year, month - 1, day + 1, 0, 0, 0, 0) / 1000);
|
|
15533
15808
|
// Create a new daily history entry with the most recent hourly values
|
|
15534
15809
|
const updatedDailyHistory = {
|
|
15535
15810
|
...dailyHistory,
|