@adaptic/utils 0.0.905 → 0.0.907
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.cjs +47 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +47 -15
- package/dist/index.mjs.map +1 -1
- package/dist/test.js +38 -6
- package/dist/test.js.map +1 -1
- package/dist/types/alpaca-market-data-api.d.ts +3 -0
- package/dist/types/alpaca-market-data-api.d.ts.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/rate-limiter.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1315,7 +1315,7 @@ async function fetchWithRetry(url, options = {}, retries = 3, initialBackoff = 1
|
|
|
1315
1315
|
*/
|
|
1316
1316
|
async function validatePolygonApiKey(apiKey) {
|
|
1317
1317
|
try {
|
|
1318
|
-
const response = await fetch(`https://api.
|
|
1318
|
+
const response = await fetch(`https://api.massive.com/v1/meta/symbols?apikey=${apiKey}&limit=1`);
|
|
1319
1319
|
if (response.status === 401) {
|
|
1320
1320
|
throw new Error("Invalid or expired Polygon.io API key");
|
|
1321
1321
|
}
|
|
@@ -5187,7 +5187,7 @@ const fetchTickerInfo = async (symbol, options) => {
|
|
|
5187
5187
|
}
|
|
5188
5188
|
const apiKey = options?.apiKey || POLYGON_API_KEY;
|
|
5189
5189
|
validatePolygonApiKey$1(apiKey);
|
|
5190
|
-
const baseUrl = `https://api.
|
|
5190
|
+
const baseUrl = `https://api.massive.com/v3/reference/tickers/${encodeURIComponent(symbol)}`;
|
|
5191
5191
|
const params = new URLSearchParams({
|
|
5192
5192
|
apiKey,
|
|
5193
5193
|
});
|
|
@@ -5274,7 +5274,7 @@ const fetchLastTrade = async (symbol, options) => {
|
|
|
5274
5274
|
}
|
|
5275
5275
|
const apiKey = options?.apiKey || POLYGON_API_KEY;
|
|
5276
5276
|
validatePolygonApiKey$1(apiKey);
|
|
5277
|
-
const baseUrl = `https://api.
|
|
5277
|
+
const baseUrl = `https://api.massive.com/v2/last/trade/${encodeURIComponent(symbol)}`;
|
|
5278
5278
|
const params = new URLSearchParams({
|
|
5279
5279
|
apiKey,
|
|
5280
5280
|
});
|
|
@@ -5339,7 +5339,7 @@ const fetchPrices = async (params, options) => {
|
|
|
5339
5339
|
const apiKey = options?.apiKey || POLYGON_API_KEY;
|
|
5340
5340
|
validatePolygonApiKey$1(apiKey);
|
|
5341
5341
|
const { ticker, start, end = Date.now().valueOf(), multiplier, timespan, limit = 1000, } = params;
|
|
5342
|
-
const baseUrl = `https://api.
|
|
5342
|
+
const baseUrl = `https://api.massive.com/v2/aggs/ticker/${encodeURIComponent(ticker)}/range/${multiplier}/${timespan}/${start}/${end}`;
|
|
5343
5343
|
const urlParams = new URLSearchParams({
|
|
5344
5344
|
apiKey,
|
|
5345
5345
|
adjusted: "true",
|
|
@@ -5461,7 +5461,7 @@ const fetchGroupedDaily = async (date, options) => {
|
|
|
5461
5461
|
if (!options?.apiKey && !POLYGON_API_KEY) {
|
|
5462
5462
|
throw new Error("Polygon API key is missing");
|
|
5463
5463
|
}
|
|
5464
|
-
const baseUrl = `https://api.
|
|
5464
|
+
const baseUrl = `https://api.massive.com/v2/aggs/grouped/locale/us/market/stocks/${date}`;
|
|
5465
5465
|
const params = new URLSearchParams({
|
|
5466
5466
|
apiKey: options?.apiKey || POLYGON_API_KEY,
|
|
5467
5467
|
adjusted: options?.adjusted !== false ? "true" : "false",
|
|
@@ -5554,7 +5554,7 @@ symbol, date = new Date(), options) => {
|
|
|
5554
5554
|
throw new Error("Polygon API key is missing");
|
|
5555
5555
|
}
|
|
5556
5556
|
const formattedDate = date.toISOString().split("T")[0]; // Format as YYYY-MM-DD
|
|
5557
|
-
const baseUrl = `https://api.
|
|
5557
|
+
const baseUrl = `https://api.massive.com/v1/open-close/${encodeURIComponent(symbol)}/${formattedDate}`;
|
|
5558
5558
|
const params = new URLSearchParams({
|
|
5559
5559
|
apiKey: options?.apiKey || POLYGON_API_KEY,
|
|
5560
5560
|
adjusted: (options?.adjusted ?? true).toString(),
|
|
@@ -5604,7 +5604,7 @@ const fetchTrades = async (symbol, options) => {
|
|
|
5604
5604
|
if (!options?.apiKey && !POLYGON_API_KEY) {
|
|
5605
5605
|
throw new Error("Polygon API key is missing");
|
|
5606
5606
|
}
|
|
5607
|
-
const baseUrl = `https://api.
|
|
5607
|
+
const baseUrl = `https://api.massive.com/v3/trades/${encodeURIComponent(symbol)}`;
|
|
5608
5608
|
const params = new URLSearchParams({
|
|
5609
5609
|
apiKey: options?.apiKey || POLYGON_API_KEY,
|
|
5610
5610
|
});
|
|
@@ -5670,7 +5670,7 @@ const { ALPACA_INDICES_API_KEY } = process.env;
|
|
|
5670
5670
|
const POLYGON_INDICES_CONCURRENCY_LIMIT = 5;
|
|
5671
5671
|
const polygonIndicesLimit = pLimit(POLYGON_INDICES_CONCURRENCY_LIMIT);
|
|
5672
5672
|
// Base URL for Polygon API
|
|
5673
|
-
const POLYGON_API_BASE_URL = "https://api.
|
|
5673
|
+
const POLYGON_API_BASE_URL = "https://api.massive.com";
|
|
5674
5674
|
/**
|
|
5675
5675
|
* Validates that an API key is available
|
|
5676
5676
|
* @param {string | undefined} apiKey - Optional API key to use
|
|
@@ -12357,6 +12357,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12357
12357
|
quotes: [],
|
|
12358
12358
|
bars: [],
|
|
12359
12359
|
};
|
|
12360
|
+
reconnectAttempts = {};
|
|
12361
|
+
reconnectTimers = {};
|
|
12360
12362
|
setMode(mode = "production") {
|
|
12361
12363
|
if (mode === "sandbox") {
|
|
12362
12364
|
// sandbox mode
|
|
@@ -12395,7 +12397,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12395
12397
|
// when env vars are not available. Features will be unavailable until
|
|
12396
12398
|
// credentials are provided at runtime.
|
|
12397
12399
|
const apiKey = process.env.ALPACA_API_KEY || "";
|
|
12398
|
-
const apiSecret = process.env.ALPACA_SECRET_KEY || "";
|
|
12400
|
+
const apiSecret = process.env.ALPACA_API_SECRET || process.env.ALPACA_SECRET_KEY || "";
|
|
12399
12401
|
this.credentialsValid = validateAlpacaCredentials({
|
|
12400
12402
|
apiKey,
|
|
12401
12403
|
apiSecret,
|
|
@@ -12437,6 +12439,12 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12437
12439
|
else {
|
|
12438
12440
|
url = this.cryptoStreamUrl;
|
|
12439
12441
|
}
|
|
12442
|
+
const apiKey = process.env.ALPACA_API_KEY || "";
|
|
12443
|
+
const apiSecret = process.env.ALPACA_API_SECRET || process.env.ALPACA_SECRET_KEY || "";
|
|
12444
|
+
if (!apiKey || !apiSecret) {
|
|
12445
|
+
log$l(`Cannot connect ${streamType} stream: missing Alpaca credentials (ALPACA_API_KEY=${apiKey ? "set" : "MISSING"}, ALPACA_API_SECRET/ALPACA_SECRET_KEY=${apiSecret ? "set" : "MISSING"})`, { type: "error" });
|
|
12446
|
+
return;
|
|
12447
|
+
}
|
|
12440
12448
|
const ws = new WebSocket(url);
|
|
12441
12449
|
if (streamType === "stock") {
|
|
12442
12450
|
this.stockWs = ws;
|
|
@@ -12451,8 +12459,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12451
12459
|
log$l(`${streamType} stream connected`, { type: "info" });
|
|
12452
12460
|
const authMessage = {
|
|
12453
12461
|
action: "auth",
|
|
12454
|
-
key:
|
|
12455
|
-
secret:
|
|
12462
|
+
key: apiKey,
|
|
12463
|
+
secret: apiSecret,
|
|
12456
12464
|
};
|
|
12457
12465
|
ws.send(JSON.stringify(authMessage));
|
|
12458
12466
|
});
|
|
@@ -12469,6 +12477,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12469
12477
|
for (const message of messages) {
|
|
12470
12478
|
if (message.T === "success" && message.msg === "authenticated") {
|
|
12471
12479
|
log$l(`${streamType} stream authenticated`, { type: "info" });
|
|
12480
|
+
this.reconnectAttempts[streamType] = 0;
|
|
12472
12481
|
this.sendSubscription(streamType);
|
|
12473
12482
|
}
|
|
12474
12483
|
else if (message.T === "success" && message.msg === "connected") {
|
|
@@ -12491,8 +12500,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12491
12500
|
}
|
|
12492
12501
|
}
|
|
12493
12502
|
});
|
|
12494
|
-
ws.on("close", () => {
|
|
12495
|
-
log$l(`${streamType} stream disconnected`, { type: "warn" });
|
|
12503
|
+
ws.on("close", (code) => {
|
|
12504
|
+
log$l(`${streamType} stream disconnected (code: ${code})`, { type: "warn" });
|
|
12496
12505
|
if (streamType === "stock") {
|
|
12497
12506
|
this.stockWs = null;
|
|
12498
12507
|
}
|
|
@@ -12502,12 +12511,35 @@ class AlpacaMarketDataAPI extends EventEmitter {
|
|
|
12502
12511
|
else {
|
|
12503
12512
|
this.cryptoWs = null;
|
|
12504
12513
|
}
|
|
12505
|
-
//
|
|
12514
|
+
// Reconnect with exponential backoff (unless intentionally closed with code 1000)
|
|
12515
|
+
if (code !== 1000) {
|
|
12516
|
+
this.scheduleReconnect(streamType);
|
|
12517
|
+
}
|
|
12506
12518
|
});
|
|
12507
12519
|
ws.on("error", (error) => {
|
|
12508
12520
|
log$l(`${streamType} stream error: ${error.message}`, { type: "error" });
|
|
12509
12521
|
});
|
|
12510
12522
|
}
|
|
12523
|
+
scheduleReconnect(streamType) {
|
|
12524
|
+
const attempts = this.reconnectAttempts[streamType] ?? 0;
|
|
12525
|
+
const maxAttempts = 10;
|
|
12526
|
+
if (attempts >= maxAttempts) {
|
|
12527
|
+
log$l(`${streamType} stream: max reconnect attempts (${maxAttempts}) reached, giving up`, { type: "error" });
|
|
12528
|
+
return;
|
|
12529
|
+
}
|
|
12530
|
+
// Exponential backoff: 1s, 2s, 4s, 8s, 16s, 30s (capped)
|
|
12531
|
+
const delayMs = Math.min(1000 * Math.pow(2, attempts), 30000);
|
|
12532
|
+
this.reconnectAttempts[streamType] = attempts + 1;
|
|
12533
|
+
log$l(`${streamType} stream: scheduling reconnect attempt ${attempts + 1}/${maxAttempts} in ${delayMs}ms`, { type: "info" });
|
|
12534
|
+
// Clear any existing reconnect timer for this stream
|
|
12535
|
+
if (this.reconnectTimers[streamType]) {
|
|
12536
|
+
clearTimeout(this.reconnectTimers[streamType]);
|
|
12537
|
+
}
|
|
12538
|
+
this.reconnectTimers[streamType] = setTimeout(() => {
|
|
12539
|
+
log$l(`${streamType} stream: reconnecting (attempt ${attempts + 1}/${maxAttempts})`, { type: "info" });
|
|
12540
|
+
this.connect(streamType);
|
|
12541
|
+
}, delayMs);
|
|
12542
|
+
}
|
|
12511
12543
|
sendSubscription(streamType) {
|
|
12512
12544
|
let ws;
|
|
12513
12545
|
let subscriptions;
|
|
@@ -15202,7 +15234,7 @@ const rateLimiters = {
|
|
|
15202
15234
|
*
|
|
15203
15235
|
* Configured for 5 requests per second (basic plan).
|
|
15204
15236
|
* Adjust if you have a different subscription tier.
|
|
15205
|
-
* See: https://
|
|
15237
|
+
* See: https://massive.com/pricing
|
|
15206
15238
|
*/
|
|
15207
15239
|
polygon: new TokenBucketRateLimiter({
|
|
15208
15240
|
maxTokens: 5,
|