@adaptic/utils 0.0.914 → 0.0.915

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 CHANGED
@@ -7161,18 +7161,29 @@ const fetchLastTrade = async (symbol, options) => {
7161
7161
  }
7162
7162
  const apiKey = options?.apiKey || MASSIVE_API_KEY;
7163
7163
  validateMassiveApiKey$1(apiKey);
7164
- const baseUrl = `https://api.massive.com/v2/last/trade/${encodeURIComponent(symbol)}`;
7164
+ const baseUrl = `https://api.massive.com/v3/trades/${encodeURIComponent(symbol)}`;
7165
7165
  const params = new URLSearchParams({
7166
7166
  apiKey,
7167
+ limit: "1",
7168
+ sort: "timestamp",
7169
+ order: "desc",
7167
7170
  });
7168
7171
  return massiveLimit(async () => {
7169
7172
  try {
7170
7173
  const response = await fetchWithRetry(`${baseUrl}?${params.toString()}`, {}, 3, 1000);
7171
- const data = await response.json();
7172
- if (!MASSIVE_VALID_STATUSES.has(data.status) || !data.results) {
7173
- throw new Error(`Massive.com API error: ${data.status || "No results"} ${data.error || ""}`);
7174
+ const data = (await response.json());
7175
+ if ("message" in data) {
7176
+ throw new Error(`Massive.com API error: ${data.message}`);
7174
7177
  }
7175
- const { p: price, s: vol, t: timestamp } = data.results;
7178
+ if (!data.results ||
7179
+ !Array.isArray(data.results) ||
7180
+ data.results.length === 0) {
7181
+ throw new Error(`Massive.com API error: No trade results for ${symbol}`);
7182
+ }
7183
+ const latestTrade = data.results[0];
7184
+ const price = latestTrade.price;
7185
+ const vol = latestTrade.size;
7186
+ const timestamp = latestTrade.sip_timestamp;
7176
7187
  if (typeof price !== "number" ||
7177
7188
  typeof vol !== "number" ||
7178
7189
  typeof timestamp !== "number") {
@@ -7205,6 +7216,77 @@ const fetchLastTrade = async (symbol, options) => {
7205
7216
  }
7206
7217
  });
7207
7218
  };
7219
+ /**
7220
+ * Fetches the latest NBBO quote for a given stock ticker using the Massive v3 API.
7221
+ * Returns processed spread information including bid, ask, spread, and mid price.
7222
+ *
7223
+ * @param symbol - The stock ticker symbol.
7224
+ * @param options - Optional parameters including API key override.
7225
+ * @returns Processed quote data with spread metrics.
7226
+ */
7227
+ const fetchLastQuote = async (symbol, options) => {
7228
+ if (!options?.apiKey && !MASSIVE_API_KEY) {
7229
+ throw new Error("Massive API key is missing");
7230
+ }
7231
+ const apiKey = options?.apiKey || MASSIVE_API_KEY;
7232
+ validateMassiveApiKey$1(apiKey);
7233
+ const baseUrl = `https://api.massive.com/v3/quotes/${encodeURIComponent(symbol)}`;
7234
+ const params = new URLSearchParams({
7235
+ apiKey,
7236
+ limit: "1",
7237
+ sort: "timestamp",
7238
+ order: "desc",
7239
+ });
7240
+ return massiveLimit(async () => {
7241
+ try {
7242
+ const response = await fetchWithRetry(`${baseUrl}?${params.toString()}`, {}, 3, 1000);
7243
+ const data = (await response.json());
7244
+ if ("message" in data) {
7245
+ throw new Error(`Massive.com API error: ${data.message}`);
7246
+ }
7247
+ const results = data.results;
7248
+ if (!Array.isArray(results) || results.length === 0) {
7249
+ throw new Error(`Massive.com API error: No quote results for ${symbol}`);
7250
+ }
7251
+ const quote = results[0];
7252
+ if (typeof quote.bid_price !== "number" ||
7253
+ typeof quote.ask_price !== "number") {
7254
+ throw new Error("Invalid quote data received from Massive.com API");
7255
+ }
7256
+ const midPrice = (quote.bid_price + quote.ask_price) / 2;
7257
+ const spread = quote.ask_price - quote.bid_price;
7258
+ return {
7259
+ bid: quote.bid_price,
7260
+ ask: quote.ask_price,
7261
+ spread,
7262
+ spreadPercent: midPrice > 0 ? (spread / midPrice) * 100 : 0,
7263
+ midPrice,
7264
+ bidSize: quote.bid_size,
7265
+ askSize: quote.ask_size,
7266
+ time: new Date(Math.floor(quote.sip_timestamp / 1000000)),
7267
+ };
7268
+ }
7269
+ catch (error) {
7270
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
7271
+ const contextualMessage = `Error fetching last quote for ${symbol}`;
7272
+ getLogger().error(`${contextualMessage}: ${errorMessage}`, {
7273
+ symbol,
7274
+ errorType: error instanceof Error && error.message.includes("AUTH_ERROR")
7275
+ ? "AUTH_ERROR"
7276
+ : error instanceof Error && error.message.includes("RATE_LIMIT")
7277
+ ? "RATE_LIMIT"
7278
+ : error instanceof Error &&
7279
+ error.message.includes("NETWORK_ERROR")
7280
+ ? "NETWORK_ERROR"
7281
+ : "UNKNOWN",
7282
+ url: hideApiKeyFromurl(`${baseUrl}?${params.toString()}`),
7283
+ source: "MassiveAPI.fetchLastQuote",
7284
+ timestamp: new Date().toISOString(),
7285
+ });
7286
+ throw new Error(`${contextualMessage}: ${errorMessage}`);
7287
+ }
7288
+ });
7289
+ };
7208
7290
  // use Massive for all price data fetching
7209
7291
  /**
7210
7292
  * Fetches price data for a given stock ticker.
@@ -66228,22 +66310,25 @@ const MassiveTradesResponseSchema = objectType({
66228
66310
  results: arrayType(MassiveTradeSchema),
66229
66311
  });
66230
66312
  // ===== Last Trade Schemas =====
66231
- /** Schema for Massive last trade response */
66313
+ /** Schema for Massive last trade response (v3 format - returns array of trades) */
66232
66314
  const MassiveLastTradeResponseSchema = objectType({
66233
66315
  status: stringType(),
66234
66316
  request_id: stringType(),
66235
- results: objectType({
66236
- T: stringType(),
66237
- p: numberType(),
66238
- s: numberType(),
66239
- t: numberType(),
66240
- c: arrayType(numberType()).optional(),
66241
- e: numberType().optional(),
66242
- i: stringType().optional(),
66243
- q: numberType().optional(),
66244
- x: numberType().optional(),
66245
- z: numberType().optional(),
66246
- }),
66317
+ results: arrayType(objectType({
66318
+ conditions: arrayType(numberType()).optional(),
66319
+ correction: numberType().optional(),
66320
+ exchange: numberType(),
66321
+ id: stringType(),
66322
+ participant_timestamp: numberType(),
66323
+ price: numberType(),
66324
+ sequence_number: numberType(),
66325
+ sip_timestamp: numberType(),
66326
+ size: numberType(),
66327
+ tape: numberType().optional(),
66328
+ trf_id: numberType().optional(),
66329
+ trf_timestamp: numberType().optional(),
66330
+ }))
66331
+ .min(1),
66247
66332
  });
66248
66333
  // ===== Aggregates (Bars) Schemas =====
66249
66334
  /** Schema for Massive aggregates (bars) response */
@@ -67419,6 +67504,7 @@ const adaptic = {
67419
67504
  fetchTickerInfo: fetchTickerInfo,
67420
67505
  fetchGroupedDaily: fetchGroupedDaily,
67421
67506
  fetchLastTrade: fetchLastTrade,
67507
+ fetchLastQuote: fetchLastQuote,
67422
67508
  fetchTrades: fetchTrades,
67423
67509
  fetchPrices: fetchPrices,
67424
67510
  analyseMassivePriceData: analyseMassivePriceData,