tradeblocks-mcp 3.0.0-beta.1 → 3.0.0-beta.2

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.
@@ -1831,6 +1831,27 @@ async function fetchSymbolHistoryRows(ticker, from, to, assetClass, endpoint, in
1831
1831
  }
1832
1832
  return rows;
1833
1833
  }
1834
+ function isThetaWildcardServerError(err) {
1835
+ const message = err instanceof Error ? err.message : String(err);
1836
+ return message.startsWith("ThetaData 500 ");
1837
+ }
1838
+ async function listExpirationsForRoot(root, date, env = process.env) {
1839
+ const rows = await requestThetaArray(
1840
+ "/v3/option/list/contracts/quote",
1841
+ { symbol: root, date: compactDate(date), format: "json" },
1842
+ env
1843
+ );
1844
+ const expirations = /* @__PURE__ */ new Set();
1845
+ for (const row of rows) {
1846
+ const exp = String(row.expiration ?? "").trim();
1847
+ if (exp) expirations.add(exp);
1848
+ }
1849
+ return [...expirations].sort();
1850
+ }
1851
+ function bulkQuoteRootsForUnderlying(underlying) {
1852
+ const upper = underlying.toUpperCase();
1853
+ return upper === "SPX" ? ["SPX", "SPXW"] : [upper];
1854
+ }
1834
1855
  var ThetaDataProvider = class {
1835
1856
  name = "thetadata";
1836
1857
  capabilities() {
@@ -1898,13 +1919,16 @@ var ThetaDataProvider = class {
1898
1919
  * Yields rows in **chunks** (`BULK_YIELD_CHUNK`) rather than one-by-one —
1899
1920
  * per-row yield/await overhead dominated the runtime at 5M+ rows when tested.
1900
1921
  *
1901
- * Wildcard failures (500/471/571 mid-stream) surface as thrown errors the
1902
- * ingestor decides whether to fall back to per-ticker or abort.
1922
+ * Wildcard 500s (ThetaTerminal NPE on specific historical dates) trigger an
1923
+ * in-producer fallback: enumerate expirations via the contract-list endpoint,
1924
+ * then stream each expiration individually with `strike=*`. Other errors
1925
+ * (471/571/mid-stream failures) still surface as thrown errors for the
1926
+ * ingestor to handle.
1903
1927
  */
1904
1928
  async *fetchBulkQuotes(options) {
1905
- const { underlying, date } = options;
1929
+ const { underlying, date, onGroupComplete } = options;
1906
1930
  const upperUnderlying = underlying.toUpperCase();
1907
- const roots = upperUnderlying === "SPX" ? ["SPX", "SPXW"] : [upperUnderlying];
1931
+ const roots = bulkQuoteRootsForUnderlying(upperUnderlying);
1908
1932
  const rights = ["call", "put"];
1909
1933
  const groups = roots.flatMap((root) => rights.map((right) => ({ root, right })));
1910
1934
  const BULK_YIELD_CHUNK = 5e4;
@@ -1929,15 +1953,15 @@ var ThetaDataProvider = class {
1929
1953
  }
1930
1954
  };
1931
1955
  const producers = groups.map(async ({ root, right }) => {
1932
- const params = {
1956
+ const baseParams = {
1933
1957
  symbol: root,
1934
- expiration: "*",
1935
1958
  strike: "*",
1936
1959
  right: normalizeThetaBulkRight(right),
1937
1960
  interval: "1m",
1938
1961
  date: compactDate(date)
1939
1962
  };
1940
1963
  let buf = [];
1964
+ let errored = false;
1941
1965
  const flushLocal = async () => {
1942
1966
  while (queue.length >= QUEUE_CAP) {
1943
1967
  await new Promise((r) => {
@@ -1948,8 +1972,8 @@ var ThetaDataProvider = class {
1948
1972
  buf = [];
1949
1973
  wakeConsumer();
1950
1974
  };
1951
- try {
1952
- for await (const row of streamThetaNdjson("/v3/option/history/quote", params)) {
1975
+ const ingestFromParams = async (streamParams) => {
1976
+ for await (const row of streamThetaNdjson("/v3/option/history/quote", streamParams)) {
1953
1977
  const symbol = String(row.symbol ?? root).toUpperCase();
1954
1978
  const strike = toNumber(row.strike);
1955
1979
  if (strike == null) continue;
@@ -1966,11 +1990,29 @@ var ThetaDataProvider = class {
1966
1990
  buf.push({ ticker, timestamp: etTimestamp, bid, ask });
1967
1991
  if (buf.length >= BULK_YIELD_CHUNK) await flushLocal();
1968
1992
  }
1993
+ };
1994
+ try {
1995
+ try {
1996
+ await ingestFromParams({ ...baseParams, expiration: "*" });
1997
+ } catch (wildcardErr) {
1998
+ if (!isThetaWildcardServerError(wildcardErr)) throw wildcardErr;
1999
+ const expirations = await listExpirationsForRoot(root, date);
2000
+ for (const exp of expirations) {
2001
+ await ingestFromParams({ ...baseParams, expiration: compactDate(exp) });
2002
+ }
2003
+ }
1969
2004
  if (buf.length > 0) await flushLocal();
1970
2005
  } catch (e) {
2006
+ errored = true;
1971
2007
  if (!producerError) producerError = e instanceof Error ? e : new Error(String(e));
1972
2008
  } finally {
1973
2009
  producersLeft--;
2010
+ if (onGroupComplete) {
2011
+ try {
2012
+ onGroupComplete({ root, right, date, status: errored ? "error" : "ok" });
2013
+ } catch {
2014
+ }
2015
+ }
1974
2016
  wakeConsumer();
1975
2017
  }
1976
2018
  });
@@ -2234,4 +2276,4 @@ export {
2234
2276
  getProvider,
2235
2277
  _resetProvider
2236
2278
  };
2237
- //# sourceMappingURL=chunk-LYN3NRIP.js.map
2279
+ //# sourceMappingURL=chunk-JBD3XSOA.js.map