@discomedia/utils 1.0.31 → 1.0.33
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/alpaca-crypto-pairs-DMOvRyzw.js +571 -0
- package/dist/alpaca-crypto-pairs-DMOvRyzw.js.map +1 -0
- package/dist/index-frontend.cjs +355 -4
- package/dist/index-frontend.cjs.map +1 -1
- package/dist/index-frontend.mjs +355 -4
- package/dist/index-frontend.mjs.map +1 -1
- package/dist/index.cjs +356 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +356 -5
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +4 -4
- package/dist/test.js +568 -692
- 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/index-frontend.d.ts +1 -1
- package/dist/types/index.d.ts +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 +146 -0
- 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/index-frontend.d.ts +1 -1
- package/dist/types-frontend/index.d.ts +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 +146 -0
- package/dist/types-frontend/types/alpaca-types.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index-frontend.cjs
CHANGED
|
@@ -269,7 +269,7 @@ const safeJSON = (text) => {
|
|
|
269
269
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
270
270
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
271
271
|
|
|
272
|
-
const VERSION = '5.
|
|
272
|
+
const VERSION = '5.16.0'; // x-release-please-version
|
|
273
273
|
|
|
274
274
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
275
275
|
const isRunningInBrowser = () => {
|
|
@@ -1609,6 +1609,36 @@ class CursorPage extends AbstractPage {
|
|
|
1609
1609
|
};
|
|
1610
1610
|
}
|
|
1611
1611
|
}
|
|
1612
|
+
class ConversationCursorPage extends AbstractPage {
|
|
1613
|
+
constructor(client, response, body, options) {
|
|
1614
|
+
super(client, response, body, options);
|
|
1615
|
+
this.data = body.data || [];
|
|
1616
|
+
this.has_more = body.has_more || false;
|
|
1617
|
+
this.last_id = body.last_id || '';
|
|
1618
|
+
}
|
|
1619
|
+
getPaginatedItems() {
|
|
1620
|
+
return this.data ?? [];
|
|
1621
|
+
}
|
|
1622
|
+
hasNextPage() {
|
|
1623
|
+
if (this.has_more === false) {
|
|
1624
|
+
return false;
|
|
1625
|
+
}
|
|
1626
|
+
return super.hasNextPage();
|
|
1627
|
+
}
|
|
1628
|
+
nextPageRequestOptions() {
|
|
1629
|
+
const cursor = this.last_id;
|
|
1630
|
+
if (!cursor) {
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
return {
|
|
1634
|
+
...this.options,
|
|
1635
|
+
query: {
|
|
1636
|
+
...maybeObj(this.options.query),
|
|
1637
|
+
after: cursor,
|
|
1638
|
+
},
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1612
1642
|
|
|
1613
1643
|
const checkFileSupport = () => {
|
|
1614
1644
|
if (typeof File === 'undefined') {
|
|
@@ -4699,6 +4729,74 @@ class Containers extends APIResource {
|
|
|
4699
4729
|
}
|
|
4700
4730
|
Containers.Files = Files$2;
|
|
4701
4731
|
|
|
4732
|
+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
4733
|
+
class Items extends APIResource {
|
|
4734
|
+
/**
|
|
4735
|
+
* Create items in a conversation with the given ID.
|
|
4736
|
+
*/
|
|
4737
|
+
create(conversationID, params, options) {
|
|
4738
|
+
const { include, ...body } = params;
|
|
4739
|
+
return this._client.post(path `/conversations/${conversationID}/items`, {
|
|
4740
|
+
query: { include },
|
|
4741
|
+
body,
|
|
4742
|
+
...options,
|
|
4743
|
+
});
|
|
4744
|
+
}
|
|
4745
|
+
/**
|
|
4746
|
+
* Get a single item from a conversation with the given IDs.
|
|
4747
|
+
*/
|
|
4748
|
+
retrieve(itemID, params, options) {
|
|
4749
|
+
const { conversation_id, ...query } = params;
|
|
4750
|
+
return this._client.get(path `/conversations/${conversation_id}/items/${itemID}`, { query, ...options });
|
|
4751
|
+
}
|
|
4752
|
+
/**
|
|
4753
|
+
* List all items for a conversation with the given ID.
|
|
4754
|
+
*/
|
|
4755
|
+
list(conversationID, query = {}, options) {
|
|
4756
|
+
return this._client.getAPIList(path `/conversations/${conversationID}/items`, (ConversationCursorPage), { query, ...options });
|
|
4757
|
+
}
|
|
4758
|
+
/**
|
|
4759
|
+
* Delete an item from a conversation with the given IDs.
|
|
4760
|
+
*/
|
|
4761
|
+
delete(itemID, params, options) {
|
|
4762
|
+
const { conversation_id } = params;
|
|
4763
|
+
return this._client.delete(path `/conversations/${conversation_id}/items/${itemID}`, options);
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
|
|
4767
|
+
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
4768
|
+
class Conversations extends APIResource {
|
|
4769
|
+
constructor() {
|
|
4770
|
+
super(...arguments);
|
|
4771
|
+
this.items = new Items(this._client);
|
|
4772
|
+
}
|
|
4773
|
+
/**
|
|
4774
|
+
* Create a conversation.
|
|
4775
|
+
*/
|
|
4776
|
+
create(body, options) {
|
|
4777
|
+
return this._client.post('/conversations', { body, ...options });
|
|
4778
|
+
}
|
|
4779
|
+
/**
|
|
4780
|
+
* Get a conversation with the given ID.
|
|
4781
|
+
*/
|
|
4782
|
+
retrieve(conversationID, options) {
|
|
4783
|
+
return this._client.get(path `/conversations/${conversationID}`, options);
|
|
4784
|
+
}
|
|
4785
|
+
/**
|
|
4786
|
+
* Update a conversation's metadata with the given ID.
|
|
4787
|
+
*/
|
|
4788
|
+
update(conversationID, body, options) {
|
|
4789
|
+
return this._client.post(path `/conversations/${conversationID}`, { body, ...options });
|
|
4790
|
+
}
|
|
4791
|
+
/**
|
|
4792
|
+
* Delete a conversation with the given ID.
|
|
4793
|
+
*/
|
|
4794
|
+
delete(conversationID, options) {
|
|
4795
|
+
return this._client.delete(path `/conversations/${conversationID}`, options);
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
Conversations.Items = Items;
|
|
4799
|
+
|
|
4702
4800
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
4703
4801
|
class Embeddings extends APIResource {
|
|
4704
4802
|
/**
|
|
@@ -4863,7 +4961,7 @@ let Files$1 = class Files extends APIResource {
|
|
|
4863
4961
|
/**
|
|
4864
4962
|
* Upload a file that can be used across various endpoints. Individual files can be
|
|
4865
4963
|
* up to 512 MB, and the size of all files uploaded by one organization can be up
|
|
4866
|
-
* to
|
|
4964
|
+
* to 1 TB.
|
|
4867
4965
|
*
|
|
4868
4966
|
* The Assistants API supports files up to 2 million tokens and of specific file
|
|
4869
4967
|
* types. See the
|
|
@@ -6292,6 +6390,7 @@ class OpenAI {
|
|
|
6292
6390
|
this.batches = new Batches(this);
|
|
6293
6391
|
this.uploads = new Uploads(this);
|
|
6294
6392
|
this.responses = new Responses(this);
|
|
6393
|
+
this.conversations = new Conversations(this);
|
|
6295
6394
|
this.evals = new Evals(this);
|
|
6296
6395
|
this.containers = new Containers(this);
|
|
6297
6396
|
if (apiKey === undefined) {
|
|
@@ -6682,7 +6781,7 @@ class OpenAI {
|
|
|
6682
6781
|
// Preserve legacy string encoding behavior for now
|
|
6683
6782
|
headers.values.has('content-type')) ||
|
|
6684
6783
|
// `Blob` is superset of `File`
|
|
6685
|
-
body instanceof Blob ||
|
|
6784
|
+
(globalThis.Blob && body instanceof globalThis.Blob) ||
|
|
6686
6785
|
// `FormData` -> `multipart/form-data`
|
|
6687
6786
|
body instanceof FormData ||
|
|
6688
6787
|
// `URLSearchParams` -> `application/x-www-form-urlencoded`
|
|
@@ -6737,6 +6836,7 @@ OpenAI.Beta = Beta;
|
|
|
6737
6836
|
OpenAI.Batches = Batches;
|
|
6738
6837
|
OpenAI.Uploads = Uploads;
|
|
6739
6838
|
OpenAI.Responses = Responses;
|
|
6839
|
+
OpenAI.Conversations = Conversations;
|
|
6740
6840
|
OpenAI.Evals = Evals;
|
|
6741
6841
|
OpenAI.Containers = Containers;
|
|
6742
6842
|
|
|
@@ -13782,6 +13882,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
|
|
|
13782
13882
|
dataURL;
|
|
13783
13883
|
apiURL;
|
|
13784
13884
|
v1beta1url;
|
|
13885
|
+
v1beta3url;
|
|
13785
13886
|
stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip'; // production values
|
|
13786
13887
|
optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // production values
|
|
13787
13888
|
stockWs = null;
|
|
@@ -13824,6 +13925,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
|
|
|
13824
13925
|
? 'https://paper-api.alpaca.markets/v2'
|
|
13825
13926
|
: 'https://api.alpaca.markets/v2'; // used by some, e.g. getAssets
|
|
13826
13927
|
this.v1beta1url = 'https://data.alpaca.markets/v1beta1'; // used for options endpoints
|
|
13928
|
+
this.v1beta3url = 'https://data.alpaca.markets/v1beta3'; // used for crypto endpoints
|
|
13827
13929
|
this.setMode('production'); // sets stockStreamUrl and optionStreamUrl
|
|
13828
13930
|
this.headers = {
|
|
13829
13931
|
'APCA-API-KEY-ID': process.env.ALPACA_API_KEY,
|
|
@@ -13961,7 +14063,9 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
|
|
|
13961
14063
|
}
|
|
13962
14064
|
}
|
|
13963
14065
|
async makeRequest(endpoint, method = 'GET', params, baseUrlName = 'data') {
|
|
13964
|
-
const baseUrl = baseUrlName === 'data' ? this.dataURL :
|
|
14066
|
+
const baseUrl = baseUrlName === 'data' ? this.dataURL :
|
|
14067
|
+
baseUrlName === 'api' ? this.apiURL :
|
|
14068
|
+
baseUrlName === 'v1beta1' ? this.v1beta1url : this.v1beta3url;
|
|
13965
14069
|
const url = new URL(`${baseUrl}${endpoint}`);
|
|
13966
14070
|
try {
|
|
13967
14071
|
if (params) {
|
|
@@ -14669,6 +14773,253 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
|
|
|
14669
14773
|
}
|
|
14670
14774
|
return newsArticles;
|
|
14671
14775
|
}
|
|
14776
|
+
// ===== CRYPTO MARKET DATA METHODS =====
|
|
14777
|
+
/**
|
|
14778
|
+
* Get historical OHLCV bars for crypto symbols
|
|
14779
|
+
* Automatically handles pagination to fetch all available data
|
|
14780
|
+
* @param params Parameters for crypto historical bars request
|
|
14781
|
+
* @returns Historical bars data with all pages combined
|
|
14782
|
+
*/
|
|
14783
|
+
async getCryptoHistoricalBars(params) {
|
|
14784
|
+
const symbols = params.symbols;
|
|
14785
|
+
const symbolsStr = symbols.join(',');
|
|
14786
|
+
let allBars = {};
|
|
14787
|
+
let pageToken = null;
|
|
14788
|
+
let hasMorePages = true;
|
|
14789
|
+
let totalBarsCount = 0;
|
|
14790
|
+
let pageCount = 0;
|
|
14791
|
+
// Initialize bar arrays for each symbol
|
|
14792
|
+
symbols.forEach((symbol) => {
|
|
14793
|
+
allBars[symbol] = [];
|
|
14794
|
+
});
|
|
14795
|
+
log(`Starting crypto historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
|
|
14796
|
+
while (hasMorePages) {
|
|
14797
|
+
pageCount++;
|
|
14798
|
+
const requestParams = {
|
|
14799
|
+
...params,
|
|
14800
|
+
symbols: symbolsStr,
|
|
14801
|
+
...(pageToken && { page_token: pageToken }),
|
|
14802
|
+
};
|
|
14803
|
+
const response = await this.makeRequest('/crypto/us/bars', 'GET', requestParams, 'v1beta3');
|
|
14804
|
+
if (!response.bars) {
|
|
14805
|
+
log(`No crypto bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
|
|
14806
|
+
break;
|
|
14807
|
+
}
|
|
14808
|
+
// Combine bars for each symbol
|
|
14809
|
+
let pageBarsCount = 0;
|
|
14810
|
+
Object.entries(response.bars).forEach(([symbol, bars]) => {
|
|
14811
|
+
if (bars && bars.length > 0) {
|
|
14812
|
+
allBars[symbol] = [...allBars[symbol], ...bars];
|
|
14813
|
+
pageBarsCount += bars.length;
|
|
14814
|
+
}
|
|
14815
|
+
});
|
|
14816
|
+
totalBarsCount += pageBarsCount;
|
|
14817
|
+
pageToken = response.next_page_token || null;
|
|
14818
|
+
hasMorePages = !!pageToken;
|
|
14819
|
+
log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} crypto bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
|
|
14820
|
+
// Prevent infinite loops
|
|
14821
|
+
if (pageCount > 1000) {
|
|
14822
|
+
log(`Stopping crypto bars pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
|
|
14823
|
+
break;
|
|
14824
|
+
}
|
|
14825
|
+
}
|
|
14826
|
+
log(`Crypto historical bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages`, { type: 'info' });
|
|
14827
|
+
return {
|
|
14828
|
+
bars: allBars,
|
|
14829
|
+
next_page_token: null, // Always null since we fetch all pages
|
|
14830
|
+
};
|
|
14831
|
+
}
|
|
14832
|
+
/**
|
|
14833
|
+
* Get the most recent minute bar for requested crypto symbols
|
|
14834
|
+
* @param symbols Array of crypto symbols to query
|
|
14835
|
+
* @returns Latest bar data for each symbol
|
|
14836
|
+
*/
|
|
14837
|
+
async getCryptoLatestBars(symbols) {
|
|
14838
|
+
return this.makeRequest('/crypto/us/latest/bars', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14839
|
+
}
|
|
14840
|
+
/**
|
|
14841
|
+
* Get historical quotes for crypto symbols
|
|
14842
|
+
* Automatically handles pagination to fetch all available data
|
|
14843
|
+
* @param params Parameters for crypto historical quotes request
|
|
14844
|
+
* @returns Historical quotes data with all pages combined
|
|
14845
|
+
*/
|
|
14846
|
+
async getCryptoHistoricalQuotes(params) {
|
|
14847
|
+
const symbols = params.symbols;
|
|
14848
|
+
const symbolsStr = symbols.join(',');
|
|
14849
|
+
let allQuotes = {};
|
|
14850
|
+
let pageToken = null;
|
|
14851
|
+
let hasMorePages = true;
|
|
14852
|
+
let totalQuotesCount = 0;
|
|
14853
|
+
let pageCount = 0;
|
|
14854
|
+
// Initialize quotes arrays for each symbol
|
|
14855
|
+
symbols.forEach((symbol) => {
|
|
14856
|
+
allQuotes[symbol] = [];
|
|
14857
|
+
});
|
|
14858
|
+
log(`Starting crypto historical quotes fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
|
|
14859
|
+
while (hasMorePages) {
|
|
14860
|
+
pageCount++;
|
|
14861
|
+
const requestParams = {
|
|
14862
|
+
...params,
|
|
14863
|
+
symbols: symbolsStr,
|
|
14864
|
+
...(pageToken && { page_token: pageToken }),
|
|
14865
|
+
};
|
|
14866
|
+
const response = await this.makeRequest('/crypto/us/quotes', 'GET', requestParams, 'v1beta3');
|
|
14867
|
+
if (!response.quotes) {
|
|
14868
|
+
log(`No crypto quotes data found in response for ${symbols.length} symbols`, { type: 'warn' });
|
|
14869
|
+
break;
|
|
14870
|
+
}
|
|
14871
|
+
// Combine quotes for each symbol
|
|
14872
|
+
let pageQuotesCount = 0;
|
|
14873
|
+
Object.entries(response.quotes).forEach(([symbol, quotes]) => {
|
|
14874
|
+
if (quotes && quotes.length > 0) {
|
|
14875
|
+
allQuotes[symbol] = [...allQuotes[symbol], ...quotes];
|
|
14876
|
+
pageQuotesCount += quotes.length;
|
|
14877
|
+
}
|
|
14878
|
+
});
|
|
14879
|
+
totalQuotesCount += pageQuotesCount;
|
|
14880
|
+
pageToken = response.next_page_token || null;
|
|
14881
|
+
hasMorePages = !!pageToken;
|
|
14882
|
+
log(`Page ${pageCount}: Fetched ${pageQuotesCount.toLocaleString()} crypto quotes (total: ${totalQuotesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
|
|
14883
|
+
// Prevent infinite loops
|
|
14884
|
+
if (pageCount > 1000) {
|
|
14885
|
+
log(`Stopping crypto quotes pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
|
|
14886
|
+
break;
|
|
14887
|
+
}
|
|
14888
|
+
}
|
|
14889
|
+
log(`Crypto historical quotes fetch complete: ${totalQuotesCount.toLocaleString()} total quotes across ${pageCount} pages`, { type: 'info' });
|
|
14890
|
+
return {
|
|
14891
|
+
quotes: allQuotes,
|
|
14892
|
+
next_page_token: null, // Always null since we fetch all pages
|
|
14893
|
+
};
|
|
14894
|
+
}
|
|
14895
|
+
/**
|
|
14896
|
+
* Get the most recent quotes for requested crypto symbols
|
|
14897
|
+
* @param symbols Array of crypto symbols to query
|
|
14898
|
+
* @returns Latest quote data for each symbol
|
|
14899
|
+
*/
|
|
14900
|
+
async getCryptoLatestQuotes(symbols) {
|
|
14901
|
+
if (!symbols || symbols.length === 0) {
|
|
14902
|
+
log('No symbols provided to getCryptoLatestQuotes, returning empty response', { type: 'warn' });
|
|
14903
|
+
return { quotes: {} };
|
|
14904
|
+
}
|
|
14905
|
+
return this.makeRequest('/crypto/us/latest/quotes', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14906
|
+
}
|
|
14907
|
+
/**
|
|
14908
|
+
* Get historical trades for crypto symbols
|
|
14909
|
+
* Automatically handles pagination to fetch all available data
|
|
14910
|
+
* @param params Parameters for crypto historical trades request
|
|
14911
|
+
* @returns Historical trades data with all pages combined
|
|
14912
|
+
*/
|
|
14913
|
+
async getCryptoHistoricalTrades(params) {
|
|
14914
|
+
const symbols = params.symbols;
|
|
14915
|
+
const symbolsStr = symbols.join(',');
|
|
14916
|
+
let allTrades = {};
|
|
14917
|
+
let pageToken = null;
|
|
14918
|
+
let hasMorePages = true;
|
|
14919
|
+
let totalTradesCount = 0;
|
|
14920
|
+
let pageCount = 0;
|
|
14921
|
+
// Initialize trades arrays for each symbol
|
|
14922
|
+
symbols.forEach((symbol) => {
|
|
14923
|
+
allTrades[symbol] = [];
|
|
14924
|
+
});
|
|
14925
|
+
log(`Starting crypto historical trades fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
|
|
14926
|
+
while (hasMorePages) {
|
|
14927
|
+
pageCount++;
|
|
14928
|
+
const requestParams = {
|
|
14929
|
+
...params,
|
|
14930
|
+
symbols: symbolsStr,
|
|
14931
|
+
...(pageToken && { page_token: pageToken }),
|
|
14932
|
+
};
|
|
14933
|
+
const response = await this.makeRequest('/crypto/us/trades', 'GET', requestParams, 'v1beta3');
|
|
14934
|
+
if (!response.trades) {
|
|
14935
|
+
log(`No crypto trades data found in response for ${symbols.length} symbols`, { type: 'warn' });
|
|
14936
|
+
break;
|
|
14937
|
+
}
|
|
14938
|
+
// Combine trades for each symbol
|
|
14939
|
+
let pageTradesCount = 0;
|
|
14940
|
+
Object.entries(response.trades).forEach(([symbol, trades]) => {
|
|
14941
|
+
if (trades && trades.length > 0) {
|
|
14942
|
+
allTrades[symbol] = [...allTrades[symbol], ...trades];
|
|
14943
|
+
pageTradesCount += trades.length;
|
|
14944
|
+
}
|
|
14945
|
+
});
|
|
14946
|
+
totalTradesCount += pageTradesCount;
|
|
14947
|
+
pageToken = response.next_page_token || null;
|
|
14948
|
+
hasMorePages = !!pageToken;
|
|
14949
|
+
log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} crypto trades (total: ${totalTradesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
|
|
14950
|
+
// Prevent infinite loops
|
|
14951
|
+
if (pageCount > 1000) {
|
|
14952
|
+
log(`Stopping crypto trades pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
|
|
14953
|
+
break;
|
|
14954
|
+
}
|
|
14955
|
+
}
|
|
14956
|
+
log(`Crypto historical trades fetch complete: ${totalTradesCount.toLocaleString()} total trades across ${pageCount} pages`, { type: 'info' });
|
|
14957
|
+
return {
|
|
14958
|
+
trades: allTrades,
|
|
14959
|
+
next_page_token: null, // Always null since we fetch all pages
|
|
14960
|
+
};
|
|
14961
|
+
}
|
|
14962
|
+
/**
|
|
14963
|
+
* Get the most recent trades for requested crypto symbols
|
|
14964
|
+
* @param symbols Array of crypto symbols to query
|
|
14965
|
+
* @returns Latest trade data for each symbol
|
|
14966
|
+
*/
|
|
14967
|
+
async getCryptoLatestTrades(symbols) {
|
|
14968
|
+
if (!symbols || symbols.length === 0) {
|
|
14969
|
+
log('No symbols provided to getCryptoLatestTrades, returning empty response', { type: 'warn' });
|
|
14970
|
+
return { trades: {} };
|
|
14971
|
+
}
|
|
14972
|
+
return this.makeRequest('/crypto/us/latest/trades', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14973
|
+
}
|
|
14974
|
+
/**
|
|
14975
|
+
* Get snapshots for crypto symbols
|
|
14976
|
+
* Returns the latest trade, latest quote, latest minute bar, latest daily bar, and previous daily bar data
|
|
14977
|
+
* @param symbols Array of crypto symbols to query
|
|
14978
|
+
* @returns Snapshot data for each symbol
|
|
14979
|
+
*/
|
|
14980
|
+
async getCryptoSnapshots(symbols) {
|
|
14981
|
+
if (!symbols || symbols.length === 0) {
|
|
14982
|
+
log('No symbols provided to getCryptoSnapshots, returning empty response', { type: 'warn' });
|
|
14983
|
+
return { snapshots: {} };
|
|
14984
|
+
}
|
|
14985
|
+
return this.makeRequest('/crypto/us/snapshots', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14986
|
+
}
|
|
14987
|
+
/**
|
|
14988
|
+
* Get the latest orderbook for requested crypto symbols
|
|
14989
|
+
* @param symbols Array of crypto symbols to query
|
|
14990
|
+
* @returns Latest orderbook data for each symbol
|
|
14991
|
+
*/
|
|
14992
|
+
async getCryptoLatestOrderbooks(symbols) {
|
|
14993
|
+
if (!symbols || symbols.length === 0) {
|
|
14994
|
+
log('No symbols provided to getCryptoLatestOrderbooks, returning empty response', { type: 'warn' });
|
|
14995
|
+
return { orderbooks: {} };
|
|
14996
|
+
}
|
|
14997
|
+
return this.makeRequest('/crypto/us/latest/orderbooks', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
|
|
14998
|
+
}
|
|
14999
|
+
/**
|
|
15000
|
+
* Analyzes an array of crypto bars and returns a summary string
|
|
15001
|
+
* @param bars Array of crypto bars to analyze
|
|
15002
|
+
* @returns A string summarizing the crypto price data
|
|
15003
|
+
*/
|
|
15004
|
+
static analyzeCryptoBars(bars) {
|
|
15005
|
+
if (!bars || bars.length === 0) {
|
|
15006
|
+
return 'No crypto price data available';
|
|
15007
|
+
}
|
|
15008
|
+
const firstBar = bars[0];
|
|
15009
|
+
const lastBar = bars[bars.length - 1];
|
|
15010
|
+
const priceChange = lastBar.c - firstBar.o;
|
|
15011
|
+
const percentChange = (priceChange / firstBar.o) * 100;
|
|
15012
|
+
const volumeChange = lastBar.v - firstBar.v;
|
|
15013
|
+
const percentVolumeChange = firstBar.v > 0 ? (volumeChange / firstBar.v) * 100 : 0;
|
|
15014
|
+
const high = Math.max(...bars.map((bar) => bar.h));
|
|
15015
|
+
const low = Math.min(...bars.map((bar) => bar.l));
|
|
15016
|
+
const totalVolume = bars.reduce((sum, bar) => sum + bar.v, 0);
|
|
15017
|
+
const avgVolume = totalVolume / bars.length;
|
|
15018
|
+
return (`Crypto Price: $${firstBar.o.toFixed(6)} -> $${lastBar.c.toFixed(6)} (${percentChange.toFixed(2)}%), ` +
|
|
15019
|
+
`Volume: ${firstBar.v.toLocaleString()} -> ${lastBar.v.toLocaleString()} (${percentVolumeChange.toFixed(2)}%), ` +
|
|
15020
|
+
`High: $${high.toFixed(6)}, Low: $${low.toFixed(6)}, ` +
|
|
15021
|
+
`Avg Volume: ${avgVolume.toLocaleString()}`);
|
|
15022
|
+
}
|
|
14672
15023
|
}
|
|
14673
15024
|
// Export the singleton instance
|
|
14674
15025
|
const marketDataAPI = AlpacaMarketDataAPI.getInstance();
|